Repository: ecadlabs/taquito
Pull request #1422 (2022-02-28 20:27:57)

feat(localforger): add protocol hash to the constructor of LocalForger
In preparation for Ithaca support, where some operation schema has changed (ie endorsement), the protocol hash will be passed to the constructor of the LocalForger in order to select the right encoder, decoder.

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 added unit tests
- [ ] 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. 

## 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.__


github-actions[bot]: New packages have been deployed to the preview repository at https://npm.preview.tezostaquito.io/.

### Published packages:
```
npm i @taquito/remote-signer@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/rpc@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tezbridge-signer@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/contracts-library@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/taquito@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/utils@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/local-forging@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/http-utils@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/ledger-signer@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/signer@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tzip12@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/beacon-wallet@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/michelson-encoder@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tezbridge-wallet@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/michel-codec@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tzip16@11.2.0-49ef1b82--1422 --registry https://npm.preview.tezostaquito.io/
```
github-actions[bot]: A new deploy preview is available on Netlify at https://49ef1b82--tezostaquito.netlify.app
codecov-commenter: # [Codecov](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs) Report
> Merging [#1422](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs) (7d9c9af) into [master](https://codecov.io/gh/ecadlabs/taquito/commit/80afcaf01e457403e834a10379b7f6bbda906b1a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs) (80afcaf) will **increase** coverage by `0.03%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/ecadlabs/taquito/pull/1422/graphs/tree.svg?width=650&height=150&src=pr&token=wXiK0M3HfY&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs)](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs)

```diff
@@            Coverage Diff             @@
##           master    #1422      +/-   ##
==========================================
+ Coverage   79.35%   79.39%   +0.03%     
==========================================
  Files         188      188              
  Lines       10174    10187      +13     
  Branches     2312     2312              
==========================================
+ Hits         8074     8088      +14     
+ Misses       2091     2090       -1     
  Partials        9        9              
```


| [Impacted Files](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs) | Coverage Δ | |
|---|---|---|
| [packages/taquito-local-forging/src/constants.ts](https://codecov.io/gh/ecadlabs/taquito/pull/1422/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs#diff-cGFja2FnZXMvdGFxdWl0by1sb2NhbC1mb3JnaW5nL3NyYy9jb25zdGFudHMudHM=) | `100.00% <100.00%> (ø)` | |
| [...taquito-local-forging/src/taquito-local-forging.ts](https://codecov.io/gh/ecadlabs/taquito/pull/1422/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs#diff-cGFja2FnZXMvdGFxdWl0by1sb2NhbC1mb3JnaW5nL3NyYy90YXF1aXRvLWxvY2FsLWZvcmdpbmcudHM=) | `100.00% <100.00%> (ø)` | |
| [...kages/taquito-local-forging/src/michelson/codec.ts](https://codecov.io/gh/ecadlabs/taquito/pull/1422/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs#diff-cGFja2FnZXMvdGFxdWl0by1sb2NhbC1mb3JnaW5nL3NyYy9taWNoZWxzb24vY29kZWMudHM=) | `98.82% <0.00%> (+0.58%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs). Last update [80afcaf...7d9c9af](https://codecov.io/gh/ecadlabs/taquito/pull/1422?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ecadlabs).

github-actions[bot]: New packages have been deployed to the preview repository at https://npm.preview.tezostaquito.io/.

### Published packages:
```
npm i @taquito/remote-signer@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/rpc@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tezbridge-signer@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/contracts-library@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/taquito@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/utils@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/local-forging@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/http-utils@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/ledger-signer@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/signer@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tzip12@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/beacon-wallet@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/michelson-encoder@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tezbridge-wallet@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/michel-codec@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
npm i @taquito/tzip16@11.2.0-aad802b2--1422 --registry https://npm.preview.tezostaquito.io/
```
github-actions[bot]: A new deploy preview is available on Netlify at https://aad802b2--tezostaquito.netlify.app

Patch:
diff --git a/packages/taquito-local-forging/src/codec.ts b/packages/taquito-local-forging/src/codec.ts
index d1b114e31..2c9d83025 100644
--- a/packages/taquito-local-forging/src/codec.ts
+++ b/packages/taquito-local-forging/src/codec.ts
@@ -116,6 +116,26 @@ export const int32Decoder = (val: Uint8ArrayConsumer) => {
   return finalNum;
 };
 
+export const int16Encoder = (val: number | string): string => {
+  const num = parseInt(String(val), 10);
+  const byte = [];
+  for (let i = 0; i < 2; i++) {
+    const shiftBy = (2 - (i + 1)) * 8;
+    byte.push((num & (0xff << shiftBy)) >> shiftBy);
+  }
+  return Buffer.from(byte).toString('hex');
+};
+
+export const int16Decoder = (val: Uint8ArrayConsumer) => {
+  const num = val.consume(2);
+  let finalNum = 0;
+  for (let i = 0; i < num.length; i++) {
+    finalNum = finalNum | (num[i] << ((num.length - (i + 1)) * 8));
+  }
+
+  return finalNum;
+};
+
 export const boolDecoder = (val: Uint8ArrayConsumer): boolean => {
   const bool = val.consume(1);
   return bool[0] === 0xff;
diff --git a/packages/taquito-local-forging/src/constants.ts b/packages/taquito-local-forging/src/constants.ts
index e11425039..955428edc 100644
--- a/packages/taquito-local-forging/src/constants.ts
+++ b/packages/taquito-local-forging/src/constants.ts
@@ -22,6 +22,7 @@ export enum CODEC {
   PROPOSAL = 'proposal',
   PROPOSAL_ARR = 'proposalArr',
   INT32 = 'int32',
+  INT16 = 'int16',
   PARAMETERS = 'parameters',
   ADDRESS = 'address',
   OPERATION = 'operation',
@@ -238,3 +239,17 @@ export const entrypointMappingReverse = (() => {
   });
   return result;
 })();
+
+export enum ProtocolsHash {
+  Pt24m4xi = 'Pt24m4xiPbLDhVgVfABUjirbmda3yohdN82Sp9FeuAXJ4eV9otd',
+  PsBABY5H = 'PsBABY5HQTSkA4297zNHfsZNKtxULfL18y95qb3m53QJiXGmrbU',
+  PsBabyM1 = 'PsBabyM1eUXZseaJdmXFApDSBqj8YBfwELoxZHHW77EMcAbbwAS',
+  PsCARTHA = 'PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb',
+  PsDELPH1 = 'PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo',
+  PtEdo2Zk = 'PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA',
+  PsFLorena = 'PsFLorenaUUuikDWvMDr6fGBRG8kt3e3D3fHoXK1j1BFRxeSH4i',
+  PtGRANADs = 'PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV',
+  PtHangz2 = 'PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx',
+  PsiThaCa = 'PsiThaCaT47Zboaw71QWScM8sXeMM7bbQFncK9FLqYc6EKdpjVP',
+  Psithaca2 = 'Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A',
+}
diff --git a/packages/taquito-local-forging/src/proto12-ithaca/codec.ts b/packages/taquito-local-forging/src/proto12-ithaca/codec.ts
new file mode 100644
index 000000000..25e326a37
--- /dev/null
+++ b/packages/taquito-local-forging/src/proto12-ithaca/codec.ts
@@ -0,0 +1,5 @@
+import { Prefix } from '@taquito/utils';
+import { prefixDecoder, prefixEncoder } from '../codec';
+
+export const blockPayloadHashEncoder = prefixEncoder(Prefix.VH);
+export const blockPayloadHashDecoder = prefixDecoder(Prefix.VH);
diff --git a/packages/taquito-local-forging/src/proto12-ithaca/constants.ts b/packages/taquito-local-forging/src/proto12-ithaca/constants.ts
new file mode 100644
index 000000000..0ce915f14
--- /dev/null
+++ b/packages/taquito-local-forging/src/proto12-ithaca/constants.ts
@@ -0,0 +1,22 @@
+import { kindMapping } from '../constants';
+import { pad } from '../utils';
+
+export const enum CODEC_PROT0_12 {
+  BLOCK_PAYLOAD_HASH = 'blockPayloadHash',
+}
+
+const { [0x00]: ommited, ...kindMappingNoEndorsement } = kindMapping;
+
+export const kindMappingProto12: { [key: number]: string } = {
+  ...kindMappingNoEndorsement,
+  0x15: 'endorsement',
+};
+
+export const kindMappingReverseProto12 = (() => {
+  const result: { [key: string]: string } = {};
+  Object.keys(kindMappingProto12).forEach((key: number | string) => {
+    const keyNum = typeof key === 'string' ? parseInt(key, 10) : key;
+    result[kindMappingProto12[keyNum]] = pad(keyNum, 2);
+  });
+  return result;
+})();
diff --git a/packages/taquito-local-forging/src/proto12-ithaca/decoder.ts b/packages/taquito-local-forging/src/proto12-ithaca/decoder.ts
new file mode 100644
index 000000000..b24f969fe
--- /dev/null
+++ b/packages/taquito-local-forging/src/proto12-ithaca/decoder.ts
@@ -0,0 +1,49 @@
+import { int16Decoder } from '../codec';
+import { CODEC } from '../constants';
+import { Decoder, decoders } from '../decoder';
+import {
+  schemaDecoder,
+  ActivationSchema,
+  DelegationSchema,
+  TransactionSchema,
+  OriginationSchema,
+  BallotSchema,
+  SeedNonceRevelationSchema,
+  ProposalsSchema,
+  RevealSchema,
+  RegisterGlobalConstantSchema,
+  ManagerOperationSchema,
+} from '../schema/operation';
+import { Uint8ArrayConsumer } from '../uint8array-consumer';
+import { blockPayloadHashDecoder } from './codec';
+import { CODEC_PROT0_12 } from './constants';
+import { EndorsementSchemaProto12, operationDecoderProto12 } from './schema';
+
+export const decodersProto12: { [key: string]: Decoder } = {
+  ...decoders,
+  [CODEC.INT16]: int16Decoder,
+  [CODEC_PROT0_12.BLOCK_PAYLOAD_HASH]: blockPayloadHashDecoder,
+};
+
+decodersProto12[CODEC.OPERATION] = operationDecoderProto12(decodersProto12);
+decodersProto12[CODEC.OP_ACTIVATE_ACCOUNT] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(ActivationSchema)(val);
+decodersProto12[CODEC.OP_DELEGATION] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(DelegationSchema)(val);
+decodersProto12[CODEC.OP_TRANSACTION] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(TransactionSchema)(val);
+decodersProto12[CODEC.OP_ORIGINATION] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(OriginationSchema)(val);
+decodersProto12[CODEC.OP_BALLOT] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(BallotSchema)(val);
+decodersProto12[CODEC.OP_ENDORSEMENT] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(EndorsementSchemaProto12)(val);
+decodersProto12[CODEC.OP_SEED_NONCE_REVELATION] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(SeedNonceRevelationSchema)(val);
+decodersProto12[CODEC.OP_PROPOSALS] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(ProposalsSchema)(val);
+decodersProto12[CODEC.OP_REVEAL] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(RevealSchema)(val);
+decodersProto12[CODEC.OP_REGISTER_GLOBAL_CONSTANT] = (val: Uint8ArrayConsumer) =>
+  schemaDecoder(decodersProto12)(RegisterGlobalConstantSchema)(val);
+decodersProto12[CODEC.MANAGER] = schemaDecoder(decodersProto12)(ManagerOperationSchema);
diff --git a/packages/taquito-local-forging/src/proto12-ithaca/encoder.ts b/packages/taquito-local-forging/src/proto12-ithaca/encoder.ts
new file mode 100644
index 000000000..2fb93cff9
--- /dev/null
+++ b/packages/taquito-local-forging/src/proto12-ithaca/encoder.ts
@@ -0,0 +1,46 @@
+import { int16Encoder } from '../codec';
+import { CODEC } from '../constants';
+import { Encoder, encoders } from '../encoder';
+import {
+  schemaEncoder,
+  ActivationSchema,
+  DelegationSchema,
+  TransactionSchema,
+  OriginationSchema,
+  BallotSchema,
+  SeedNonceRevelationSchema,
+  ProposalsSchema,
+  RevealSchema,
+  RegisterGlobalConstantSchema,
+  ManagerOperationSchema,
+} from '../schema/operation';
+import { blockPayloadHashEncoder } from './codec';
+import { CODEC_PROT0_12 } from './constants';
+import { EndorsementSchemaProto12, operationEncoderProto12 } from './schema';
+
+export const encodersProto12: { [key: string]: Encoder<any> } = {
+  ...encoders,
+  [CODEC.INT16]: int16Encoder,
+  [CODEC_PROT0_12.BLOCK_PAYLOAD_HASH]: blockPayloadHashEncoder,
+};
+
+encodersProto12[CODEC.OPERATION] = operationEncoderProto12(encodersProto12);
+encodersProto12[CODEC.OP_ACTIVATE_ACCOUNT] = (val: any) =>
+  schemaEncoder(encodersProto12)(ActivationSchema)(val);
+encodersProto12[CODEC.OP_DELEGATION] = (val: any) =>
+  schemaEncoder(encodersProto12)(DelegationSchema)(val);
+encodersProto12[CODEC.OP_TRANSACTION] = (val: any) =>
+  schemaEncoder(encodersProto12)(TransactionSchema)(val);
+encodersProto12[CODEC.OP_ORIGINATION] = (val: any) =>
+  schemaEncoder(encodersProto12)(OriginationSchema)(val);
+encodersProto12[CODEC.OP_BALLOT] = (val: any) => schemaEncoder(encodersProto12)(BallotSchema)(val);
+encodersProto12[CODEC.OP_ENDORSEMENT] = (val: any) =>
+  schemaEncoder(encodersProto12)(EndorsementSchemaProto12)(val);
+encodersProto12[CODEC.OP_SEED_NONCE_REVELATION] = (val: any) =>
+  schemaEncoder(encodersProto12)(SeedNonceRevelationSchema)(val);
+encodersProto12[CODEC.OP_PROPOSALS] = (val: any) =>
+  schemaEncoder(encodersProto12)(ProposalsSchema)(val);
+encodersProto12[CODEC.OP_REVEAL] = (val: any) => schemaEncoder(encodersProto12)(RevealSchema)(val);
+encodersProto12[CODEC.OP_REGISTER_GLOBAL_CONSTANT] = (val: any) =>
+  schemaEncoder(encodersProto12)(RegisterGlobalConstantSchema)(val);
+encodersProto12[CODEC.MANAGER] = schemaEncoder(encodersProto12)(ManagerOperationSchema);
diff --git a/packages/taquito-local-forging/src/proto12-ithaca/schema.ts b/packages/taquito-local-forging/src/proto12-ithaca/schema.ts
new file mode 100644
index 000000000..376f691f5
--- /dev/null
+++ b/packages/taquito-local-forging/src/proto12-ithaca/schema.ts
@@ -0,0 +1,40 @@
+import { Decoder } from '../decoder';
+import { Uint8ArrayConsumer } from '../uint8array-consumer';
+import { kindMappingProto12, kindMappingReverseProto12 } from './constants';
+
+export const EndorsementSchemaProto12 = {
+  slot: 'int16',
+  level: 'int32',
+  round: 'int32',
+  block_payload_hash: 'blockPayloadHash',
+};
+
+export const operationEncoderProto12 =
+  (encoders: { [key: string]: (val: object) => string }) => (operation: { kind: string }) => {
+    if (!(operation.kind in encoders) || !(operation.kind in kindMappingReverseProto12)) {
+      throw new Error(`Unsupported operation kind: ${operation.kind}`);
+    }
+
+    return kindMappingReverseProto12[operation.kind] + encoders[operation.kind](operation);
+  };
+
+export const operationDecoderProto12 =
+  (decoders: { [key: string]: Decoder }) => (value: Uint8ArrayConsumer) => {
+    const op = value.consume(1);
+
+    const operationName = kindMappingProto12[op[0]];
+    const decodedObj = decoders[operationName](value);
+
+    if (typeof decodedObj !== 'object') {
+      throw new Error('Decoded invalid operation');
+    }
+
+    if (operationName) {
+      return {
+        kind: operationName,
+        ...decodedObj,
+      };
+    } else {
+      throw new Error(`Unsupported operation ${op[0]}`);
+    }
+  };
diff --git a/packages/taquito-local-forging/src/schema/operation.ts b/packages/taquito-local-forging/src/schema/operation.ts
index fc5bd059a..644df091c 100644
--- a/packages/taquito-local-forging/src/schema/operation.ts
+++ b/packages/taquito-local-forging/src/schema/operation.ts
@@ -167,7 +167,7 @@ export const schemaDecoder =
 
         const result = decoder(value);
 
-        if (result) {
+        if (typeof result !== 'undefined') {
           return {
             ...prev,
             [key]: result,
diff --git a/packages/taquito-local-forging/src/taquito-local-forging.ts b/packages/taquito-local-forging/src/taquito-local-forging.ts
index c5f766d1a..d9312829d 100644
--- a/packages/taquito-local-forging/src/taquito-local-forging.ts
+++ b/packages/taquito-local-forging/src/taquito-local-forging.ts
@@ -4,31 +4,44 @@
  */
 
 import { ForgeParams, Forger } from './interface';
-import { CODEC } from './constants';
+import { CODEC, ProtocolsHash } from './constants';
 import { decoders } from './decoder';
 import { encoders } from './encoder';
 import { Uint8ArrayConsumer } from './uint8array-consumer';
+import { decodersProto12 } from './proto12-ithaca/decoder';
+import { encodersProto12 } from './proto12-ithaca/encoder';
 
-export { CODEC } from './constants';
+export { CODEC, ProtocolsHash } from './constants';
 export * from './decoder';
 export * from './encoder';
 export * from './uint8array-consumer';
 export * from './interface';
+export { VERSION } from './version';
 
-export function getCodec(codec: CODEC) {
-  return {
-    encoder: encoders[codec],
-    decoder: (hex: string) => {
-      const consumer = Uint8ArrayConsumer.fromHexString(hex);
-      return decoders[codec](consumer) as any;
-    },
-  };
+export function getCodec(codec: CODEC, proto: ProtocolsHash) {
+  if (proto === ProtocolsHash.Psithaca2) {
+    return {
+      encoder: encodersProto12[codec],
+      decoder: (hex: string) => {
+        const consumer = Uint8ArrayConsumer.fromHexString(hex);
+        return decodersProto12[codec](consumer) as any;
+      },
+    };
+  } else {
+    return {
+      encoder: encoders[codec],
+      decoder: (hex: string) => {
+        const consumer = Uint8ArrayConsumer.fromHexString(hex);
+        return decoders[codec](consumer) as any;
+      },
+    };
+  }
 }
 
-export { VERSION } from './version';
-
 export class LocalForger implements Forger {
-  private codec = getCodec(CODEC.MANAGER);
+  constructor(public readonly protocolHash = ProtocolsHash.Psithaca2) {}
+
+  private codec = getCodec(CODEC.MANAGER, this.protocolHash);
 
   forge(params: ForgeParams): Promise<string> {
     return Promise.resolve(this.codec.encoder(params));
diff --git a/packages/taquito-utils/src/constants.ts b/packages/taquito-utils/src/constants.ts
index 13b9441e7..09a8ef463 100644
--- a/packages/taquito-utils/src/constants.ts
+++ b/packages/taquito-utils/src/constants.ts
@@ -35,6 +35,8 @@ export enum Prefix {
 
   EXPR = 'expr',
   TZ = 'TZ',
+
+  VH = 'vh', // block_payload_hash
 }
 
 export const prefix = {
@@ -75,6 +77,8 @@ export const prefix = {
   [Prefix.EXPR]: new Uint8Array([13, 44, 64, 27]),
   // Legacy prefix
   [Prefix.TZ]: new Uint8Array([2, 90, 121]),
+
+  [Prefix.VH]: new Uint8Array([1, 106, 242]),
 };
 
 export const prefixLength: { [key: string]: number } = {
@@ -93,5 +97,6 @@ export const prefixLength: { [key: string]: number } = {
   [Prefix.NET]: 4,
   [Prefix.B]: 32,
   [Prefix.P]: 32,
-  [Prefix.O]: 32
+  [Prefix.O]: 32,
+  [Prefix.VH]: 32,
 };
diff --git a/packages/taquito/src/context.ts b/packages/taquito/src/context.ts
index c1795b1a6..164ca4cfd 100644
--- a/packages/taquito/src/context.ts
+++ b/packages/taquito/src/context.ts
@@ -1,7 +1,6 @@
 import { RpcClient, RpcClientInterface } from '@taquito/rpc';
 import { Protocols } from './constants';
 import { Forger } from '@taquito/local-forging';
-import { RpcForger } from './forger/rpc-forger';
 import { Injector } from './injector/interface';
 import { RpcInjector } from './injector/rpc-injector';
 import { Signer } from './signer/interface';
@@ -24,6 +23,7 @@ import { TzReadProvider } from './read-provider/interface';
 import { RpcReadAdapter } from './read-provider/rpc-read-adapter';
 import { SubscribeProvider } from './subscribe/interface';
 import { PollingSubscribeProvider } from './subscribe/polling-subcribe-provider';
+import { TaquitoLocalForger } from './forger/taquito-local-forger';
 
 export interface TaquitoProvider<T, K extends Array<any>> {
   new (context: Context, ...rest: K): T;
@@ -81,7 +81,7 @@ export class Context {
     } else {
       this._rpcClient = this._rpc;
     }
-    this._forger = forger ? forger : new RpcForger(this);
+    this._forger = forger ? forger : new TaquitoLocalForger(this);
     this._injector = injector ? injector : new RpcInjector(this);
     this.operationFactory = new OperationFactory(this);
     this._walletProvider = wallet ? wallet : new LegacyWalletProvider(this);
diff --git a/packages/taquito/src/forger/taquito-local-forger.ts b/packages/taquito/src/forger/taquito-local-forger.ts
new file mode 100644
index 000000000..fecc842ff
--- /dev/null
+++ b/packages/taquito/src/forger/taquito-local-forger.ts
@@ -0,0 +1,26 @@
+import {
+  LocalForger,
+  Forger,
+  ForgeParams,
+  ForgeResponse,
+  ProtocolsHash,
+} from '@taquito/local-forging';
+import { Protocols } from '../constants';
+import { Context } from '../context';
+
+export class TaquitoLocalForger implements Forger {
+  constructor(private context: Context) {}
+
+  private async getNextProto(): Promise<ProtocolsHash> {
+    if (!this.context.proto) {
+      const nextProto = await this.context.readProvider.getNextProtocol('head');
+      this.context.proto = nextProto as Protocols;
+    }
+    return this.context.proto as unknown as ProtocolsHash;
+  }
+
+  async forge({ branch, contents }: ForgeParams): Promise<ForgeResponse> {
+    const forger = new LocalForger(await this.getNextProto());
+    return forger.forge({ branch, contents });
+  }
+}
diff --git a/packages/taquito/src/parser/michel-codec-parser.ts b/packages/taquito/src/parser/michel-codec-parser.ts
index c7270e1cc..0595a4429 100644
--- a/packages/taquito/src/parser/michel-codec-parser.ts
+++ b/packages/taquito/src/parser/michel-codec-parser.ts
@@ -5,13 +5,17 @@ import { OriginateParams } from '../operations/types';
 import { InvalidInitParameter, InvalidCodeParameter } from '../contract/errors';
 import { Schema } from '@taquito/michelson-encoder';
 import { MichelsonV1Expression } from '@taquito/rpc';
+import { Protocols } from '../constants';
 
 export class MichelCodecParser implements ParserProvider {
   constructor(private context: Context) {}
 
   private async getNextProto(): Promise<ProtocolID> {
-    const next_protocol = await this.context.readProvider.getNextProtocol('head');
-    return next_protocol as ProtocolID;
+    if (!this.context.proto) {
+      const nextProto = await this.context.readProvider.getNextProtocol('head');
+      this.context.proto = nextProto as Protocols;
+    }
+    return this.context.proto as ProtocolID;
   }
 
   async parseScript(src: string): Promise<Expr[] | null> {
diff --git a/packages/taquito/src/taquito.ts b/packages/taquito/src/taquito.ts
index ed3561d07..99fb48713 100644
--- a/packages/taquito/src/taquito.ts
+++ b/packages/taquito/src/taquito.ts
@@ -4,7 +4,7 @@
  */
 
 import { RpcClient, RpcClientInterface } from '@taquito/rpc';
-import { LocalForger, Forger } from '@taquito/local-forging';
+import { Forger } from '@taquito/local-forging';
 import { RPCBatchProvider } from './batch/rpc-batch-provider';
 import { Protocols } from './constants';
 import { ConfigConfirmation, Context, TaquitoProvider } from './context';
@@ -25,6 +25,7 @@ import { TzProvider } from './tz/interface';
 import { VERSION } from './version';
 import { LegacyWalletProvider, Wallet, WalletProvider } from './wallet';
 import { OperationFactory } from './wallet/operation-factory';
+import { TaquitoLocalForger } from './forger/taquito-local-forger';
 
 export { MichelsonMap, UnitValue } from '@taquito/michelson-encoder';
 export { Forger, ForgeParams, ForgeResponse } from '@taquito/local-forging';
@@ -182,10 +183,11 @@ export class TezosToolkit {
 
   /**
    * @description Sets forger provider on the Tezos Taquito instance
+   * The `LocalForger` from `@taquito/local-forging` is set by default.
    *
    * @param options forger to use to interact with the Tezos network
    *
-   * @example Tezos.setForgerProvider(localForger)
+   * @example Tezos.setForgerProvider(this.getFactory(RpcForger)())
    *
    */
   setForgerProvider(forger?: SetProviderOptions['forger']) {
@@ -193,7 +195,7 @@ export class TezosToolkit {
       this._options.forger = forger;
       this._context.forger = forger;
     } else if (this._options.forger === undefined) {
-      const f = new LocalForger();
+      const f = this.getFactory(TaquitoLocalForger)();
       this._options.forger = f;
       this._context.forger = f;
     }
@@ -250,9 +252,14 @@ export class TezosToolkit {
    *
    */
   setPackerProvider(packer?: SetProviderOptions['packer']) {
-    const p = typeof packer === 'undefined' ? this.getFactory(RpcPacker)() : packer;
-    this._options.packer = p;
-    this._context.packer = p;
+    if (!this._options.packer && typeof packer === 'undefined') {
+      const p = this.getFactory(RpcPacker)();
+      this._context.packer = p;
+      this._options.packer = p;
+    } else if (typeof packer !== 'undefined') {
+      this._context.packer = packer;
+      this._options.packer = packer;
+    }
   }
 
   /**
@@ -274,12 +281,14 @@ export class TezosToolkit {
   setGlobalConstantsProvider(
     globalConstantsProvider?: SetProviderOptions['globalConstantsProvider']
   ) {
-    const g =
-      typeof globalConstantsProvider === 'undefined'
-        ? new NoopGlobalConstantsProvider()
-        : globalConstantsProvider;
-    this._options.globalConstantsProvider = g;
-    this._context.globalConstantsProvider = g;
+    if (!this._options.globalConstantsProvider && typeof globalConstantsProvider === 'undefined') {
+      const g = new NoopGlobalConstantsProvider();
+      this._context.globalConstantsProvider = g;
+      this._options.globalConstantsProvider = g;
+    } else if (typeof globalConstantsProvider !== 'undefined') {
+      this._context.globalConstantsProvider = globalConstantsProvider;
+      this._options.globalConstantsProvider = globalConstantsProvider;
+    }
   }
 
   /**


Test patch:
diff --git a/integration-tests/local-forging.spec.ts b/integration-tests/local-forging.spec.ts
index 1328a2902..b371ae5c1 100644
--- a/integration-tests/local-forging.spec.ts
+++ b/integration-tests/local-forging.spec.ts
@@ -1,34 +1,46 @@
 import { CONFIGS } from "./config";
-import { commonCases, hangzhouCases, ithacaCases } from '../packages/taquito-local-forging/test/allTestsCases';
-import { localForger } from '@taquito/local-forging'
+import { commonCases, hangzhouCases, ithacaCases, priorIthacaCases } from '../packages/taquito-local-forging/test/allTestsCases';
+import { LocalForger, ProtocolsHash } from '@taquito/local-forging'
 import { Protocols, TezosToolkit } from "@taquito/taquito";
 
 CONFIGS().forEach(({ rpc, protocol }) => {
     const Tezos = new TezosToolkit(rpc);
-    const hangzhounetOrHigher = (protocol === Protocols.PtHangz2 || protocol === Protocols.Psithaca2) ? test : test.skip;
+    const hangzhounet = (protocol === Protocols.PtHangz2) ? test : test.skip;
     const ithacanet = (protocol === Protocols.Psithaca2) ? test : test.skip;
 
     describe(`Test local forger: ${rpc}`, () => {
 
+        // all protocols
         commonCases.forEach(({ name, operation, expected }) => {
 
             it(`Should give the same result as when forging with the rpc: ${name} (${rpc})`, async done => {
-                if (protocol === Protocols.Psithaca2 && name === 'Endorsement') {
-                    // skip
-                    console.log('Temporarily skip endorsement forging for Ithacanet')
-                } else {
-                    const result = await localForger.forge(operation);
-                    const rpcResult = await Tezos.rpc.forgeOperations(operation);
-                    expect(result).toEqual(rpcResult);
-                    expect(await localForger.parse(result)).toEqual(expected || operation);
-                }
+                const localForger = new LocalForger(protocol as unknown as ProtocolsHash);
+
+                const result = await localForger.forge(operation);
+                const rpcResult = await Tezos.rpc.forgeOperations(operation);
+                expect(result).toEqual(rpcResult);
+                expect(await localForger.parse(result)).toEqual(expected || operation);
+
                 done();
             });
         });
 
+        // all protocols since hangzhounet
         hangzhouCases.forEach(({ name, operation, expected }) => {
+            const localForger = new LocalForger(ProtocolsHash.PtHangz2);
+            it(`Should give the same result as when forging with the rpc: ${name} (${rpc})`, async done => {
+                const result = await localForger.forge(operation);
+                const rpcResult = await Tezos.rpc.forgeOperations(operation);
+                expect(result).toEqual(rpcResult);
+                expect(await localForger.parse(result)).toEqual(expected || operation);
+                done();
+            });
+        });
 
-            hangzhounetOrHigher(`Should give the same result as when forging with the rpc: ${name} (${rpc})`, async done => {
+        // all protocols except Ithaca
+        priorIthacaCases.forEach(({ name, operation, expected }) => {
+            const localForger = new LocalForger(ProtocolsHash.PtHangz2);
+            hangzhounet(`Should give the same result as when forging with the rpc: ${name} (${rpc})`, async done => {
                 const result = await localForger.forge(operation);
                 const rpcResult = await Tezos.rpc.forgeOperations(operation);
                 expect(result).toEqual(rpcResult);
@@ -37,8 +49,10 @@ CONFIGS().forEach(({ rpc, protocol }) => {
             });
         });
 
-        ithacaCases.forEach(({ name, operation, expected }) => {
 
+        // only ithaca
+        ithacaCases.forEach(({ name, operation, expected }) => {
+            const localForger = new LocalForger(ProtocolsHash.Psithaca2);
             ithacanet(`Should give the same result as when forging with the rpc: ${name} (${rpc})`, async done => {
                 const result = await localForger.forge(operation);
                 const rpcResult = await Tezos.rpc.forgeOperations(operation);
diff --git a/packages/taquito-local-forging/test/allTestsCases.ts b/packages/taquito-local-forging/test/allTestsCases.ts
index 979891096..8e2d1275e 100644
--- a/packages/taquito-local-forging/test/allTestsCases.ts
+++ b/packages/taquito-local-forging/test/allTestsCases.ts
@@ -99,18 +99,6 @@ export const commonCases: TestCase[] = [
       ],
     },
   },
-  {
-    name: 'Endorsement',
-    operation: {
-      branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX',
-      contents: [
-        {
-          kind: 'endorsement',
-          level: 300,
-        },
-      ],
-    },
-  },
   {
     name: 'Seed nonce revelation',
     operation: {
@@ -943,6 +931,21 @@ export const hangzhouCases: TestCase[] = [
   },
 ];
 
+export const priorIthacaCases: TestCase[] = [
+  {
+    name: 'Endorsement',
+    operation: {
+      branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX',
+      contents: [
+        {
+          kind: 'endorsement',
+          level: 300,
+        },
+      ],
+    },
+  },
+];
+
 export const ithacaCases: TestCase[] = [
   // In `opMapping` from the file `constants.ts`, the operations and types starting at `sub_mutez` were added in the ithaca protocol
   ...extractOp(147, 147).map((op) => {
@@ -968,6 +971,21 @@ export const ithacaCases: TestCase[] = [
       },
     };
   }),
+  {
+    name: 'Endorsement',
+    operation: {
+      branch: 'BLzyjjHKEKMULtvkpSHxuZxx6ei6fpntH2BTkYZiLgs8zLVstvX',
+      contents: [
+        {
+          kind: 'endorsement',
+          slot: 0,
+          level: 66299,
+          round: 5,
+          block_payload_hash: 'vh3FEkypvxUYLwjGYd2Sme7aWyfX8npDsqxcL6imVpBWnAZeNn2n',
+        },
+      ],
+    },
+  },
   {
     name: `Origination of a contract that contains the instructions SUB_MUTEZ`,
     operation: {
diff --git a/packages/taquito-local-forging/test/taquito-local-forging.spec.ts b/packages/taquito-local-forging/test/taquito-local-forging.spec.ts
index e75d4c5bf..fa056029c 100644
--- a/packages/taquito-local-forging/test/taquito-local-forging.spec.ts
+++ b/packages/taquito-local-forging/test/taquito-local-forging.spec.ts
@@ -1,26 +1,48 @@
-import { localForger } from '../src/taquito-local-forging';
-import { commonCases, hangzhouCases, ithacaCases } from './allTestsCases';
+import { LocalForger, ProtocolsHash } from '../src/taquito-local-forging';
+import { commonCases, hangzhouCases, ithacaCases, priorIthacaCases } from './allTestsCases';
 
-commonCases.forEach(({ name, operation, expected }) => {
-  test(`Common test: ${name}`, async (done) => {
-    const result = await localForger.forge(operation);
-    expect(await localForger.parse(result)).toEqual(expected || operation);
-    done();
+describe('Forge and parse operations hangzhou', () => {
+  const localForger = new LocalForger(ProtocolsHash.PtHangz2);
+  commonCases.forEach(({ name, operation, expected }) => {
+    test(`Common test: ${name}`, async (done) => {
+      const result = await localForger.forge(operation);
+      expect(await localForger.parse(result)).toEqual(expected || operation);
+      done();
+    });
+  });
+
+  priorIthacaCases.forEach(({ name, operation, expected }) => {
+    test(`Hangzhou test: ${name}`, async (done) => {
+      const result = await localForger.forge(operation);
+      expect(await localForger.parse(result)).toEqual(expected || operation);
+      done();
+    });
   });
-});
 
-hangzhouCases.forEach(({ name, operation, expected }) => {
-  test(`Hangzhou test: ${name}`, async (done) => {
-    const result = await localForger.forge(operation);
-    expect(await localForger.parse(result)).toEqual(expected || operation);
-    done();
+  hangzhouCases.forEach(({ name, operation, expected }) => {
+    test(`Hangzhou test: ${name}`, async (done) => {
+      const result = await localForger.forge(operation);
+      expect(await localForger.parse(result)).toEqual(expected || operation);
+      done();
+    });
   });
 });
 
-ithacaCases.forEach(({ name, operation, expected }) => {
-  test(`Ithaca test: ${name}`, async (done) => {
-    const result = await localForger.forge(operation);
-    expect(await localForger.parse(result)).toEqual(expected || operation);
-    done();
+describe('Forge and parse operations ithaca', () => {
+  const localForger = new LocalForger(ProtocolsHash.Psithaca2);
+  commonCases.forEach(({ name, operation, expected }) => {
+    test(`Common test: ${name}`, async (done) => {
+      const result = await localForger.forge(operation);
+      expect(await localForger.parse(result)).toEqual(expected || operation);
+      done();
+    });
+  });
+
+  ithacaCases.forEach(({ name, operation, expected }) => {
+    test(`Ithaca test: ${name}`, async (done) => {
+      const result = await localForger.forge(operation);
+      expect(await localForger.parse(result)).toEqual(expected || operation);
+      done();
+    });
   });
 });
diff --git a/packages/taquito/test/batch/rpc-batch-provider.spec.ts b/packages/taquito/test/batch/rpc-batch-provider.spec.ts
index 4f3bc2922..ea9563ec3 100644
--- a/packages/taquito/test/batch/rpc-batch-provider.spec.ts
+++ b/packages/taquito/test/batch/rpc-batch-provider.spec.ts
@@ -7,6 +7,8 @@ import { OpKind, ParamsWithKind } from '../../src/operations/types';
  * OperationBatch test
  */
 describe('OperationBatch test', () => {
+  let context: Context;
+  let operationBatch: OperationBatch;
   let mockRpcClient: {
     getScript: jest.Mock<any, any>;
     getStorage: jest.Mock<any, any>;
@@ -18,7 +20,6 @@ describe('OperationBatch test', () => {
     getBlock: jest.Mock<any, any>;
     getContract: jest.Mock<any, any>;
     getBlockMetadata: jest.Mock<any, any>;
-    forgeOperations: jest.Mock<any, any>;
     injectOperation: jest.Mock<any, any>;
     packData: jest.Mock<any, any>;
     preapplyOperations: jest.Mock<any, any>;
@@ -43,6 +44,10 @@ describe('OperationBatch test', () => {
     registerGlobalConstant: jest.Mock<any, any>;
   };
 
+  let mockForger: {
+    forge: jest.Mock<any, any>;
+  };
+
   beforeEach(() => {
     mockRpcClient = {
       getBigMapExpr: jest.fn(),
@@ -55,7 +60,6 @@ describe('OperationBatch test', () => {
       getBlockHeader: jest.fn(),
       getBlockMetadata: jest.fn(),
       getContract: jest.fn(),
-      forgeOperations: jest.fn(),
       injectOperation: jest.fn(),
       packData: jest.fn(),
       preapplyOperations: jest.fn(),
@@ -80,6 +84,10 @@ describe('OperationBatch test', () => {
       registerGlobalConstant: jest.fn(),
     };
 
+    mockForger = {
+      forge: jest.fn(),
+    };
+
     // Required for operations confirmation polling
     mockRpcClient.getBlock.mockResolvedValue({
       operations: [[], [], [], []],
@@ -101,6 +109,10 @@ describe('OperationBatch test', () => {
     mockRpcClient.injectOperation.mockResolvedValue(
       'onwtjK2Q32ndjF9zbEPPtmifdBq5qB59wjMP2oCH22mARjyKnGP'
     );
+
+    context = new Context(mockRpcClient as any, mockSigner as any);
+    context.forger = mockForger;
+    operationBatch = new OperationBatch(context, mockEstimate as any);
   });
 
   describe('withRegisterGlobalConstant', () => {
@@ -108,11 +120,6 @@ describe('OperationBatch test', () => {
       const estimate = new Estimate(1230000, 93, 142, 250);
       mockEstimate.batch.mockResolvedValue([estimate]);
 
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const batchOp = await operationBatch
         .withRegisterGlobalConstant({ value: { int: '2' } })
         .send();
@@ -143,11 +150,6 @@ describe('OperationBatch test', () => {
       const estimate = new Estimate(1230000, 93, 142, 250);
       mockEstimate.batch.mockResolvedValue([estimate]);
 
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const batchOp = await operationBatch
         .withRegisterGlobalConstant({
           value: { int: '2' },
@@ -186,11 +188,6 @@ describe('OperationBatch test', () => {
       const estimate = new Estimate(1230000, 93, 142, 250);
       mockEstimate.batch.mockResolvedValue([estimateReveal, estimate]);
 
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const batchOp = await operationBatch
         .withRegisterGlobalConstant({ value: { int: '2' } })
         .send();
@@ -232,11 +229,6 @@ describe('OperationBatch test', () => {
       const estimate = new Estimate(1230000, 93, 142, 250);
       mockEstimate.batch.mockResolvedValue([estimate]);
 
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const opToBatch: ParamsWithKind[] = [
         {
           kind: OpKind.REGISTER_GLOBAL_CONSTANT,
@@ -269,11 +261,6 @@ describe('OperationBatch test', () => {
     });
 
     it('should produce a batch operation which contains a registerGlobalConstant operation where fee, gas limit and storage limit are specified by the user', async (done) => {
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const opToBatch: ParamsWithKind[] = [
         {
           kind: OpKind.REGISTER_GLOBAL_CONSTANT,
@@ -314,11 +301,6 @@ describe('OperationBatch test', () => {
       const estimate = new Estimate(1230000, 93, 142, 250);
       mockEstimate.batch.mockResolvedValue([estimateReveal, estimate]);
 
-      const operationBatch = new OperationBatch(
-        new Context(mockRpcClient as any, mockSigner as any),
-        mockEstimate as any
-      );
-
       const opToBatch: ParamsWithKind[] = [
         {
           kind: OpKind.REGISTER_GLOBAL_CONSTANT,
diff --git a/packages/taquito/test/contract/rpc-contract-provider.spec.ts b/packages/taquito/test/contract/rpc-contract-provider.spec.ts
index 6d13077f4..123077cb8 100644
--- a/packages/taquito/test/contract/rpc-contract-provider.spec.ts
+++ b/packages/taquito/test/contract/rpc-contract-provider.spec.ts
@@ -53,7 +53,6 @@ describe('RpcContractProvider test', () => {
     getBlock: jest.Mock<any, any>;
     getContract: jest.Mock<any, any>;
     getBlockMetadata: jest.Mock<any, any>;
-    forgeOperations: jest.Mock<any, any>;
     injectOperation: jest.Mock<any, any>;
     packData: jest.Mock<any, any>;
     preapplyOperations: jest.Mock<any, any>;
@@ -68,6 +67,10 @@ describe('RpcContractProvider test', () => {
     sign: jest.Mock<any, any>;
   };
 
+  let mockForger: {
+    forge: jest.Mock<any, any>;
+  };
+
   let mockEstimate: {
     originate: jest.Mock<any, any>;
     transfer: jest.Mock<any, any>;
@@ -101,7 +104,6 @@ describe('RpcContractProvider test', () => {
       getBlockHeader: jest.fn(),
       getBlockMetadata: jest.fn(),
       getContract: jest.fn(),
-      forgeOperations: jest.fn(),
       injectOperation: jest.fn(),
       packData: jest.fn(),
       preapplyOperations: jest.fn(),
@@ -110,6 +112,10 @@ describe('RpcContractProvider test', () => {
       getProtocols: jest.fn(),
     };
 
+    mockForger = {
+      forge: jest.fn(),
+    };
+
     mockSigner = {
       publicKeyHash: jest.fn(),
       publicKey: jest.fn(),
@@ -134,8 +140,11 @@ describe('RpcContractProvider test', () => {
       },
     });
 
+    const context = new Context(mockRpcClient as any, mockSigner as any);
+    context.forger = mockForger;
     rpcContractProvider = new RpcContractProvider(
-      new Context(mockRpcClient as any, mockSigner as any),
+      // deepcode ignore no-any: any is good enough
+      context,
       mockEstimate as any
     );
 
diff --git a/packages/taquito/test/contract/rpc-estimate-provider.spec.ts b/packages/taquito/test/contract/rpc-estimate-provider.spec.ts
index d44bc10b1..19215ca53 100644
--- a/packages/taquito/test/contract/rpc-estimate-provider.spec.ts
+++ b/packages/taquito/test/contract/rpc-estimate-provider.spec.ts
@@ -32,7 +32,6 @@ describe('RPCEstimateProvider test', () => {
     getBlock: jest.Mock<any, any>;
     getContract: jest.Mock<any, any>;
     getBlockMetadata: jest.Mock<any, any>;
-    forgeOperations: jest.Mock<any, any>;
     runOperation: jest.Mock<any, any>;
     injectOperation: jest.Mock<any, any>;
     preapplyOperations: jest.Mock<any, any>;
@@ -41,6 +40,10 @@ describe('RPCEstimateProvider test', () => {
     getProtocols: jest.Mock<any, any>;
   };
 
+  let mockForger: {
+    forge: jest.Mock<any, any>;
+  };
+
   let mockSigner: {
     publicKeyHash: jest.Mock<any, any>;
     publicKey: jest.Mock<any, any>;
@@ -58,7 +61,6 @@ describe('RPCEstimateProvider test', () => {
       getBlockHeader: jest.fn(),
       getBlockMetadata: jest.fn(),
       getContract: jest.fn(),
-      forgeOperations: jest.fn(),
       injectOperation: jest.fn(),
       preapplyOperations: jest.fn(),
       getChainId: jest.fn(),
@@ -66,6 +68,10 @@ describe('RPCEstimateProvider test', () => {
       getProtocols: jest.fn(),
     };
 
+    mockForger = {
+      forge: jest.fn(),
+    };
+
     mockSigner = {
       publicKeyHash: jest.fn(),
       publicKey: jest.fn(),
@@ -86,7 +92,7 @@ describe('RPCEstimateProvider test', () => {
     mockRpcClient.getBlockHeader.mockResolvedValue({ hash: 'test' });
     mockRpcClient.getBlockMetadata.mockResolvedValue({ next_protocol: 'test_proto' });
     mockRpcClient.getProtocols.mockResolvedValue({ next_protocol: 'test_proto' });
-    mockRpcClient.forgeOperations.mockResolvedValue('1234');
+    mockForger.forge.mockResolvedValue('1234');
     mockRpcClient.preapplyOperations.mockResolvedValue([]);
     mockRpcClient.getChainId.mockResolvedValue('chain-id');
     mockRpcClient.getConstants.mockResolvedValue({
@@ -99,9 +105,9 @@ describe('RPCEstimateProvider test', () => {
     mockSigner.sign.mockResolvedValue({ sbytes: 'test', prefixSig: 'test_sig' });
     mockSigner.publicKey.mockResolvedValue('test_pub_key');
     mockSigner.publicKeyHash.mockResolvedValue('test_pub_key_hash');
-    estimateProvider = new RPCEstimateProvider(
-      new Context(mockRpcClient as any, mockSigner as any)
-    );
+    const context = new Context(mockRpcClient as any, mockSigner as any);
+    context.forger = mockForger;
+    estimateProvider = new RPCEstimateProvider(context);
   });
 
   describe('originate', () => {
@@ -137,7 +143,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(multipleInternalOrigination());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(297).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(297).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn',
         amount: 2,
@@ -153,7 +159,7 @@ describe('RPCEstimateProvider test', () => {
     test('return the correct estimate for multiple internal origination, no reveal', async (done) => {
       mockRpcClient.runOperation.mockResolvedValue(multipleInternalOriginationNoReveal());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(297).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(297).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn',
         amount: 2,
@@ -170,7 +176,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(multipleInternalTransfer());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(285).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(285).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'KT1Fe71jyjrxFg9ZrYqtvaX7uQjcLo7svE4D',
         amount: 2,
@@ -187,7 +193,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(delegate());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(149).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(149).fill('aa').join(''));
       const estimate = await estimateProvider.setDelegate({
         source: 'tz1QZ6KY7d3BuZDT1d19dUxoQrtFPN2QJ3hn',
         delegate: 'KT1Fe71jyjrxFg9ZrYqtvaX7uQjcLo7svE4D',
@@ -204,7 +210,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(origination());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(445).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(445).fill('aa').join(''));
       const estimate = await estimateProvider.originate({
         code: ligoSample,
         storage: 0,
@@ -221,7 +227,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(internalTransfer());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(226).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(226).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'KT1Fe71jyjrxFg9ZrYqtvaX7uQjcLo7svE4D',
         amount: 2,
@@ -238,7 +244,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(transferWithoutAllocation());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(153).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(153).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'KT1Fe71jyjrxFg9ZrYqtvaX7uQjcLo7svE4D',
         amount: 2,
@@ -255,7 +261,7 @@ describe('RPCEstimateProvider test', () => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
       mockRpcClient.runOperation.mockResolvedValue(transferWithAllocation());
       // Simulate real op size
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(153).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(153).fill('aa').join(''));
       const estimate = await estimateProvider.transfer({
         to: 'KT1Fe71jyjrxFg9ZrYqtvaX7uQjcLo7svE4D',
         amount: 2,
@@ -543,7 +549,7 @@ describe('RPCEstimateProvider test', () => {
 
     it('should produce a batch operation, with reveal', async (done) => {
       mockRpcClient.getManagerKey.mockResolvedValue(null);
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(224).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(224).fill('aa').join(''));
       mockRpcClient.runOperation.mockResolvedValue({
         contents: [
           {
@@ -639,7 +645,7 @@ describe('RPCEstimateProvider test', () => {
           },
         },
       };
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(149).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(149).fill('aa').join(''));
       mockRpcClient.runOperation.mockResolvedValue({
         contents: [transactionResult, transactionResult, transactionResult, transactionResult],
       });
@@ -684,7 +690,7 @@ describe('RPCEstimateProvider test', () => {
           },
         },
       };
-      mockRpcClient.forgeOperations.mockResolvedValue(new Array(149).fill('aa').join(''));
+      mockForger.forge.mockResolvedValue(new Array(149).fill('aa').join(''));
       mockRpcClient.runOperation.mockResolvedValue({
         contents: [
           transactionResult,
diff --git a/packages/taquito/test/forger/taquito-local-forger.spec.ts b/packages/taquito/test/forger/taquito-local-forger.spec.ts
new file mode 100644
index 000000000..cc83bdb07
--- /dev/null
+++ b/packages/taquito/test/forger/taquito-local-forger.spec.ts
@@ -0,0 +1,49 @@
+import { TaquitoLocalForger } from '../../src/forger/taquito-local-forger';
+import { Context, Protocols } from '../../src/taquito';
+
+describe('Taquito local forger', () => {
+  const mockRpcClient = {
+    getProtocols: jest.fn(),
+  };
+
+  beforeEach(() => {
+    mockRpcClient.getProtocols.mockResolvedValue({
+      next_protocol: 'Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A',
+    });
+  });
+
+  it('is instantiable', () => {
+    expect(new TaquitoLocalForger(new Context('url'))).toBeInstanceOf(TaquitoLocalForger);
+  });
+
+  it('should take the protocol hash from context.proto if it is defined', async (done) => {
+    const context = new Context(mockRpcClient as any);
+    context.proto = Protocols.PtHangz2;
+    const forger = new TaquitoLocalForger(context);
+
+    // When calling the forge method, an instance of LocalForger is created
+    // which required the protocol hash in its constructor
+    await forger.forge({
+      branch: 'BMbqNeX9fZKsuKmu5B2gX7ayA9ZUNjbHEeHCgYd7VdTMsTCALFF',
+      contents: [],
+    });
+    expect(mockRpcClient.getProtocols).toHaveBeenCalledTimes(0);
+
+    done();
+  });
+
+  it('should fetch protocol hash from the Rpc', async (done) => {
+    const forger = new TaquitoLocalForger(new Context(mockRpcClient as any));
+
+    // When calling the forge method, an instance of LocalForger is created
+    // which required the protocol hash in its constructor
+    // fetch the protocol hash from the RPC if context.proto is undefined
+    await forger.forge({
+      branch: 'BMbqNeX9fZKsuKmu5B2gX7ayA9ZUNjbHEeHCgYd7VdTMsTCALFF',
+      contents: [],
+    });
+    expect(mockRpcClient.getProtocols).toHaveBeenCalledTimes(1);
+
+    done();
+  });
+});
diff --git a/packages/taquito/test/taquito.spec.ts b/packages/taquito/test/taquito.spec.ts
index d7969e598..db7fe353f 100644
--- a/packages/taquito/test/taquito.spec.ts
+++ b/packages/taquito/test/taquito.spec.ts
@@ -1,4 +1,4 @@
-import { TezosToolkit, SetProviderOptions, Wallet } from '../src/taquito';
+import { TezosToolkit, SetProviderOptions, Wallet, RpcPacker } from '../src/taquito';
 import { RpcTzProvider } from '../src/tz/rpc-tz-provider';
 import { RpcContractProvider } from '../src/contract/rpc-contract-provider';
 import { PollingSubscribeProvider } from '../src/subscribe/polling-subcribe-provider';
@@ -7,6 +7,7 @@ import { RpcClient } from '@taquito/rpc';
 import { RPCEstimateProvider } from '../src/contract/rpc-estimate-provider';
 import { OperationFactory } from '../src/wallet/operation-factory';
 import { NoopGlobalConstantsProvider } from '../src/global-constants/noop-global-constants-provider';
+import { TaquitoLocalForger } from '../src/forger/taquito-local-forger';
 
 describe('TezosToolkit test', () => {
   let mockRpcClient: any;
@@ -112,6 +113,43 @@ describe('TezosToolkit test', () => {
       });
     });
 
+  providerKey
+    .filter((x) => x !== 'packer')
+    .forEach((key) => {
+      it(`setting ${key} provider should not override the packer provider`, () => {
+        expect(toolkit['_context'].packer).toBeInstanceOf(RpcPacker);
+        toolkit.setProvider({ packer: 'test' as any });
+        const instance = toolkit['_context'].packer;
+        expect(instance).toEqual('test');
+        toolkit.setProvider({ [key]: 'test' as any });
+        expect(toolkit['_context'].packer).toEqual(instance);
+      });
+    });
+
+  providerKey
+    .filter((x) => x !== 'globalConstantsProvider')
+    .forEach((key) => {
+      it(`setting ${key} provider should not override the globalConstantsProvider`, () => {
+        expect(toolkit.globalConstants).toBeInstanceOf(NoopGlobalConstantsProvider);
+        toolkit.setProvider({ globalConstantsProvider: 'test' as any });
+        const instance = toolkit.globalConstants;
+        expect(instance).toEqual('test');
+        toolkit.setProvider({ [key]: 'test' as any });
+        expect(toolkit.globalConstants).toEqual(instance);
+      });
+    });
+
+  providerKey.forEach((key) => {
+    it(`setting ${key} provider should not override the forger provider`, () => {
+      expect(toolkit['_context'].forger).toBeInstanceOf(TaquitoLocalForger);
+      toolkit.setProvider({ forger: 'test' as any });
+      const instance = toolkit['_context'].forger;
+      expect(instance).toEqual('test');
+      toolkit.setProvider({ [key]: 'test' as any });
+      expect(toolkit['_context'].forger).toEqual(instance);
+    });
+  });
+
   it('getVersionInfo returns well formed response', () => {
     const versionInfo = toolkit.getVersionInfo();
     expect(versionInfo.commitHash).toBeTruthy();
diff --git a/packages/taquito/test/tz/rpc-tz-provider.spec.ts b/packages/taquito/test/tz/rpc-tz-provider.spec.ts
index 17ff0bca1..daf279941 100644
--- a/packages/taquito/test/tz/rpc-tz-provider.spec.ts
+++ b/packages/taquito/test/tz/rpc-tz-provider.spec.ts
@@ -59,10 +59,12 @@ describe('RpcTzProvider test', () => {
         getBlockHeader: jest.fn(),
         getProtocols: jest.fn(),
         getContract: jest.fn(),
-        forgeOperations: jest.fn(),
         injectOperation: jest.fn(),
         preapplyOperations: jest.fn(),
       };
+      const mockForger = {
+        forge: jest.fn(),
+      };
       // Required for operations confirmation polling
       mockRpcClient.getBlock.mockResolvedValue({
         operations: [[], [], [], []],
@@ -84,12 +86,14 @@ describe('RpcTzProvider test', () => {
       });
       mockRpcClient.preapplyOperations.mockResolvedValue([]);
       mockRpcClient.getProtocols.mockResolvedValue({ next_protocol: 'test_proto' });
-      mockRpcClient.forgeOperations.mockResolvedValue('test');
+      mockForger.forge.mockResolvedValue('test');
       mockRpcClient.injectOperation.mockResolvedValue(
         'ood2Y1FLHH9izvYghVcDGGAkvJFo1CgSEjPfWvGsaz3qypCmeUj'
       );
 
-      const provider = new RpcTzProvider(new Context(mockRpcClient as any, mockSigner as any));
+      const context = new Context(mockRpcClient as any, mockSigner as any);
+      context.forger = mockForger;
+      const provider = new RpcTzProvider(context);
       const result = await provider.activate('tz2TSvNTh2epDMhZHrw73nV9piBX7kLZ9K9m', '123');
       expect(result.raw).toEqual({
         counter: 0,