├── .editorconfig
├── .gitattributes
├── .gitignore
├── .npmignore
├── .npmignore.swp
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── bin
└── ethdeploy.js
├── dist
├── ethdeploy.js
├── ethdeploy.js.map
└── ethdeploy.min.js
├── ethdeploy-logo.png
├── example
├── .gitignore
├── contracts
│ └── SimpleStore.sol
├── environments.json
├── environments.json_backup_2017-05-15T19:14:14.867Z
├── environments.json_backup_2017-10-22T22:29:29.858Z
├── environments.json_backup_2017-10-22T22:30:12.659Z
├── ethdeploy.testrpc.config.js
├── index.js
└── package.json
├── internals
└── webpack
│ └── webpack.config.js
├── package.json
└── src
├── index.js
├── lib
├── index.js
└── tests
│ └── test.index.js
├── loaders
├── environment
│ ├── index.js
│ └── package.json
├── raw-environment
│ ├── index.js
│ └── package.json
├── raw-solc
│ ├── index.js
│ └── package.json
├── solc-json
│ ├── index.js
│ └── package.json
└── solc
│ ├── index.js
│ └── package.json
├── plugins
├── index.js
└── tests
│ └── test.index.js
├── tests
├── configs
│ ├── ethdeploy.config.minimum.testnet.js
│ ├── ethdeploy.config.minimumNoDefinedPlugins.testnet.js
│ ├── ethdeploy.config.minimumNoOutput.testnet.js
│ ├── ethdeploy.config.minimumNoPlugins.testnet.js
│ ├── ethdeploy.config.noloaders.testnet.js
│ ├── ethdeploy.config.noplugin.testnet.js
│ ├── ethdeploy.config.nopreloaders.testnet.js
│ ├── ethdeploy.config.notxobject.testnet.js
│ ├── ethdeploy.config.rawenv.testnet.js
│ ├── ethdeploy.config.rawsolc.testnet.js
│ ├── ethdeploy.config.solcLoader.testnet.js
│ ├── ethdeploy.config.stringEntry.testnet.js
│ ├── ethdeploy.config.testnet.js
│ └── ethdeploy.config.zeroloaders.testnet.js
├── contracts
│ ├── SimpleStore.sol
│ └── test.SimpleStore.sol
└── test.index.js
└── utils
├── index.js
└── tests
├── test.index.js
└── testSources
├── someDir
├── AnotherDir
│ └── something.json
├── anotherFile
└── someDeeperDir
│ └── AnotherDeeperDir
│ └── anotherFile
├── someFile.json
└── someFile.s
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = false
6 | indent_style = space
7 | indent_size = 2
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
2 |
3 | # Handle line endings automatically for files detected as text
4 | # and leave all files detected as binary untouched.
5 | * text=auto
6 |
7 | #
8 | # The above will handle all files NOT found below
9 | #
10 |
11 | #
12 | ## These files are text and should be normalized (Convert crlf => lf)
13 | #
14 |
15 | # source code
16 | *.php text
17 | *.css text
18 | *.sass text
19 | *.scss text
20 | *.less text
21 | *.styl text
22 | *.js text eol=lf
23 | *.coffee text
24 | *.json text
25 | *.htm text
26 | *.html text
27 | *.xml text
28 | *.svg text
29 | *.txt text
30 | *.ini text
31 | *.inc text
32 | *.pl text
33 | *.rb text
34 | *.py text
35 | *.scm text
36 | *.sql text
37 | *.sh text
38 | *.bat text
39 |
40 | # templates
41 | *.ejs text
42 | *.hbt text
43 | *.jade text
44 | *.haml text
45 | *.hbs text
46 | *.dot text
47 | *.tmpl text
48 | *.phtml text
49 |
50 | # server config
51 | .htaccess text
52 |
53 | # git config
54 | .gitattributes text
55 | .gitignore text
56 | .gitconfig text
57 |
58 | # code analysis config
59 | .jshintrc text
60 | .jscsrc text
61 | .jshintignore text
62 | .csslintrc text
63 |
64 | # misc config
65 | *.yaml text
66 | *.yml text
67 | .editorconfig text
68 |
69 | # build config
70 | *.npmignore text
71 | *.bowerrc text
72 |
73 | # Heroku
74 | Procfile text
75 | .slugignore text
76 |
77 | # Documentation
78 | *.md text
79 | LICENSE text
80 | AUTHORS text
81 |
82 |
83 | #
84 | ## These files are binary and should be left untouched
85 | #
86 |
87 | # (binary is a macro for -text -diff)
88 | *.png binary
89 | *.jpg binary
90 | *.jpeg binary
91 | *.gif binary
92 | *.ico binary
93 | *.mov binary
94 | *.mp4 binary
95 | *.mp3 binary
96 | *.flv binary
97 | *.fla binary
98 | *.swf binary
99 | *.gz binary
100 | *.zip binary
101 | *.7z binary
102 | *.ttf binary
103 | *.eot binary
104 | *.woff binary
105 | *.pyc binary
106 | *.pdf binary
107 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Don't check auto-generated stuff into git
2 | node_modules
3 | package-lock.json
4 | coverage
5 |
6 | lib
7 | !src/lib/
8 | !src/lib
9 | !src/*
10 |
11 | # Cruft
12 | .DS_Store
13 | npm-debug.log
14 |
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | example
2 |
--------------------------------------------------------------------------------
/.npmignore.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilentCicero/ethdeploy/acb1212e1942ce604eb82e34ce2f8c9d6b20a0a6/.npmignore.swp
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: true
2 | language: node_js
3 | node_js:
4 | - "6"
5 | compiler:
6 | - gcc
7 | - clang
8 | install:
9 | env:
10 | - CXX=g++-4.8
11 | addons:
12 | apt:
13 | sources:
14 | - ubuntu-toolchain-r-test
15 | packages:
16 | - gcc-4.8
17 | - g++-4.8
18 | - clang
19 | after_success: npm run coveralls
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.0.0 -- main export, tests
2 |
3 | 1. Basic testing
4 | 2. Basic docs
5 | 3. License
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of
4 | fostering an open and welcoming community, we pledge to respect all people who
5 | contribute through reporting issues, posting feature requests, updating
6 | documentation, submitting pull requests or patches, and other activities.
7 |
8 | We are committed to making participation in this project a harassment-free
9 | experience for everyone, regardless of level of experience, gender, gender
10 | identity and expression, sexual orientation, disability, personal appearance,
11 | body size, race, ethnicity, age, religion, or nationality.
12 |
13 | Examples of unacceptable behavior by participants include:
14 |
15 | * The use of sexualized language or imagery
16 | * Personal attacks
17 | * Trolling or insulting/derogatory comments
18 | * Public or private harassment
19 | * Publishing other's private information, such as physical or electronic
20 | addresses, without explicit permission
21 | * Other unethical or unprofessional conduct
22 |
23 | Project maintainers have the right and responsibility to remove, edit, or
24 | reject comments, commits, code, wiki edits, issues, and other contributions
25 | that are not aligned to this Code of Conduct, or to ban temporarily or
26 | permanently any contributor for other behaviors that they deem inappropriate,
27 | threatening, offensive, or harmful.
28 |
29 | By adopting this Code of Conduct, project maintainers commit themselves to
30 | fairly and consistently applying these principles to every aspect of managing
31 | this project. Project maintainers who do not follow or enforce the Code of
32 | Conduct may be permanently removed from the project team.
33 |
34 | This Code of Conduct applies both within project spaces and in public spaces
35 | when an individual is representing the project or its community.
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
38 | reported by contacting the project maintainer at nick.dodson@consensys.net. All
39 | complaints will be reviewed and investigated and will result in a response that
40 | is deemed necessary and appropriate to the circumstances. Maintainers are
41 | obligated to maintain confidentiality with regard to the reporter of an
42 | incident.
43 |
44 |
45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
46 | version 1.3.0, available at
47 | [http://contributor-covenant.org/version/1/3/0/][version]
48 |
49 | [homepage]: http://contributor-covenant.org
50 | [version]: http://contributor-covenant.org/version/1/3/0/
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2016 Nick Dodson. nickdodson.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## ethdeploy | webpack for smart-contracts ;)
4 |
5 | A first pass at a highly configurable contract staging and deployment utility.
6 |
7 | Made with ❤︎ by Nick Dodson. If you're using this tool, we'd love to hear from you!
8 |
9 | ## Features
10 | - Highly unopinionated
11 | - Just deployment, that's it! (does not compile or tests contracts, but plugins though ;=D)
12 | - Composable, to be integrated into other things like `webpack` loaders, the `cli` or any other frameworks
13 | - Extremely configurable (deploy contracts with different settings to multiple environments in different ways)
14 | - Extensible, deployment staging can happen in any environment to any environment
15 | - Lightly abstracted, promisified but mostly unopinionated deployment scripting (maybe no promises in the future though)
16 | - Lightweight, does not include a ton of dependencies
17 | - Simple and robust, intakes data/config >> outputs result data
18 | - Is meant to aid in contract deployment, not dictate entire application design
19 |
20 | ## About
21 |
22 | Deploy your Ethereum smart-contracts to multiple environments, with a range of configurations, using lightly abstracted promisified deployment staging modules. The end result is an environments object, that contains all configured contract information (e.g. address, receipt, gas, abi, etc.) for each selected environment and their contracts.
23 |
24 | Once central purpose of this module is to deploy contracts which need to be deployed, and skip the deployment of contracts which have already been deployed with the same inputs and bytecode.
25 |
26 | Note, this module is highly experimental and requires more testing and research before being using on the main network. Use at your own risk.
27 |
28 | ## Installation
29 |
30 | ```
31 | npm install --save ethdeploy
32 | ```
33 |
34 | ## Example
35 |
36 | Checkout the ethdeploy [example](/example/index.js) provided. This will launch a testrpc server (a dummy ethereum api) in the background, and then deploy a bunch of contracts.
37 |
38 | ```
39 | npm install
40 | cd example
41 | npm install
42 | npm start
43 | ```
44 |
45 | ## CLI
46 |
47 | The CLI allows you to deploy an ethdeploy module from the command line. You can use this globally or loally.
48 |
49 | ```
50 | ethdeploy ./ethdeploy.testnet.js
51 |
52 | // or locally as:
53 |
54 | node ./node_modules/ethdeploy/bin/ethdeploy.js ./ethdeploy.testnet.js
55 | #NOTE: currently it is necessary to first
56 | (cd node_modules/ethdeploy/ && npm install)
57 | ```
58 |
59 | ## Example Deployment Module
60 |
61 | Here we have an example `ethdeploy` deployment configuration module.
62 |
63 | ```js
64 | const HttpProvider = require('ethjs-provider-http');
65 |
66 | module.exports = (options) => ({ // eslint-disable-line
67 | entry: [
68 | './environments.json',
69 | './src/tests/contracts',
70 | ],
71 | output: {
72 | path: './',
73 | filename: 'environments.json',
74 | safe: true,
75 | },
76 | module: {
77 | environment: {
78 | name: 'localtestnet',
79 | provider: new HttpProvider('http://localhost:8545'),
80 | defaultTxObject: {
81 | from: 1,
82 | gas: 3000001,
83 | },
84 | },
85 | preLoaders: [
86 | { test: /\.(json)$/, loader: 'ethdeploy-environment-loader' },
87 | ],
88 | loaders: [
89 | { test: /\.(sol)$/, loader: 'ethdeploy-solc-loader', optimize: 1 },
90 | ],
91 | deployment: (deploy, contracts, done) => {
92 | deploy(contracts.SimpleStore, { from: 0 }).then(() => {
93 | done();
94 | });
95 | },
96 | },
97 | plugins: [
98 | new options.plugins.JSONFilter(),
99 | new options.plugins.JSONMinifier(),
100 | ],
101 | });
102 | ```
103 |
104 | Here we have a simple configuraton loading in previous contract builds, and new contract data. The deployment module just deploys the single contract and fires the `done` method, which will then output the result in an `environments.json` file as specified by the `config` output. The `solc` loader loads new contract data from `.sol` files, while the `environments` loader processes data from `.json` files. The default environment states that account `1` should be used to deploy contracts, while in deployment, the developer state they want the `SimpleStore` contract to be deployed from account `0`.
105 |
106 | This module will produce JSON like this:
107 |
108 | ```js
109 | {
110 | "localtestnet": {
111 | "SimpleStore": {
112 | "bytecode": "0x...",
113 | "interface": "[{....}]",
114 | "address": "0x3a70a6765746af3bfa974fff9d753d4b6c56b333",
115 | "inputs": [],
116 | "transactionObject": {
117 | "from": "0x7f3e74e3dbb4091973ea1b449692c504c35ef768",
118 | "gas": 3000001
119 | }
120 | }
121 | }
122 | }
123 | ```
124 |
125 | ## Config Module Description
126 |
127 | Ethdeploy modules are `Object`s or `Funtion`s, much like webpack modules. You specify a single deployment environment per file. This includes the inputs, loaders, environment, deployment schedule and output. This follows in some way the webpack data processing flow, from entry, to output.
128 |
129 | ```js
130 | module.exports = {
131 | entry: [], // entry data, usually paths to files or directors
132 | output: {}, // output specifications, output file name etc
133 | module: {}, // the deployment configuration, environment, deployment schedule
134 | plugins: {}, // plugins that format output data, do special things
135 | };
136 | ```
137 |
138 | ### Entry
139 |
140 | The module entry is usually file or directory paths, but can actually be any kind of object. If you would like to use your own custom object, you can specify your own `module.sourceMapper` which will help produce a sourcemap of the entry for loaders to intake.
141 |
142 | ### Output
143 |
144 | The output specifies information about the output file or object. Usually things like the output filename.
145 |
146 | ### Module
147 |
148 | The module is where you specify all your deployment environment and schedule. This is where the action happens for `ethdeploy`.
149 |
150 | ### Plugins
151 |
152 | Plugins help format the output which is usually JSON. It processes output data into a format that you want.
153 |
154 | ## Data Processing Flow
155 |
156 | `ethdeploy` is designed to help load and deploy your Ethereum contracts, then output the data (in a file or object). The data process flow is at first glass complicated, but is designed for complete configuratbility of contract deployment of Ethereum contracts. Here is the `ethdeploy` data processing flow. In simple terms, `ethdeploy` intakes the configuration module, and should output a single data output/file.
157 |
158 | Stages of processing:
159 |
160 | 1. [source mapping of entry] : source map all entry data into a single output source map object
161 | 2. [environment configuration] : configure environment (load accounts, set gas and defaults)
162 | 3. [pre loader processing] : load all pre configured data such as previous builds/deployments
163 | 4. [loader processing] : load all new data, like new contract bytecode or interfaces
164 | 5. [deployment module processing] : run the module.deployment method, which will trigger the deployment process
165 | 6. [output plugin processing] : run the specified output plugins if any
166 | 7. [final data write/output] : write the final output object/data
167 |
168 | ## `ethdeploy` module
169 |
170 | The `ethdeploy` module can be required and used in normal nodejs javascript contexts. `ethdeploy` should also be able to be used client-side in the browser, although this has not beed tested yet. Here is the `ethdeploy` using in a nodejs context. The module simply intakes the config file and returns a standard callback result.
171 |
172 | ```js
173 | const ethdeploy = require('ethdeploy');
174 | const deploymentConfig = require('ethdeploy.testrpc.config.js');
175 |
176 | ethdeploy(deploymentConfig, (err, result) => {
177 | console.log(err, result);
178 | });
179 | ```
180 |
181 | ## Loaders
182 |
183 | Ethdeploy has a simple loader API that allows you to build or plugin existing loaders. There are two kinds of loaders, `preLoaders` and `loaders`. PreLoaders are for pre data change loading, such as loading in previous environments or deployment data. The loaders are for loading in new contract data like new build data from a recent solc build. The loader loads in a sourceMapped data object from the module sourcemapper, then spits out a environment JSON structure object.
184 |
185 | Exmaple loader:
186 |
187 | ```js
188 | /**
189 | * Loads an environments.json file, produced by ethdeploy
190 | *
191 | * @method loader
192 | * @param {Object} sourceMap the file source map
193 | * @param {Object} loaderConfig the config for the specified loader
194 | * @param {Object} environment the loaded environment object
195 | * @return {Object} contracts the output contracts object
196 | */
197 | module.exports = function loader(sourceMap, loaderConfig, environment) { // eslint-disable-line
198 | // loader code
199 | }
200 | ```
201 |
202 | ### Available Loaders:
203 |
204 | Here are some available loaders. The `environment` and `solc` loaders are most likely the ones you would use the most (i.e. loading your previous deployments and your new contract builds).
205 |
206 | - `ethdeploy-environment-loader`: loads standard ethdeploy environment files (for loading previous deployments)
207 | - `ethdeploy-solc-loader`: compiles `.sol` Ethereum contracts for ethdeploy (for loading solc contract data)
208 | - `ethdeploy-solc-json-loader`: loads and processes solc-json files (the output from solc as a JSON)
209 |
210 | ### Loader Config
211 |
212 | Ethdeploy loaders, much like webpack loaders, use regex to test if the sourcemap presented should be laoded by the required loader module. There are three regex properties you can use to select the correct files for your loader.
213 |
214 | - `test`: regex test must be positive to include in loader
215 | - `include`: regex test must be positive or null to include in loader
216 | - `exclude`: if specified, file path must be negative against this test to include in loader
217 |
218 | Sometimes you want specific files to be excluded from specific loaders, like tests in final build and deployment. The `exclude` test is good for this, it allows you to do things like exclude Solidity test files from final build deployment.
219 |
220 | ## Plugins
221 |
222 | Plugins help format the output data, they simple intake the data string (usually a JSON string) and format that data however the developer wants. The most used plugin is the JSON minifier plugin which just minifies the outputted JSON information. There is a default set of plugins which are fed in through the ethdeploy method options input. See the `example` for more details.
223 |
224 | Here is the JSON Minifier plugin:
225 |
226 | ```js
227 | /**
228 | * Minifies JSON output
229 | *
230 | * @method JSONMinifier
231 | * @param {String} output the final build file produced by ethdeploy
232 | * @return {String} parsedOutput parsed output
233 | */
234 | function JSONMinifier() {
235 | const self = this;
236 | self.process = ({ output, baseContracts, contracts, environment }) => JSON.stringify(JSON.parse(output));
237 | }
238 | ```
239 |
240 | ### Available Plugins
241 |
242 | Here are some available plugins for `ethdeploy`. The main one will most likely be the `JSONMinifier` plugin, used to minify output JSON. Note, these plugins come with ethdeploy and are fed in through the options object of your deployment module (if you use a type `Function` module).
243 |
244 | - `JSONMinifier`: minifies output JSON from ethdeploy
245 | - `JSONExpander`: expands output JSON from ethdeploy
246 | - `JSONFilter`: filters the JSON output to `address`, `bytecode`, `interface`, `transactionObject` and `inputs` properties.
247 | - `IncludeContracts` includes selected contracts from the build process and includes them in a special `contracts` environment
248 |
249 | #### IncludeContracts Plugin
250 |
251 | This is an overview of the important IncludeContracts plugin. This plugin is used to include certain contracts that you may not need to deploy, but do need to include the interface or bytecode of for your dApp.
252 |
253 | Example in Use:
254 |
255 | ```js
256 |
257 | plugins:
258 | new options.plugins.IncludeContracts(['SimpleStoreInterface', 'Token', 'Proxy']),
259 |
260 | new options.plugins.JSONFilter(),
261 | new options.plugins.JSONMinifier(),
262 | ],
263 | ```
264 |
265 | Here we see the IncludeContracts plugin including the interface to the `SimpleStore` contract and the `Token` interface and the `Proxy` contract. This will then produce an output object like this:
266 |
267 | ```js
268 | {
269 | "ropsten": {
270 | "SimpleStore": {
271 | "bytecode": "0x...",
272 | "interface": "[{....}]",
273 | "address": "0x3a70a6765746af3bfa974fff9d753d4b6c56b333",
274 | "inputs": [],
275 | "transactionObject": {
276 | "from": "0x7f3e74e3dbb4091973ea1b449692c504c35ef768",
277 | "gas": 3000001
278 | }
279 | }
280 | },
281 | "contracts": {
282 | "SimpleStoreInterface": {
283 | "bytecode": "0x...",
284 | "interface": "[{....}]",
285 | },
286 | "Token": {
287 | "bytecode": "0x...",
288 | "interface": "[{....}]",
289 | },
290 | "Proxy": {
291 | "bytecode": "0x...",
292 | "interface": "[{....}]",
293 | }
294 | }
295 | }
296 | ```
297 |
298 | Now with this input, you can take the interface data into your dApp. Note, this will override the contracts environment everytime.
299 |
300 | ## Deployment Modules
301 |
302 | The `ethdeploy` config allows you to specify your deployment in the `module` object. Within this module are a few critical configuration requirements. In your module you must specify a `environment`, and `deployment` function. Loaders should also be used to load in the entry data into the deployment module. The loaders will intake data loaded in from the source mapping stage, and output the final data to the `contracts` object used in `module.deployment`. The `deploy` method intakes the formatted contract data, compares it with what was loaded at the preLoaded stage, if there are different properties like `bytecode` or new contracts, it will deploy those, otherwise it will skip and return the exiting contract instance. Once the deployment module is done, the `done` method should be fired, to trigger the output processing.
303 |
304 | Example:
305 |
306 | ```js
307 | module: {
308 | environment: {
309 | name: 'localtestnet',
310 | provider: new HttpProvider('http://localhost:8545'),
311 | defaultTxObject: {
312 | from: 1,
313 | gas: 3000001,
314 | },
315 | },
316 | preLoaders: [
317 | { test: /\.(json)$/, loader: 'ethdeploy-environment-loader' },
318 | ],
319 | loaders: [
320 | { test: /\.(sol)$/, loader: 'ethdeploy-solc-loader', optimize: 1 },
321 | ],
322 | deployment: (deploy, contracts, done) => {
323 | deploy(contracts.SimpleStore, 'constructor argument 1', 'argument 2...', { from: 0 }).then(() => done());
324 | },
325 | },
326 | ```
327 |
328 | ### environment
329 |
330 | The environment specifies your deployment env. provider, name and default transaction object.
331 |
332 | ### preLoaders
333 |
334 | The pre-loaders load all previous deployment information, such as previous contract builds or deployoments.
335 |
336 | ### loaders
337 |
338 | The loaders load all current contract builds, such as new solc contracts or new contract bytecode.
339 |
340 | ### deployment
341 |
342 | The deployment module is where the contract deployment schedule is specified. This is where the main action happens for contract deployment. The `deploy` method is used to deploy pre-formatted `contracts` data. Once the process is completed, the `done` method should be fired to complete the process.
343 |
344 | ## Deloyment Scheduling
345 |
346 | Ethdeploy allows you to specify your own complex deployment schedule. The inputs provided to the deployment property are `deploy`, `contracts`, `done` and `environment`. The `deploy` method is used to deploy the contract object. The `contracts` object is fed in by the loaders, and is used by the `deploy` method to deploy the contracts. The `done` method should be fired at the end of deployment to stop the deployment process and begin the outputting process. The `environment` object is used for including environmental information into your schedule like accounts, balances and other things of this sort.
347 |
348 | Basic Example:
349 |
350 | ```js
351 | deployment: (deploy, contracts, done) => {
352 | deploy(contracts.SimpleStore).then(() => done());
353 | },
354 | ```
355 |
356 | Here we have a very basic deployment schedule. This will deploy contract SimpleStore with the `deploy` method. Then once the contract is deployed, the `done` method is fired.
357 |
358 |
359 | Complex Example:
360 |
361 | ```js
362 | deployment: (deploy, contracts, done) => {
363 | deploy(contracts.SimpleStore, 45, 'My Simple Store', { from: 0 })
364 | .then((contractInstance) => deploy(contracts.StandardToken, contractInstance.address))
365 | .then(done);
366 | },
367 | ```
368 |
369 | Here we have a more complex example. The first contract `SimpleStore` is being deployed with two constructor arguments, (1) the value `45` and (2) the String `'My Simple Store'`. Contract `SimpleStore` is being deployed from account `0`, as specified by the transaction object `{ from: 0 }`. Then once `SimpleStore` is deployed, the StandardToken contract is being deployed with a single constructor argument, the address of the newly deployed `SimpleStore` contract instance. Once the `StandardToken` contract is deployed, the `done` method is fired to end the deployment schedule.
370 |
371 | This is a more complex deployment example, where one contract relies on the others address for deployment. Also note that if SimpleStore had beed deployed previously for example, the outputted environment was loaded back into `ethdeploy`, it would not be redeployed if all inputs are the same. The inputs to re-interate are: `address`, `transactionObject`, `bytecode`, `inputs` and `interface`. If any of these values had changed, then the `SimpleStore` contract would re-deploy, otherwise the contractInstance retured is simply that of the previously deployed contract.
372 |
373 | ## Environments Object (the output object)
374 |
375 | `ethdeploy` will output your contracts either as an object within execution or to a file system, if used in the CLI. The final object output follows a very simple organizational pattern. Namely, envrionment, then contracts. A single environments output can contain multiple environments and multiple deployments of different contracts within each environment.
376 |
377 | Example output:
378 |
379 | ```js
380 | {
381 | "ropsten": {
382 | "SimpleStore": {
383 | "address": "0x..",
384 | "bytecode": "0x..",
385 | "interface": "[{}]",
386 | },
387 | "SimpleStoreFactory": {
388 | "address": "0x..",
389 | "bytecode": "0x..",
390 | "interface": "[{}]",
391 | }
392 | }
393 | }
394 | ```
395 |
396 | Above, we see that there is the environment ropsten and the subsequent contracts deployed to environment `ropsten`. From this object, you would either include or `require` the object into your dApp, where it can be used by the front end.
397 |
398 | The common properties used by `ethdeploy` in the outputted environments JSON are:
399 |
400 | 1. `address` {String} the address of the deployed contract instance
401 | 2. `transactionObject` {Object} the transaction object used to deploy that contract instance
402 | 3. `bytecode` {String} the Ethereum virtual machine code (bytecode) of that contract instance
403 | 4. `inputs` {Array} the inputs used to deploy that contract (with numbers formatted as hex base 16)
404 | 5. `interface` {String} the interface of the contract, specified as a JSON string
405 |
406 | These properties are both outputed by ethdeploy and looked at by the default deployment method `deploy` when the deployment schedule is being used.
407 |
408 | Other additional properties are:
409 |
410 | 1. `receipt` {Object} the transaction receipt data
411 | 2. `assembly` {Object} the contract assembly code
412 |
413 | ### License
414 |
415 | This module is under The MIT License. Please see the `LICENCE` file for more details.
416 |
--------------------------------------------------------------------------------
/bin/ethdeploy.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const meow = require('meow');
4 | const path = require('path');
5 | const fs = require('fs');
6 | const ethdeploy = require('../src/index.js');
7 | const log = require('../src/utils/index.js').log;
8 |
9 | function noop2Callback(v, d, cb) {
10 | cb(null, null);
11 | }
12 |
13 | function renameIfExsits(renamePath, renamePathOutput, cb) {
14 | if (fs.existsSync(renamePath)) {
15 | return fs.rename(renamePath, renamePathOutput, cb);
16 | }
17 |
18 | cb(null, null);
19 | return null;
20 | }
21 |
22 | // handle cli
23 | const cli = meow(`
24 | Usage
25 | $ ethdeploy
26 | Options
27 | --help the help CLI
28 | --version, -v the package verson number
29 | Example
30 | $ ethdeploy ./ethdeploy.config.testnet.js
31 | `, {
32 | alias: {},
33 | });
34 |
35 | if (typeof cli.input[0] === 'undefined') {
36 | cli.showHelp();
37 | } else {
38 | const configPath = path.resolve(cli.input[0]);
39 | const configObject = require(configPath); // eslint-disable-line
40 |
41 | ethdeploy(configObject, (deployError, deployResult) => {
42 | if (deployError) {
43 | log('Deployment error', deployError);
44 | process.exit(1);
45 | }
46 |
47 | // config from result
48 | const config = deployResult.config;
49 |
50 | // if config output file is specified
51 | if (typeof config.output === 'object') {
52 | const outputString = deployResult.output;
53 | const outputFile = path.resolve(config.output.path, config.output.filename);
54 | const outputSafe = config.output.safe || false;
55 | const outputFileSafe = `${outputFile}_backup_${(new Date()).toISOString()}`;
56 | const renameMethod = outputSafe ? renameIfExsits : noop2Callback;
57 |
58 | // backup output from previous builds
59 | renameMethod(outputFile, outputFileSafe, renameError => {
60 | if (renameError) {
61 | log(`while writting safe output backup file: ${renameError}`);
62 | process.exit(1);
63 | }
64 |
65 | fs.writeFile(outputFile, outputString, (writeFileError) => {
66 | if (writeFileError) {
67 | log(`while writting output file to ${outputFile}: ${writeFileError}`);
68 | process.exit(1);
69 | }
70 |
71 | log(`Deployment file written to: ${outputFile}`);
72 | });
73 | });
74 | }
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/ethdeploy-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilentCicero/ethdeploy/acb1212e1942ce604eb82e34ce2f8c9d6b20a0a6/ethdeploy-logo.png
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/example/contracts/SimpleStore.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.4;
2 |
3 | contract SimpleStore {
4 | uint256 public store;
5 |
6 | function SimpleStore(uint256 _initialValue) public {
7 | store = _initialValue;
8 | }
9 |
10 | function setStore(uint256 _value) public {
11 | store = _value;
12 | }
13 |
14 | function getValue() public constant returns (uint256) {
15 | return store;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/environments.json:
--------------------------------------------------------------------------------
1 | {
2 | "testrpc": {
3 | "contracts/SimpleStore.sol:SimpleStore": {
4 | "bytecode": "6060604052341561000f57600080fd5b60405160208061014b833981016040528080519060200190919050508060008190555050610109806100426000396000f3006060604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551460585780637f626f1a14607e578063975057e714609e575b600080fd5b3415606257600080fd5b606860c4565b6040518082815260200191505060405180910390f35b3415608857600080fd5b609c600480803590602001909190505060cd565b005b341560a857600080fd5b60ae60d7565b6040518082815260200191505060405180910390f35b60008054905090565b8060008190555050565b600054815600a165627a7a72305820848c6b285029df686c3ea20ad4e3351824524f5fde4b95dc5257b0c3c8c80e5a0029",
5 | "interface": "[{\"constant\":true,\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setStore\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"store\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialValue\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]",
6 | "address": "0x3f7db06cf4e3b31b104c3aff154adfbcbf0a07fa",
7 | "inputs": [
8 | 458977,
9 | {
10 | "from": "0x491e14be6fc0d633c2f8f20da25fe1962dba4ff3"
11 | }
12 | ],
13 | "transactionObject": {
14 | "from": "0x491e14be6fc0d633c2f8f20da25fe1962dba4ff3",
15 | "gas": 3000001
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/example/environments.json_backup_2017-05-15T19:14:14.867Z:
--------------------------------------------------------------------------------
1 | {
2 | "testrpc": {
3 | "SimpleStore": {
4 | "bytecode": "606060405234610000576040516020806100fd833981016040528080519060200190919050505b806000819055505b505b60c08061003d6000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480632096525514604a5780637f626f1a14606a578063975057e7146084575b6000565b34600057605460a4565b6040518082815260200191505060405180910390f35b346000576082600480803590602001909190505060af565b005b34600057608e60ba565b6040518082815260200191505060405180910390f35b600060005490505b90565b806000819055505b50565b6000548156",
5 | "interface": "[{\"constant\":true,\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setStore\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"store\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialValue\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"constructor\"}]",
6 | "address": "0xb48cb3dc0f55ffb06a4810fdb1142cd2ae3a61a7",
7 | "inputs": [
8 | 458977,
9 | {
10 | "from": "0xc9dd9662133b86564046506ce9dc7ade1898e1a6"
11 | }
12 | ],
13 | "transactionObject": {
14 | "from": "0xc9dd9662133b86564046506ce9dc7ade1898e1a6",
15 | "gas": 3000001
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example/environments.json_backup_2017-10-22T22:29:29.858Z:
--------------------------------------------------------------------------------
1 | {
2 | "testrpc": {
3 | "SimpleStore": {
4 | "bytecode": "606060405234610000576040516020806100fd833981016040528080519060200190919050505b806000819055505b505b60c08061003d6000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480632096525514604a5780637f626f1a14606a578063975057e7146084575b6000565b34600057605460a4565b6040518082815260200191505060405180910390f35b346000576082600480803590602001909190505060af565b005b34600057608e60ba565b6040518082815260200191505060405180910390f35b600060005490505b90565b806000819055505b50565b6000548156",
5 | "interface": "[{\"constant\":true,\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setStore\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"store\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialValue\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"constructor\"}]",
6 | "address": "0xf5fcf5c4301a8d4a78127ffdd455f224812436cb",
7 | "inputs": [
8 | 458977,
9 | {
10 | "from": "0x94eec4dd61d256c7ff79f07798b8ed6188e72ccf"
11 | }
12 | ],
13 | "transactionObject": {
14 | "from": "0x94eec4dd61d256c7ff79f07798b8ed6188e72ccf",
15 | "gas": 3000001
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/example/environments.json_backup_2017-10-22T22:30:12.659Z:
--------------------------------------------------------------------------------
1 | {
2 | "testrpc": {
3 | "contracts/SimpleStore.sol:SimpleStore": {
4 | "bytecode": "6060604052341561000f57600080fd5b60405160208061014b833981016040528080519060200190919050508060008190555050610109806100426000396000f3006060604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063209652551460585780637f626f1a14607e578063975057e714609e575b600080fd5b3415606257600080fd5b606860c4565b6040518082815260200191505060405180910390f35b3415608857600080fd5b609c600480803590602001909190505060cd565b005b341560a857600080fd5b60ae60d7565b6040518082815260200191505060405180910390f35b60008054905090565b8060008190555050565b600054815600a165627a7a72305820848c6b285029df686c3ea20ad4e3351824524f5fde4b95dc5257b0c3c8c80e5a0029",
5 | "interface": "[{\"constant\":true,\"inputs\":[],\"name\":\"getValue\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"setStore\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"store\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialValue\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]",
6 | "address": "0x0603a73643c3dfeb7dc0bd37555cea9eb8edb6f7",
7 | "inputs": [
8 | 458977,
9 | {
10 | "from": "0x5039d118b33d197451d794ccd0376281caeccc20"
11 | }
12 | ],
13 | "transactionObject": {
14 | "from": "0x5039d118b33d197451d794ccd0376281caeccc20",
15 | "gas": 3000001
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/example/ethdeploy.testrpc.config.js:
--------------------------------------------------------------------------------
1 | const TestRPC = require('ethereumjs-testrpc'); // eslint-disable-line
2 |
3 | module.exports = (options) => ({ // eslint-disable-line
4 | entry: [
5 | './environments.json',
6 | './contracts',
7 | ],
8 | output: {
9 | path: './',
10 | filename: 'environments.json',
11 | safe: true,
12 | },
13 | module: {
14 | environment: {
15 | name: 'testrpc',
16 | provider: TestRPC.provider(),
17 | defaultTxObject: {
18 | from: 1,
19 | gas: 3000001,
20 | },
21 | },
22 | preLoaders: [
23 | { test: /\.(json)$/, loader: 'ethdeploy-environment-loader', build: true },
24 | ],
25 | loaders: [
26 | { test: /\.(sol)$/, loader: 'ethdeploy-solc-loader' },
27 | ],
28 | deployment: (deploy, contracts, done) => {
29 | deploy(contracts['contracts/SimpleStore.sol:SimpleStore'], 458977, { from: 0 }).then(() => {
30 | done();
31 | });
32 | },
33 | },
34 | plugins: [
35 | new options.plugins.JSONFilter(),
36 | new options.plugins.JSONExpander(),
37 | ],
38 | });
39 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | const contracts = require('./environments.json'); // eslint-disable-line
2 |
3 | console.log('This is being logged from the index.js', contracts); // eslint-disable-line
4 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-example",
3 | "version": "1.0.0",
4 | "description": "A simple ethdeploy example configuration ",
5 | "main": "index.js",
6 | "scripts": {
7 | "deploy": "node ../bin/ethdeploy.js ./ethdeploy.testrpc.config.js",
8 | "start": "npm run deploy && node ./index.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "private": true,
12 | "keywords": [
13 | "ethdeploy",
14 | "example"
15 | ],
16 | "author": "Nick Dodson",
17 | "license": "MIT",
18 | "dependencies": {
19 | "ethdeploy-solc-loader": "file:../src/loaders/solc",
20 | "ethdeploy-environment-loader": "file:../src/loaders/environment",
21 | "ethdeploy": "file:../",
22 | "ethereumjs-testrpc": "*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/internals/webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack'); // eslint-disable-line
2 |
3 | var env = process.env.NODE_ENV; // eslint-disable-line
4 | var filename = 'ethdeploy'; // eslint-disable-line
5 | var library = 'ethdeploy'; // eslint-disable-line
6 | var config = { // eslint-disable-line
7 | entry: [
8 | './lib/index.js',
9 | ],
10 | module: {
11 | loaders: [
12 | {
13 | test: /\.js$/,
14 | loaders: ['babel-loader'],
15 | exclude: /node_modules/,
16 | },
17 | {
18 | test: /\.json$/,
19 | loader: 'json',
20 | },
21 | ],
22 | },
23 | node: {
24 | fs: 'empty',
25 | },
26 | devtool: 'cheap-module-source-map',
27 | output: {
28 | path: 'dist',
29 | filename: filename + '.js', // eslint-disable-line
30 | library: library, // eslint-disable-line
31 | libraryTarget: 'umd',
32 | umdNamedDefine: true,
33 | },
34 | plugins: [
35 | new webpack.BannerPlugin({ banner: ' /* eslint-disable */ ', raw: true, entryOnly: true }),
36 | new webpack.optimize.OccurrenceOrderPlugin(),
37 | new webpack.DefinePlugin({
38 | 'process.env.NODE_ENV': JSON.stringify(env),
39 | }),
40 | ],
41 | };
42 |
43 |
44 | if (env === 'production') {
45 | config.output.filename = filename + '.min.js'; // eslint-disable-line
46 | config.plugins
47 | .push(new webpack.optimize.UglifyJsPlugin({
48 | compressor: {
49 | pure_getters: true,
50 | unsafe: true,
51 | unsafe_comps: true,
52 | warnings: false,
53 | screw_ie8: false,
54 | },
55 | mangle: {
56 | screw_ie8: false,
57 | },
58 | output: {
59 | screw_ie8: false,
60 | },
61 | }));
62 | config.plugins.push(new webpack.optimize.DedupePlugin());
63 | }
64 |
65 |
66 | module.exports = config;
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy",
3 | "version": "1.0.8",
4 | "description": "A highly configurable contract deployment and staging utiltiy.",
5 | "main": "lib/index.js",
6 | "files": [
7 | "dist",
8 | "internals",
9 | "bin",
10 | "lib",
11 | "src"
12 | ],
13 | "bin": {
14 | "ethdeploy": "./bin/ethdeploy.js"
15 | },
16 | "scripts": {
17 | "start": "npm test",
18 | "release": "npmpub",
19 | "pretest": "npm run lint",
20 | "prepublish": "npm run test",
21 | "prebuild": "npm run build:clean && npm run test",
22 | "build:clean": "npm run test:clean && rimraf ./dist",
23 | "build:commonjs": "cross-env BABEL_ENV=commonjs babel src --out-dir lib --copy-files",
24 | "build:umd": "cross-env BABEL_ENV=commonjs NODE_ENV=development webpack --config ./internals/webpack/webpack.config.js --progress",
25 | "build:umd:min": "cross-env BABEL_ENV=commonjs NODE_ENV=production webpack --config ./internals/webpack/webpack.config.js --progress --profile",
26 | "build:umd:stats": "cross-env BABEL_ENV=commonjs NODE_ENV=production webpack --config ./internals/webpack/webpack.config.js --progress --profile --json > dist/stats.json",
27 | "build": "npm run build:commonjs && npm run test:lib && npm run build:umd && npm run build:umd:min",
28 | "testrpc": "testrpc",
29 | "lint": "npm run lint:js",
30 | "lint:eslint": "eslint --ignore-path .gitignore --ignore-pattern **/**.min.js",
31 | "lint:js": "npm run lint:eslint -- . ",
32 | "lint:staged": "lint-staged",
33 | "test:clean": "rimraf ./coverage",
34 | "test": "mocha ./src/**/test.*.js ./src/**/*/test.*.js -R spec --timeout 2000000",
35 | "test:lib": "mocha ./lib/tests/**/*.js -R spec --timeout 2000000",
36 | "test:utils": "mocha ./src/utils/tests/**/*.js -R spec --timeout 2000000",
37 | "test:plugins": "mocha ./src/plugins/tests/**/*.js -R spec --timeout 2000000",
38 | "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- ./src/**/test.*.js ./src/**/*/test.*.js -R spec --timeout 2000000",
39 | "coveralls": "npm run test-travis && cat ./coverage/lcov.info | coveralls"
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
44 | },
45 | "babel": {
46 | "plugins": [
47 | [
48 | "transform-es2015-template-literals",
49 | {
50 | "loose": true
51 | }
52 | ],
53 | "transform-es2015-literals",
54 | "transform-es2015-function-name",
55 | "transform-es2015-arrow-functions",
56 | "transform-es2015-block-scoped-functions",
57 | [
58 | "transform-es2015-classes",
59 | {
60 | "loose": true
61 | }
62 | ],
63 | "transform-es2015-object-super",
64 | "transform-es2015-shorthand-properties",
65 | [
66 | "transform-es2015-computed-properties",
67 | {
68 | "loose": true
69 | }
70 | ],
71 | [
72 | "transform-es2015-for-of",
73 | {
74 | "loose": true
75 | }
76 | ],
77 | "transform-es2015-sticky-regex",
78 | "transform-es2015-unicode-regex",
79 | "check-es2015-constants",
80 | [
81 | "transform-es2015-spread",
82 | {
83 | "loose": true
84 | }
85 | ],
86 | "transform-es2015-parameters",
87 | [
88 | "transform-es2015-destructuring",
89 | {
90 | "loose": true
91 | }
92 | ],
93 | "transform-es2015-block-scoping",
94 | "transform-object-rest-spread",
95 | "transform-es3-member-expression-literals",
96 | "transform-es3-property-literals"
97 | ],
98 | "env": {
99 | "commonjs": {
100 | "plugins": [
101 | [
102 | "transform-es2015-modules-commonjs",
103 | {
104 | "loose": true
105 | }
106 | ]
107 | ]
108 | }
109 | }
110 | },
111 | "dependencies": {
112 | "clone-deep": "^0.2.4",
113 | "deep-assign": "2.0.0",
114 | "deep-equal": "1.0.1",
115 | "ethjs-contract": "0.1.7",
116 | "ethjs-query": "0.2.6",
117 | "fs": "0.0.1-security",
118 | "meow": "3.7.0",
119 | "node-dir": "0.1.16",
120 | "strip-hex-prefix": "1.0.0"
121 | },
122 | "devDependencies": {
123 | "ethdeploy-environment-loader": "file:./src/loaders/environment",
124 | "ethdeploy-solc-loader": "file:./src/loaders/solc",
125 | "ethdeploy-raw-solc-loader": "file:./src/loaders/raw-solc",
126 | "babel-cli": "6.18.0",
127 | "babel-core": "6.18.2",
128 | "babel-eslint": "7.1.0",
129 | "babel-loader": "6.2.8",
130 | "babel-plugin-check-es2015-constants": "6.8.0",
131 | "babel-plugin-transform-class-properties": "6.18.0",
132 | "babel-plugin-transform-es2015-arrow-functions": "6.8.0",
133 | "babel-plugin-transform-es2015-block-scoped-functions": "6.8.0",
134 | "babel-plugin-transform-es2015-block-scoping": "6.18.0",
135 | "babel-plugin-transform-es2015-classes": "6.18.0",
136 | "babel-plugin-transform-es2015-computed-properties": "6.8.0",
137 | "babel-plugin-transform-es2015-destructuring": "6.19.0",
138 | "babel-plugin-transform-es2015-for-of": "6.18.0",
139 | "babel-plugin-transform-es2015-function-name": "6.9.0",
140 | "babel-plugin-transform-es2015-literals": "6.8.0",
141 | "babel-plugin-transform-es2015-modules-commonjs": "6.18.0",
142 | "babel-plugin-transform-es2015-object-super": "6.8.0",
143 | "babel-plugin-transform-es2015-parameters": "6.18.0",
144 | "babel-plugin-transform-es2015-shorthand-properties": "6.18.0",
145 | "babel-plugin-transform-es2015-spread": "6.8.0",
146 | "babel-plugin-transform-es2015-sticky-regex": "6.8.0",
147 | "babel-plugin-transform-es2015-template-literals": "6.8.0",
148 | "babel-plugin-transform-es2015-unicode-regex": "6.11.0",
149 | "babel-plugin-transform-es3-member-expression-literals": "6.5.0",
150 | "babel-plugin-transform-es3-property-literals": "6.5.0",
151 | "babel-plugin-transform-object-rest-spread": "6.19.0",
152 | "babel-plugin-transform-runtime": "^6.15.0",
153 | "babel-polyfill": "6.16.0",
154 | "babel-register": "6.18.0",
155 | "bignumber.js": "^3.0.1",
156 | "bn.js": "^4.11.6",
157 | "chai": "3.5.0",
158 | "check-es3-syntax-cli": "0.1.3",
159 | "coveralls": "2.11.9",
160 | "cross-env": "1.0.7",
161 | "eslint": "2.10.1",
162 | "eslint-config-airbnb": "9.0.1",
163 | "eslint-import-resolver-webpack": "0.2.4",
164 | "eslint-plugin-import": "1.8.0",
165 | "eslint-plugin-jsx-a11y": "1.2.0",
166 | "eslint-plugin-react": "5.1.1",
167 | "ethereumjs-testrpc": "3.0.2",
168 | "ethjs-provider-signer": "^0.1.0",
169 | "ethjs-signer": "^0.1.0",
170 | "eventsource-polyfill": "0.9.6",
171 | "istanbul": "0.4.5",
172 | "json-loader": "0.5.4",
173 | "lint-staged": "1.0.1",
174 | "mocha": "3.1.2",
175 | "pre-commit": "1.1.3",
176 | "rimraf": "2.3.4",
177 | "solc": "^0.4.6",
178 | "webpack": "2.1.0-beta.15"
179 | },
180 | "engines": {
181 | "npm": ">=3",
182 | "node": ">=6.5.0"
183 | },
184 | "keywords": [
185 | "ethereum",
186 | "events",
187 | "rpc"
188 | ],
189 | "author": "Nick Dodson ",
190 | "license": "MIT",
191 | "bugs": {
192 | "url": "https://github.com/SilentCicero/ethdeploy/issues"
193 | },
194 | "homepage": "https://github.com/SilentCicero/ethdeploy#readme",
195 | "lint-staged": {
196 | "lint:eslint": "*.js"
197 | },
198 | "eslintConfig": {
199 | "parser": "babel-eslint",
200 | "extends": "airbnb",
201 | "env": {
202 | "node": true,
203 | "mocha": true,
204 | "es6": true
205 | },
206 | "parserOptions": {
207 | "ecmaVersion": 6,
208 | "sourceType": "module"
209 | },
210 | "rules": {
211 | "import/no-unresolved": 2,
212 | "comma-dangle": [
213 | 2,
214 | "always-multiline"
215 | ],
216 | "indent": [
217 | 2,
218 | 2,
219 | {
220 | "SwitchCase": 1
221 | }
222 | ],
223 | "no-console": 1,
224 | "max-len": 0,
225 | "prefer-template": 2,
226 | "no-use-before-define": 0,
227 | "newline-per-chained-call": 0,
228 | "arrow-body-style": [
229 | 2,
230 | "as-needed"
231 | ]
232 | }
233 | },
234 | "pre-commit": "test"
235 | }
236 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const utils = require('./utils/index.js');
2 | const lib = require('./lib/index.js');
3 | const cloneDeep = require('clone-deep');
4 | const deployPlugins = require('./plugins/index.js');
5 | const bnToString = utils.bnToString;
6 | const error = utils.error;
7 | const configError = lib.configError;
8 | const entrySourceMap = lib.entrySourceMap;
9 | const loadEnvironment = lib.loadEnvironment;
10 | const loadContracts = lib.loadContracts;
11 | const buildDeployer = lib.buildDeployMethod;
12 | const processOutput = lib.processOutput;
13 | const transformContracts = lib.transformContracts;
14 |
15 |
16 | /**
17 | * Intakes config object, deploys contracts, outputs result as string for file writting.
18 | *
19 | * @method ethdeploy
20 | * @param {Object|Function} config the ethdeploy config object or method
21 | * @param {Function} callbackInput the final callback that returns the output
22 | * @callback {Object} outputObject returns the final config object, and contracts output
23 | */
24 | module.exports = function ethdeploy(config, callbackInput) { // eslint-disable-line
25 | // this is the initial deployed contracts store
26 | var deployedContracts = {}; // eslint-disable-line
27 | const callback = callbackInput || function cb() {};
28 |
29 | if (typeof config !== 'function' && typeof config !== 'object') {
30 | return callback(error('config input param must be a type Object or Function.'), null);
31 | }
32 |
33 | // build config object with options, plugins
34 | const configObject = (typeof config !== 'function') ? config : config({ plugins: deployPlugins });
35 |
36 | // validate the config object, if error, stop process
37 | if (configError(configObject) !== null) {
38 | return callback(error(configError(configObject)), null);
39 | }
40 |
41 | // stage loaders, deployer, environment transform, plugins, entry, env, deployment
42 | const buildDeployMethod = configObject.deployer || buildDeployer;
43 | const buildEnvironment = configObject.environmentLoader || loadEnvironment;
44 | const entry = configObject.entry;
45 | const modulePreLoaders = configObject.module.preLoaders || [];
46 | const moduleLoaders = configObject.module.loaders || [];
47 | const moduleEnvironment = configObject.module.environment;
48 | const moduleDeloyment = configObject.module.deployment;
49 | const plugins = configObject.plugins || [];
50 | const loadEntry = configObject.sourceMapper || entrySourceMap;
51 |
52 | // build report method
53 | const reportMethod = (name, data, address, inputs, transactionObject, receipt) => {
54 | deployedContracts = Object.assign({}, cloneDeep(deployedContracts), bnToString({
55 | [name]: Object.assign({}, cloneDeep(deployedContracts[name] || {}), cloneDeep(data), { name, address, inputs, transactionObject, receipt }),
56 | }, 16, true));
57 | };
58 |
59 | // build sourcemap from entry
60 | loadEntry(entry, (sourceMapError, sourceMap) => { // eslint-disable-line
61 | if (sourceMapError !== null) { return callback(error(sourceMapError), null); }
62 |
63 | // transform environment
64 | buildEnvironment(moduleEnvironment, (envTransformError, environment) => { // eslint-disable-line
65 | if (envTransformError !== null) { return callback(error(envTransformError, null)); }
66 |
67 | // load and process contracts from sourcemap, base contracts layer
68 | loadContracts(modulePreLoaders, {}, sourceMap, environment, (preLoaderError, baseContracts) => { // eslint-disable-line
69 | if (preLoaderError !== null) { return callback(error(preLoaderError), null); }
70 |
71 | // load and process contracts from sourcemap
72 | loadContracts(moduleLoaders, baseContracts, sourceMap, environment, (loaderError, contracts) => { // eslint-disable-line
73 | if (loaderError !== null) { return callback(error(loaderError), null); }
74 |
75 | // scoped base contracts
76 | const scopedBaseContracts = transformContracts(baseContracts, environment.name);
77 |
78 | // scope the contracts only to the environment being deployed
79 | const scopedContracts = transformContracts(contracts, environment.name);
80 |
81 | // build done method
82 | const doneMethod = () => {
83 | const finalOutput = Object.assign({}, cloneDeep(baseContracts), {
84 | [environment.name]: cloneDeep(deployedContracts),
85 | });
86 |
87 | // final output processing with plugins
88 | processOutput(plugins, finalOutput, configObject, scopedBaseContracts, scopedContracts, environment, (pluginError, outputString) => {
89 | if (pluginError) {
90 | callback(pluginError, null);
91 | } else {
92 | callback(null, { config: configObject, output: outputString });
93 | }
94 | });
95 |
96 | utils.log('Deployment module completed!');
97 | };
98 |
99 | // build deploy method
100 | const deployMethod = buildDeployMethod(scopedBaseContracts, environment, reportMethod);
101 |
102 | // run the deployment module
103 | moduleDeloyment(deployMethod, scopedContracts, doneMethod, environment);
104 | });
105 | });
106 | });
107 | });
108 | };
109 |
--------------------------------------------------------------------------------
/src/lib/index.js:
--------------------------------------------------------------------------------
1 | const deepAssign = require('deep-assign');
2 | const deepEqual = require('deep-equal');
3 | const Eth = require('ethjs-query');
4 | const EthUtils = require('ethjs-util');
5 | const EthContract = require('ethjs-contract');
6 | const stripHexPrefix = require('strip-hex-prefix');
7 | const cloneDeep = require('clone-deep');
8 | const utils = require('../utils/index.js');
9 | const bnToString = utils.bnToString;
10 | const error = utils.error;
11 | const filterSourceMap = utils.filterSourceMap;
12 | const deployContract = utils.deployContract;
13 | const getInputSources = utils.getInputSources;
14 |
15 | /**
16 | * Transform default tx object with accounts (mainly account 0 => accounts[0])
17 | *
18 | * @method transformTxObject
19 | * @param {Object} txObject the input default tx object
20 | * @param {Array} accounts the accounts from Ethereum RPC
21 | * @return {Object} output the transformed tx object
22 | */
23 | function transformTxObject(txObject, accounts) {
24 | // if no tx object, bypass
25 | if (typeof txObject !== 'object') { return txObject; }
26 |
27 | const from = typeof txObject.from === 'number' ? accounts[txObject.from] : txObject.from;
28 |
29 | return Object.assign({}, cloneDeep(txObject), { from });
30 | }
31 |
32 | /**
33 | * Load the environment, get accounts, gas limits, balances etc.
34 | *
35 | * @method loadEnvironment
36 | * @param {Object} environment the environment object specified in the config.js
37 | * @param {Function} callback the callback to return the environment
38 | * @callback {Object} output the transformed environment object
39 | */
40 | function loadEnvironment(environment, callback) {
41 | const errorMsgBase = 'while transforming environment, ';
42 |
43 | var transformedEnvironment = cloneDeep(environment); // eslint-disable-line
44 |
45 | const query = new Eth(transformedEnvironment.provider);
46 | query.net_version((versionError, result) => { // eslint-disable-line
47 | if (versionError) { return callback(error(`${errorMsgBase}error attempting to connect to node environment '${transformedEnvironment.name}': ${versionError}`), null); }
48 |
49 | query.accounts((accountsError, accounts) => { // eslint-disable-line
50 | if (accountsError !== null) { return callback(error(`${errorMsgBase}error while getting accounts for deployment: ${accountsError}`), null); }
51 |
52 | callback(accountsError, Object.assign({}, cloneDeep(transformedEnvironment), {
53 | accounts,
54 | defaultTxObject: transformTxObject(environment.defaultTxObject, accounts),
55 | }));
56 | });
57 | });
58 | }
59 |
60 | /**
61 | * Prepair the contracts for deployment, scope contracts array, add name
62 | *
63 | * @method transformContracts
64 | * @param {Object} contracts the environment object specified in the config.js
65 | * @param {String} environmentName the callback to return the environment
66 | * @return {Object} output the scoped contracts object, ready for deployment method
67 | */
68 | function transformContracts(contracts, environmentName) {
69 | const scopedContracts = Object.assign({}, cloneDeep(contracts[environmentName] || {}));
70 |
71 | // add name property to all contracts for deployment identification
72 | Object.keys(scopedContracts).forEach((contractName) => {
73 | scopedContracts[contractName].name = contractName;
74 | });
75 |
76 | // return new scroped contracts
77 | return scopedContracts;
78 | }
79 |
80 | /**
81 | * Validate the config, after the method has been called
82 | *
83 | * @method configError
84 | * @param {Object} config the environment object specified in the config.js
85 | * @return {Object|Null} output the config error, if any
86 | */
87 | function configError(config) {
88 | if (typeof config !== 'object') { return `the config method must return a config object, got type ${typeof config}`; }
89 | if (typeof config.entry === 'undefined') { return `No defined entry! 'config.entry' must be defined, got type ${typeof config.entry}.`; }
90 | if (typeof config.module !== 'object') { return `No defined deployment module! 'config.module' must be an object, got type ${typeof config.module}`; }
91 | if (typeof config.module.deployment !== 'function') { return `No defined deployment function! 'config.module.deployment' must be type Function (i.e. 'module.deployment = (deploy, contracts, done){}' ), got ${typeof config.module.deployment}`; }
92 | if (typeof config.module.environment !== 'object') { return `No defined module environment object! 'config.module.environment' must be type Object, got ${typeof config.module.environment}`; }
93 |
94 | const environment = config.module.environment;
95 | if (typeof environment.provider !== 'object') { return `No defined provider object! 'config.module.environment' must have a defined 'provider' object, got ${typeof environment.provider}`; }
96 | if (typeof environment.name !== 'string') { return `No defined environment name! 'config.module.environment.name' must be type String, got ${typeof environment.name}`; }
97 |
98 | return null;
99 | }
100 |
101 | /**
102 | * Require the loader
103 | *
104 | * @method requireLoader
105 | * @param {Object} loaderConfig the loader config
106 | * @return {Object} loader the required loader
107 | */
108 | function requireLoader(loaderConfig) {
109 | const errMsgBase = `while requiring loader ${JSON.stringify(loaderConfig)} config,`;
110 | if (typeof loaderConfig !== 'object') { throw error(`${errMsgBase}, config must be object, got ${typeof loaderConfig}`); }
111 | if (typeof loaderConfig.loader !== 'string') { throw error(`${errMsgBase}, config.loader must be String, got ${JSON.stringify(loaderConfig.loader)}`); }
112 |
113 | return require(loaderConfig.loader); // eslint-disable-line
114 | }
115 |
116 | /**
117 | * Load contracts from the sourcemap, start from base
118 | *
119 | * @method loadContracts
120 | * @param {Array} loaders the array of loaders
121 | * @param {Object} base the base contracts object from which to assign to
122 | * @param {Object} sourceMap the sourcemap from load time
123 | * @param {Object} environment the environments object
124 | * @param {Function} callback the method callback that returns the contracts
125 | * @callback {Object} contracts the loaded contracts
126 | */
127 | function loadContracts(loaders, base, sourceMap, environment, callback) { // eslint-disable-line
128 | var outputContracts = cloneDeep(base); // eslint-disable-line
129 | const errMsgBase = 'while processing entry data, ';
130 | if (!Array.isArray(loaders)) { return callback(error(`${errMsgBase}loaders must be type Array, got ${typeof loaders}`)); }
131 |
132 | // process loaders
133 | try {
134 | loaders.forEach((loaderConfig) => {
135 | // require the loader for use
136 | const loader = requireLoader(loaderConfig);
137 |
138 | // filtered sourcemap based on regex/include
139 | const filteredSourceMap = filterSourceMap(loaderConfig.test,
140 | loaderConfig.include,
141 | sourceMap,
142 | loaderConfig.exclude);
143 |
144 | // get loaded contracts
145 | const loadedEnvironment = loader(cloneDeep(filteredSourceMap), loaderConfig, environment);
146 |
147 | // output the new contracts
148 | outputContracts = Object.assign({}, cloneDeep(outputContracts), cloneDeep(loadedEnvironment));
149 | });
150 |
151 | // transform final contracts output
152 | callback(null, outputContracts);
153 | } catch (loaderError) {
154 | callback(error(`${errMsgBase}loader error: ${loaderError}`));
155 | }
156 | }
157 |
158 | /**
159 | * Process the final load output into string output for file creation.
160 | *
161 | * @method processOutput
162 | * @param {Array} plugins the array of plugins, if any
163 | * @param {Object} outputObject the deplyed contracts
164 | * @param {Object} configObject the config js object
165 | * @param {Function} callback the method callback that returns the final string output
166 | * @callback {String} outputString the loaded contracts
167 | */
168 | function processOutput(plugins, outputObject, configObject, baseContracts, contracts, environment, callback) { // eslint-disable-line
169 | // the final string to be outputed
170 | let outputString = JSON.stringify(outputObject, null, 2); // eslint-disable-line
171 |
172 | // the err msg base
173 | const errMsgBase = 'while processing output with plugins, ';
174 | if (!Array.isArray(plugins)) { return callback(error(`${errMsgBase}plugins must be type Array, got ${typeof plugins}`)); }
175 |
176 | // process deployers
177 | try {
178 | plugins.forEach((plugin) => {
179 | // process deployer method
180 | outputString = plugin.process({ output: outputString, config: configObject, baseContracts, contracts, environment });
181 | });
182 |
183 | // return final output string
184 | callback(null, outputString);
185 | } catch (deployerError) {
186 | callback(error(`${errMsgBase}plugins error: ${deployerError}`));
187 | }
188 | }
189 |
190 | /**
191 | * Determine if the contract has already been deployed.
192 | *
193 | * @method contractIsDeployed
194 | * @param {Object} baseContract the base contracts on which to deploy new ones
195 | * @param {Object} stagedContract the transformed environment
196 | * @return {Boolean} isDeployed has the contract already been deployed
197 | */
198 | function contractIsDeployed(baseContract, stagedContract) {
199 | // if bytecode and inputs match, then skip with instance
200 | if (deepEqual(typeof baseContract.address, 'string')
201 | && deepEqual(baseContract.transactionObject, stagedContract.transactionObject)
202 | && deepEqual(`0x${stripHexPrefix(baseContract.bytecode)}`, `0x${stripHexPrefix(stagedContract.bytecode)}`)
203 | && deepEqual(baseContract.inputs, stagedContract.inputs)) {
204 | return true;
205 | }
206 |
207 | return false;
208 | }
209 |
210 | function isDefined(value) {
211 | return typeof value !== 'undefined';
212 | }
213 |
214 | /**
215 | * Is the value a transaction object.
216 | *
217 | * @method isTransactionObject
218 | * @param {Optional} value the potential tx object
219 | * @return {Boolean} isTransactionObject is the object a tx object
220 | */
221 | function isTransactionObject(value) {
222 | if (typeof value !== 'object') { return false; }
223 | const keys = Object.keys(value);
224 |
225 | if (keys.length > 5) { return false; }
226 | if (keys.length === 0) { return true; }
227 |
228 | if (keys.length > 0 && isDefined(value.from) || isDefined(value.to) || isDefined(value.data) || isDefined(value.gas) || isDefined(value.gasPrice)) {
229 | return true;
230 | }
231 |
232 | return false;
233 | }
234 |
235 | /**
236 | * Basic deployer, if not deployed, deploy, else, skip and return instance
237 | *
238 | * @method buildDeployMethod
239 | * @param {Array} baseContracts the base contracts on which to compare to see if already deployed
240 | * @param {Object} transformedEnvironment the transformed environment
241 | * @param {Object} report the reporter method to report newly deployed contracts
242 | * @callback {Function} deploy the deply method used in module.deployment
243 | */
244 | function buildDeployMethod(baseContracts, transformedEnvironment, report) {
245 | return (...args) => {
246 | let transactionObject = {};
247 | const defaultTxObject = transformedEnvironment.defaultTxObject || {};
248 | const contractData = args[0];
249 |
250 |
251 | if (typeof contractData !== 'object') {
252 | const noContractError = 'A contract you are trying to deploy does not exist in your contracts object. Please check your entry, loaders and contracts object.';
253 |
254 | return Promise.reject(error(noContractError));
255 | }
256 |
257 | const baseContract = baseContracts[contractData.name] || {};
258 | const contractNewArguments = args.slice(1);
259 | const contractInputs = bnToString(Array.prototype.slice.call(contractNewArguments));
260 | const contractBytecode = `0x${stripHexPrefix(contractData.bytecode)}`;
261 | const contractABI = JSON.parse(contractData.interface);
262 | const eth = new Eth(transformedEnvironment.provider);
263 | const contract = new EthContract(eth);
264 | const contractFactory = contract(contractABI, contractBytecode, defaultTxObject);
265 |
266 | // trim callback from inputs, not args
267 | // custom tx object not handled yet.....
268 | if (typeof contractInputs[contractInputs.length - 1] === 'function') {
269 | contractInputs.pop();
270 | }
271 |
272 | // if there is a tx object provided for just this contractInputs
273 | // then get tx object, assign over default and use as the latest tx object
274 | if (isTransactionObject(contractInputs[contractInputs.length - 1])) {
275 | const transformedTransactionObject = transformTxObject(contractInputs[contractInputs.length - 1], transformedEnvironment.accounts);
276 | contractInputs[contractInputs.length - 1] = transformedTransactionObject;
277 | transactionObject = Object.assign({}, cloneDeep(defaultTxObject), cloneDeep(transformedTransactionObject));
278 | } else {
279 | transactionObject = Object.assign({}, cloneDeep(defaultTxObject));
280 | }
281 |
282 | // check contract has transaction object, either default or specified
283 | if (!EthUtils.isHexString(transactionObject.from, 20)) {
284 | const invalidFromAccount = `Attempting to deploy contract '${contractData.name}' with an invalid 'from' account specified. The 'from' account must be a valid 20 byte hex prefixed Ethereum address, got value '${transactionObject.from}'. Please specify a defaultTxObject in the module.environment.defaultTxObject (i.e. 'defaultTxObject: { from: 0 }') object or in the in the deploy method.`;
285 |
286 | return Promise.reject(error(invalidFromAccount));
287 | }
288 |
289 | // check if contract is already deployed, if so, return instance
290 | return new Promise((resolve, reject) => {
291 | const resolveAndReport = (contractInstance) => {
292 | // report the contract
293 | report(contractData.name,
294 | contractData,
295 | contractInstance.address,
296 | contractInputs,
297 | transactionObject,
298 | (contractInstance.receipt || baseContract.receipt));
299 |
300 | // resolve deployment
301 | resolve(contractInstance);
302 | };
303 |
304 | // if the contract is deployed, resolve with base base contract, else deploy
305 | if (contractIsDeployed(baseContract, {
306 | transactionObject,
307 | bytecode: contractBytecode,
308 | inputs: contractInputs,
309 | })) {
310 | resolveAndReport(contractFactory.at(baseContract.address));
311 | } else {
312 | deployContract(eth, contractFactory, contractInputs, (deployError, instance) => {
313 | if (deployError) {
314 | console.log(error(`while deploying contract '${contractData.name}': `, JSON.stringify(deployError.value, null, 2))); // eslint-disable-line
315 | reject(deployError);
316 | } else {
317 | resolveAndReport(instance);
318 | }
319 | });
320 | }
321 | });
322 | };
323 | }
324 |
325 | /**
326 | * Get source map for a single config object entry path.
327 | *
328 | * @method singleEntrySourceMap
329 | * @param {String} entryItem the entry item string, generally a file or dir path
330 | * @param {Array} entryData the entry data
331 | * @param {Object} sourceMap the source map
332 | * @callback {Function} callback the callback
333 | */
334 | function singleEntrySourceMap(entryItem, entryData, sourceMap, callback) { // eslint-disable-line
335 | if (typeof entryItem !== 'string') { return callback(null, sourceMap); }
336 |
337 | // get input sources for this entry
338 | getInputSources(entryItem, (inputSourceError, inputSourceMap) => { // eslint-disable-line
339 | if (inputSourceError) { return callback(inputSourceError, null); }
340 |
341 | // get source data
342 | const sourceData = deepAssign({}, sourceMap, inputSourceMap);
343 |
344 | // get next entry item
345 | const nextEntryItem = entryData[entryData.indexOf(entryItem) + 1];
346 |
347 | // recursively go through tree
348 | singleEntrySourceMap(nextEntryItem, entryData, sourceData, callback);
349 | });
350 | }
351 |
352 | /**
353 | * Build complete file source map of all entry items from the config.entry.
354 | *
355 | * @method entrySourceMap
356 | * @param {Array|String} entry the entry object
357 | * @param {Function} callback the callback that will return the source map
358 | * @callback {Object} sourceMap the source map object with all files/dirs in entry
359 | */
360 | function entrySourceMap(entry, callback) {
361 | const entryData = typeof entry === 'string' ? [entry] : entry;
362 |
363 | // get source map
364 | singleEntrySourceMap(entryData[0], entryData, {}, callback);
365 | }
366 |
367 | // critical concept methods for ethdeploy
368 | module.exports = {
369 | transformTxObject,
370 | processOutput,
371 | buildDeployMethod,
372 | loadContracts,
373 | requireLoader,
374 | configError,
375 | transformContracts,
376 | loadEnvironment,
377 | singleEntrySourceMap,
378 | entrySourceMap,
379 | };
380 |
--------------------------------------------------------------------------------
/src/lib/tests/test.index.js:
--------------------------------------------------------------------------------
1 | const lib = require('../index.js');
2 | const assert = require('chai').assert;
3 |
4 | /*
5 | SMALL:
6 | transformTxObject,
7 | requireLoader,
8 | configError,
9 | transformContracts,
10 | loadEnvironment,
11 |
12 | LARGE:
13 | processOutput,
14 | buildDeployMethod,
15 | loadContracts,
16 |
17 | MEDIUM:
18 | singleEntrySourceMap,
19 | entrySourceMap,
20 | */
21 |
22 | describe('lib', () => {
23 | // smaller methods first
24 |
25 | describe('transformTxObject', () => {
26 | it('should function properly', () => {
27 | assert.equal(typeof lib.transformTxObject, 'function');
28 | });
29 | });
30 |
31 | describe('requireLoader', () => {
32 | it('should function properly', () => {
33 | assert.equal(typeof lib.requireLoader, 'function');
34 | });
35 | });
36 |
37 | describe('configError', () => {
38 | it('should function properly', () => {
39 | assert.equal(typeof lib.configError, 'function');
40 | });
41 | });
42 |
43 | describe('loadEnvironment', () => {
44 | it('should function properly', () => {
45 | assert.equal(typeof lib.loadEnvironment, 'function');
46 | });
47 | });
48 |
49 | describe('transformContracts', () => {
50 | it('should function properly', () => {
51 | assert.equal(typeof lib.transformContracts, 'function');
52 | });
53 | });
54 |
55 | // medium size methods
56 |
57 | describe('singleEntrySourceMap', () => {
58 | it('should function properly', () => {
59 | assert.equal(typeof lib.singleEntrySourceMap, 'function');
60 | });
61 | });
62 |
63 | describe('entrySourceMap', () => {
64 | it('should function properly', () => {
65 | assert.equal(typeof lib.entrySourceMap, 'function');
66 | });
67 | });
68 |
69 | // larger methods second
70 |
71 | describe('processOutput', () => {
72 | it('should function properly', () => {
73 | assert.equal(typeof lib.processOutput, 'function');
74 | });
75 | });
76 |
77 | describe('buildDeployMethod', () => {
78 | it('should function properly', () => {
79 | assert.equal(typeof lib.buildDeployMethod, 'function');
80 | });
81 | });
82 |
83 | describe('loadContracts', () => {
84 | it('should function properly', () => {
85 | assert.equal(typeof lib.loadContracts, 'function');
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/src/loaders/environment/index.js:
--------------------------------------------------------------------------------
1 | const deepAssign = require('deep-assign');
2 |
3 | /**
4 | * Loads an environments.json file, produced by ethdeploy
5 | *
6 | * @method loader
7 | * @param {Object} sourceMap the file source map
8 | * @param {Object} loaderConfig the config for the specified loader
9 | * @param {Object} environment the loaded environment object
10 | * @return {Object} contracts the output contracts
11 | */
12 | module.exports = function loader(sourceMap, loaderConfig, environment) { // eslint-disable-line
13 | let outputObject = Object.assign({});
14 |
15 | Object.keys(sourceMap).forEach((fileName) => {
16 | try {
17 | const source = (sourceMap[fileName] === '' && loaderConfig.build) ? '{}' : sourceMap[fileName];
18 | const testJSON = JSON.parse(source);
19 |
20 | // if not an object
21 | if (typeof testJSON !== 'object') { throw new Error(`the file '${fileName}' did not result in a type Object json result.`); }
22 |
23 | outputObject = deepAssign({}, outputObject, testJSON);
24 | } catch (jsonError) {
25 | throw new Error(`while loading environment JSON from file '${fileName}', JSON error: ${JSON.stringify(jsonError)}`);
26 | }
27 | });
28 |
29 | return outputObject;
30 | };
31 |
--------------------------------------------------------------------------------
/src/loaders/environment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-environment-loader",
3 | "version": "1.0.0",
4 | "description": "A solc loader for ethdeploy.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
12 | },
13 | "keywords": [
14 | "environment",
15 | "loader",
16 | "ethdeploy",
17 | "ethereum",
18 | "environment"
19 | ],
20 | "dependencies": {
21 | "deep-assign": "*"
22 | },
23 | "author": "Nick Dodson",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/SilentCicero/ethdeploy-solc-loader/issues"
27 | },
28 | "homepage": "https://github.com/SilentCicero/ethdeploy-solc-loader#readme"
29 | }
30 |
--------------------------------------------------------------------------------
/src/loaders/raw-environment/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Here the sourcemap is the environments JSON, so we can just copy and return.
3 | *
4 | * @method loader
5 | * @param {Object} sourceMap the file source map
6 | * @param {Object} loaderConfig the config for the specified loader
7 | * @param {Object} environment the loaded environment object
8 | * @return {Object} contracts the output contracts
9 | */
10 | module.exports = function loader(sourceMap, loaderConfig, environment) { // eslint-disable-line
11 | return Object.assign({}, sourceMap);
12 | };
13 |
--------------------------------------------------------------------------------
/src/loaders/raw-environment/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-raw-environment-loader",
3 | "version": "1.0.0",
4 | "description": "A raw environment loader for ethdeploy.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
12 | },
13 | "keywords": [
14 | "solc",
15 | "loader",
16 | "ethdeploy",
17 | "ethereum",
18 | "solidity"
19 | ],
20 | "author": "Nick Dodson",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/SilentCicero/ethdeploy-raw-solc-loader/issues"
24 | },
25 | "homepage": "https://github.com/SilentCicero/ethdeploy-raw-solc-loader#readme"
26 | }
27 |
--------------------------------------------------------------------------------
/src/loaders/raw-solc/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Here the sourcemap is the contracts JSON, so we can just copy, name and return.
3 | *
4 | * @method loader
5 | * @param {Object} sourceMap the file source map
6 | * @param {Object} loaderConfig the config for the specified loader
7 | * @param {Object} environment the loaded environment object
8 | * @return {Object} contracts the output contracts
9 | */
10 | module.exports = function loader(sourceMap, loaderConfig, environment) { // eslint-disable-line
11 | return Object.assign({}, { [environment.name]: sourceMap });
12 | };
13 |
--------------------------------------------------------------------------------
/src/loaders/raw-solc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-raw-solc-loader",
3 | "version": "1.0.0",
4 | "description": "A raw solc loader for ethdeploy.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
12 | },
13 | "keywords": [
14 | "solc",
15 | "loader",
16 | "ethdeploy",
17 | "ethereum",
18 | "solidity"
19 | ],
20 | "author": "Nick Dodson",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/SilentCicero/ethdeploy-raw-solc-loader/issues"
24 | },
25 | "homepage": "https://github.com/SilentCicero/ethdeploy-raw-solc-loader#readme"
26 | }
27 |
--------------------------------------------------------------------------------
/src/loaders/solc-json/index.js:
--------------------------------------------------------------------------------
1 | // like solc output
2 | function solcOutputLike(obj) {
3 | let isSolcLike = false;
4 |
5 | Object.keys(obj).forEach((key) => {
6 | if (!isSolcLike && typeof obj[key] === 'object') {
7 | isSolcLike = (typeof obj[key].bytecode === 'string'
8 | && typeof obj[key].interface === 'string');
9 | }
10 | });
11 |
12 | return isSolcLike;
13 | }
14 |
15 | /**
16 | * Loads JSON produced by solc, formats it for environment style
17 | *
18 | * @method loader
19 | * @param {Object} sourceMap the file source map
20 | * @param {Object} loaderConfig the config for the specified loader
21 | * @param {Object} environment the loaded environment object
22 | * @return {Object} contracts the output contracts
23 | */
24 | module.exports = function loader(sourceMap, loaderConfig, environment) {
25 | const solcContracts = {};
26 |
27 | sourceMap.forEach((fileName) => {
28 | try {
29 | const testJSON = JSON.parse(sourceMap[fileName]);
30 |
31 | // if not an object
32 | if (typeof testJSON !== 'object') { throw new Error(`the file '${fileName}' did not result in a type Object json result.`); }
33 |
34 | // if the file looks like a solc output file
35 | if (solcOutputLike(testJSON)) {
36 | solcContracts[fileName] = testJSON;
37 | }
38 | } catch (jsonError) {
39 | throw new Error(`[solc-json] while loading solc JSON, error: ${JSON.stringify(jsonError)}`);
40 | }
41 | });
42 |
43 | return { [environment.name]: solcContracts };
44 | };
45 |
--------------------------------------------------------------------------------
/src/loaders/solc-json/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-solc-json-loader",
3 | "version": "1.0.0",
4 | "description": "A solc json loader for ethdeploy.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
12 | },
13 | "keywords": [
14 | "solc",
15 | "loader",
16 | "ethdeploy",
17 | "ethereum",
18 | "solidity"
19 | ],
20 | "author": "Nick Dodson",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/SilentCicero/ethdeploy-solc-loader/issues"
24 | },
25 | "homepage": "https://github.com/SilentCicero/ethdeploy-solc-loader#readme"
26 | }
27 |
--------------------------------------------------------------------------------
/src/loaders/solc/index.js:
--------------------------------------------------------------------------------
1 | const solc = require('solc');
2 |
3 | // detect error type from error messages
4 | function errortype(message) {
5 | return (String(message).match(/^(.*:[0-9]*:[0-9]* )?Warning: /) ? 'warning' : 'error');
6 | }
7 |
8 | // remove warnings from errors
9 | function filterErrorWarnings(errors) {
10 | return (errors || []).filter(error => errortype(error) === 'error');
11 | }
12 |
13 | /**
14 | * Compiles solidity files in sourcemap, returns contracts.
15 | *
16 | * @method loader
17 | * @param {Object} sourceMap the file source map
18 | * @param {Object} loaderConfig the config for the specified loader
19 | * @param {Object} environment the loaded environment object
20 | * @return {Object} contracts the output contracts
21 | */
22 | module.exports = function solcLoader(sourceMap, loaderConfig, environment) {
23 | const adjustBase = loaderConfig.base;
24 | const filterWarnings = loaderConfig.filterWarnings;
25 | const filterFilenames = loaderConfig.filterFilenames;
26 | const adjustedSourceMap = {};
27 |
28 | if (adjustBase) {
29 | Object.keys(sourceMap).forEach(filePath => {
30 | adjustedSourceMap[filePath.replace(adjustBase, '').replace(/\/\//g, '').trim()] = sourceMap[filePath];
31 | });
32 | }
33 |
34 | const output = solc.compile({ sources: (adjustBase ? adjustedSourceMap : sourceMap) }, (loaderConfig.optimize || 0));
35 | const errors = (filterWarnings ? filterErrorWarnings(output.errors) : output.errors) || [];
36 | const outputContracts = Object.assign({}, output.contracts);
37 |
38 | if (errors.length > 0) {
39 | throw new Error(`[solc-loader] while compiling contracts, errors: ${JSON.stringify((output.errors || []), null, 2)}`);
40 | }
41 |
42 | if (filterFilenames) {
43 | Object.keys(outputContracts)
44 | .forEach(sourceKey => {
45 | const savedSource = Object.assign({}, outputContracts[sourceKey]);
46 | delete outputContracts[sourceKey];
47 |
48 | const filteredName = String(sourceKey).split(':').pop();
49 | outputContracts[filteredName] = savedSource;
50 | });
51 | }
52 |
53 | return { [environment.name]: outputContracts };
54 | };
55 |
--------------------------------------------------------------------------------
/src/loaders/solc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ethdeploy-solc-loader",
3 | "version": "1.0.6",
4 | "description": "A solc loader for ethdeploy.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+ssh://git@github.com/SilentCicero/ethdeploy.git"
12 | },
13 | "keywords": [
14 | "solc",
15 | "loader",
16 | "ethdeploy",
17 | "ethereum",
18 | "solidity"
19 | ],
20 | "author": "Nick Dodson",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/SilentCicero/ethdeploy-solc-loader/issues"
24 | },
25 | "peerDependencies": {
26 | "solc": "*"
27 | },
28 | "homepage": "https://github.com/SilentCicero/ethdeploy-solc-loader#readme"
29 | }
30 |
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Minifies JSON output
3 | *
4 | * @method JSONMinifier
5 | * @param {String} output the final build file produced by ethdeploy
6 | * @return {String} parsedOutput parsed output
7 | */
8 | function JSONMinifier() {
9 | const self = this;
10 | self.process = ({ output }) => JSON.stringify(JSON.parse(output));
11 | }
12 |
13 | /**
14 | * Expands JSON output
15 | *
16 | * @method JSONMinifier
17 | * @param {String} output the final build file produced by ethdeploy with indents
18 | * @return {String} parsedOutput parsed output
19 | */
20 | function JSONExpander() {
21 | const self = this;
22 | self.process = ({ output }) => JSON.stringify(JSON.parse(output), null, 2);
23 | }
24 |
25 | // a basic util filter function
26 | function keyFilter(obj, predicate) {
27 | var result = {}, key; // eslint-disable-line
28 |
29 | for (key in obj) { // eslint-disable-line
30 | if (obj[key] && predicate(key)) {
31 | result[key] = obj[key];
32 | }
33 | }
34 |
35 | return result;
36 | }
37 |
38 | /**
39 | * This wil include contracts data in the environments output
40 | *
41 | * @method IncludeContracts
42 | * @param {Array} contractsSelector contracts to include in a contracts property
43 | * @param {Array} propertyFilter if fiter is not truthy, then ignore filtering
44 | * @param {String} environmentName environment name is usually contracts
45 | * @return {String} parsedOutput parsed output
46 | */
47 | function IncludeContracts(
48 | contractsSelector,
49 | propertyFilter = ['interface', 'bytecode'],
50 | environmentName = 'contracts') {
51 | const self = this;
52 | self.process = ({ output, contracts }) => {
53 | // parse output
54 | const parsedOutput = JSON.parse(output);
55 |
56 | // add contracts environment
57 | parsedOutput[environmentName] = Object.assign({});
58 |
59 | // go through selected contracts
60 | contractsSelector.forEach((contractName) => {
61 | // if the contracts object has the selectedcontract
62 | // include it
63 | if ((contracts || {})[contractName]) {
64 | const contractObject = Object.assign({}, contracts[contractName]);
65 |
66 | // include contract data in environments output with property filter
67 | parsedOutput[environmentName][contractName] = propertyFilter ? keyFilter(contractObject, (key => propertyFilter.indexOf(key) !== -1)) : contractObject;
68 | }
69 | });
70 |
71 | return JSON.stringify(parsedOutput);
72 | };
73 | }
74 |
75 | /**
76 | * JSONFilter
77 | *
78 | * @method JSONFilter
79 | * @param {String} output the final build file produced by ethdeploy
80 | * @return {String} parsedOutput parsed output
81 | */
82 | function JSONFilter(contractProperties = ['address', 'interface', 'bytecode', 'transactionObject', 'inputs']) {
83 | const self = this;
84 | self.process = ({ output }) => {
85 | const jsonObject = JSON.parse(output);
86 | const outputObject = Object.assign({});
87 |
88 | Object.keys(jsonObject).forEach((environmentName) => {
89 | outputObject[environmentName] = Object.assign({});
90 |
91 | Object.keys(jsonObject[environmentName]).forEach((contractName) => {
92 | outputObject[environmentName][contractName] = Object.assign({});
93 |
94 | Object.keys(jsonObject[environmentName][contractName]).forEach((contactProperty) => {
95 | if (contractProperties.indexOf(contactProperty) !== -1) {
96 | outputObject[environmentName][contractName][contactProperty] = jsonObject[environmentName][contractName][contactProperty];
97 | }
98 | });
99 | });
100 | });
101 |
102 | return JSON.stringify(outputObject);
103 | };
104 | }
105 |
106 | // export plugins
107 | module.exports = {
108 | JSONMinifier,
109 | JSONExpander,
110 | IncludeContracts,
111 | JSONFilter,
112 | };
113 |
--------------------------------------------------------------------------------
/src/plugins/tests/test.index.js:
--------------------------------------------------------------------------------
1 | const plugins = require('../index.js');
2 | const assert = require('chai').assert;
3 |
4 | describe('plugins', () => {
5 | describe('JSONMinifier', () => {
6 | it('should function properly', () => {
7 | assert.equal(typeof plugins.JSONMinifier, 'function');
8 | const data = {
9 | output: '{}',
10 | };
11 | const result = (new plugins.JSONMinifier()).process(data);
12 | const expected = '{}';
13 | assert.deepEqual(result, expected);
14 | });
15 | });
16 |
17 | describe('JSONExpander', () => {
18 | it('should function properly', () => {
19 | assert.equal(typeof plugins.JSONExpander, 'function');
20 | const data = {
21 | output: '{"a":"b"}',
22 | };
23 | const expected =
24 | `{
25 | "a": "b"
26 | }`;
27 | const result = (new plugins.JSONExpander()).process(data);
28 | assert.deepEqual(result, expected);
29 | });
30 | });
31 |
32 | describe('IncludeContracts', () => {
33 | it('should function properly', () => {
34 | assert.equal(typeof plugins.IncludeContracts, 'function');
35 | const data = {
36 | output: '{}',
37 | contracts: {
38 | SimpleStoreInterface: {
39 | bytecode: '0x...',
40 | interface: '[{....}]',
41 | },
42 | Token: {
43 | bytecode: '0x...',
44 | interface: '[{....}]',
45 | },
46 | Proxy: {
47 | bytecode: '0x...',
48 | interface: '[{....}]',
49 | },
50 | },
51 | };
52 | const result = (new plugins.IncludeContracts(['SimpleStoreInterface', 'Token', 'Proxy'])).process(data);
53 | const expected = '{"contracts":{"SimpleStoreInterface":{"bytecode":"0x...","interface":"[{....}]"},"Token":{"bytecode":"0x...","interface":"[{....}]"},"Proxy":{"bytecode":"0x...","interface":"[{....}]"}}}';
54 | assert.deepEqual(result, expected);
55 | });
56 | });
57 |
58 |
59 | describe('JSONFilter', () => {
60 | it('should function properly', () => {
61 | assert.equal(typeof plugins.JSONFilter, 'function');
62 | const unfilteredObjectString =
63 | `
64 | {
65 | "ropsten": {
66 | "SimpleStore": {
67 | "bytecode": "0x...",
68 | "interface": "[{}]",
69 | "address": "0x3a70a6765746af3bfa974fff9d753d4b6c56b333",
70 | "inputs": [],
71 | "transactionObject": {
72 | "from": "0x7f3e74e3dbb4091973ea1b449692c504c35ef768",
73 | "gas": 3000001
74 | },
75 | "WILL_BE_FILTERED": "OUT"
76 | }
77 | }
78 | }
79 | `;
80 | const data = {
81 | output: unfilteredObjectString,
82 | };
83 | const result = (new plugins.JSONFilter()).process(data);
84 | const expected = '{"ropsten":{"SimpleStore":{"bytecode":"0x...","interface":"[{}]","address":"0x3a70a6765746af3bfa974fff9d753d4b6c56b333","inputs":[],"transactionObject":{"from":"0x7f3e74e3dbb4091973ea1b449692c504c35ef768","gas":3000001}}}}';
85 | assert.deepEqual(result, expected);
86 | });
87 | });
88 | });
89 |
90 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.minimum.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [],
6 | output: {
7 | path: './',
8 | filename: 'environments.json',
9 | },
10 | module: {
11 | environment: {
12 | name: 'ropsten',
13 | provider: new SignerProvider('https://ropsten.infura.io', {
14 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
15 | signTransaction: (rawTx, cb) => {
16 | cb(null, sign(rawTx, '0x..privateKey...'));
17 | },
18 | }),
19 | },
20 | deployment: (deploy, contracts, done) => {
21 | done();
22 | },
23 | },
24 | plugins: [
25 | new options.plugins.JSONMinifier(),
26 | ],
27 | });
28 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.minimumNoDefinedPlugins.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [],
6 | output: {
7 | path: './',
8 | filename: 'environments.json',
9 | },
10 | module: {
11 | environment: {
12 | name: 'ropsten',
13 | provider: new SignerProvider('https://ropsten.infura.io', {
14 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
15 | signTransaction: (rawTx, cb) => {
16 | cb(null, sign(rawTx, '0x..privateKey...'));
17 | },
18 | }),
19 | },
20 | deployment: (deploy, contracts, done) => {
21 | done();
22 | },
23 | },
24 | plugins: [
25 | new options.plugins.JSONMinifier(),
26 | ],
27 | });
28 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.minimumNoOutput.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = () => ({
5 | entry: [],
6 | module: {
7 | environment: {
8 | name: 'ropsten',
9 | provider: new SignerProvider('https://ropsten.infura.io', {
10 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
11 | signTransaction: (rawTx, cb) => {
12 | cb(null, sign(rawTx, '0x..privateKey...'));
13 | },
14 | }),
15 | },
16 | deployment: (deploy, contracts, done) => {
17 | done();
18 | },
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.minimumNoPlugins.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = () => ({
5 | entry: [],
6 | output: {
7 | path: './',
8 | filename: 'environments.json',
9 | },
10 | module: {
11 | environment: {
12 | name: 'ropsten',
13 | provider: new SignerProvider('https://ropsten.infura.io', {
14 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
15 | signTransaction: (rawTx, cb) => {
16 | cb(null, sign(rawTx, '0x..privateKey...'));
17 | },
18 | }),
19 | },
20 | deployment: (deploy, contracts, done) => {
21 | done();
22 | },
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.noloaders.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | preLoaders: [
15 | { test: /\.(json)$/, loader: '../loaders/solc-json.js' },
16 | ],
17 | environment: {
18 | name: 'ropsten',
19 | provider: new SignerProvider('http://localhost:8545', {
20 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
21 | signTransaction: (rawTx, cb) => {
22 | cb(null, sign(rawTx, '0x..privateKey...'));
23 | },
24 | }),
25 | defaultTxObject: {
26 | from: 0,
27 | gas: 3000000,
28 | },
29 | },
30 | deployment: (deploy, contracts, done) => {
31 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
32 | console.log(simpleStoreInstance); // eslint-disable-line
33 |
34 | done();
35 | });
36 | },
37 | },
38 | plugins: [
39 | new options.plugins.JSONMinifier(),
40 | ],
41 | });
42 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.noplugin.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = () => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | preLoaders: [
15 | { test: /\.(json)$/, loader: '../loaders/environment.js', build: true, include: /(environments)/ },
16 | ],
17 | loaders: [
18 | { test: /\.(sol)$/, loader: '../loaders/solc.js', optimize: 1 },
19 | { test: /\.(json)$/, loader: '../loaders/solc-json.js' },
20 | ],
21 | environment: {
22 | name: 'ropsten',
23 | provider: new SignerProvider('http://localhost:8545', {
24 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
25 | signTransaction: (rawTx, cb) => {
26 | cb(null, sign(rawTx, '0x..privateKey...'));
27 | },
28 | }),
29 | defaultTxObject: {
30 | from: 0,
31 | gas: 3000000,
32 | },
33 | },
34 | deployment: (deploy, contracts, done) => {
35 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
36 | console.log(simpleStoreInstance); // eslint-disable-line
37 |
38 | done();
39 | });
40 | },
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.nopreloaders.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | loaders: [
15 | { test: /\.(json)$/, loader: '../loaders/solc-json.js' },
16 | ],
17 | environment: {
18 | name: 'ropsten',
19 | provider: new SignerProvider('http://localhost:8545', {
20 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
21 | signTransaction: (rawTx, cb) => {
22 | cb(null, sign(rawTx, '0x..privateKey...'));
23 | },
24 | }),
25 | defaultTxObject: {
26 | from: 0,
27 | gas: 3000000,
28 | },
29 | },
30 | deployment: (deploy, contracts, done) => {
31 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
32 | console.log(simpleStoreInstance); // eslint-disable-line
33 |
34 | done();
35 | });
36 | },
37 | },
38 | plugins: [
39 | new options.plugins.JSONMinifier(),
40 | ],
41 | });
42 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.notxobject.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | preLoaders: [
15 | { test: /\.(json)$/, loader: '../loaders/environment.js', build: true, include: /(environments)/ },
16 | ],
17 | loaders: [
18 | { test: /\.(sol)$/, loader: '../loaders/solc.js', optimize: 1 },
19 | { test: /\.(json)$/, loader: '../loaders/solc-json.js' },
20 | ],
21 | environment: {
22 | name: 'ropsten',
23 | provider: new SignerProvider('http://localhost:8545', {
24 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
25 | signTransaction: (rawTx, cb) => {
26 | cb(null, sign(rawTx, '0x..privateKey...'));
27 | },
28 | }),
29 | },
30 | deployment: (deploy, contracts, done) => {
31 | deploy(contracts.SimpleStore, {
32 | from: '0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC',
33 | gas: 3000000,
34 | }).then((simpleStoreInstance) => {
35 | console.log(simpleStoreInstance); // eslint-disable-line
36 |
37 | done();
38 | });
39 | },
40 | },
41 | plugins: [
42 | new options.plugins.JSONMinifier(),
43 | ],
44 | });
45 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.rawenv.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = () => ({
5 | entry: {
6 | ropsten: {
7 | SimpleStore: {
8 | },
9 | },
10 | mainnet: {
11 | SomeOtherContract: {
12 | },
13 | },
14 | },
15 | sourceMapper: (entry, cb) => cb(null, entry),
16 | module: {
17 | preLoaders: [
18 | { loader: '../loaders/raw-environment.js' },
19 | ],
20 | environment: {
21 | name: 'ropsten',
22 | provider: new SignerProvider('http://localhost:8545', {
23 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
24 | signTransaction: (rawTx, cb) => {
25 | cb(null, sign(rawTx, '0x..privateKey...'));
26 | },
27 | }),
28 | },
29 | deployment: (deploy, contracts, done) => {
30 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
31 | console.log(simpleStoreInstance); // eslint-disable-line
32 |
33 | done();
34 | });
35 | },
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.rawsolc.testnet.js:
--------------------------------------------------------------------------------
1 | const TestRPC = require('ethereumjs-testrpc');
2 |
3 | module.exports = () => ({
4 | entry: {
5 | SimpleStore: {
6 | },
7 | },
8 | sourceMapper: (entry, cb) => cb(null, entry),
9 | module: {
10 | preLoaders: [
11 | { loader: '../loaders/raw-solc-json.js' },
12 | ],
13 | environment: {
14 | name: 'ropsten',
15 | provider: TestRPC.provider(),
16 | defaultTxObject: {
17 | from: 0,
18 | gas: 3000000,
19 | },
20 | },
21 | deployment: (deploy, contracts, done) => {
22 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
23 | console.log(simpleStoreInstance); // eslint-disable-line
24 |
25 | done();
26 | });
27 | },
28 | },
29 | });
30 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.solcLoader.testnet.js:
--------------------------------------------------------------------------------
1 | const TestRPC = require('ethereumjs-testrpc'); // eslint-disable-line
2 | // const HttpProvider = require('ethjs-provider-http');
3 |
4 | module.exports = (options) => ({ // eslint-disable-line
5 | entry: [
6 | './environments.json',
7 | './src/tests/contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | safe: true,
13 | },
14 | module: {
15 | environment: {
16 | name: 'testrpc',
17 | provider: TestRPC.provider(),
18 | defaultTxObject: {
19 | from: 1,
20 | gas: 3000001,
21 | },
22 | },
23 | preLoaders: [
24 | { test: /\.(json)$/, loader: 'ethdeploy-environment-loader', build: true },
25 | ],
26 | loaders: [
27 | { test: /\.(sol)$/, exclude: /(test\.)/, loader: 'ethdeploy-solc-loader', optimize: 1 },
28 | ],
29 | deployment: (deploy, contracts, done) => {
30 | deploy(contracts.SimpleStore, { from: 0 }).then(() => {
31 | done();
32 | });
33 | },
34 | },
35 | plugins: [
36 | // new options.plugins.JSONMinifier(),
37 | ],
38 | });
39 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.stringEntry.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: 'contracts',
6 | output: {
7 | path: './',
8 | filename: 'environments.json',
9 | },
10 | module: {
11 | preLoaders: [
12 | { test: /\.(json)$/, loader: '../loaders/environment.js', build: true, include: /(environments)/ },
13 | ],
14 | loaders: [
15 | { test: /\.(sol)$/, loader: '../loaders/solc.js', optimize: 1 },
16 | ],
17 | environment: {
18 | name: 'ropsten',
19 | provider: new SignerProvider('http://localhost:8545', {
20 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
21 | signTransaction: (rawTx, cb) => {
22 | cb(null, sign(rawTx, '0x..privateKey...'));
23 | },
24 | }),
25 | defaultTxObject: {
26 | from: 0,
27 | gas: 3000000,
28 | },
29 | },
30 | deployment: (deploy, contracts, done) => {
31 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
32 | console.log(simpleStoreInstance); // eslint-disable-line
33 |
34 | done();
35 | });
36 | },
37 | },
38 | plugins: [
39 | new options.plugins.JSONMinifier(),
40 | ],
41 | });
42 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | preLoaders: [
15 | { test: /\.(json)$/, loader: '../loaders/environment.js', build: true, include: /(environments)/ },
16 | ],
17 | loaders: [
18 | { test: /\.(sol)$/, loader: '../loaders/solc.js', optimize: 1 },
19 | { test: /\.(json)$/, loader: '../loaders/solc-json.js' },
20 | ],
21 | environment: {
22 | name: 'ropsten',
23 | provider: new SignerProvider('http://localhost:8545', {
24 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
25 | signTransaction: (rawTx, cb) => {
26 | cb(null, sign(rawTx, '0x..privateKey...'));
27 | },
28 | }),
29 | defaultTxObject: {
30 | from: 0,
31 | gas: 3000000,
32 | },
33 | },
34 | deployment: (deploy, contracts, done) => {
35 | deploy(contracts.SimpleStore).then((simpleStoreInstance) => {
36 | console.log(simpleStoreInstance); // eslint-disable-line
37 |
38 | done();
39 | });
40 | },
41 | },
42 | plugins: [
43 | new options.plugins.JSONMinifier(),
44 | ],
45 | });
46 |
--------------------------------------------------------------------------------
/src/tests/configs/ethdeploy.config.zeroloaders.testnet.js:
--------------------------------------------------------------------------------
1 | const sign = require('ethjs-signer').sign;
2 | const SignerProvider = require('ethjs-provider-signer');
3 |
4 | module.exports = (options) => ({
5 | entry: [
6 | 'environments.json',
7 | 'contracts',
8 | ],
9 | output: {
10 | path: './',
11 | filename: 'environments.json',
12 | },
13 | module: {
14 | environment: {
15 | name: 'ropsten',
16 | provider: new SignerProvider('http://localhost:8545', {
17 | accounts: (cb) => cb(null, ['0x2233eD250Ea774146B0fBbC1da0Ffa6a81514cCC']),
18 | signTransaction: (rawTx, cb) => {
19 | cb(null, sign(rawTx, '0x..privateKey...'));
20 | },
21 | }),
22 | defaultTxObject: {
23 | from: 0,
24 | gas: 3000000,
25 | },
26 | },
27 | deployment: (deploy, contracts, done) => {
28 | done();
29 | },
30 | },
31 | plugins: [
32 | new options.plugins.JSONMinifier(),
33 | ],
34 | });
35 |
--------------------------------------------------------------------------------
/src/tests/contracts/SimpleStore.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.4;
2 |
3 | contract SimpleStore {
4 | uint256 public store;
5 |
6 | function setStore(uint256 _value) public {
7 | store = _value;
8 | }
9 |
10 | function getValue() public constant returns (uint256) {
11 | return store;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/tests/contracts/test.SimpleStore.sol:
--------------------------------------------------------------------------------
1 | dsfsdsfd
2 |
--------------------------------------------------------------------------------
/src/tests/test.index.js:
--------------------------------------------------------------------------------
1 | const assert = require('chai').assert;
2 | const BN = require('bn.js'); // eslint-disable-line
3 | const BigNumber = require('bignumber.js'); // eslint-disable-line
4 | const ethdeploy = require('../index.js'); // eslint-disable-line
5 | const HttpProvider = require('ethjs-provider-http'); // eslint-disable-line
6 | const TestRPC = require('ethereumjs-testrpc');
7 |
8 | describe('ethdeploy', () => {
9 | describe('main method', () => {
10 | it('should instantiate properly', () => {
11 | assert.equal(typeof ethdeploy, 'function');
12 | });
13 |
14 | it('should handle undefined', (done) => {
15 | ethdeploy(undefined, (err, result) => {
16 | assert.isOk(err);
17 | assert.isNotOk(result);
18 | done();
19 | });
20 | });
21 |
22 | it('should handle empty object', (done) => {
23 | ethdeploy({}, (err, result) => {
24 | assert.isOk(err);
25 | assert.isNotOk(result);
26 | done();
27 | });
28 | });
29 |
30 | it('should handle empty function', (done) => {
31 | ethdeploy(() => {}, (err, result) => {
32 | assert.isOk(err);
33 | assert.isNotOk(result);
34 | done();
35 | });
36 | });
37 |
38 | it('should handle normal config', (done) => {
39 | ethdeploy({
40 | entry: [],
41 | output: {},
42 | module: {},
43 | }, (err, result) => {
44 | assert.isOk(err);
45 | assert.isNotOk(result);
46 | done();
47 | });
48 | });
49 |
50 | it('should handle normal config no provider', (done) => {
51 | ethdeploy({
52 | entry: [],
53 | output: {
54 | },
55 | module: {
56 | deployment: () => {},
57 | },
58 | }, (err, result) => {
59 | assert.isOk(err);
60 | assert.isNotOk(result);
61 | done();
62 | });
63 | });
64 |
65 | it('should handle normal config with no env', (done) => {
66 | ethdeploy({
67 | entry: [],
68 | output: {
69 | },
70 | module: {
71 | environment: {},
72 | deployment: () => {},
73 | },
74 | }, (err, result) => {
75 | assert.isOk(err);
76 | assert.isNotOk(result);
77 | done();
78 | });
79 | });
80 |
81 | it('should handle normal config with provider', (done) => {
82 | ethdeploy({
83 | entry: [],
84 | output: {
85 | },
86 | module: {
87 | environment: {
88 | provider: new HttpProvider('http://localhost:3000'),
89 | },
90 | deployment: () => {},
91 | },
92 | }, (err, result) => {
93 | assert.isOk(err);
94 | assert.isNotOk(result);
95 | done();
96 | });
97 | });
98 |
99 | it('should handle normal config with testrpc', (done) => {
100 | ethdeploy({
101 | entry: [],
102 | output: {},
103 | module: {
104 | environment: {
105 | name: 'localhost',
106 | provider: TestRPC.provider(),
107 | },
108 | deployment: (deploy, c, done1) => done(), // eslint-disable-line
109 | },
110 | }, (err, result) => {
111 | assert.isOk(result);
112 | assert.isNotOk(err);
113 | done();
114 | });
115 | });
116 |
117 | it('should handle normal entry with testrpc', (done) => {
118 | ethdeploy({
119 | entry: {
120 | SimpleStore: 1,
121 | },
122 | output: {},
123 | sourceMapper: (v, cb) => cb(null, v),
124 | module: {
125 | loaders: [
126 | { loader: 'ethdeploy-raw-solc-loader' },
127 | ],
128 | environment: {
129 | name: 'localhost',
130 | provider: TestRPC.provider(),
131 | },
132 | deployment: (deploy, contracts, done1) => {
133 | assert.equal(contracts.SimpleStore, 1);
134 |
135 | done1();
136 | }, // eslint-disable-line
137 | },
138 | }, () => done());
139 | });
140 |
141 | it('should handle normal entry with testrpc/raw solc', (done) => {
142 | ethdeploy({
143 | entry: {
144 | SimpleStore: {
145 | bytecode: '',
146 | interface: '',
147 | },
148 | },
149 | output: {},
150 | sourceMapper: (v, cb) => cb(null, v),
151 | module: {
152 | loaders: [
153 | { loader: 'ethdeploy-raw-solc-loader' },
154 | ],
155 | environment: {
156 | name: 'localhost',
157 | provider: TestRPC.provider(),
158 | },
159 | deployment: (deploy, contracts, done1) => {
160 | assert.equal(contracts.SimpleStore, 1);
161 |
162 | done1();
163 | }, // eslint-disable-line
164 | },
165 | }, () => done());
166 | });
167 | });
168 | });
169 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | const dir = require('node-dir');
2 | const fs = require('fs');
3 | const path = require('path');
4 | const ethUtil = require('ethjs-util');
5 |
6 | /**
7 | * Returns the ISO current date time.
8 | *
9 | * @method isoTime
10 | * @return {String} ISOTime the ISO time string
11 | */
12 | function isoTime() {
13 | return (new Date()).toISOString();
14 | }
15 |
16 | /**
17 | * Basic error method for ethdeploy
18 | *
19 | * @method isoTime
20 | * @param {String} msg the error message
21 | * @return {Object} Error the new Error object
22 | */
23 | function log(...args) {
24 | return console.log(`[ethdeploy ${isoTime()}] `, ...args); // eslint-disable-line
25 | }
26 |
27 | /**
28 | * Basic error method for ethdeploy
29 | *
30 | * @method isoTime
31 | * @param {String} msg the error message
32 | * @return {Object} Error the new Error object
33 | */
34 | function error(...args) {
35 | return new Error(`[ethdeploy ${isoTime()}] ${args.map((arg) => arg)}`);
36 | }
37 |
38 | // recursively converts all BN or BigNumber instances into string
39 |
40 | /**
41 | * Converts all BigNumber and BN instances to string, even nested deep in Arrays or Objects.
42 | *
43 | * @method bnToString
44 | * @param {Optional} objInput optional input type, bypasses most
45 | * @param {Number} baseInput the base number (usually either 10 or 16).
46 | * @param {Boolean} hexPrefixed hex prefix the output
47 | * @return {Optional} output returns optional type output with converted bn's.
48 | */
49 | function bnToString(objInput, baseInput, hexPrefixed) {
50 | var obj = objInput; // eslint-disable-line
51 | const base = baseInput || 10;
52 |
53 | // obj is an array
54 | if (typeof obj === 'object' && obj !== null) {
55 | if (Array.isArray(obj)) {
56 | // convert items in array
57 | obj = obj.map((item) => bnToString(item, base, hexPrefixed));
58 | } else {
59 | if (obj.toString && (obj.lessThan || obj.dividedToIntegerBy || obj.isBN || obj.toTwos)) {
60 | return hexPrefixed ? `0x${ethUtil.padToEven(obj.toString(16))}` : obj.toString(base);
61 | } else { // eslint-disable-line
62 | // recurively converty item
63 | Object.keys(obj).forEach((key) => {
64 | obj[key] = bnToString(obj[key], base, hexPrefixed);
65 | });
66 | }
67 | }
68 | }
69 |
70 | return obj;
71 | }
72 |
73 | /**
74 | * Filters a given sourcemap by specific test regex's.
75 | *
76 | * @method filterSourceMap
77 | * @param {Regex} testRegex the test regex (only include these files)
78 | * @param {Regex} includeRegex the include test regex (only include these files)
79 | * @param {Object} sourceMap the complete file sourcemap
80 | * @return {Object} filteredSourceMap the filtered sourcemap
81 | */
82 | /*
83 | function filterSourceMap(testRegex, includeRegex, sourceMap, excludeRegex) {
84 | const outputData = Object.assign({});
85 | const testTestRegex = testRegex || /./g;
86 | const testIncludeRegex = includeRegex || /./g;
87 | const testExcludeRegex = excludeRegex || null;
88 |
89 | if (typeof testTestRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, test regex must be type object, got ${typeof testRegex}.`); }
90 | if (typeof testIncludeRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, include regex must be type object, got ${typeof testIncludeRegex}.`); }
91 | if (typeof testExcludeRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, exclude regex must be type object, got ${typeof testExcludeRegex}.`); }
92 |
93 | Object.keys(sourceMap).forEach((key) => {
94 | if (testTestRegex.test(key) && testIncludeRegex.test(key) && (testExcludeRegex === null || !testExcludeRegex.test(key))) {
95 | if (sourceMap[key]) {
96 | outputData[key] = sourceMap[key];
97 | }
98 | }
99 | });
100 |
101 | return outputData;
102 | }*/
103 | function filterSourceMap(testRegex, includeRegex, sourceMap, excludeRegex) {
104 | const outputData = Object.assign({});
105 | const testTestRegex = testRegex || /./g;
106 | const testIncludeRegex = includeRegex || null;
107 | const testExcludeRegex = excludeRegex || null;
108 |
109 | if (typeof testTestRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, test regex must be type object, got ${typeof testRegex}.`); }
110 | if (typeof testIncludeRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, include regex must be type object, got ${typeof testIncludeRegex}.`); }
111 | if (typeof testExcludeRegex !== 'object') { throw error(`while filtering source map, ${JSON.stringify(sourceMap)}, exclude regex must be type object, got ${typeof testExcludeRegex}.`); }
112 |
113 | Object.keys(sourceMap).forEach((key) => {
114 | if (testTestRegex.test(key)
115 | && (testIncludeRegex === null || testIncludeRegex.test(key))
116 | && (testExcludeRegex === null || !testExcludeRegex.test(key))) {
117 | Object.assign(outputData, { [key]: sourceMap[key] });
118 | }
119 | });
120 |
121 | return outputData;
122 | }
123 |
124 |
125 | /**
126 | * This will wait for a transaction to present a receipt
127 | *
128 | * @method getTransactionSuccess
129 | * @param {Object} eth the eth query instance
130 | * @param {Object} txHash the transaction hash
131 | * @param {Object} timeout settings
132 | * @param {Function} callback the final callback
133 | * @callback {Object} contractInstance the deployed contract instance with receipt prop
134 | */
135 | function getTransactionSuccess(eth, txHash, timeout = 800000, callback) {
136 | const cb = callback || function cb() {};
137 | let count = 0;
138 | return new Promise((resolve, reject) => {
139 | const txInterval = setInterval(() => {
140 | eth.getTransactionReceipt(txHash, (err, result) => {
141 | if (err) {
142 | clearInterval(txInterval);
143 | cb(err, null);
144 | reject(err);
145 | }
146 |
147 | if (!err && result) {
148 | clearInterval(txInterval);
149 | cb(null, result);
150 | resolve(result);
151 | }
152 | });
153 |
154 | if (count >= timeout) {
155 | clearInterval(txInterval);
156 | const errMessage = `Receipt timeout waiting for tx hash: ${txHash}`;
157 | cb(errMessage, null);
158 | reject(errMessage);
159 | }
160 | count += 7000;
161 | }, 7000);
162 | });
163 | }
164 |
165 | /**
166 | * Deploy the contract with eth, factory, and args
167 | *
168 | * @method deployContract
169 | * @param {Object} eth the eth query instance
170 | * @param {Object} factory the contract factory
171 | * @param {Array} args the contract constructor arguments
172 | * @param {Function} callback the final callback
173 | * @callback {Object} contractInstance the deployed contract instance with receipt prop
174 | */
175 | function deployContract(eth, factory, args, callback) {
176 | factory.new.apply(factory, args).then((txHash) => {
177 | getTransactionSuccess(eth, txHash, 8000000, (receiptError, receipt) => {
178 | if (receiptError) {
179 | callback(receiptError, null);
180 | }
181 |
182 | if (receipt) {
183 | const contractInstance = factory.at(receipt.contractAddress);
184 | contractInstance.receipt = receipt;
185 | callback(null, contractInstance);
186 | }
187 | });
188 | }).catch(callback);
189 | }
190 |
191 | /**
192 | * Get all input source for a specific pathname, used for mapping config entry
193 | *
194 | * @method getInputSources
195 | * @param {String} pathname a path string to a file or directory
196 | * @param {Function} callback the final callback that returns the sources
197 | * @callback {Object} sourceMap returns a source map for this pathname
198 | */
199 | function getInputSources(pathname, callback) {
200 | let filesRead = 0;
201 | let sources = {};
202 |
203 | // test file is a file, the last section contains a extention period
204 | if (String(pathname).split('/').pop().indexOf('.') !== -1) {
205 | const searchPathname = pathname.substr(0, 2) === './' ? pathname.slice(2) : pathname;
206 |
207 | fs.readFile(searchPathname, 'utf8', (readFileError, fileData) => { // eslint-disable-line
208 | if (readFileError) {
209 | return callback(error(`while while getting input sources ${readFileError}`));
210 | }
211 |
212 | callback(null, { [searchPathname]: fileData });
213 | });
214 | } else {
215 | // get all file names
216 | dir.files(pathname, (filesError, files) => { // eslint-disable-line
217 | if (filesError) {
218 | return callback(error(`while while getting input sources ${filesError}`));
219 | }
220 |
221 | // if no files in directory, then fire callback with empty sources
222 | if (files.length === 0) {
223 | callback(null, sources);
224 | } else {
225 | // read all files
226 | dir.readFiles(pathname, (readFilesError, content, filename, next) => { // eslint-disable-line
227 | if (readFilesError) {
228 | return callback(error(`while getting input sources ${readFilesError}`));
229 | }
230 |
231 | // add input sources to output
232 | sources = Object.assign({}, sources, {
233 | [path.join('./', filename)]: content,
234 | });
235 |
236 | // increase files readFiles
237 | filesRead += 1;
238 |
239 | // process next file
240 | if (filesRead === files.length) {
241 | callback(null, sources);
242 | } else {
243 | next();
244 | }
245 | });
246 | }
247 | });
248 | }
249 | }
250 |
251 | // the base utilty methods for ethdeploy
252 | module.exports = {
253 | isoTime,
254 | error,
255 | log,
256 | deployContract,
257 | filterSourceMap,
258 | getInputSources,
259 | bnToString,
260 | };
261 |
--------------------------------------------------------------------------------
/src/utils/tests/test.index.js:
--------------------------------------------------------------------------------
1 | const utils = require('../index.js');
2 | const assert = require('chai').assert;
3 | const BN = require('bn.js');
4 | const BigNumber = require('bignumber.js');
5 |
6 | describe('utils', () => {
7 | describe('isoTime', () => {
8 | it('should function properly', () => {
9 | assert.equal(typeof utils.isoTime, 'function');
10 | assert.equal(typeof utils.isoTime(), 'string');
11 | });
12 | });
13 |
14 | describe('error', () => {
15 | it('should function properly', () => {
16 | assert.equal(typeof utils.error, 'function');
17 | assert.equal(typeof utils.error(), 'object');
18 | });
19 | });
20 |
21 | describe('deployContract', () => {
22 | it('should function properly', () => {
23 | assert.equal(typeof utils.deployContract, 'function');
24 | });
25 | });
26 |
27 | describe('filterSourceMap', () => {
28 | it('should function properly', () => {
29 | const testMap = {
30 | 'somefile.json': '{}',
31 | '#.': 'ssf',
32 | 'sdkffjsd.js': 'jsdkfk',
33 | 'anotherfile.json': '{}',
34 | 'sdflskjlfsd.sol': 'sdf',
35 | 'sdfk/jsdfjksdf/kjsdfkjsfd.js': 'fssdf',
36 | };
37 | const JSONTestMap = {
38 | 'anotherfile.json': '{}',
39 | 'somefile.json': '{}',
40 | };
41 | const anotherFileJSON = {
42 | 'anotherfile.json': '{}',
43 | };
44 |
45 | assert.equal(typeof utils.filterSourceMap, 'function');
46 |
47 | assert.deepEqual(utils.filterSourceMap(/\.(json)$/, null, testMap), JSONTestMap);
48 | assert.deepEqual(utils.filterSourceMap(null, /\.(json)$/, testMap), JSONTestMap);
49 | assert.deepEqual(utils.filterSourceMap(null, null, testMap), testMap);
50 | assert.deepEqual(utils.filterSourceMap(null, /anotherfile/, testMap), anotherFileJSON);
51 | assert.throws(() => utils.filterSourceMap(null, 4232, testMap), Error);
52 | assert.throws(() => utils.filterSourceMap(24323424, /anotherfile/, testMap), Error);
53 | });
54 | });
55 |
56 | describe('getInputSources', () => {
57 | it('should function properly', (done) => {
58 | assert.equal(typeof utils.getInputSources, 'function');
59 |
60 | utils.getInputSources('./src/utils/tests/testSources', (err, result) => {
61 | assert.equal(err, null);
62 | assert.deepEqual(result, {
63 | 'src/utils/tests/testSources/someDir/AnotherDir/something.json': '{"yes": 1}\n',
64 | 'src/utils/tests/testSources/someDir/anotherFile': '',
65 | 'src/utils/tests/testSources/someDir/someDeeperDir/AnotherDeeperDir/anotherFile': '',
66 | 'src/utils/tests/testSources/someFile.json': '{}\n',
67 | 'src/utils/tests/testSources/someFile.s': 'const cool = 1;\n\nmodule.exports = cool;\n' });
68 | done();
69 | });
70 | });
71 |
72 | it('should handle a single file', (done) => {
73 | utils.getInputSources('./src/utils/tests/testSources/someFile.json', (err, result) => {
74 | assert.equal(err, null);
75 | assert.deepEqual(result, { 'src/utils/tests/testSources/someFile.json': '{}\n' });
76 | done();
77 | });
78 | });
79 |
80 | it('should handle invalid file', (done) => {
81 | utils.getInputSources('src/utils/tests/testSources/someFile.jssdn', (err) => {
82 | assert.equal(typeof err, 'object');
83 | done();
84 | });
85 | });
86 |
87 | it('should handle invalid file', (done) => {
88 | utils.getInputSources('src/utils/tests/testSousdfeFile', (err) => {
89 | assert.equal(typeof err, 'object');
90 | done();
91 | });
92 | });
93 |
94 | it('should handle invalid file', (done) => {
95 | utils.getInputSources('src/utils/tests/testSousdfeFile.jssdn', (err) => {
96 | assert.equal(typeof err, 'object');
97 | done();
98 | });
99 | });
100 |
101 | it('should handle a single file', (done) => {
102 | utils.getInputSources('src/utils/tests/testSources/someFile.json', (err, result) => {
103 | assert.equal(err, null);
104 | assert.deepEqual(result, { 'src/utils/tests/testSources/someFile.json': '{}\n' });
105 | done();
106 | });
107 | });
108 | });
109 |
110 | describe('bnToString', () => {
111 | it('should function properly', () => {
112 | assert.equal(typeof utils.bnToString, 'function');
113 |
114 | const bnToStringTests = [
115 | { actual: [], expected: [] },
116 | { actual: 0, expected: 0 },
117 | { actual: 'something', expected: 'something' },
118 | { actual: true, expected: true },
119 | { actual: false, expected: false },
120 | { actual: { something: true }, expected: { something: true } },
121 | { actual: { something: 'someString' }, expected: { something: 'someString' } },
122 | { actual: null, expected: null },
123 | { actual: undefined, expected: undefined },
124 | { actual: [22, 333, 'hello', null], expected: [22, 333, 'hello', null] },
125 | { actual: new BN(1), expected: '1' },
126 | { actual: new BigNumber(0), expected: '0' },
127 | { actual: new BN('1000'), expected: '1000' },
128 | { actual: [23498, 'sdfhjs', null, true, new BN(1), true], expected: [23498, 'sdfhjs', null, true, '1', true] },
129 | { actual: [23498, new BN(1), null, true, new BN(1), true], expected: [23498, '1', null, true, '1', true] },
130 | { actual: { cool: 45, something: new BN(1), arr: [23498, new BigNumber(1), null, true, new BN(1), true] },
131 | expected: { cool: 45, something: '1', arr: [23498, '1', null, true, '1', true] } },
132 | { actual: { cool: 45, something: new BN(1), arr: [23498, new BN(1), null, true, { another: new BN(1), cool: 222 }, true] },
133 | expected: { cool: 45, something: '1', arr: [23498, '1', null, true, { another: '1', cool: 222 }, true] } },
134 | ];
135 |
136 | bnToStringTests.forEach((testCase) => {
137 | assert.deepEqual(utils.bnToString(testCase.actual), testCase.expected);
138 | });
139 | });
140 |
141 | it('should hex prefix properly', () => {
142 | assert.deepEqual(utils.bnToString(new BN('249824987'), 16, true), `0x0${new BN('249824987').toString(16)}`);
143 | });
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/src/utils/tests/testSources/someDir/AnotherDir/something.json:
--------------------------------------------------------------------------------
1 | {"yes": 1}
2 |
--------------------------------------------------------------------------------
/src/utils/tests/testSources/someDir/anotherFile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilentCicero/ethdeploy/acb1212e1942ce604eb82e34ce2f8c9d6b20a0a6/src/utils/tests/testSources/someDir/anotherFile
--------------------------------------------------------------------------------
/src/utils/tests/testSources/someDir/someDeeperDir/AnotherDeeperDir/anotherFile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SilentCicero/ethdeploy/acb1212e1942ce604eb82e34ce2f8c9d6b20a0a6/src/utils/tests/testSources/someDir/someDeeperDir/AnotherDeeperDir/anotherFile
--------------------------------------------------------------------------------
/src/utils/tests/testSources/someFile.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/src/utils/tests/testSources/someFile.s:
--------------------------------------------------------------------------------
1 | const cool = 1;
2 |
3 | module.exports = cool;
4 |
--------------------------------------------------------------------------------