Skip to content

Latest commit

 

History

History

solidity-devops

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.

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.

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 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 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 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 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 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.

Note: fsr is a shorthand for forge script run.

# 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 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.
# 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 to save the deployment artifacts.

Note: sd is a shorthand for save deployment.

# 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.

Note: fvc is a shorthand for forge verify contract.

# 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.

Note: vp is a shorthand for verify proxy.

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