Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SC-1034] Fix register #109

Merged
merged 7 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions hardhat-setup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# `Networks` Class

This class is your go-to assistant for setting up various blockchain networks for Hardhat setup. It provides convenient methods for create network objects, managing Etherscan keys for contract verifications, and configuring Hardhat networks, including hardhat forking.

## Key Components:
1. **`networks` Property**: Stores configurations for all registered networks.
2. **`etherscan` Property**: Keeps Etherscan API keys for all registered networks for contract verify process with Etherscan explorers.

## Methods:

1. **`_parseRpcEnv(envRpc: string)`**: A helper method to parse RPC configuration strings. Checks that the string is in the expected format.

2. **`constructor(useHardhat: boolean, forkingNetworkName?: string, saveHardhatDeployments: boolean)`**: Initializes the class. It can set up a Hardhat network, including Hardhad forking, and determines whether to save deployments files.

**Constructor Parameters**

- **`useHardhat`** (default: `true`):

Determines whether to configure the class for using the Hardhat network. If `true`, sets up the class to operate with Hardhat.

- **`forkingNetworkName`** (optional):

Specifies the network to fork in Hardhat. If provided, Hardhat will simulate the specified network, allowing for local testing and development. If not provided, Hardhat will not perform any network forking.

- **`saveHardhatDeployments`** (default: `false`):

Decides whether to save deployment files in the Hardhat environment. If `true`, saves deployment details such as contract addresses in json file, useful for tracking and referencing in subsequent testing or deployments.

4. **`register(...)`**: Registers a new network with specified parameters, such as the RPC URL, private key, and Etherscan keys.

5. **`registerCustom(...)`**: Allows registering custom networks with custom parameters for Etherscan contract verifications.

6. **`registerZksync(...)`**: A specialized method for registering zkSync networks with additional parameters which specified for zksync network.

7. **`registerAll()`**: Registers multiple standard networks using environment variables for configuration.

## Predefined environment variable names and their format
1. **`rpc`**

`<NETWORK_NAME>_RPC_URL` - RPC configuration string of network with `<NETWORK_NAME>`.

Format:
- `<RPC_URL>` or `<RPC_URL>|<AUTH_KEY_HTTP_HEADER>`
where `<RPC_URL>` is blockchain node node, `<AUTH_KEY_HTTP_HEADER>` is HTTP `auth-key` header's value.
- Examples:
- For using simple node with only access by RPC URL
`MAINNET_RPC_URL=https://mainnet.infura.io/v3/YOUR_API_KEY`
- For using node with access by HTTP `auth-key` header
`MAINNET_RPC_URL=http://localhost:1234|AUTH_KEY_HEADER_VALUE`

2. **`private key`**

`PRIVATE_KEY` - The Common private key used for all networks.

`<NETWORK_NAME>_PRIVATE_KEY>` - The private key used for the specific network with `<NETWORK_NAME>`.

Format:
- 32 bytes of the private key represented as a 64-character hex string without the 0x prefix
- Examples:
- `PRIVATE_KEY=f040ec294d83b8abf0803b713ebdac7e6ef8c104bd644a45d32114e7a210ce74`
- `MAINNET_PRIVATE_KEY=f040ec294d83b8abf0803b713ebdac7e6ef8c104bd644a45d32114e7a210ce74`

4. **`etherscan key`**

`<NETWORK_NAME>_ETHERSCAN_KEY` - The Etherscan's key to verify contracts for the specific network with `<NETWORK_NAME>`.

Format:
- Just a string with Etherscan API KEY
- Example: `MAINNET_ETHERSCAN_KEY=UFAPYWUQYZMR1NTER4G0BKB52WIOE6LKD9`

4. **`verify url in zksync`**

`<NETWORK_NAME>_VERIFY_URL` - The veridy url to verify contracts for the specific network with `<NETWORK_NAME>`.

Format:
- Just a link to verification service
- Example: `ZKSYNC_VERIFY_URL=https://zksync2-mainnet-explorer.zksync.io/contract_verification`
- Default value (**only for mainnet**): `https://zksync2-mainnet-explorer.zksync.io/contract_verification`

5. **`eth network in zksync`**

`ZKSYNC_LOCAL_ETH_NETWORK` - rpc or name of ethNetwork for local zksync node.

