plugin.ts 3.32 KB
Newer Older
1
import * as path from 'path'
2
import * as fs from 'fs'
3 4 5 6 7 8 9

import { extendEnvironment, extendConfig } from 'hardhat/config'
import {
  HardhatConfig,
  HardhatRuntimeEnvironment,
  HardhatUserConfig,
} from 'hardhat/types'
10
import { lazyObject } from 'hardhat/plugins'
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
import { ethers } from 'ethers'

// From: https://github.com/wighawag/hardhat-deploy/blob/master/src/index.ts#L63-L76
const normalizePath = (
  config: HardhatConfig,
  userPath: string | undefined,
  defaultPath: string
): string => {
  if (userPath === undefined) {
    userPath = path.join(config.paths.root, defaultPath)
  } else {
    if (!path.isAbsolute(userPath)) {
      userPath = path.normalize(path.join(config.paths.root, userPath))
    }
  }
  return userPath
}

export const loadDeployConfig = (hre: HardhatRuntimeEnvironment): any => {
  let config: any
  try {
32 33
    const base = `${hre.config.paths.deployConfig}/${hre.network.name}`
    if (fs.existsSync(`${base}.ts`)) {
34
      // eslint-disable-next-line @typescript-eslint/no-var-requires
35 36 37 38 39 40 41
      config = require(`${base}.ts`).default
    } else if (fs.existsSync(`${base}.json`)) {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      config = require(`${base}.json`)
    } else {
      throw new Error('not found')
    }
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
  } catch (err) {
    throw new Error(
      `error while loading deploy config for network: ${hre.network.name}, ${err}`
    )
  }

  return new Proxy(parseDeployConfig(hre, config), {
    get: (target, prop) => {
      if (target.hasOwnProperty(prop)) {
        return target[prop]
      }

      // Explicitly throw if the property is not found since I can't yet figure out a good way to
      // handle the necessary typings.
      throw new Error(
        `property does not exist in deploy config: ${String(prop)}`
      )
    },
  })
}

export const parseDeployConfig = (
  hre: HardhatRuntimeEnvironment,
  config: any
): any => {
  // 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 }

  // If the deployConfigSpec is not provided, do no validation
  if (!hre.config.deployConfigSpec) {
    return parsed
  }

  for (const [key, spec] of Object.entries(hre.config.deployConfigSpec)) {
    // 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
}

extendConfig(
  (config: HardhatConfig, userConfig: Readonly<HardhatUserConfig>) => {
    config.paths.deployConfig = normalizePath(
      config,
109
      userConfig.paths?.deployConfig,
110 111 112 113 114 115
      'deploy-config'
    )
  }
)

extendEnvironment((hre) => {
116
  hre.deployConfig = lazyObject(() => loadDeployConfig(hre))
117
})