Skip to content
This repository has been archived by the owner on Dec 16, 2023. It is now read-only.

Commit

Permalink
feat: implement static contract store (#5)
Browse files Browse the repository at this point in the history
* feat: implement static contract stores

* feat: udpate tests

* feat: expose multi network

* feat: update README

* feat: remove commented code

* feat: fix name
  • Loading branch information
VGLoic authored Jul 24, 2022
1 parent 2bd3469 commit dae163b
Show file tree
Hide file tree
Showing 15 changed files with 913 additions and 161 deletions.
233 changes: 151 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,79 +21,176 @@ yarn add contract-store@alpha

Then, one can start storing and using the needed **ABIs** or **deployments**
```ts
import { MultiNetworkContractStore } from 'contract-store';

const networks = {
mainnet: 1,
goerli: 5,
};
// Store is registered for Mainnet and Goerli
const store = new MultiNetworkContractStore([
networks.mainnet,
networks.goerli
]);

const MY_ABI = [...];
// Register the ABI and the deployment at key 'FOO' on Mainnet network
store.registerContract(networks.mainnet, 'FOO', {
address: '0xCe6afb858673550635b49F8Ffb855b20334228dF',
abi: MY_ABI
import { ContractStore } from 'contract-store';

const networks = { mainnet: 1, goerli: 5 }
const store = new ContractStore({
// ABIs available on every networks
globalAbis: { FOO: [...] },
networks: {
[networks.mainnet]: {
// Specific ABIs for Mainnet
abis: { BAR: [...] },
// Deployed contracts on Mainnet
deployments: {
PING: { abiKey: "FOO", address: "0x12..." },
PONG: { abiKey: "BAR", address: "0x34..." }
}
},
[networks.goerli]: {
// Specific ABIs for Goerli
abis: { RAB: [...] },
// Deployed contracts on Goerli
deployments: {
ZIP: { abiKey: "RAB", address: "0x56..." },
// ERC20, ERC721 and ERC1155 ABIs are available on every networks by default
ZAP: { abiKey: "ERC20", address: "0x78" }
}
}
}
});

// ABI and/or deployment address can then be retrieved anywhere
const erc721Abi = store.getGlobalAbi("ERC721");
const rabAbi = store.getAbi(networks.goerli, "RAB");
...

// ABI and deployment address can then be retrieved anywhere
const myContractArtifacts = store.getContract(networks.mainnet, 'FOO');
const myContractArtifacts = store.getContract(networks.mainnet, 'PING');
// `buildContract` is an arbitrary function used as an example here
const myContract = buildContract(
myContractArtifacts.address,
myContractArtifacts.abi
);
```

## Default ABIs
## Static Contract Store

The contract store already comes with registered ABIs `ERC20`, `ERC721` and `ERC1155` standard ABIs. The ABIs have been generated using [Open Zeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) implementations.
The default `ContractStore` is **static**. All the ABIs and deployments are defined in the constructor and typing ensures that non existing data can not be retrieved.

The `ContractStore` methods are organised around two distinct pieces of a contract, the ABI and address.

The ABI is meaningful by itself, while an address is necessarily linked to one ABI underneath.

For this reason, the `ABIs` and the `deployments` have different methods.

### Key value store

Each data stored is organised by a **string key** defined by the developer in the constructor. This key is then used in order to retrieve the data.

### One sub store per network

The networks are decoupled from each other, meaning that registering ABIs or deployments on a network will not make them accessible from another network. For this reason, most methods take the `chain ID` of the network as first argument.

### ABI

ABIs can be retrieved from their configured network.
```ts
import { MultiNetworkContractStore } from 'contract-store';

const networks = { goerli: 5 };
const store = new MultiNetworkContractStore([
networks.goerli
]);

// Register a deployment at key 'BAR' with the default ERC20 ABI on Goerli network
store.registerDeployment(networks.goerli, 'BAR', {
address: '0x5A22EA0DC6553267bDB273eB55Ccb40EFA78F804',
// ERC20, ERC721 and ERC1155 abis are by default included in the store
abiKey: 'ERC20'
});
// "FOO" has been registered on Goerli
const abi = store.getAbi(networks.goerli, "FOO");
// Retrieving it on another network where it has not been registered will throw
const thisWillFail = store.getAbi(networks.mainnet, "FOO");
```

...
Some ABI deserves to be available on every networks, for this reason, the global ABIs have been introduced. A global ABI is the same on every network and can be retrieved globally or directly on a network.
```ts
// "GLOB" has been registered globally
// One can retrieve a global ABI without specifying a network
const globalAbi = store.getGlobalAbi("GLOB");
// Or by targeting a specific network
const abi = store.getAbi(networks.goerli, "GLOB");
```

// ABI and deployment address can then be retrieved anywhere
const tokenArtifacts = store.getContract(networks.goerli, 'BAR');
// `buildContract` is an arbitrary function used as an example here
const token = buildContract(tokenArtifacts.address, tokenArtifacts.abi);
### Deployment

A deployment is defined as an Ethereum address linked to an ABI string key.

When a deployment has been registered, one can retrieve the full contract or just the address using the deployment key
```ts
// Only the address is retrieved
const address = store.getAddress(networks.goerli, "BAR");
// Retrieve both the address and the ABI, even if the ABI is using another key than "BAR"
const contract = store.getContract(networks.goerli, "BAR");
```

## API overview
One can also retrieve all the addresses deployed on a network
```ts
// Array of deployed addresses
const addresses = store.getAddresses(networks.goerli);
```

The `MultiNetworkContractStore` methods are organised around two distinct pieces of a contract, the ABI and address.
### Networks

The ABI is meaningful by itself, while an address is necessarily linked to one ABI underneath.
The list of supported networks can be retrieved as
```ts
const chainIds = store.getChainIds();
```

For this reason, the `ABIs` and the `deployments` have different methods.
Here is non exhaustive list of networks and their chain IDs
```ts
const networks = {
mainnet: 1, // 0x1
// Test nets
goerli: 5, // 0x5
ropsten: 3, // 0x3
rinkeby: 4, // 0x4
kovan: 42, // 42 0x2a
mumbai: 80001, // 0x13881
// Layers 2
arbitrum: 42161, // 0xa4b1
optimism: 10, // 0xa
// Side chains
polygon: 137, // 0x89
gnosisChain: 100, // 0x64
// Alt layer 1
binanceSmartChain: 56, // 0x38
avalanche: 43114, // 0xa86a
cronos: 25, // 0x19
fantom: 250 // 0xfa
}
```

### Default ABIs

### Key value store
The contract store already comes with registered ABIs `ERC20`, `ERC721` and `ERC1155` standard ABIs. The ABIs have been generated using [Open Zeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) implementations.

Each data stored is organised by a **string key** defined by the developer at insertion. This key is then used in order to retrieve the data.
## Dynamic Contract Store

### One sub store per network
The `DynamicContractStore` follows the same principles than the `ContractStore` except that it allows to dynamically register, update or remove any global ABI, ABI or deployments.

The networks are decoupled from each other, meaning that registering ABIs or deployments on a network will not make them accessible from another network. For this reason, most methods take the `chain ID` of the network as first argument.
```ts
import { DynamicContractStore } from 'contract-store';

const networks = { mainnet: 1, goerli: 5 }
const store = new DynamicContractStore({
globalAbis: { },
networks: {
[networks.mainnet]: {
abis: {},
deployments: {}
},
[networks.goerli]: {
abis: {},
deployments: {}
}
}
});


const MY_ABI = [...];
// Register the ABI and the deployment at key 'FOO' on Mainnet network
store.registerContract(networks.mainnet, 'FOO', {
address: '0xCe6afb858673550635b49F8Ffb855b20334228dF',
abi: MY_ABI
});
// ABI and deployment address can then be retrieved anywhere
const myContractArtifacts = store.getContract(networks.mainnet, 'FOO');
// `buildContract` is an arbitrary function used as an example here
const myContract = buildContract(
myContractArtifacts.address,
myContractArtifacts.abi
);

...
```

### ABI

Expand All @@ -106,7 +203,7 @@ const abi = store.getAbi(networks.goerli, "FOO");
const thisWillFail = store.getAbi(networks.mainnet, "FOO");
```

Some ABI deserves to be available on every networks, for this reason, the global ABIs have been introduced. A global ABI is the same on every network and can be registered, updated or deleted using dedicated methods
A global ABI is the same on every network and can be registered, updated or deleted using dedicated methods
```ts
// Register an ABI globally, it will be available for every configured networks in the store
store.registerGlobalAbi("FOO", MY_ABI);
Expand All @@ -123,8 +220,6 @@ store.registerDeployment(networks.goerli, "BAR", {

### Deployment

A deployment is defined as an Ethereum address linked to an ABI string key.

When the ABI is already registered in the contract, one can register a deployment as
```ts
// The ABI with key "FOO" has already been registered on Goerli
Expand All @@ -151,15 +246,9 @@ const address = store.getAddress(networks.goerli, "BAR");
const contract = store.getContract(networks.goerli, "BAR");
```

One can also retrieve all the addresses deployed on a network
```ts
// Array of deployed addresses
const addresses = store.getAddresses(networks.goerli);
```

## Networks
### Networks

The `MultiNetworkContractStore` is configured for an initial list of networks from an array of chain IDs given in the constructor. It is then possible to manage the networks using the API exposed by the store.
The `DynamicContractStore` allows to manage the networks using the API exposed by the store.

One can add a network
```ts
Expand All @@ -171,29 +260,9 @@ Or removing one
store.removeNetwork(networks.polygon);
```

Here is non exhaustive list of networks and their chain IDs
```ts
const networks = {
mainnet: 1, // 0x1
// Test nets
goerli: 5, // 0x5
ropsten: 3, // 0x3
rinkeby: 4, // 0x4
kovan: 42, // 42 0x2a
mumbai: 80001, // 0x13881
// Layers 2
arbitrum: 42161, // 0xa4b1
optimism: 10, // 0xa
// Side chains
polygon: 137, // 0x89
gnosisChain: 100, // 0x64
// Alt layer 1
binanceSmartChain: 56, // 0x38
avalanche: 43114, // 0xa86a
cronos: 25, // 0x19
fantom: 250 // 0xfa
}
```
## Single Network Contract Store

Static and dynamic contract store exist also in a single network mode with accordingly `SingleNetworkContractStore` and `DynamicSingleNetworkContractStore`.

## Contributing :rocket:

Expand Down
14 changes: 14 additions & 0 deletions src/helper-types.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
import { JsonFragment } from "./ethers-type";

export type LiteralUnion<T extends U, U = string> = T | (U & {});
export type NumericUnion<T extends U, U = number> = T | (U & {});

export type ABI = string | readonly (string | JsonFragment)[];

export type Deployment<ABIKey = string> = {
address: string;
abiKey: ABIKey;
};

export type Contract = {
address: string;
abi: ABI;
};
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./ethers-type";
export * from "./contract-store";
export * from "./multi-network-contract-store";
export * from "./single-network";
export * from "./multi-network";
3 changes: 3 additions & 0 deletions src/multi-network/common-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type MultiNetworkOptions = {
withoutDefaultABIs?: boolean;
};
Loading

0 comments on commit dae163b

Please sign in to comment.