Format:
- ethNetwork name (for mainnet or testnet) or rpc for custom eth node
- Example: `ZKSYNC_LOCAL_ETH_NETWORK=http://localhost:8545`
- Default value: `http://localhost:8545`

## Example Usage:
```javascript
// hardhat.config.js
...
const networksConfig = new Networks();
networksConfig.register('mainnet', 1, 'https://mainnet.infura.io/v3/YOUR_API_KEY', 'YOUR_PRIVATE_KEY', 'mainnet', 'YOUR_ETHERSCAN_API_KEY');

...

module.exports = {
solidity: { ... }
networks: networksConfig.networks,
etherscan: networksConfig.etherscan,
...
}
```
This code creates a new instance of the Networks class and registers the Ethereum Mainnet network with your configuration data.
110 changes: 47 additions & 63 deletions hardhat-setup/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ export class Networks {
networks: NetworksUserConfig = {};
etherscan: Etherscan = { apiKey: {}, customChains: [] };

constructor(useHardhat: boolean = true, forkingNetworkName?: string, needAuthKeyHttpHeader: boolean = false, saveHardhatDeployments: boolean = false) {
dotenv.config();
this.updateHardhatNetwork(useHardhat, forkingNetworkName, needAuthKeyHttpHeader, saveHardhatDeployments);
_parseRpcEnv(envRpc: string): { url: string, authKeyHttpHeader?: string } {
const [ url, authKeyHttpHeader, overflow ] = envRpc.split('|');
if (overflow || url === '') {
throw new Error(`Invalid RPC PARAM: ${envRpc}. It should be in the format: <RPC_URL> or <RPC_URL>|<AUTH_KEY_HTTP_HEADER>`);
}
return { url, authKeyHttpHeader };
}

updateHardhatNetwork(useHardhat: boolean, forkingNetworkName?: string, needAuthKeyHttpHeader: boolean = false, saveHardhatDeployments: boolean = false) {
constructor(useHardhat: boolean = true, forkingNetworkName?: string, saveHardhatDeployments: boolean = false) {
dotenv.config();

if (useHardhat || forkingNetworkName) {
this.networks.hardhat = {
chainId: Number(process.env.FORK_CHAIN_ID) || 31337,
Expand All @@ -29,19 +34,20 @@ export class Networks {
}

if (forkingNetworkName) {
const { url, authKeyHttpHeader } = this._parseRpcEnv(process.env[`${forkingNetworkName.toUpperCase()}_RPC_URL`] || '');
this.networks.hardhat!.forking = {
url: process.env[`${forkingNetworkName.toUpperCase()}_RPC_URL`] || '',
httpHeaders: needAuthKeyHttpHeader
? { 'auth-key': process.env.RPC_AUTH_HEADER || '' }
: undefined,
url,
httpHeaders: authKeyHttpHeader ? { 'auth-key': authKeyHttpHeader } : undefined,
};
}
}

register(name: string, chainId: number, url?: string, privateKey?: string, etherscanNetworkName?: string, etherscanKey?: string, hardfork: string = 'paris') {
if (url && privateKey && etherscanNetworkName && etherscanKey) {
register(name: string, chainId: number, rpc?: string, privateKey?: string, etherscanNetworkName?: string, etherscanKey?: string, hardfork: string = 'paris') {
if (rpc && privateKey && etherscanNetworkName && etherscanKey) {
const { url, authKeyHttpHeader } = this._parseRpcEnv(rpc);
this.networks[name] = {
url,
httpHeaders: authKeyHttpHeader ? { 'auth-key': authKeyHttpHeader } : undefined,
chainId,
accounts: [privateKey],
hardfork,
Expand All @@ -60,66 +66,44 @@ export class Networks {
}
}

registerZksync() {
if (process.env.ZKSYNC_PRIVATE_KEY) {
this.networks.zksync = {
url: process.env.ZKSYNC_RPC_URL || 'https://mainnet.era.zksync.io',
ethNetwork: 'mainnet',
zksync: true,
chainId: 324,
verifyURL: 'https://zksync2-mainnet-explorer.zksync.io/contract_verification',
accounts: [process.env.ZKSYNC_PRIVATE_KEY],
hardfork: 'paris',
} as NetworkUserConfig;
this.networks.zksyncFork = {
url: process.env.ZKSYNC_RPC_URL || '', // you should use zksync fork node: https://github.com/matter-labs/era-test-node
ethNetwork: 'mainnet',
zksync: true,
chainId: 260,
hardfork: 'paris',
} as NetworkUserConfig;
this.networks.zksyncLocal = {
url: process.env.ZKSYNC_RPC_URL || 'http://localhost:3050',
ethNetwork: process.env.ZKSYNC_ETH_NETWORK || 'http://localhost:8545',
zksync: true,
chainId: 270,
hardfork: 'paris',
} as NetworkUserConfig;
this.networks.zksyncTest = {
url: process.env.ZKSYNC_RPC_URL || 'https://testnet.era.zksync.dev',
ethNetwork: 'goerli',
registerZksync(name: string, chainId: number, rpc: string, ethNetwork: string, privateKey?: string, verifyUrl?: string, hardfork: string = 'paris') {
if (privateKey) {
const { url, authKeyHttpHeader } = this._parseRpcEnv(rpc);
this.networks[name] = {
url,
httpHeaders: authKeyHttpHeader ? { 'auth-key': authKeyHttpHeader } : undefined,
zksync: true,
chainId: 280,
hardfork: 'paris',
chainId,
accounts: [privateKey],
hardfork,
verifyUrl,
ethNetwork,
} as NetworkUserConfig;
console.log('Network \'zksync\' registered');
console.log(`Network '${name}' registered`);
} else {
console.log('Network \'zksync\' not registered');
console.log(`Network '${name}' not registered`);
}
}

registerAll(): { networks: NetworksUserConfig, etherscan: Etherscan } {
this.register('mainnet', 1, process.env.MAINNET_RPC_URL, process.env.MAINNET_PRIVATE_KEY, 'mainnet', process.env.MAINNET_ETHERSCAN_KEY, 'shanghai');
this.register('bsc', 56, process.env.BSC_RPC_URL, process.env.BSC_PRIVATE_KEY, 'bsc', process.env.BSC_ETHERSCAN_KEY);
this.register('kovan', 42, process.env.KOVAN_RPC_URL, process.env.KOVAN_PRIVATE_KEY, 'kovan', process.env.KOVAN_ETHERSCAN_KEY);
this.register('optimistic', 10, process.env.OPTIMISTIC_RPC_URL, process.env.OPTIMISTIC_PRIVATE_KEY, 'optimisticEthereum', process.env.OPTIMISTIC_ETHERSCAN_KEY);
this.register('matic', 137, process.env.MATIC_RPC_URL, process.env.MATIC_PRIVATE_KEY, 'polygon', process.env.MATIC_ETHERSCAN_KEY);
this.register('arbitrum', 42161, process.env.ARBITRUM_RPC_URL, process.env.ARBITRUM_PRIVATE_KEY, 'arbitrumOne', process.env.ARBITRUM_ETHERSCAN_KEY);
this.register('xdai', 100, process.env.XDAI_RPC_URL, process.env.XDAI_PRIVATE_KEY, 'xdai', process.env.XDAI_ETHERSCAN_KEY);
this.register('avax', 43114, process.env.AVAX_RPC_URL, process.env.AVAX_PRIVATE_KEY, 'avalanche', process.env.AVAX_ETHERSCAN_KEY);
this.register('fantom', 250, process.env.FANTOM_RPC_URL, process.env.FANTOM_PRIVATE_KEY, 'opera', process.env.FANTOM_ETHERSCAN_KEY);
this.register('aurora', 1313161554, process.env.AURORA_RPC_URL, process.env.AURORA_PRIVATE_KEY, 'aurora', process.env.AURORA_ETHERSCAN_KEY);
this.register('base', 8453, process.env.BASE_RPC_URL, process.env.BASE_PRIVATE_KEY, 'base', process.env.BASE_ETHERSCAN_KEY);
this.registerCustom('klaytn', 8217, process.env.KLAYTN_RPC_URL, process.env.KLAYTN_PRIVATE_KEY, process.env.KLAYTN_ETHERSCAN_KEY, 'https://scope.klaytn.com/', 'https://scope.klaytn.com/');
this.registerZksync();
const privateKey = process.env.PRIVATE_KEY;
this.register('mainnet', 1, process.env.MAINNET_RPC_URL, process.env.MAINNET_PRIVATE_KEY || privateKey, 'mainnet', process.env.MAINNET_ETHERSCAN_KEY, 'shanghai');
this.register('bsc', 56, process.env.BSC_RPC_URL, process.env.BSC_PRIVATE_KEY || privateKey, 'bsc', process.env.BSC_ETHERSCAN_KEY);
this.register('kovan', 42, process.env.KOVAN_RPC_URL, process.env.KOVAN_PRIVATE_KEY || privateKey, 'kovan', process.env.KOVAN_ETHERSCAN_KEY);
this.register('optimistic', 10, process.env.OPTIMISTIC_RPC_URL, process.env.OPTIMISTIC_PRIVATE_KEY || privateKey, 'optimisticEthereum', process.env.OPTIMISTIC_ETHERSCAN_KEY);
this.register('matic', 137, process.env.MATIC_RPC_URL, process.env.MATIC_PRIVATE_KEY || privateKey, 'polygon', process.env.MATIC_ETHERSCAN_KEY);
this.register('arbitrum', 42161, process.env.ARBITRUM_RPC_URL, process.env.ARBITRUM_PRIVATE_KEY || privateKey, 'arbitrumOne', process.env.ARBITRUM_ETHERSCAN_KEY);
this.register('xdai', 100, process.env.XDAI_RPC_URL, process.env.XDAI_PRIVATE_KEY || privateKey, 'xdai', process.env.XDAI_ETHERSCAN_KEY);
this.register('avax', 43114, process.env.AVAX_RPC_URL, process.env.AVAX_PRIVATE_KEY || privateKey, 'avalanche', process.env.AVAX_ETHERSCAN_KEY);
this.register('fantom', 250, process.env.FANTOM_RPC_URL, process.env.FANTOM_PRIVATE_KEY || privateKey, 'opera', process.env.FANTOM_ETHERSCAN_KEY);
this.register('aurora', 1313161554, process.env.AURORA_RPC_URL, process.env.AURORA_PRIVATE_KEY || privateKey, 'aurora', process.env.AURORA_ETHERSCAN_KEY);
this.register('base', 8453, process.env.BASE_RPC_URL, process.env.BASE_PRIVATE_KEY || privateKey, 'base', process.env.BASE_ETHERSCAN_KEY);
this.registerCustom('klaytn', 8217, process.env.KLAYTN_RPC_URL, process.env.KLAYTN_PRIVATE_KEY || privateKey, process.env.KLAYTN_ETHERSCAN_KEY, 'https://scope.klaytn.com/', 'https://scope.klaytn.com/'); // eslint-disable-line max-len
this.registerZksync('zksync', 324, process.env.ZKSYNC_RPC_URL || 'https://mainnet.era.zksync.io', 'mainnet', process.env.ZKSYNC_PRIVATE_KEY || privateKey, process.env.ZKSYNC_VERIFY_URL || 'https://zksync2-mainnet-explorer.zksync.io/contract_verification'); // eslint-disable-line max-len
// For 'zksyncFork' network you should use zksync fork node: https://github.com/matter-labs/era-test-node
this.registerZksync('zksyncFork', 260, process.env.ZKSYNC_FORK_RPC_URL || '', 'mainnet', process.env.ZKSYNC_FORK_PRIVATE_KEY || privateKey);
this.registerZksync('zksyncLocal', 270, process.env.ZKSYNC_LOCAL_RPC_URL || 'http://localhost:3050', process.env.ZKSYNC_LOCAL_ETH_NETWORK || 'http://localhost:8545', process.env.ZKSYNC_PRIVATE_KEY || privateKey); // eslint-disable-line max-len
this.registerZksync('zksyncTest', 280, process.env.ZKSYNC_TEST_RPC_URL || 'https://testnet.era.zksync.dev', 'goerli', process.env.ZKSYNC_TEST_PRIVATE_KEY || privateKey, process.env.ZKSYNC_TEST_VERIFY_URL); // eslint-disable-line max-len
return { networks: this.networks, etherscan: this.etherscan };
}

unregister(name: string) {
delete this.networks[name];
}

unregisterBatch(names: string[]) {
names.forEach((name) => this.unregister(name));
}
}
Loading