npm package: @synapsecns/solidity-devops
Description: A collection of utils to effortlessly test, deploy and maintain the smart contracts on EVM compatible blockchains
Latest version: 0.4.11

# Solidity Development Operations

This package provides a set of tools and scripts to help with the development of Solidity smart contracts:

- Utilities to simplify the testing of smart contracts using Foundry.
- Utilities to simplify the deployment of smart contracts using Foundry.
- Utilities to manage the existing deployments of smart contracts.

## Disclaimer

This package is still under development and should be used with caution. It is not recommended to use it in a production environment.

## Docs

Forge docs are automatically deployed [here](https://solidity-devops.vercel.app).

## Setup

In order to use this package, it needs to be installed as a dependency in your project. It is recommended to install it as a dev dependency.

```bash
npm install --save-dev @synapsecns/solidity-devops
# Or, if you are using yarn
yarn add --dev @synapsecns/solidity-devops
```

Following files are necessary to be present in the root of your project:

- `.env`: storage for the sensitive configuration variables.
- `devops.json`: configuration file for the devops package.
- `foundry.toml`: configuration file for Foundry.

### `.env`

**Note: This file must be added to `.gitignore` to avoid sharing sensitive information.**

See the [example .env](./.env.example) file. Following structure is required:

- `Wallets`: defines the deployer wallets used for running the scripts. Assuming the `chad` is the name of the wallet, the following values are required:
  - `CHAD_ADDRESS`: the address of the account to use for signing
  - `CHAD_TYPE`: the type of the wallet:
    - `keystore`: use the encrypted keystore file.
      - `CHAD_JSON`: the keystore file path
    - `ledger` or `trezor`: use the hardware wallet.
      - TODO: find out if ledger/trezor specific options are needed
    - `pk`: use the plaintext private key. STRONGLY DISCOURAGED for production usage, meant for local devnet purposes.
      - `CHAD_PK`: the private key to the wallet in 0x format.
    - Any other value triggers the interactive prompt to enter the private key.
- `Chains`: defines the list of chains where the scripts will run. Assuming the `chain` is the name of the chain, the following values are required:
  - `CHAIN_RPC`: the RPC endpoint of the chain
  - `CHAIN_VERIFIER`: verifier for the smart contracts. Possible values:
    - `etherscan`
    - `blockscout`
    - `sourcify`
  - `CHAIN_VERIFIER_URL`: the Verifier API endpoint (required if verifier is not sourcify)
    - **Note: Blockscout URL needs to end with "/api?" for the verification to work.**
  - `CHAIN_VERIFIER_KEY`: the Verifier API key (required if verifier is not sourcify)
    - **Note: Use any non-empty string for Blockscout API key: it is not required per se, but foundry will complain if it's empty.**

### `devops.json`

See the [example devops.json](./devops.json) file. Following values are required:

- `chains`: list of chain-specific options for `forge script` command.
  - Check the available options by running `forge script --help`.
  - Two additional options to handle the gas pricing are introduced:
    - `--auto-gas-1559`: will fetch the current base fee and priority fee from the chain's RPC node, and use following values for submitting the transactions:
      - `maxPriorityFee = priorityFee`
      - `maxGasPrice = 2 * baseFee + priorityFee`
    - `--auto-gas-legacy`: will fetch the current gas price from the chain's RPC node, and use it for submitting the transactions.
      > Note: these options are introduced as foundry script hardcodes the 3 Gwei priority fee, which is not ideal for all the chains.
- `deployConfigs`: path to the directory that contains the deployment configuration files. These configuration files are expected to be either chain-specific or global:
  - The chain-specific configuration files should be located in `deployConfigsDir/{chainName}/{contractName}.dc.json`.
  - The global configuration files for production should be located in `deployConfigsDir/global/{contractName}.json`.
  - The global configuration files for other environments (e.g. testnet) should be located in `deployConfigsDir/global/{environment}/{contractName}.json`.
  - The configuration files usually include the variables required for the contract deployment or its subsequent management.
- `deployments`: path to the directory that contains the deployment artifact files. These files are automatically generated by `fsr` and alike commands.
- `forgeArtifacts`: path to the directory that `forge` uses to store the artifacts for the compiled contracts.
  - **Must match the `out` value in the `foundry.toml` file.**
- `freshDeployments`: path to the directory that contains the **fresh** deployment artifact files. These files are generated during the local run of the `forge script` command.
  - It is recommended to add this directory to `.gitignore` to avoid unnecessary changes in the repository.
  - The artifacts in this directory are automatically moved into the `deployments` directory by `fsr` and alike commands.
    > Note: we opted to use this approach, as the local run of the `forge script` is always done before the actual broadcast of the deployment transactions. Therefore, the end results might not match the local run, if there was an unexpected on-chain revert, or if transactions were not included in the block. A separate job is automatically run to check all the new artifacts and move them to the `deployments` directory, if the on-chain deployment was successful.

### `foundry.toml`

See the [example foundry.toml](./foundry.toml) file. Following values are required:

- `fs_permissions`:
  - `{ access = "read", path = "./" }` to allow reading files from the repository root
  - `{ access = "read-write", path = "<freshDeployments>" }` to allow reading/writing fresh deployment artifacts
- `rpc_endpoints`:
  - It is recommended to follow the structure defined in the example file to avoid duplication:
    - `arbitrum = "${ARBITRUM_RPC}"`
- `etherscan`:
  - It is recommended to follow the structure defined in the example file to avoid duplication:
    - `arbitrum = { key = "${ARBITRUM_VERIFIER_KEY}", url = "${ARBITRUM_VERIFIER_URL}" }`
    - You should omit values for chains that are using Sourcify for verification.

See the [Foundry Book](https://book.getfoundry.sh/config/) for more information.

## Usage

### Writing scripts

- Your scripts should inherit from the `SynapseScript` class (or `SynapseScript06` if you're using Solidity 0.6 compiler).
  - This class already inherits from `forge-std`'s `Script` class, so you can use all the methods from it.
  - See the [example script](./script/DeployBasicContract.s.sol) for more information.
- It is recommended to use separate scripts for initial deployments and subsequent configuration of the contracts.
  - The initial deployment scripts should be named `Deploy{ContractName}.s.sol`.
  - The configuration scripts should be named `Configure{ContractName}.s.sol`.

### Running scripts

Your main point of entry for running the scripts should be the `npx fsr` [command](./js/forgeScriptRun.js).

> Note: `fsr` is a shorthand for `forge script run`.

```bash
# npx fsr <path-to-script> <chain-name> <wallet-name> [<options>]
# To simulate the script without broadcasting, using wallet named "chad"
npx fsr script/DeployBasicContract.s.sol eth_sepolia chad
# To broadcast the script using wallet named "chad"
# NOTE: there is no need to add --verify option, as it is automatically added for the broadcasted scripts.
npx fsr script/DeployBasicContract.s.sol eth_sepolia chad --broadcast
```

This command is responsible for the following:

- Initializing the chain's deployment directories with the `.chainid` file, if they don't exist.
- Logging the wallet's address, balance and nonce.
- Running the script using the `forge script` command.
  - This executes the `run()` method of the script.
  - `--verify` option is added if the script is broadcasted.
- Collecting the receipts for the newly deployed contracts, and saving their deployment artifacts.

You can also utilize the `npx fsr-str` [command](./js/forgeScriptRunString.js) to provide a single string argument for the running script:

- This executes the `run(string memory arg)` method of the script, passing the provided string argument.

```bash
# npx fsr-str <path-to-script> <chain-name> <wallet-name> <string-arg> [<options>]
# To simulate the script without broadcasting, using wallet named "chad"
npx fsr-str script/DeployBasicContract.s.sol eth_sepolia chad "AAAAAAAA"
# To broadcast the script using wallet named "chad"
# NOTE: there is no need to add --verify option, as it is automatically added for the broadcasted scripts.
npx fsr-str script/DeployBasicContract.s.sol eth_sepolia chad "AAAAAAAA" --broadcast
```

### Managing deployments

If for whatever reason the deployment script was interrupted, you can use `npx sd` [command](./js/saveDeployment.js) to save the deployment artifacts.

> Note: `sd` is a shorthand for `save deployment`.

```bash
# npx sd <chain-name> <contract-alias>
npx sd eth_sepolia BasicContract
# Or, if the contract alias is used
npx sd eth_sepolia BasicContract.Alias
```

### Contract verification

You can verify the contracts with the saved deployment artifacts using the `npx fvc` [command](./js/forgeVerifyContract.js).

> Note: `fvc` is a shorthand for `forge verify contract`.

```bash
# npx fvc <chain-name> <contract-alias>
npx fvc eth_sepolia BasicContract
# Or, if the contract alias is used
npx fvc eth_sepolia BasicContract.Alias
```

### Proxy verification

You can verify the proxy's implementation in Etherscan-alike explorers using the `npx vp` [command](./js/verifyProxy.js).

> Note: `vp` is a shorthand for `verify proxy`.

```bash
# npx vp <chain-name> <contract-alias>
npx vp eth_sepolia BasicContract
# Or, if the contract alias is used
npx vp eth_sepolia TransparentUpgradeableProxy.BasicContract
```