From 2bd3469a5d3f36f7fadb1a7da44eeb84ec44c1f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Vincent-Genod?=
 <37535451+VGLoic@users.noreply.github.com>
Date: Sun, 17 Jul 2022 14:55:06 +0200
Subject: [PATCH] feat: add init config (#4)

---
 src/__tests__/contract-store.test.ts          |  50 +++--
 .../multi-network-contract-store.test.ts      | 172 ++++++++++++++----
 src/contract-store.ts                         |  50 +++--
 src/multi-network-contract-store.ts           | 106 ++++++++---
 4 files changed, 288 insertions(+), 90 deletions(-)

diff --git a/src/__tests__/contract-store.test.ts b/src/__tests__/contract-store.test.ts
index 31283bb..521dd32 100644
--- a/src/__tests__/contract-store.test.ts
+++ b/src/__tests__/contract-store.test.ts
@@ -62,8 +62,32 @@ describe("Contract Store", () => {
   ];
   const address = "0x65056df4226b218A79C9473189FC5Ec7D41D8198";
 
+  describe("initialization", () => {
+    test("it should initialize according to the given configuration", () => {
+      const store = new ContractStore(1, {
+        abis: {
+          FOO: testAbi,
+        },
+        deployments: {
+          BAR: {
+            abiKey: "ERC20",
+            address,
+          },
+        },
+      });
+      expect(store.getAbi("ERC20")).toEqual(ERC20);
+      expect(store.getAbi("ERC721")).toEqual(ERC721);
+      expect(store.getAbi("ERC1155")).toEqual(ERC1155);
+      expect(store.getAbi("FOO")).toEqual(testAbi);
+      expect(store.getContract("BAR")).toEqual({
+        address,
+        abi: ERC20,
+      });
+    });
+  });
+
   test("it should contain the default abis if not explictly written in the options", () => {
-    const store = new ContractStore(1);
+    const store = new ContractStore(1, {});
 
     expect(store.getAbi("ERC20")).toEqual(ERC20);
     expect(store.getAbi("ERC721")).toEqual(ERC721);
@@ -71,7 +95,7 @@ describe("Contract Store", () => {
   });
 
   test("it should not contain the default ABIs if specified in the options", () => {
-    const store = new ContractStore(1, { withoutDefaultABIs: true });
+    const store = new ContractStore(1, {}, { withoutDefaultABIs: true });
     expect(() => store.getAbi("ERC20")).toThrow();
     expect(() => store.getAbi("ERC721")).toThrow();
     expect(() => store.getAbi("ERC1155")).toThrow();
@@ -79,7 +103,7 @@ describe("Contract Store", () => {
 
   describe("ABI management", () => {
     test("it should allow to register, update and delete an ABI", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerAbi("FOO", testAbi);
       expect(store.getAbi("FOO")).toEqual(testAbi);
@@ -94,7 +118,7 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to register an ABI under an already used key", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
       store.registerAbi("FOO", testAbi);
       expect(store.getAbi("FOO")).toEqual(testAbi);
 
@@ -102,12 +126,12 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to update an ABI if it is not registered", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
       expect(() => store.updateAbi("FOO", otherTestAbi)).toThrow();
     });
 
     test("it should not allow to delete an ABI if it is used in a deployment", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
       store.registerContract("FOO", {
         abi: testAbi,
         address,
@@ -118,7 +142,7 @@ describe("Contract Store", () => {
 
   describe("deployment management", () => {
     test("it should allow to register, update, and delete a deployment using an existing ABI", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerDeployment("FOO", {
         address,
@@ -143,7 +167,7 @@ describe("Contract Store", () => {
     });
 
     test("it should allow to register an ABI and a deployment at once", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerContract("FOO", {
         address,
@@ -158,7 +182,7 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to register a deployment with an already used key", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerDeployment("FOO", {
         address,
@@ -173,7 +197,7 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to register a deployment with an unknown ABI key", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       expect(() =>
         store.registerDeployment("FOO", {
@@ -184,7 +208,7 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to update a deployment with an unknown ABI key", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerDeployment("FOO", {
         address,
@@ -195,13 +219,13 @@ describe("Contract Store", () => {
     });
 
     test("it should not allow to update an unknown deployment", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       expect(() => store.updateDeployment("FOO", "ERC20")).toThrow();
     });
 
     test("it should allow to retrieve the deployments addresses", () => {
-      const store = new ContractStore(1);
+      const store = new ContractStore(1, {});
 
       store.registerDeployment("FOO", {
         address,
diff --git a/src/__tests__/multi-network-contract-store.test.ts b/src/__tests__/multi-network-contract-store.test.ts
index b8580e4..58c6ca7 100644
--- a/src/__tests__/multi-network-contract-store.test.ts
+++ b/src/__tests__/multi-network-contract-store.test.ts
@@ -64,9 +64,62 @@ describe("Multi network contract store", () => {
 
   const chainIds = [1, 2];
 
+  describe("initialization", () => {
+    test("it should initialize according to the given configuration", () => {
+      const store = new MultiNetworkContractStore({
+        networks: {
+          1: {
+            abis: {
+              FOO: testAbi,
+            },
+            deployments: {
+              BAR: {
+                abiKey: "ERC20",
+                address,
+              },
+            },
+          },
+          2: {
+            abis: {
+              FOO2: otherTestAbi,
+            },
+            deployments: {
+              BAR2: {
+                abiKey: "FOO2",
+                address,
+              },
+            },
+          },
+        },
+      });
+      for (const chainId of chainIds) {
+        expect(store.getAbi(chainId, "ERC20")).toEqual(ERC20);
+        expect(store.getAbi(chainId, "ERC721")).toEqual(ERC721);
+        expect(store.getAbi(chainId, "ERC1155")).toEqual(ERC1155);
+      }
+
+      expect(store.getGlobalAbi("ERC20")).toEqual(ERC20);
+      expect(store.getGlobalAbi("ERC721")).toEqual(ERC721);
+      expect(store.getGlobalAbi("ERC1155")).toEqual(ERC1155);
+
+      expect(store.getAbi(1, "FOO")).toEqual(testAbi);
+      expect(store.getContract(1, "BAR")).toEqual({
+        address,
+        abi: ERC20,
+      });
+      expect(store.getAbi(2, "FOO2")).toEqual(otherTestAbi);
+      expect(store.getContract(2, "BAR2")).toEqual({
+        address,
+        abi: otherTestAbi,
+      });
+    });
+  });
+
   describe("network management", () => {
     test("it should contain the default abis as global ABI if not explictly written in the options", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
       for (const chainId of chainIds) {
         expect(store.getAbi(chainId, "ERC20")).toEqual(ERC20);
@@ -80,9 +133,12 @@ describe("Multi network contract store", () => {
     });
 
     test("it should not contain the default ABIs if specified in the options", () => {
-      const store = new MultiNetworkContractStore(chainIds, {
-        withoutDefaultABIs: true,
-      });
+      const store = new MultiNetworkContractStore(
+        { networks: { 1: {}, 2: {} } },
+        {
+          withoutDefaultABIs: true,
+        }
+      );
       for (const chainId of chainIds) {
         expect(() => store.getAbi(chainId, "ERC20")).toThrow();
         expect(() => store.getAbi(chainId, "ERC721")).toThrow();
@@ -94,12 +150,16 @@ describe("Multi network contract store", () => {
     });
 
     test("`gerChainIds` should return the list of chain IDs", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       expect(store.getChainIds()).toEqual(chainIds);
     });
 
     test("`addNetwork` should add the chain ID with the global ABIs", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
       store.addNetwork(3);
       expect(store.getAbi(3, "ERC20")).toEqual(ERC20);
@@ -108,26 +168,34 @@ describe("Multi network contract store", () => {
     });
 
     test("`addNetwork` should throw if the network is already configured", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       expect(() => store.addNetwork(2)).toThrow();
     });
 
     test("`removeNetwork` should delete the store associated to the chain ID", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.removeNetwork(1);
       expect(() => store.getAbi(1, "ERC20")).toThrow();
       expect(store.getChainIds()).toEqual([2]);
     });
 
     test("`removeNetwork` should throw if the network is not registered", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       expect(() => store.removeNetwork(24324234)).toThrow();
     });
   });
 
   describe("ABI management", () => {
     test("`registerGlobalAbi` should register the ABI on every networks", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       expect(store.getGlobalAbi("FOO")).toEqual(testAbi);
@@ -137,21 +205,27 @@ describe("Multi network contract store", () => {
     });
 
     test("`registerGlobalAbi` should throw if a global ABI with the same key exist", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       expect(() => store.registerGlobalAbi("FOO", otherTestAbi)).toThrow();
     });
 
     test("`registerGlobalAbi` should throw if an ABI with the same key exist on a network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerAbi(chainIds[0], "FOO", testAbi);
 
       expect(() => store.registerGlobalAbi("FOO", otherTestAbi)).toThrow();
     });
 
     test("`updateGlobalABI` should update the ABI on every networks", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       store.updateGlobalAbi("FOO", otherTestAbi);
@@ -163,12 +237,16 @@ describe("Multi network contract store", () => {
     });
 
     test("`updateGlobalABI` should throw if the key is not a global ABI", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       expect(() => store.updateGlobalAbi("FOO", otherTestAbi)).toThrow();
     });
 
     test("`deleteGlobalAbi` should delete the ABI on every network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
       store.deleteGlobalAbi("FOO");
 
@@ -179,14 +257,18 @@ describe("Multi network contract store", () => {
     });
 
     test("`deleteGlobalABI` should throw if the key is not one of a global ABI", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       expect(() => store.deleteGlobalAbi("FOO")).toThrow();
     });
 
     test("`deleteGlobalABI` should throw if the ABI is used in a deployment", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
-      store.registerDeployement(chainIds[0], "MY_FOO", {
+      store.registerDeployment(chainIds[0], "MY_FOO", {
         abiKey: "FOO",
         address,
       });
@@ -195,13 +277,17 @@ describe("Multi network contract store", () => {
     });
 
     test("`registerAbi` should register the ABI on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerAbi(chainIds[0], "FOO", testAbi);
       expect(store.getAbi(chainIds[0], "FOO")).toEqual(testAbi);
     });
 
     test("`registerAbi` should throw if the key already exists as global", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       expect(() =>
@@ -210,21 +296,27 @@ describe("Multi network contract store", () => {
     });
 
     test("`updateAbi` should update the ABI on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerAbi(chainIds[0], "FOO", testAbi);
       store.updateAbi(chainIds[0], "FOO", otherTestAbi);
       expect(store.getAbi(chainIds[0], "FOO")).toEqual(otherTestAbi);
     });
 
     test("`updateAbi` should throw if the key is associated to a global ABI", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       expect(() => store.updateAbi(chainIds[0], "FOO", otherTestAbi)).toThrow();
     });
 
     test("`deleteAbi` should delete the ABI on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerAbi(chainIds[0], "FOO", testAbi);
       store.deleteAbi(chainIds[0], "FOO");
 
@@ -232,7 +324,9 @@ describe("Multi network contract store", () => {
     });
 
     test("`deleteAbi` should throw if the key is associated to a global ABI", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
       store.registerGlobalAbi("FOO", testAbi);
 
       expect(() => store.deleteAbi(chainIds[0], "FOO")).toThrow();
@@ -241,9 +335,11 @@ describe("Multi network contract store", () => {
 
   describe("Deployment management", () => {
     test("`registerDeployment` should register a deployment on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
-      store.registerDeployement(chainIds[0], "BAR", {
+      store.registerDeployment(chainIds[0], "BAR", {
         abiKey: "ERC20",
         address,
       });
@@ -256,7 +352,9 @@ describe("Multi network contract store", () => {
     });
 
     test("`registerContract` should register a deployment and an ABI on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
       store.registerContract(chainIds[0], "BAR", {
         abi: testAbi,
@@ -272,9 +370,11 @@ describe("Multi network contract store", () => {
     });
 
     test("`updateDeployment` should update the deployment on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
-      store.registerDeployement(chainIds[0], "BAR", {
+      store.registerDeployment(chainIds[0], "BAR", {
         abiKey: "ERC20",
         address,
       });
@@ -289,9 +389,11 @@ describe("Multi network contract store", () => {
     });
 
     test("`deleteDeployment` should delete the deployment on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
-      store.registerDeployement(chainIds[0], "BAR", {
+      store.registerDeployment(chainIds[0], "BAR", {
         abiKey: "ERC20",
         address,
       });
@@ -303,15 +405,17 @@ describe("Multi network contract store", () => {
     });
 
     test("`getAddresses` should get the deployments addresses on the proper network", () => {
-      const store = new MultiNetworkContractStore(chainIds);
+      const store = new MultiNetworkContractStore({
+        networks: { 1: {}, 2: {} },
+      });
 
-      store.registerDeployement(chainIds[0], "FOO", {
+      store.registerDeployment(chainIds[0], "FOO", {
         abiKey: "ERC20",
         address,
       });
 
       const otherAddress = "0xf1d1F31983ffd497519C17f081CFf3B35B2A04b2";
-      store.registerDeployement(chainIds[0], "BAR", {
+      store.registerDeployment(chainIds[0], "BAR", {
         abiKey: "ERC20",
         address: otherAddress,
       });
diff --git a/src/contract-store.ts b/src/contract-store.ts
index 799ee61..2805a7c 100644
--- a/src/contract-store.ts
+++ b/src/contract-store.ts
@@ -21,18 +21,29 @@ export type Options = {
   withoutDefaultABIs?: boolean;
 };
 
-type Network = {
-  abis?: Record<string, unknown>;
-  deployments?: Record<string, unknown>;
+type GenericConfiguration = {
+  abis?: Record<string, ABI>;
+  deployments?: Record<string, Deployment>;
 };
-type GenericConfiguration = Network;
 
 type OriginalDeploymentKey<Config extends GenericConfiguration> = Extract<
   keyof Config["deployments"],
   string
 >;
-type OriginalABIKey<Config extends GenericConfiguration> = Extract<
-  keyof Config["abis"],
+type WithoutDefaultABIs<Opts extends Options | undefined> = Opts extends Options
+  ? Opts["withoutDefaultABIs"] extends true
+    ? true
+    : false
+  : false;
+
+type OriginalABIKey<
+  Config extends GenericConfiguration,
+  Opts extends Options | undefined
+> = Extract<
+  | keyof Config["abis"]
+  | (WithoutDefaultABIs<Opts> extends true
+      ? keyof {}
+      : "ERC20" | "ERC721" | "ERC1155"),
   string
 >;
 
@@ -40,14 +51,15 @@ type OriginalABIKey<Config extends GenericConfiguration> = Extract<
  * Contract Store for managing ABIs and deployments on a single network
  */
 export class ContractStore<
-  OriginalConfig extends GenericConfiguration = { abis: {}; deployments: {} }
+  Opts extends Options | undefined,
+  OriginalConfig extends GenericConfiguration = {}
 > extends EventEmitter {
   public readonly chainId: number;
 
   private abis: Record<string, ABI> = {};
   private deployments: Record<string, Deployment> = {};
 
-  constructor(chainId: number, opts?: Options) {
+  constructor(chainId: number, originalConfig: OriginalConfig, opts?: Opts) {
     super();
     this.chainId = chainId;
     if (!opts?.withoutDefaultABIs) {
@@ -55,6 +67,18 @@ export class ContractStore<
       this.registerAbi("ERC721", ERC721);
       this.registerAbi("ERC1155", ERC1155);
     }
+    if (originalConfig.abis) {
+      Object.entries(originalConfig.abis).forEach(([key, abi]) => {
+        this.registerAbi(key, abi);
+      });
+    }
+    if (originalConfig.deployments) {
+      Object.entries(originalConfig.deployments).forEach(
+        ([key, deployment]) => {
+          this.registerDeployment(key, deployment);
+        }
+      );
+    }
   }
 
   /**
@@ -122,7 +146,7 @@ export class ContractStore<
    * @param key String key of the ABI
    * @param abi New ABI
    */
-  public updateAbi<ABIKey extends OriginalABIKey<OriginalConfig>>(
+  public updateAbi<ABIKey extends OriginalABIKey<OriginalConfig, Opts>>(
     key: LiteralUnion<ABIKey>,
     abi: ABI
   ) {
@@ -143,7 +167,7 @@ export class ContractStore<
    */
   public updateDeployment<
     DeploymentKey extends OriginalDeploymentKey<OriginalConfig>,
-    ABIKey extends OriginalABIKey<OriginalConfig>
+    ABIKey extends OriginalABIKey<OriginalConfig, Opts>
   >(key: LiteralUnion<DeploymentKey>, abiKey: LiteralUnion<ABIKey>) {
     if (!this.deployments[key]) {
       throw new Error(
@@ -177,7 +201,7 @@ export class ContractStore<
    * Delete an ABI, the ABI can not be currently used in a deployment
    * @param key String key of the ABI
    */
-  public deleteAbi<ABIKey extends OriginalABIKey<OriginalConfig>>(
+  public deleteAbi<ABIKey extends OriginalABIKey<OriginalConfig, Opts>>(
     key: LiteralUnion<ABIKey>
   ) {
     const abi = this.getAbi(key);
@@ -196,7 +220,7 @@ export class ContractStore<
    * @param key String key of the ABI
    * @returns True if the ABI is used in a deployment, false otherwise
    */
-  public isAbiUsed<ABIKey extends OriginalABIKey<OriginalConfig>>(
+  public isAbiUsed<ABIKey extends OriginalABIKey<OriginalConfig, Opts>>(
     key: LiteralUnion<ABIKey>
   ) {
     return Object.values(this.deployments).some(
@@ -209,7 +233,7 @@ export class ContractStore<
    * @param key String key of the ABI
    * @returns The ABI
    */
-  public getAbi<ABIKey extends OriginalABIKey<OriginalConfig>>(
+  public getAbi<ABIKey extends OriginalABIKey<OriginalConfig, Opts>>(
     key: LiteralUnion<ABIKey>
   ) {
     const abi = this.abis[key];
diff --git a/src/multi-network-contract-store.ts b/src/multi-network-contract-store.ts
index 7cf9be8..4598058 100644
--- a/src/multi-network-contract-store.ts
+++ b/src/multi-network-contract-store.ts
@@ -10,16 +10,29 @@ type MultiNetworkOptions = {
 };
 
 type Network = {
-  abis?: Record<string, unknown>;
-  deployments?: Record<string, unknown>;
+  abis?: Record<string, ABI>;
+  deployments?: Record<string, Deployment>;
 };
 type GenericConfiguration = {
-  globalAbis?: Record<string, unknown>;
-  networks?: Record<number, Network>;
+  globalAbis?: Record<string, ABI>;
+  networks: Record<number, Network>;
 };
 
-type OriginalGlobalABIKey<Config extends GenericConfiguration> = Extract<
-  keyof Config["globalAbis"],
+type WithoutDefaultABIs<Opts extends MultiNetworkOptions | undefined> =
+  Opts extends MultiNetworkOptions
+    ? Opts["withoutDefaultABIs"] extends true
+      ? true
+      : false
+    : false;
+
+type OriginalGlobalABIKey<
+  Config extends GenericConfiguration,
+  Opts extends MultiNetworkOptions
+> = Extract<
+  | keyof Config["globalAbis"]
+  | (WithoutDefaultABIs<Opts> extends true
+      ? keyof {}
+      : "ERC20" | "ERC721" | "ERC1155"),
   string
 >;
 type OriginalChainId<Config extends GenericConfiguration> = Extract<
@@ -34,35 +47,69 @@ type OriginalDeploymentKey<
   : never;
 type OriginalABIKey<
   Config extends GenericConfiguration,
-  ChainId extends OriginalChainId<Config>
+  ChainId extends OriginalChainId<Config>,
+  Opts extends MultiNetworkOptions
 > = Config["networks"][ChainId] extends Network
-  ? Extract<keyof Config["networks"][ChainId]["abis"], string>
+  ? Extract<
+      | keyof Config["networks"][ChainId]["abis"]
+      | keyof Config["globalAbis"]
+      | (WithoutDefaultABIs<Opts> extends true
+          ? keyof {}
+          : "ERC20" | "ERC721" | "ERC1155"),
+      string
+    >
   : never;
 
 /**
  * Contract store for managing ABIs and deployments on multiple networks
  */
 export class MultiNetworkContractStore<
+  Opts extends MultiNetworkOptions,
   OriginalConfig extends GenericConfiguration
 > extends EventEmitter {
-  private stores: Record<number, ContractStore>;
+  private stores: Record<number, ContractStore<Opts>>;
   private globalAbis: Record<string, ABI> = {};
 
-  constructor(chainIds: number[], opts?: MultiNetworkOptions) {
+  constructor(originalConfig: OriginalConfig, opts?: Opts) {
     super();
-    const stores = Array.from(new Set(chainIds)).reduce(
-      (acc, chainId) => ({
+    const chainIds = Object.keys(originalConfig.networks);
+    const stores = chainIds.reduce((acc, chainId) => {
+      return {
         ...acc,
-        [chainId]: new ContractStore(chainId, { withoutDefaultABIs: true }),
-      }),
-      {} as Record<number, ContractStore>
-    );
+        [chainId]: new ContractStore(
+          Number(chainId),
+          {},
+          { withoutDefaultABIs: true }
+        ),
+      };
+    }, {} as Record<OriginalChainId<OriginalConfig>, ContractStore<Opts>>);
     this.stores = stores;
     if (!opts?.withoutDefaultABIs) {
       this.registerGlobalAbi("ERC20", ERC20);
       this.registerGlobalAbi("ERC721", ERC721);
       this.registerGlobalAbi("ERC1155", ERC1155);
     }
+    if (originalConfig.globalAbis) {
+      Object.entries(originalConfig.globalAbis).forEach(([key, abi]) => {
+        this.registerGlobalAbi(key, abi);
+      });
+    }
+    chainIds.forEach((chainId) => {
+      const formattedChainId = Number(chainId);
+      const networkConfig = originalConfig.networks[formattedChainId];
+      if (networkConfig.abis) {
+        Object.entries(networkConfig.abis).forEach(([key, abi]) => {
+          this.registerAbi(formattedChainId, key, abi);
+        });
+      }
+      if (networkConfig.deployments) {
+        Object.entries(networkConfig.deployments).forEach(
+          ([key, deployment]) => {
+            this.registerDeployment(formattedChainId, key, deployment);
+          }
+        );
+      }
+    });
   }
 
   /**
@@ -81,7 +128,7 @@ export class MultiNetworkContractStore<
     if (this.stores[chainId]) {
       throw new Error(`Chain ID ${chainId} is already configured.`);
     }
-    const store = new ContractStore(chainId, { withoutDefaultABIs: true });
+    const store = new ContractStore(chainId, {}, { withoutDefaultABIs: true });
     Object.entries(this.globalAbis).forEach(([key, abi]) => {
       store.registerAbi(key, abi);
     });
@@ -128,10 +175,9 @@ export class MultiNetworkContractStore<
    * @param key String key of the ABI
    * @param abi ABI
    */
-  public updateGlobalAbi<KeyType extends OriginalGlobalABIKey<OriginalConfig>>(
-    key: LiteralUnion<KeyType>,
-    abi: ABI
-  ) {
+  public updateGlobalAbi<
+    KeyType extends OriginalGlobalABIKey<OriginalConfig, Opts>
+  >(key: LiteralUnion<KeyType>, abi: ABI) {
     if (!this.globalAbis[key]) {
       throw new Error(`No global ABI for key ${key} has been found.`);
     }
@@ -146,9 +192,9 @@ export class MultiNetworkContractStore<
    * Delete a global ABI, replicating the delete in every networks
    * @param key String key of the ABI
    */
-  public deleteGlobalAbi<KeyType extends OriginalGlobalABIKey<OriginalConfig>>(
-    key: LiteralUnion<KeyType>
-  ) {
+  public deleteGlobalAbi<
+    KeyType extends OriginalGlobalABIKey<OriginalConfig, Opts>
+  >(key: LiteralUnion<KeyType>) {
     if (!this.globalAbis[key]) {
       throw new Error(`No global ABI for key ${key} has been found.`);
     }
@@ -192,7 +238,7 @@ export class MultiNetworkContractStore<
    * @param deployment.address Address of the contract
    * @param deployment.abiKey String key of the already registered ABI
    */
-  public registerDeployement<ChainId extends OriginalChainId<OriginalConfig>>(
+  public registerDeployment<ChainId extends OriginalChainId<OriginalConfig>>(
     chainId: NumericUnion<ChainId>,
     key: string,
     deployment: Deployment
@@ -225,7 +271,7 @@ export class MultiNetworkContractStore<
    */
   public updateAbi<
     ChainId extends OriginalChainId<OriginalConfig>,
-    ABIKey extends OriginalABIKey<OriginalConfig, ChainId>
+    ABIKey extends OriginalABIKey<OriginalConfig, ChainId, Opts>
   >(chainId: NumericUnion<ChainId>, key: LiteralUnion<ABIKey>, abi: ABI) {
     if (this.globalAbis[key]) {
       throw new Error(
@@ -245,7 +291,7 @@ export class MultiNetworkContractStore<
   public updateDeployment<
     ChainId extends OriginalChainId<OriginalConfig>,
     DeploymentKey extends OriginalDeploymentKey<OriginalConfig, ChainId>,
-    ABIKey extends OriginalABIKey<OriginalConfig, ChainId>
+    ABIKey extends OriginalABIKey<OriginalConfig, ChainId, Opts>
   >(
     chainId: NumericUnion<ChainId>,
     key: LiteralUnion<DeploymentKey>,
@@ -275,7 +321,7 @@ export class MultiNetworkContractStore<
    */
   public deleteAbi<
     ChainId extends OriginalChainId<OriginalConfig>,
-    ABIKey extends OriginalABIKey<OriginalConfig, ChainId>
+    ABIKey extends OriginalABIKey<OriginalConfig, ChainId, Opts>
   >(chainId: NumericUnion<ChainId>, key: LiteralUnion<ABIKey>) {
     if (this.globalAbis[key]) {
       throw new Error(
@@ -292,7 +338,7 @@ export class MultiNetworkContractStore<
    * @returns The ABI
    */
   public getGlobalAbi<
-    GlobalABIKey extends OriginalGlobalABIKey<OriginalConfig>
+    GlobalABIKey extends OriginalGlobalABIKey<OriginalConfig, Opts>
   >(key: LiteralUnion<GlobalABIKey>) {
     if (!this.globalAbis[key]) {
       throw new Error(`Key ${key} is not associated to a global ABI.`);
@@ -308,7 +354,7 @@ export class MultiNetworkContractStore<
    */
   public getAbi<
     ChainId extends OriginalChainId<OriginalConfig>,
-    ABIKey extends OriginalABIKey<OriginalConfig, ChainId>
+    ABIKey extends OriginalABIKey<OriginalConfig, ChainId, Opts>
   >(chainId: NumericUnion<ChainId>, key: LiteralUnion<ABIKey>) {
     return this.getStore(chainId).getAbi(key);
   }