deploy-config.ts 5.62 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
import { ethers } from 'ethers'

/**
 * Defines the configuration for a deployment.
 */
export interface DeployConfig {
  /**
   * Whether or not this network is a forked network.
   */
  isForkedNetwork?: boolean

  /**
   * Optional number of confs to wait during deployment.
   */
  numDeployConfirmations?: number

  /**
   * Optional gas price to use for deployment transactions.
   */
  gasPrice?: number

  /**
   * Estimated average L1 block time in seconds.
   */
  l1BlockTimeSeconds: number

  /**
   * Gas limit for blocks on L2.
   */
  l2BlockGasLimit: number

  /**
   * Chain ID for the L2 network.
   */
  l2ChainId: number

  /**
   * Discount divisor used to calculate gas burn for L1 to L2 transactions.
   */
  ctcL2GasDiscountDivisor: number

  /**
   * Cost of the "enqueue" function in the CTC.
   */
  ctcEnqueueGasCost: number

  /**
   * Fault proof window in seconds.
   */
  sccFaultProofWindowSeconds: number

  /**
   * Sequencer publish window in seconds.
   */
  sccSequencerPublishWindowSeconds: number

  /**
   * Address of the Sequencer (publishes to CTC).
   */
  ovmSequencerAddress: string

  /**
   * Address of the Proposer (publishes to SCC).
   */
  ovmProposerAddress: string

  /**
   * Address of the account that will sign blocks.
   */
  ovmBlockSignerAddress: string

  /**
   * Address that will receive fees on L1.
   */
  ovmFeeWalletAddress: string

  /**
   * Address of the owner of the AddressManager contract on L1.
   */
  ovmAddressManagerOwner: string

  /**
   * Address of the owner of the GasPriceOracle contract on L2.
   */
  ovmGasPriceOracleOwner: string

  /**
   * Optional whitelist owner address.
   */
  ovmWhitelistOwner?: string

  /**
   * Optional initial overhead value for GPO (default: 2750).
   */
  gasPriceOracleOverhead?: number

  /**
   * Optional initial scalar value for GPO (default: 1500000).
   */
  gasPriceOracleScalar?: number

  /**
   * Optional initial decimals for GPO (default: 6).
   */
  gasPriceOracleDecimals?: number

  /**
   * Optional initial L1 base fee for GPO (default: 1).
   */
  gasPriceOracleL1BaseFee?: number

  /**
   * Optional initial L2 gas price for GPO (default: 1).
   */
  gasPriceOracleL2GasPrice?: number

  /**
   * Optional block number to enable the Berlin hardfork (default: 0).
   */
  hfBerlinBlock?: number
}

/**
 * Specification for each of the configuration options.
 */
const configSpec: {
  [K in keyof DeployConfig]: {
    type: string
    default?: any
  }
} = {
  isForkedNetwork: {
    type: 'boolean',
    default: false,
  },
  numDeployConfirmations: {
    type: 'number',
    default: 0,
  },
  gasPrice: {
    type: 'number',
    default: undefined,
  },
  l1BlockTimeSeconds: {
    type: 'number',
  },
  l2BlockGasLimit: {
    type: 'number',
  },
  l2ChainId: {
    type: 'number',
  },
  ctcL2GasDiscountDivisor: {
    type: 'number',
  },
  ctcEnqueueGasCost: {
    type: 'number',
  },
  sccFaultProofWindowSeconds: {
    type: 'number',
  },
  sccSequencerPublishWindowSeconds: {
    type: 'number',
  },
  ovmSequencerAddress: {
    type: 'address',
  },
  ovmProposerAddress: {
    type: 'address',
  },
  ovmBlockSignerAddress: {
    type: 'address',
  },
  ovmFeeWalletAddress: {
    type: 'address',
  },
  ovmAddressManagerOwner: {
    type: 'address',
  },
  ovmGasPriceOracleOwner: {
    type: 'address',
  },
  ovmWhitelistOwner: {
    type: 'address',
    default: ethers.constants.AddressZero,
  },
  gasPriceOracleOverhead: {
    type: 'number',
    default: 2750,
  },
  gasPriceOracleScalar: {
    type: 'number',
    default: 1_500_000,
  },
  gasPriceOracleDecimals: {
    type: 'number',
    default: 6,
  },
  gasPriceOracleL1BaseFee: {
    type: 'number',
    default: 1,
  },
  gasPriceOracleL2GasPrice: {
    type: 'number',
    default: 1,
  },
  hfBerlinBlock: {
    type: 'number',
    default: 0,
  },
}

/**
 * Gets the deploy config for the given network.
 *
 * @param network Network name.
 * @returns Deploy config for the given network.
 */
export const getDeployConfig = (network: string): Required<DeployConfig> => {
  let config: DeployConfig
  try {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    config = require(`../deploy-config/${network}.ts`).default
  } catch (err) {
    throw new Error(
      `error while loading deploy config for network: ${network}, ${err}`
    )
  }

  return parseDeployConfig(config)
}

/**
 * Parses and validates the given deploy config, replacing any missing values with defaults.
 *
 * @param config Deploy config to parse.
 * @returns Parsed deploy config.
 */
export const parseDeployConfig = (
  config: DeployConfig
): Required<DeployConfig> => {
  // Create a clone of the config object. Shallow clone is fine because none of the input options
  // are expected to be objects or functions etc.
  const parsed = { ...config }

  for (const [key, spec] of Object.entries(configSpec)) {
    // Make sure the value is defined, or use a default.
    if (parsed[key] === undefined) {
      if ('default' in spec) {
        parsed[key] = spec.default
      } else {
        throw new Error(
          `deploy config is missing required field: ${key} (${spec.type})`
        )
      }
    } else {
      // Make sure the default has the correct type.
      if (spec.type === 'address') {
        if (!ethers.utils.isAddress(parsed[key])) {
          throw new Error(
            `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}`
          )
        }
      } else if (typeof parsed[key] !== spec.type) {
        throw new Error(
          `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}`
        )
      }
    }
  }

  return parsed as Required<DeployConfig>
}