├── .depcheckrc ├── .eslintrc.js ├── .gitignore ├── .lintstagedrc.yml ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.ts ├── plugin.ts ├── type-extensions.ts └── types.ts └── tsconfig.json /.depcheckrc: -------------------------------------------------------------------------------- 1 | ignores: [ 2 | "@babel/eslint-parser", 3 | "@typescript-eslint/parser", 4 | "eslint-plugin-import", 5 | "eslint-plugin-unicorn", 6 | "eslint-plugin-jsdoc", 7 | "eslint-plugin-prefer-arrow", 8 | "eslint-plugin-react", 9 | "@typescript-eslint/eslint-plugin", 10 | "eslint-config-prettier", 11 | "eslint-plugin-prettier", 12 | "chai" 13 | ] 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es6: true, 6 | }, 7 | ignorePatterns: ['dist', 'coverage'], 8 | extends: ['plugin:prettier/recommended'], 9 | parser: '@babel/eslint-parser', 10 | parserOptions: { 11 | es6: true, 12 | ecmaVersion: 6, 13 | sourceType: 'module', 14 | requireConfigFile: false, 15 | }, 16 | plugins: [ 17 | 'eslint-plugin-import', 18 | 'eslint-plugin-unicorn', 19 | 'eslint-plugin-jsdoc', 20 | 'eslint-plugin-prefer-arrow', 21 | 'eslint-plugin-react', 22 | '@typescript-eslint', 23 | ], 24 | overrides: [ 25 | { 26 | files: ['**/*.ts'], 27 | parser: '@typescript-eslint/parser', 28 | parserOptions: { 29 | project: './packages/**/tsconfig.json', 30 | sourceType: 'module', 31 | allowAutomaticSingleRunInference: true, 32 | }, 33 | rules: { 34 | '@typescript-eslint/adjacent-overload-signatures': 'error', 35 | '@typescript-eslint/array-type': 'off', 36 | '@typescript-eslint/ban-types': 'off', 37 | '@typescript-eslint/consistent-type-assertions': 'error', 38 | '@typescript-eslint/dot-notation': 'off', 39 | '@typescript-eslint/indent': 'off', 40 | '@typescript-eslint/member-delimiter-style': [ 41 | 'off', 42 | { 43 | multiline: { 44 | delimiter: 'none', 45 | requireLast: true, 46 | }, 47 | singleline: { 48 | delimiter: 'semi', 49 | requireLast: false, 50 | }, 51 | }, 52 | ], 53 | '@typescript-eslint/member-ordering': 'off', 54 | '@typescript-eslint/naming-convention': 'off', 55 | '@typescript-eslint/no-empty-function': 'error', 56 | '@typescript-eslint/no-empty-interface': 'off', 57 | '@typescript-eslint/no-explicit-any': 'off', 58 | '@typescript-eslint/no-misused-new': 'error', 59 | '@typescript-eslint/no-namespace': 'error', 60 | '@typescript-eslint/no-parameter-properties': 'off', 61 | '@typescript-eslint/no-shadow': [ 62 | 'error', 63 | { 64 | hoist: 'all', 65 | }, 66 | ], 67 | '@typescript-eslint/no-this-alias': 'error', 68 | '@typescript-eslint/no-unused-expressions': 'off', 69 | '@typescript-eslint/no-use-before-define': 'off', 70 | '@typescript-eslint/no-var-requires': 'error', 71 | '@typescript-eslint/prefer-for-of': 'error', 72 | '@typescript-eslint/prefer-function-type': 'error', 73 | '@typescript-eslint/prefer-namespace-keyword': 'error', 74 | '@typescript-eslint/quotes': 'off', 75 | '@typescript-eslint/semi': ['off', null], 76 | '@typescript-eslint/triple-slash-reference': [ 77 | 'error', 78 | { 79 | path: 'always', 80 | types: 'prefer-import', 81 | lib: 'always', 82 | }, 83 | ], 84 | '@typescript-eslint/type-annotation-spacing': 'off', 85 | '@typescript-eslint/unified-signatures': 'error', 86 | '@typescript-eslint/no-unused-vars': 'error', 87 | }, 88 | }, 89 | ], 90 | rules: { 91 | 'prettier/prettier': 'warn', 92 | 'arrow-parens': ['off', 'always'], 93 | 'brace-style': ['off', 'off'], 94 | 'comma-dangle': 'off', 95 | complexity: 'off', 96 | 'constructor-super': 'error', 97 | curly: 'error', 98 | 'dot-notation': 'off', 99 | 'eol-last': 'off', 100 | eqeqeq: ['error', 'smart'], 101 | 'guard-for-in': 'error', 102 | 'id-blacklist': 'off', 103 | 'id-match': 'off', 104 | 'import/no-extraneous-dependencies': ['error'], 105 | 'import/no-internal-modules': 'off', 106 | 'import/order': [ 107 | 'error', 108 | { 109 | groups: ['builtin', 'external', 'internal'], 110 | 'newlines-between': 'always', 111 | }, 112 | ], 113 | indent: 'off', 114 | 'jsdoc/check-alignment': 'error', 115 | 'jsdoc/check-indentation': 'error', 116 | 'jsdoc/newline-after-description': 'error', 117 | 'linebreak-style': 'off', 118 | 'max-classes-per-file': 'off', 119 | 'max-len': 'off', 120 | 'new-parens': 'off', 121 | 'newline-per-chained-call': 'off', 122 | 'no-bitwise': 'off', 123 | 'no-caller': 'error', 124 | 'no-cond-assign': 'error', 125 | 'no-console': 'off', 126 | 'no-debugger': 'error', 127 | 'no-duplicate-case': 'error', 128 | 'no-duplicate-imports': 'error', 129 | 'no-empty': 'error', 130 | 'no-eval': 'error', 131 | 'no-extra-bind': 'error', 132 | 'no-extra-semi': 'off', 133 | 'no-fallthrough': 'off', 134 | 'no-invalid-this': 'off', 135 | 'no-irregular-whitespace': 'off', 136 | 'no-multiple-empty-lines': 'off', 137 | 'no-new-func': 'error', 138 | 'no-new-wrappers': 'error', 139 | 'no-redeclare': 'error', 140 | 'no-return-await': 'error', 141 | 'no-sequences': 'error', 142 | 'no-sparse-arrays': 'error', 143 | 'no-template-curly-in-string': 'error', 144 | 'no-throw-literal': 'error', 145 | 'no-trailing-spaces': 'off', 146 | 'no-undef-init': 'error', 147 | 'no-underscore-dangle': 'off', 148 | 'no-unsafe-finally': 'error', 149 | 'no-unused-expressions': 'off', 150 | 'no-unused-labels': 'error', 151 | 'no-use-before-define': 'off', 152 | 'no-var': 'error', 153 | 'object-shorthand': 'error', 154 | 'one-var': ['error', 'never'], 155 | 'padded-blocks': [ 156 | 'off', 157 | { 158 | blocks: 'never', 159 | }, 160 | { 161 | allowSingleLineBlocks: true, 162 | }, 163 | ], 164 | 'prefer-arrow/prefer-arrow-functions': 'error', 165 | 'prefer-const': 'error', 166 | 'prefer-object-spread': 'error', 167 | 'quote-props': 'off', 168 | quotes: 'off', 169 | radix: 'error', 170 | 'react/jsx-curly-spacing': 'off', 171 | 'react/jsx-equals-spacing': 'off', 172 | 'react/jsx-tag-spacing': [ 173 | 'off', 174 | { 175 | afterOpening: 'allow', 176 | closingSlash: 'allow', 177 | }, 178 | ], 179 | 'react/jsx-wrap-multilines': 'off', 180 | semi: 'off', 181 | 'space-before-blocks': 'error', 182 | 'space-before-function-paren': 'off', 183 | 'space-in-parens': ['off', 'never'], 184 | 'unicorn/prefer-ternary': 'off', 185 | 'use-isnan': 'error', 186 | 'valid-typeof': 'off', 187 | }, 188 | } 189 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | dist/ 4 | tsconfig.tsbuildinfo -------------------------------------------------------------------------------- /.lintstagedrc.yml: -------------------------------------------------------------------------------- 1 | "*.{ts,js}": 2 | - eslint 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | $schema: 'http://json.schemastore.org/prettierrc', 3 | trailingComma: 'es5', 4 | tabWidth: 2, 5 | semi: false, 6 | singleQuote: true, 7 | arrowParens: 'always', 8 | overrides: [ 9 | { 10 | files: '*.sol', 11 | options: { 12 | // These options are native to Prettier. 13 | printWidth: 100, 14 | tabWidth: 4, 15 | useTabs: false, 16 | singleQuote: false, 17 | bracketSpacing: true, 18 | // These options are specific to the Solidity Plugin 19 | explicitTypes: 'always', 20 | compiler: '>=0.8.15', 21 | }, 22 | }, 23 | ], 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patex-ecosystem/hardhat-deploy-config/d95432948358651814fc9836c1a48189f4183ef5/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright 2020-2021 Patex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @eth-patex/hardhat-deploy-config 2 | 3 | `hardhat-deploy-config` is a simple plugin that adds support for global deploy configuration values. 4 | 5 | ## Installation 6 | 7 | ``` 8 | yarn add @eth-patex/hardhat-deploy-config 9 | ``` 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eth-patex/hardhat-deploy-config", 3 | "version": "1.0.0", 4 | "description": "[Patex] Hardhat deploy configuration plugin", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "dist/*" 9 | ], 10 | "scripts": { 11 | "test:coverage": "echo 'No tests defined.'", 12 | "build": "tsc -p tsconfig.json", 13 | "clean": "rimraf dist/ ./tsconfig.tsbuildinfo", 14 | "lint": "yarn lint:fix && yarn lint:check", 15 | "pre-commit": "lint-staged", 16 | "lint:fix": "yarn lint:check --fix", 17 | "lint:check": "eslint . --max-warnings=0" 18 | }, 19 | "keywords": [ 20 | "patex", 21 | "ethereum", 22 | "hardhat", 23 | "deploy", 24 | "config", 25 | "plugin" 26 | ], 27 | "homepage": "https://github.com/patex-ecosystem/hardhat-deploy-config#readme", 28 | "license": "MIT", 29 | "author": "Patex", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/patex-ecosystem/hardhat-deploy-config.git" 33 | }, 34 | "devDependencies": { 35 | "ethers": "^5.7.0", 36 | "ts-node": "^10.9.1", 37 | "hardhat": "^2.9.6", 38 | "@babel/eslint-parser": "^7.18.2", 39 | "@types/chai": "^4.2.18", 40 | "@types/chai-as-promised": "^7.1.4", 41 | "@types/mocha": "^8.2.2", 42 | "@types/node": "^12.0.0", 43 | "@typescript-eslint/eslint-plugin": "^5.45.1", 44 | "@typescript-eslint/parser": "^5.45.1", 45 | "chai": "^4.2.0", 46 | "copyfiles": "^2.3.0", 47 | "depcheck": "^1.4.3", 48 | "doctoc": "^2.2.0", 49 | "eslint": "^8.16.0", 50 | "eslint-config-prettier": "^8.3.0", 51 | "eslint-config-standard": "^16.0.3", 52 | "eslint-plugin-import": "^2.26.0", 53 | "eslint-plugin-jsdoc": "^35.1.2", 54 | "eslint-plugin-node": "^11.1.0", 55 | "eslint-plugin-prefer-arrow": "^1.2.3", 56 | "eslint-plugin-prettier": "^4.0.0", 57 | "eslint-plugin-promise": "^5.1.0", 58 | "eslint-plugin-react": "^7.24.0", 59 | "eslint-plugin-unicorn": "^42.0.0", 60 | "husky": "^6.0.0", 61 | "lint-staged": "11.0.0", 62 | "markdownlint": "^0.24.0", 63 | "markdownlint-cli2": "0.4.0", 64 | "mkdirp": "^1.0.4", 65 | "nyc": "^15.1.0", 66 | "patch-package": "^6.4.7", 67 | "prettier": "^2.8.0", 68 | "prettier-plugin-solidity": "^1.0.0-beta.13", 69 | "ts-mocha": "^10.0.0", 70 | "typescript": "^4.9.3", 71 | "mocha": "^10.0.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import './type-extensions' 2 | import './plugin' 3 | -------------------------------------------------------------------------------- /src/plugin.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | import * as fs from 'fs' 3 | 4 | import { extendEnvironment, extendConfig } from 'hardhat/config' 5 | import { 6 | HardhatConfig, 7 | HardhatRuntimeEnvironment, 8 | HardhatUserConfig, 9 | } from 'hardhat/types' 10 | import { lazyObject, lazyFunction } from 'hardhat/plugins' 11 | import { ethers } from 'ethers' 12 | 13 | // From: https://github.com/wighawag/hardhat-deploy/blob/master/src/index.ts#L63-L76 14 | const normalizePath = ( 15 | config: HardhatConfig, 16 | userPath: string | undefined, 17 | defaultPath: string 18 | ): string => { 19 | if (userPath === undefined) { 20 | userPath = path.join(config.paths.root, defaultPath) 21 | } else { 22 | if (!path.isAbsolute(userPath)) { 23 | userPath = path.normalize(path.join(config.paths.root, userPath)) 24 | } 25 | } 26 | return userPath 27 | } 28 | 29 | const getDeployConfig = ( 30 | dir: string, 31 | network: string 32 | ): { [key: string]: any } => { 33 | let config: any 34 | try { 35 | const base = `${dir}/${network}` 36 | if (fs.existsSync(`${base}.ts`)) { 37 | // eslint-disable-next-line @typescript-eslint/no-var-requires 38 | config = require(`${base}.ts`).default 39 | } else if (fs.existsSync(`${base}.json`)) { 40 | // eslint-disable-next-line @typescript-eslint/no-var-requires 41 | config = require(`${base}.json`) 42 | } else { 43 | throw new Error('not found') 44 | } 45 | } catch (err) { 46 | throw new Error( 47 | `error while loading deploy config for network: ${network}, ${err}` 48 | ) 49 | } 50 | return config 51 | } 52 | 53 | export const loadDeployConfig = (hre: HardhatRuntimeEnvironment): any => { 54 | const paths = hre.config.paths.deployConfig 55 | const conf = getDeployConfig(paths, hre.network.name) 56 | const spec = parseDeployConfig(hre, conf) 57 | 58 | return new Proxy(spec, { 59 | get: (target, prop) => { 60 | if (target.hasOwnProperty(prop)) { 61 | return target[prop] 62 | } 63 | 64 | // Explicitly throw if the property is not found 65 | throw new Error( 66 | `property does not exist in deploy config: ${String(prop)}` 67 | ) 68 | }, 69 | }) 70 | } 71 | 72 | export const parseDeployConfig = ( 73 | hre: HardhatRuntimeEnvironment, 74 | config: any 75 | ): any => { 76 | // Create a clone of the config object. Shallow clone is fine because none of the input options 77 | // are expected to be objects or functions etc. 78 | const parsed = { ...config } 79 | 80 | // If the deployConfigSpec is not provided, do no validation 81 | if (!hre.config.deployConfigSpec) { 82 | return parsed 83 | } 84 | 85 | for (const [key, spec] of Object.entries(hre.config.deployConfigSpec)) { 86 | // Make sure the value is defined, or use a default. 87 | if (parsed[key] === undefined) { 88 | if ('default' in spec) { 89 | parsed[key] = spec.default 90 | } else { 91 | throw new Error( 92 | `deploy config is missing required field: ${key} (${spec.type})` 93 | ) 94 | } 95 | } else { 96 | // Make sure the default has the correct type. 97 | if (spec.type === 'address') { 98 | if (!ethers.utils.isAddress(parsed[key])) { 99 | throw new Error( 100 | `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}` 101 | ) 102 | } 103 | } else if (typeof parsed[key] !== spec.type) { 104 | throw new Error( 105 | `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}` 106 | ) 107 | } 108 | } 109 | } 110 | 111 | return parsed 112 | } 113 | 114 | extendConfig( 115 | (config: HardhatConfig, userConfig: Readonly) => { 116 | config.paths.deployConfig = normalizePath( 117 | config, 118 | userConfig.paths?.deployConfig, 119 | 'deploy-config' 120 | ) 121 | } 122 | ) 123 | 124 | extendEnvironment((hre) => { 125 | hre.deployConfig = lazyObject(() => loadDeployConfig(hre)) 126 | hre.getDeployConfig = lazyFunction(() => { 127 | const paths = hre.config.paths.deployConfig 128 | return (network: string) => getDeployConfig(paths, network) 129 | }) 130 | }) 131 | -------------------------------------------------------------------------------- /src/type-extensions.ts: -------------------------------------------------------------------------------- 1 | import 'hardhat/types/runtime' 2 | import 'hardhat/types/config' 3 | 4 | import { DeployConfigSpec } from './types' 5 | 6 | declare module 'hardhat/types/config' { 7 | interface HardhatUserConfig { 8 | deployConfigSpec?: DeployConfigSpec 9 | } 10 | 11 | interface HardhatConfig { 12 | deployConfigSpec?: DeployConfigSpec 13 | } 14 | 15 | interface ProjectPathsUserConfig { 16 | deployConfig?: string 17 | } 18 | 19 | interface ProjectPathsConfig { 20 | deployConfig?: string 21 | } 22 | } 23 | 24 | declare module 'hardhat/types/runtime' { 25 | interface HardhatRuntimeEnvironment { 26 | deployConfig: { 27 | [key: string]: any 28 | } 29 | getDeployConfig(network: string): { [key: string]: any } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type DeployConfigSpec< 2 | TDeployConfig extends { 3 | [key: string]: any 4 | } 5 | > = { 6 | [K in keyof TDeployConfig]: { 7 | type: 'address' | 'number' | 'string' | 'boolean' 8 | default?: any 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2017", 5 | "sourceMap": true, 6 | "esModuleInterop": true, 7 | "composite": true, 8 | "resolveJsonModule": true, 9 | "declaration": true, 10 | "noImplicitAny": false, 11 | "removeComments": true, 12 | "noLib": false, 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "rootDir": "./src", 19 | "outDir": "./dist" 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "dist" 24 | ], 25 | "include": [ 26 | "src/**/*" 27 | ] 28 | } --------------------------------------------------------------------------------