rocketh 0.4.41 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/run.js DELETED
@@ -1,1575 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const colors = require('colors/safe');
4
- const semver = require('semver');
5
- const portfinder = require('portfinder');
6
- const Provider = require('./provider');
7
- const rimraf = require('rimraf');
8
- const bip39 = require('bip39');
9
- const geth = require('./geth_test_server');
10
- const runGanache = require('./run_ganache');
11
- const tmp = require('tmp');
12
- const ethers = require('ethers');
13
- // const {Logger} = require('@ethersproject/logger');
14
- // Logger.globalLogger().setLogLevel('error');
15
- const {BigNumber} = ethers;
16
- const WalletSubprovider = require('./walletprovider');
17
-
18
- function linkLibrary(bytecode, libraryName, libraryAddress) {
19
- const address = libraryAddress.replace('0x', '');
20
- const encodedLibraryName = ethers.utils
21
- .solidityKeccak256(['string'], [libraryName])
22
- .slice(2, 36);
23
- const pattern = new RegExp(`_+\\$${encodedLibraryName}\\$_+`, 'g');
24
- if (!pattern.exec(bytecode)) {
25
- throw new Error(`Can't link '${libraryName}' (${encodedLibraryName}). in \n----\n ${bytecode}\n----\n`);
26
- }
27
- return bytecode.replace(pattern, address);
28
- }
29
-
30
- const {
31
- requireLocal,
32
- log,
33
- traverse,
34
- fetchChainId,
35
- fetchReceiptViaWeb3Provider,
36
- fetchAccounts,
37
- fetchChainIdViaWeb3Provider,
38
- pause,
39
- mergeConfig,
40
- } = require('./utils');
41
-
42
- if (!global._rocketh_session) {
43
- global._rocketh_session = {};
44
- }
45
- const session = global._rocketh_session;
46
-
47
- const cleanDeployments = () => {
48
- try {
49
- rimraf.sync(path.join(writeDeploymentsPath, deploymentsSubPath));
50
- } catch (e) {
51
-
52
- }
53
- }
54
-
55
- const unRegisterDeployment = (name) => {
56
- const filepath = path.join(writeDeploymentsPath, deploymentsSubPath, name + '.json');
57
- try {
58
- fs.unlinkSync(filepath);
59
- } catch(e) {
60
- console.error('could not delete ' + filepath);
61
- }
62
- }
63
-
64
- const registerDeployment = (name, deploymentInfo, force) => {
65
- session.currentDeployments = session.currentDeployments || {};
66
- const conflict = !force && session.currentDeployments[name] && !(deploymentInfo.transactionHash == session.currentDeployments[name].transactionHash);
67
- if (conflict) {
68
- console.error(colors.red('deployment with same name (' + name + ') exists'));
69
- } else {
70
- const errors = [];
71
- if (!deploymentInfo.contractInfo) {
72
- errors.push(colors.red('deploymentInfo requires field "contractInfo" that was for deployment'));
73
- }
74
- if (!deploymentInfo.args) {
75
- errors.push(colors.red('deploymentInfo requires field "args" that was used for deployment'));
76
- }
77
- // if (!deploymentInfo.address) {
78
- // errors.push(colors.red('deploymentInfo requires field "address" of the deployed contract'));
79
- // }
80
- if (!deploymentInfo.transactionHash) {
81
- errors.push(colors.red('deploymentInfo requires field "transactionHash" of the deployed contract'));
82
- }
83
- if (errors.length > 0) {
84
- for (const error of errors) {
85
- console.error(error);
86
- }
87
- } else {
88
- const deploymentInfoToSave = {
89
- contractInfo: deploymentInfo.contractInfo,
90
- args: deploymentInfo.args,
91
- address: deploymentInfo.address,
92
- transactionHash: deploymentInfo.transactionHash,
93
- data: deploymentInfo.data,
94
- };
95
-
96
- session.deployments[name] = deploymentInfoToSave;
97
- session.currentDeployments[name] = deploymentInfoToSave;
98
-
99
- if (!disableDeploymentSave) {
100
- if (!deploymentsFolderCreated) {
101
- try { fs.mkdirSync(writeDeploymentsPath); } catch (e) { }
102
- try { fs.mkdirSync(path.join(writeDeploymentsPath, deploymentsSubPath)); } catch (e) { }
103
- deploymentsFolderCreated = true;
104
- }
105
- const content = JSON.stringify(deploymentInfoToSave, null, ' ');
106
- const filepath = path.join(writeDeploymentsPath, deploymentsSubPath, name + '.json');
107
-
108
- let inputString;
109
- let solcVersion;
110
- if (deploymentInfoToSave.contractInfo.metadata) {
111
- const metadata = JSON.parse(deploymentInfoToSave.contractInfo.metadata);
112
- const settings = metadata.settings;
113
- delete settings.compilationTarget;
114
- inputString = JSON.stringify({
115
- language: metadata.language,
116
- settings,
117
- sources: metadata.sources,
118
- });
119
- solcVersion = metadata.compiler ? metadata.compiler.version : '';
120
- if(!inputFolderCreated) {
121
- try { fs.mkdirSync(path.join(writeDeploymentsPath, deploymentsSubPath, 'inputs')); } catch (e) { }
122
- inputFolderCreated = true;
123
- }
124
- }
125
- fs.writeFileSync(filepath, content);
126
- if(inputString) {
127
- fs.writeFileSync(path.join(writeDeploymentsPath, deploymentsSubPath, 'inputs', name + '_' + solcVersion + '.json'), inputString);
128
- }
129
-
130
- // if (initialRun) {
131
- // const address = deploymentInfoToSave.address;
132
- // if(address) {
133
- // console.log('CONTRACT ' + name + ' DEPLOYED AT : ' + address);
134
- // }
135
- // }
136
- }
137
-
138
- // if(generateTruffleBuildFiles) {
139
- // let truffleBuildFile = null;
140
- // try{
141
- // truffleBuildFile = JSON.parse(fs.readFileSync('build/contracts/' + name + '.json').toString());
142
- // } catch(e) {}
143
- // if(truffleBuildFile) {
144
- // truffleBuildFile.networks[_chainId] = {
145
- // // "events": {}, // TODO ?
146
- // // "links": {}, // TODO ?
147
- // address: deploymentInfo.address.toLowerCase()
148
- // // transactionHash: // TODO ?
149
- // };
150
- // fs.writeFileSync('build/contracts/' + name + '.json', JSON.stringify(truffleBuildFile, null, ' '));
151
- // }
152
- // }
153
- }
154
- }
155
- };
156
-
157
-
158
- let deploymentsFolderCreated = false;
159
- let inputFolderCreated = false;
160
- let contractBuildFolderCreated = false;
161
-
162
- function compile(config) {
163
- return new Promise((resolve, reject) => {
164
- const rootPath = config.rootPath || './';
165
- let contractSrcPaths;
166
- if (typeof config.contractSrcPath == 'undefined') {
167
- contractSrcPaths = ['src'];
168
- } else if (typeof config.contractSrcPath == 'string') {
169
- contractSrcPaths = [config.contractSrcPath];
170
- } else {
171
- contractSrcPaths = config.contractSrcPath;
172
- }
173
- contractSrcPaths = contractSrcPaths.map((elem) => path.join(rootPath, elem));
174
-
175
- contractSrcPaths = contractSrcPaths.filter(fs.existsSync);
176
- if (contractSrcPaths.length == 0) {
177
- resolve({ contractInfos: {}, solcOutput: null, solcConfig: null });
178
- return;
179
- }
180
-
181
- let solc;
182
- try {
183
- solc = requireLocal('solc');
184
- } catch (e) {
185
- reject('you need to install your desired solc (>= 0.4.11) compiler in your own project: "npm install solc');
186
- return;
187
- }
188
- compileWithSolc(solc, contractSrcPaths, resolve, reject, config);
189
- });
190
- }
191
-
192
- async function runNode(config) {
193
- let url = config.url;
194
- let requireTesting = false;
195
-
196
- _chainId = null;
197
- if (url) {
198
- try {
199
- _chainId = await fetchChainId(url, config.useNetVersionAsChainId);
200
- } catch(e) {
201
- console.error('failed to get chainId from ' + url);
202
- if(!config.useNetVersionAsChainId) {
203
- console.log('trying with net_version...');
204
- config.useNetVersionAsChainId = true;
205
- _chainId = await fetchChainId(url, config.useNetVersionAsChainId);
206
- }
207
- }
208
-
209
- }
210
-
211
- const forceAccounts = !url;
212
-
213
- const result = getAccountsFromConfig(config, _chainId, forceAccounts);
214
- let privateKeys;
215
- let exposedMnemonic;
216
- if (result.accounts) {
217
- _accounts = result.accounts;
218
- privateKeys = result.privateKeys;
219
- exposedMnemonic = result.exposedMnemonic;
220
- } else {
221
- _accounts = await fetchAccounts(url)
222
- }
223
-
224
- let stop = () => { };
225
-
226
- if (!url) {
227
- const port = await portfinder.getPortPromise({
228
- port: 8545, // minimum port
229
- stopPort: 9999 // maximum port
230
- });
231
-
232
- const wsPort = await portfinder.getPortPromise({
233
- port: port + 1, // minimum port
234
- stopPort: 9999 // maximum port
235
- });
236
-
237
- if (config.node == 'ganache') {
238
- const ganacheOptions = config.ganacheOptions || { debug: true, vmErrorsOnRPCResponse: true };
239
- if (config.chainId) {
240
- ganacheOptions.network_id = config.chainId;
241
- }
242
- if (typeof config.blockTime !== 'undefined') {
243
- ganacheOptions.blockTime = config.blockTime; // TODO for geth
244
- }
245
- // console.log(JSON.stringify(ganacheOptions, null, ' '));
246
- const ganacheAccounts = [];
247
- for (let i = 0; i < privateKeys.length; i++) {
248
- ganacheAccounts.push({
249
- balance: config.defaultBalance || "0x56BC75E2D63100000",
250
- secretKey: privateKeys[i]
251
- });
252
- }
253
- ganacheOptions.accounts = ganacheAccounts;
254
- await runGanache(port, wsPort, ganacheOptions);
255
- } else if (config.node == 'geth') {
256
- const runningGeth = await geth.serve(port, wsPort, _accounts, config.chainId, config);
257
- stop = runningGeth.stop;
258
- } else {
259
- const message = 'node type not supprted : ' + config.node;
260
- console.error(colors.red(message));
261
- reject(message)
262
- }
263
- url = "http://localhost:" + port;
264
- requireTesting = true;
265
- }
266
-
267
- const provider = getWeb3Provider(config, url, _chainId, forceAccounts);
268
-
269
- if (requireTesting) {
270
- let success = false
271
- // TODO failed after few tries ?
272
- while (!success) {
273
- try {
274
- _chainId = await fetchChainIdViaWeb3Provider(provider, config.useNetVersionAsChainId);
275
- success = true;
276
- } catch (e) {
277
- const errorMessage = e.message || e.error ? e.error.message : undefined;
278
- if(errorMessage && errorMessage.indexOf('eth_chainId not supported') != -1) {
279
- if(!config.useNetVersionAsChainId) {
280
- console.log('eth_chainId not supported, falling back to net_version...');
281
- config.useNetVersionAsChainId = true;
282
- }
283
- } else {
284
- // log.error(e);
285
- }
286
- }
287
- } // TODO timeout
288
- } else {
289
- _chainId = await fetchChainIdViaWeb3Provider(provider, config.useNetVersionAsChainId);
290
- }
291
-
292
- return { url, chainId: _chainId, accounts: _accounts, stop, exposedMnemonic };
293
- }
294
-
295
- function compileWithSolc(solc, contractSrcPaths, resolve, reject, config) {
296
- const rootPath = config.rootPath || './';
297
-
298
- // console.log({contractSrcPaths});
299
-
300
- const contractBuildPath = path.join(rootPath, config.contractBuildPath || 'build');
301
- const cacheOutputPath = contractBuildPath + '/.compilationOutput.json';
302
- const cacheInputPath = contractBuildPath + '/.compilationInput.json';
303
-
304
- const sources = {};
305
- let latestMtimeMs = 0;
306
- for (let contractSrcPath of contractSrcPaths) {
307
- // console.log(contractSrcPath);
308
- const files = traverse(contractSrcPath);
309
- for (const file of files) {
310
- if (file.name.indexOf('.sol') === file.name.length - 4) {
311
- latestMtimeMs = Math.max(latestMtimeMs, file.mtimeMs);
312
- const relativePath = path.relative(rootPath, file.path).replace(/\\/g, '/');
313
- // console.log(relativePath);
314
- sources[relativePath] = {
315
- content: fs.readFileSync(file.path).toString()
316
- };
317
- }
318
- }
319
- }
320
-
321
- //TODO latestMtimeMs will need to take into account compiler version change / config change ....
322
-
323
-
324
- const solcVersion = solc.semver();
325
- const pre_0_5_0_solc = semver.lt(solcVersion, '0.5.0');
326
- const pre_0_6_0_solc = semver.lt(solcVersion, '0.6.0');
327
- const pre_0_4_11_solc = semver.lt(solcVersion, '0.4.11');
328
- if (pre_0_4_11_solc) {
329
- const message = 'you are using a too old solc version, rocketh only support solc >= 0.4.11';
330
- console.error(colors.red(message));
331
- reject(message);
332
- }
333
-
334
- log.green('using solc : ' + solcVersion);
335
-
336
- // TODO : config // merge from File ? add sources...
337
- const solcConfigObj = {
338
- language: "Solidity",
339
- //TODO add compiler info in some way to not use cache when compiler version is different // extra fields were fine prior to 0.5.1 or 0.5.2
340
- // compiler: {
341
- // name: "solc",
342
- // version: solcVersion
343
- // },
344
- sources,
345
- settings: mergeConfig({
346
- optimizer: {
347
- enabled: true,
348
- runs: 200,
349
- },
350
- outputSelection: {
351
- '*': {
352
- '*': [
353
- 'abi',
354
- // 'devdoc',
355
- 'userdoc',
356
- 'metadata',
357
- // 'evm.assembly',
358
- // 'evm.legacyAssembly',
359
- 'evm.bytecode',
360
- 'evm.deployedBytecode',
361
- 'evm.methodIdentifiers',
362
- // 'evm.gasEstimates',
363
- // 'ir', fails with the following:
364
- // { component: 'general',
365
- // formattedMessage:
366
- // 'UnimplementedFeatureError: Array conversion not implemented.\n',
367
- // message:
368
- // 'Unimplemented feature (/root/project/libsolidity/codegen/YulUtilFunctions.cpp:819):Array conversion not implemented.',
369
- // severity: 'error',
370
- // type: 'UnimplementedFeatureError' }
371
- ],
372
- // "": ["ast", "legacyAST"]
373
- },
374
- },
375
- metadata: {
376
- useLiteralContent: true
377
- },
378
- }, config.solcSettings || {})
379
- };
380
- // TODO check for evm.bytecode as it is required for rocketh
381
-
382
- const solcConfig = JSON.stringify(solcConfigObj, null, ' ');
383
-
384
-
385
- let rawOutput;
386
- let cachePathMTimeMS = 0;
387
- let usingCache = false;
388
- try {
389
- cachePathMTimeMS = fs.statSync(cacheOutputPath).mtimeMs;
390
- } catch (e) { }
391
- if (latestMtimeMs < cachePathMTimeMS) {
392
- let lastInput = '';
393
- try {
394
- lastInput = fs.readFileSync(cacheInputPath).toString(); // TODO buffer and stream check checksum
395
- } catch (e) { }
396
- const sameInput = lastInput === solcConfig;
397
- if (!sameInput) {
398
- log.log('config changed...');
399
- } else {
400
- usingCache = true;
401
- // TODO expose the info that cache is being used so that test runner can skip test that did not change
402
- }
403
- }
404
-
405
- const imports = {}; // TODO import saving to import.json or .compilationInput.json
406
- function findImport(importPath) {
407
- // console.log('trying to import : ' + importPath);
408
-
409
- if (path.isAbsolute(importPath)) {
410
- return { error: 'Absolute path not supported : ' + importPath };
411
- }
412
- try {
413
- const data = { contents: fs.readFileSync(importPath).toString() };
414
- imports[importPath] = data;
415
- return data;
416
- } catch (e) {
417
- try {
418
- const modulePath = path.join('./node_modules', importPath);
419
- const data = { contents: fs.readFileSync(modulePath).toString() };
420
- imports[importPath] = data;
421
- return data;
422
- } catch (e) {
423
- return { error: 'File not found ' + importPath };
424
- }
425
- }
426
- }
427
-
428
- if (!usingCache) {
429
- log.green('########################################### COMPILING #############################################################');
430
- if (pre_0_5_0_solc && !pre_0_4_11_solc) {
431
- rawOutput = solc.compileStandardWrapper(solcConfig);
432
- } else if (pre_0_6_0_solc) {
433
- rawOutput = solc.compile(solcConfig, findImport);
434
- } else {
435
- rawOutput = solc.compile(solcConfig, {import: findImport});
436
- }
437
- } else {
438
- log.blue('########################################## FROM CACHE ############################################################');
439
- rawOutput = fs.readFileSync(cacheOutputPath).toString();
440
- }
441
-
442
- const output = JSON.parse(rawOutput);
443
-
444
- const warnings = [];
445
- const errors = [];
446
- const others = []; // TODO
447
- if (output.errors && (!usingCache || config.showErrorsFromCache)) {
448
- for (const error of output.errors) {
449
- if (error.severity === 'warning') {
450
- //TODO filter warning based on // ignore comments , example "// ignore:26:5: Warning: Function state mutability can be restricted to pure"
451
- // read file content, get the line, find the // ignore: pattern and disable warning if match
452
- warnings.push(error);
453
- } else if (error.severity === 'error') {
454
- errors.push(error);
455
- } else {
456
- others.push(error);
457
- }
458
- }
459
- }
460
-
461
- if (errors.length > 0) {
462
- for (const error of errors) {
463
- log.red(error.formattedMessage);
464
- }
465
- log.red('###################################################################################################################');
466
- reject(errors);
467
- } else {
468
-
469
- if (config.cacheCompilationResult) {
470
- if (!contractBuildFolderCreated) {
471
- try { fs.mkdirSync(contractBuildPath); } catch (e) { }
472
- contractBuildFolderCreated = true;
473
- }
474
- fs.writeFileSync(cacheOutputPath, JSON.stringify(output, null, ' '));
475
- fs.writeFileSync(cacheInputPath, solcConfig);
476
- }
477
-
478
- for (const warning of warnings) {
479
- log.yellow(warning.formattedMessage);
480
- }
481
- for (const other of others) {
482
- log.cyan(other.formattedMessage);
483
- }
484
- const bar = '###################################################################################################################';
485
- if (usingCache) {
486
-
487
- if (config.showErrorsFromCache) {
488
- log.blue('########################################## FROM CACHE ############################################################');
489
- }
490
- } else {
491
- log.green(bar);
492
- }
493
-
494
- const contractInfos = {};
495
- log.log('importing contracts...');
496
- const importsPath = path.join(config.rootPath || './', config.importsPath || 'imports');
497
- let files;
498
- try {
499
- files = traverse(importsPath, [], null, (name, stats) => true);
500
- } catch (e) {
501
- files = [];
502
- }
503
- for (const file of files) {
504
- if (!file.directory && file.name.indexOf('.json') === file.name.length - 5) {
505
- contractInfos[file.name.substr(0, file.name.length - 5)] = JSON.parse(fs.readFileSync(file.path).toString());
506
- }
507
- }
508
-
509
- for (const filePath of Object.keys(output.contracts)) {
510
- for (const contractName of Object.keys(output.contracts[filePath])) {
511
- const contractInfo = output.contracts[filePath][contractName];
512
- const content = JSON.stringify(contractInfo, null, ' ');
513
- if (contractName === "") {
514
- contractName = filePath.substr(filePath.lastIndexOf('/')); // TODO remove extension?
515
- }
516
-
517
- if (!contractBuildFolderCreated) {
518
- try { fs.mkdirSync(contractBuildPath); } catch (e) { }
519
- contractBuildFolderCreated = true;
520
- }
521
- fs.writeFileSync(contractBuildPath + '/' + contractName + '.json', content);
522
-
523
- if (config.generateTruffleBuildFiles && contractInfo.evm.bytecode.object && contractInfo.evm.bytecode.object != '') {
524
- const truffleBuildFile = {
525
- bytecode: contractInfo.evm.bytecode.object,
526
- sourceMap: contractInfo.evm.bytecode.sourceMap,
527
- deployedBytecode: contractInfo.evm.deployedBytecode.object,
528
- deployedSourceMap: contractInfo.evm.deployedBytecode.sourceMap,
529
- source: sources[filePath].content,
530
- sourcePath: filePath,
531
- ast: output.contracts[filePath].ast,
532
- networks: {}, // TODO
533
- contractName
534
- };
535
- try { fs.mkdirSync('build'); } catch (e) { }
536
- try { fs.mkdirSync('build/contracts'); } catch (e) { }
537
- fs.writeFileSync('build/contracts/' + contractName + '.json', JSON.stringify(truffleBuildFile, null, ' '));
538
- }
539
- if (contractInfos[contractName]) {
540
- log.log('overriding ' + contractName);
541
- }
542
- contractInfos[contractName] = contractInfo;
543
- // if (contractInfo.evm
544
- // && contractInfo.evm.bytecode
545
- // && contractInfo.evm.bytecode.object === '') { // TODO config to skip error
546
- // console.warn(colors.yellow('contract ' + contractName + ' do not have bytecode generated'))
547
- // }
548
- }
549
- }
550
- compilationDone = true;
551
- resolve({ contractInfos, solcOutput: output, solcConfig: JSON.parse(solcConfig), solcVersion, contractSrcPaths });
552
- }
553
- }
554
-
555
- function extractContractInfos(output) {
556
- const contractInfos = {};
557
- for (const filePath of Object.keys(output.contracts)) {
558
- for (const contractName of Object.keys(output.contracts[filePath])) {
559
- const contractInfo = output.contracts[filePath][contractName];
560
- if (contractName === "") {
561
- contractName = filePath.substr(filePath.lastIndexOf('/')); // TODO remove extension?
562
- }
563
- contractInfos[contractName] = contractInfo;
564
- }
565
- }
566
- return contractInfos;
567
- }
568
-
569
- function extractDeployments(deploymentsPath) {
570
- const deployments = {};
571
-
572
- let files;
573
- try {
574
- files = traverse(deploymentsPath, [], null, (name, stats) => name != 'inputs');
575
- } catch (e) {
576
- files = [];
577
- }
578
- for (const file of files) {
579
- if (!file.directory && file.name.indexOf('.json') === file.name.length - 5) {
580
- deployments[file.name.substr(0, file.name.length - 5)] = JSON.parse(fs.readFileSync(file.path).toString());
581
- }
582
- }
583
- return deployments;
584
- }
585
-
586
- function chainConfig(config, object, chainId) {
587
- const isDeploymentChainId = config.deploymentChainIds && config.deploymentChainIds.indexOf('' + chainId) != -1;
588
- if (typeof object["" + chainId] != 'undefined') {
589
- return object["" + _chainId];
590
- } else if (typeof object[_chainId] != 'undefined') {
591
- return object[_chainId];
592
- } else if (isDeploymentChainId && typeof object['deployments'] != 'undefined') {
593
- return object['deployments'];
594
- } else {
595
- return object['default'];
596
- }
597
- }
598
-
599
- // cache
600
- let _chainId;
601
- let _accounts;
602
- let disableDeploymentSave;
603
- async function resetDeployments() {
604
- session.deployments = {};
605
- session.currentDeployments = {};
606
- }
607
-
608
- async function runStages(config, contractInfos, deployments, initialRun) {
609
- disableDeploymentSave = !deployments;
610
-
611
- session.currentDeployments = {};
612
-
613
- if(deployments) {
614
- for(let contractName of Object.keys(deployments)) {
615
- const deployment = deployments[contractName];
616
- if(deployment.transactionHash && !deployment.address) {
617
- console.log('waiting for transaction ' + deployment.transactionHash + ' to be mined');
618
- let contractAddress;
619
- while(!contractAddress) {
620
- const receipt = await fetchReceiptViaWeb3Provider(global.ethereum, deployment.transactionHash);
621
- if(!receipt) {
622
- log.log('no receipt yet for ' + deployment.transactionHash);
623
- } else if(typeof receipt.status != 'undefined' && receipt.status == 0) {
624
- console.log('transaction ' + deployment.transactionHash + ' failed.');
625
- unRegisterDeployment(contractName);
626
- // TODO exit ?
627
- break;
628
- } else if(receipt.contractAddress) {
629
- contractAddress = receipt.contractAddress;
630
- break;
631
- }
632
- await pause(2);
633
- }
634
- if(contractAddress) {
635
- deployment.address = contractAddress;
636
- console.log(' transaction ' + deployment.transactionHash + ' successful');
637
- registerDeployment(contractName, deployment, true);
638
- }
639
- }
640
- }
641
- }
642
-
643
- initialRun = initialRun || typeof deployments != 'undefined';
644
- session.deployments = deployments || {}; // override
645
-
646
- const stagesPath = path.join(config.rootPath || './', config.stagesPath || 'stages');
647
- // log.green('######################################### STAGES ##############################################################');
648
-
649
- let filesStats;
650
- try {
651
- filesStats = traverse(stagesPath);
652
- } catch (e) {
653
- log.green('no stages folder at ./' + stagesPath);
654
- return session.deployments;
655
- }
656
-
657
- let stagesFilter;
658
- if (config.stages) {
659
- stagesFilter = chainConfig(config, config.stages, _chainId);
660
- if(stagesFilter === 'all') {
661
- stagesFilter = undefined;
662
- }
663
- }
664
- filesStats = filesStats.filter((fileStat) => {
665
- const filepath = fileStat.relativePath.replace(/\\/g, '/');
666
- let matches = true;
667
- if(stagesFilter) {
668
- if(stagesFilter.matchRule === 'startsWith') {
669
- matches = false;
670
- for (let elem of stagesFilter.list) {
671
- if(filepath.startsWith(elem)){
672
-
673
- matches = true;
674
- break;
675
- }
676
- }
677
- } // TODO more rules
678
- }
679
- return matches && !fileStat.directory;
680
- });
681
- let fileNames = filesStats.map(a => a.relativePath);
682
- fileNames = fileNames.sort((a, b) => {
683
- if (a < b) { return -1; }
684
- if (a > b) { return 1; }
685
- return 0;
686
- });
687
-
688
-
689
- let argsForStages = [{
690
- ethereum,
691
- rocketh,
692
- contractInfo: (name) => contractInfos[name],
693
- accounts: _accounts,
694
- chainId: _chainId,
695
- registerDeployment,
696
- deployment: function (name) { return session.currentDeployments[name] },
697
- namedAccounts: rocketh.namedAccounts,
698
- sendTxAndWait,
699
- batchTxAndWait,
700
- call,
701
- estimateGas,
702
- initialRun,
703
- isDeploymentChainId: config.deploymentChainIds.indexOf('' + _chainId) != -1,
704
- deploy,
705
- deployIfNeverDeployed,
706
- deployIfDifferent,
707
- fetchIfDifferent,
708
- getDeployedContract,
709
- registerContract,
710
- getEvents,
711
- sendTxAndWaitOnlyFrom,
712
- }];
713
-
714
- for (const fileName of fileNames) {
715
- const migrationFilePath = path.resolve(".") + '/' + stagesPath + '/' + fileName;
716
- if (initialRun) {
717
- log.log('running ' + migrationFilePath);
718
- }
719
- const stageFunc = require(migrationFilePath);
720
- let skip = false;
721
- if (stageFunc.skip) {
722
- try {
723
- skip = await stageFunc.skip.apply(null, argsForStages);
724
- } catch (e) {
725
- throw 'ERROR processing skip func of ' + migrationFilePath + ':\n' + (e.stack || e);
726
- }
727
- }
728
- if (!skip) {
729
- try {
730
- await stageFunc.apply(null, argsForStages);
731
- } catch (e) {
732
- throw 'ERROR processing ' + migrationFilePath + ':\n' + (e.stack || e);
733
- }
734
- } else {
735
- if (initialRun) {
736
- log.log('skipping ' + migrationFilePath);
737
- }
738
- }
739
- }
740
- // log.green('###################################################################################################################');
741
- return session.deployments;
742
- }
743
-
744
-
745
- let _savedConfig;
746
- let _contractInfos;
747
-
748
- const rocketh = {
749
- runStages: () => runStages(_savedConfig, _contractInfos), // empty deployment for running Stages : blank canvas for testing
750
- deployment: (name) => {
751
- return session.currentDeployments ? (session.currentDeployments[name] || session.deployments[name]) : session.deployments[name];
752
- },
753
- deployments: () => session.deployments, // TODO remove ?
754
- getDeployedContract: getDeployedContract,
755
- contractInfo: (name) => {
756
- return _contractInfos[name];
757
- },
758
- deploy,
759
- call,
760
- rawCall,
761
- sendTxAndWait,
762
- batchTxAndWait,
763
- estimateGas,
764
- registerDeployment,
765
- registerContract,
766
- getEvents,
767
- sendTxAndWaitOnlyFrom,
768
- deployIfDifferent,
769
- resetDeployments,
770
- }
771
-
772
- let ethersProvider;
773
-
774
-
775
- let deploymentsPath;
776
- let writeDeploymentsPath;
777
- let deploymentsSubPath;
778
-
779
-
780
- let _accountsUsed;
781
- function getAccountsFromConfig(config, chainId, forceAccounts) {
782
- if (_accountsUsed) {
783
- return _accountsUsed;
784
- }
785
-
786
- // log.log('getting account for chainId ' + chainId);
787
-
788
- let mnemonic;
789
- let accounts;
790
- let privateKeys;
791
-
792
- let accountsConfig;
793
- if (chainId && config.accounts["" + chainId]) {
794
- accountsConfig = config.accounts["" + chainId];
795
- } else {
796
- accountsConfig = config.accounts["default"];
797
- }
798
- const type = accountsConfig.type
799
-
800
- let numWallets = 10; // default mnemonic when taken form _ROCKETH_MNEMONIC env (see below)
801
-
802
- forceAccounts = forceAccounts || (process.env._ROCKETH_MNEMONIC && process.env._ROCKETH_MNEMONIC != "");
803
- if (type == 'node') {
804
- log.log('using node\'s accounts');
805
- if (!forceAccounts) {
806
- return {};
807
- }
808
- } else if (type == 'mnemonic') {
809
- log.log('using mnemonic');
810
- numWallets = accountsConfig.num || 10;
811
- const mnemonicPath = accountsConfig.path || './.mnemonic';
812
- try {
813
- mnemonic = fs.readFileSync(mnemonicPath).toString();
814
- } catch (e) { }
815
- } else if (type == 'privateKeys') {
816
- log.log('using privateKeys');
817
- privateKeys = JSON.parse(fs.readFileSync('./.priv').toString());
818
- accounts = [];
819
- for (let i = 0; i < privateKeys.length; i++) {
820
- const wallet = new ethers.Wallet(privateKeys[i]);
821
- accounts.push(wallet.address);
822
- }
823
- } else if (type == 'bitski') {
824
- log.log('using bitski');
825
- try {
826
- const bitskiConfig = JSON.parse(fs.readFileSync('./.bitski').toString());
827
- accounts = bitskiConfig.accounts;
828
- } catch (e) {
829
- log.error('cannot read .bitski');
830
- }
831
- }
832
-
833
- let exposedMnemonic;
834
- if ((!accounts || accounts.length == 0) && !mnemonic) {
835
- if (process.env._ROCKETH_MNEMONIC && process.env._ROCKETH_MNEMONIC != "") {
836
- mnemonic = process.env._ROCKETH_MNEMONIC.split(',').join(' ');
837
- } else {
838
- mnemonic = bip39.generateMnemonic()
839
- exposedMnemonic = mnemonic;
840
- }
841
- }
842
-
843
- if (mnemonic) {
844
- privateKeys = [];
845
- accounts = [];
846
- for (let i = 0; i < numWallets; i++) {
847
- const wallet = ethers.Wallet.fromMnemonic(mnemonic, "m/44'/60'/0'/0/" + i);
848
- accounts.push(wallet.address);
849
- privateKeys.push(wallet.privateKey);
850
- }
851
- }
852
-
853
- _accountsUsed = { exposedMnemonic, privateKeys, accounts };
854
- return _accountsUsed;
855
- }
856
-
857
- function getWeb3Provider(config, url, chainId, forceAccounts) {
858
- let accountsConfig;
859
- if (chainId && config.accounts["" + chainId]) {
860
- accountsConfig = config.accounts["" + chainId];
861
- } else {
862
- accountsConfig = config.accounts["default"];
863
- }
864
- const type = accountsConfig.type
865
-
866
- const subProviders = [];
867
- let subProvidersConfigured = false;
868
- if (type == "bitski") {
869
- try {
870
- const bitskiConfig = JSON.parse(fs.readFileSync('./.bitski').toString());
871
-
872
- const BitskiSubProvider = require('./bitski_subprovider');
873
- const bitskiWalletSubProvider = new BitskiSubProvider(
874
- bitskiConfig.clientID,
875
- bitskiConfig.credentials.ID,
876
- bitskiConfig.credentials.secret,
877
- bitskiConfig.accounts,
878
- chainId,
879
- config,
880
- );
881
- subProviders.push(bitskiWalletSubProvider);
882
- subProvidersConfigured = true;
883
- } catch (e) {
884
- log.error('cannot read .bitski');
885
- }
886
- }
887
-
888
- if (!subProvidersConfigured) {
889
- const { privateKeys } = getAccountsFromConfig(config, chainId, forceAccounts);
890
- if (privateKeys) {
891
- const walletProvider = new WalletSubprovider(privateKeys, config);
892
- subProviders.push(walletProvider)
893
- }
894
- }
895
-
896
- return new Provider(url, subProviders);
897
- }
898
-
899
- let attached;
900
-
901
-
902
- function attach(config, { url, chainId, accounts }, contractInfos, deployments) {
903
-
904
- const ethereumNodeURl = url || process.env._ROCKETH_NODE_URL;
905
- rocketh.chainId = _chainId = chainId || process.env._ROCKETH_CHAIN_ID;
906
- rocketh.accounts = _accounts = accounts;
907
- if (!rocketh.accounts && process.env._ROCKETH_ACCOUNTS) {
908
- rocketh.accounts = _accounts = process.env._ROCKETH_ACCOUNTS.split(',');
909
- }
910
- deploymentsPath = (process.env._ROCKETH_DEPLOYMENTS && process.env._ROCKETH_DEPLOYMENTS) != "" ? process.env._ROCKETH_DEPLOYMENTS : undefined;
911
- deploymentsSubPath = _chainId;
912
- const isDeploymentChainId = config.deploymentChainIds.indexOf('' + _chainId) != -1;
913
-
914
- _savedConfig = config;
915
- rocketh.config = config;
916
- if (!deploymentsPath) {
917
- if (config.deploymentsPath) {
918
- deploymentsPath = config.deploymentsPath;
919
- deploymentsSubPath = '';
920
- } else {
921
- deploymentsPath = path.join(config.rootPath || './', config.deploymentsPath || 'deployments');
922
-
923
- }
924
- writeDeploymentsPath = deploymentsPath;
925
- if (!isDeploymentChainId) {
926
- const tmpobj = tmp.dirSync({ keep: true });
927
- writeDeploymentsPath = tmpobj.name;
928
- }
929
- } else {
930
- writeDeploymentsPath = deploymentsPath;
931
- }
932
- log.log('using deployments at ' + deploymentsPath, deploymentsSubPath, 'writing at ' + writeDeploymentsPath);
933
-
934
- const namedAccounts = {}
935
- // TODO transform into checksum address
936
- if (config.namedAccounts) {
937
- log.log('namedAccount')
938
- const nameConfig = config.namedAccounts;
939
- const accountNames = Object.keys(nameConfig);
940
- function parseSpec(spec) {
941
- let address;
942
- switch (typeof spec) {
943
- case "string":
944
- if (spec.slice(0, 5) == "from:") {
945
- const from = parseInt(spec.substr(5));
946
- address = [];
947
- if (_accounts) {
948
- for (let j = from; j < _accounts.length; j++) {
949
- address.push(_accounts[j]);
950
- }
951
- }
952
- } else if (spec.slice(0, 2).toLowerCase() == "0x") {
953
- address = spec;
954
- } else {
955
- address = parseSpec(nameConfig[spec])
956
- }
957
- break;
958
- case "number":
959
- if (_accounts) {
960
- address = _accounts[spec];
961
- }
962
- break;
963
- case "undefined":
964
- break;
965
- case "object":
966
- if (spec) {
967
- if (spec.type == 'object') {
968
- address = spec;
969
- } else if (Array.isArray(spec)) {
970
- address = [];
971
- for (let j = 0; j < spec.length; j++) {
972
- address.push(parseSpec(spec[j]));
973
- }
974
- } else {
975
- const newSpec = chainConfig(config, spec, _chainId);
976
- if(typeof newSpec != 'undefined') {
977
- address = parseSpec(newSpec);
978
- }
979
- }
980
- }
981
- break;
982
- }
983
- return address;
984
- }
985
-
986
- for (let i = 0; i < accountNames.length; i++) {
987
- const accountName = accountNames[i];
988
- const spec = nameConfig[accountName];
989
- namedAccounts[accountName] = parseSpec(spec);
990
- }
991
- }
992
- rocketh.namedAccounts = namedAccounts;
993
-
994
- if (attached) {
995
- //already setup
996
- return attached;
997
- }
998
-
999
- if (!_contractInfos) {
1000
- _contractInfos = contractInfos;
1001
- }
1002
-
1003
- let compilationInput;
1004
- if (!_contractInfos) {
1005
-
1006
-
1007
- const importedContractInfos = {};
1008
- log.log('importing contracts...');
1009
- const importsPath = path.join(config.rootPath || './', config.importsPath || 'imports');
1010
- let files;
1011
- try {
1012
- files = traverse(importsPath, [], null, (name, stats) => true);
1013
- } catch (e) {
1014
- files = [];
1015
- }
1016
- for (const file of files) {
1017
- if (!file.directory && file.name.indexOf('.json') === file.name.length - 5) {
1018
- importedContractInfos[file.name.substr(0, file.name.length - 5)] = JSON.parse(fs.readFileSync(file.path).toString());
1019
- }
1020
- }
1021
-
1022
- log.log('getting contracts info from compilation output....');
1023
- // TODO remove duplic :
1024
- try {
1025
- const contractBuildPath = path.join(config.rootPath || './', config.contractBuildPath || 'build');
1026
- const cacheOutputPath = contractBuildPath + '/.compilationOutput.json';
1027
- _contractInfos = extractContractInfos(JSON.parse(fs.readFileSync(cacheOutputPath).toString()), contractBuildPath);
1028
-
1029
- for (const contractName of Object.keys(importedContractInfos)) {
1030
- if (!_contractInfos[contractName]) {
1031
- _contractInfos[contractName] = importedContractInfos[contractName];
1032
- }
1033
- }
1034
-
1035
- const inputPath = contractBuildPath + '/.compilationInput.json';
1036
- compilationInput = JSON.parse(fs.readFileSync(inputPath).toString());
1037
- } catch (e) {
1038
- log.log('no contracts');
1039
- }
1040
- }
1041
-
1042
- log.log('deployments', session.deployments);
1043
- if (!session.deployments) {
1044
- log.log('no deployments', deployments);
1045
- session.deployments = deployments;
1046
- }
1047
-
1048
- if (!session.deployments) {
1049
- log.log('extracting deployments for chainId', _chainId, deploymentsPath, deploymentsSubPath);
1050
- session.deployments = extractDeployments(path.join(deploymentsPath, deploymentsSubPath));
1051
- }
1052
-
1053
- if(writeDeploymentsPath != deploymentsPath || isDeploymentChainId) {
1054
- let deploymentsFolderCreated = false;
1055
- for (let name of Object.keys(session.deployments)) {
1056
- if (!deploymentsFolderCreated) {
1057
- try { fs.mkdirSync(writeDeploymentsPath); } catch (e) { }
1058
- try { fs.mkdirSync(path.join(writeDeploymentsPath, deploymentsSubPath)); } catch (e) { }
1059
- deploymentsFolderCreated = true;
1060
- }
1061
- const content = JSON.stringify(session.deployments[name], null, ' ');
1062
- const filepath = path.join(writeDeploymentsPath, deploymentsSubPath, name + '.json');
1063
- fs.writeFileSync(filepath, content);
1064
- }
1065
- }
1066
-
1067
- let provider;
1068
- if (ethereumNodeURl && ethereumNodeURl !== '') {
1069
- if (url) {
1070
- log.log('using node at ' + url + ' (' + _chainId + ')' + ' ...');
1071
- }
1072
-
1073
- provider = getWeb3Provider(config, ethereumNodeURl, _chainId); // TODO for sol-trace _contractInfos, compilationInput, config.rootPath || './', config.contractSrcdPath || 'src');
1074
- } else {
1075
- console.error(colors.red('ROCKETH_NODE_URL not set'));
1076
- process.exit(1);
1077
- }
1078
-
1079
- rocketh.ethereum = provider;
1080
- rocketh.sendTxAndWait = sendTxAndWait;
1081
- rocketh.batchTxAndWait = batchTxAndWait;
1082
- rocketh.estimateGas = estimateGas;
1083
- rocketh.call = call;
1084
- global.ethereum = provider;
1085
-
1086
- ethersProvider = new ethers.providers.Web3Provider(rocketh.ethereum);
1087
-
1088
- if (config.addRocketh) {
1089
- global.rocketh = rocketh;
1090
- }
1091
-
1092
- attached = {
1093
- rocketh,
1094
- contractInfos: _contractInfos,
1095
- deployments: session.deployments,
1096
- deploymentsPath: writeDeploymentsPath,
1097
- };
1098
-
1099
- return attached;
1100
- }
1101
-
1102
- function getIndex(from) {
1103
- let i = 0;
1104
- for (const account of rocketh.accounts) {
1105
- if(account.toLowerCase() == from.toLowerCase()) {
1106
- return i;
1107
- }
1108
- i++;
1109
- }
1110
- throw new Error('no account found for ' + from);
1111
- }
1112
-
1113
- function getEthersSigner(from) {
1114
- let accountIndex = 0;
1115
- if(from) {
1116
- accountIndex = getIndex(from);
1117
- }
1118
- return ethersProvider.getSigner(accountIndex);
1119
- }
1120
-
1121
- async function batchTxAndWait(txs, batchOptions) {
1122
- const promises = [];
1123
- const currentNonces = {}
1124
- for (const tx of txs) {
1125
- const options = tx[0];
1126
- let from = options.from;
1127
- let ethersSigner;
1128
- if(from.length >= 64) {
1129
- if(from.length == 64) {
1130
- from = '0x' + from;
1131
- }
1132
- ethersSigner = new Wallet(from);
1133
- from = ethersSigner.address;
1134
- } else {
1135
- try {
1136
- ethersSigner = getEthersSigner(from);
1137
- } catch{}
1138
- }
1139
- // console.log(tx);
1140
- const nonce = options.nonce || currentNonces[from] || await ethersProvider.getTransactionCount(from);
1141
- tx[0].nonce = nonce;
1142
- currentNonces[from] = nonce + 1;
1143
- promises.push(sendTxAndWait(...tx));
1144
- }
1145
- if (batchOptions.dev_forceMine) {
1146
- try {
1147
- await ethersProvider.send('evm_mine', []);
1148
- } catch(e) {}
1149
- }
1150
- await Promise.all(promises);
1151
- }
1152
-
1153
- async function sendTxAndWaitOnlyFrom(from, options, contractName, methodName, ...args) {
1154
- const deployment = rocketh.deployment(contractName);
1155
- const abi = deployment.contractInfo.abi;
1156
- const ethersContract = new ethers.Contract(deployment.address, abi, ethersProvider);
1157
- if (from.toLowerCase() !== options.from.toLowerCase()) {
1158
- const {data} = await ethersContract.populateTransaction[methodName](...args);
1159
- const to = ethersContract.address;
1160
- console.log(options.from + ' has no right to ' + methodName);
1161
-
1162
- console.log('Please execute the following as ' + from);
1163
- console.log(JSON.stringify({
1164
- to,
1165
- data,
1166
- }, null, ' '));
1167
- console.log('if you have an interface use the following');
1168
- console.log(JSON.stringify({
1169
- to,
1170
- method: methodName,
1171
- args,
1172
- }, null, ' '));
1173
- if (options.skipError) {
1174
- return null;
1175
- }
1176
- throw new Error('ABORT, ACTION REQUIRED, see above');
1177
- }
1178
- return sendTxAndWait(options, contractName, methodName, ...args);
1179
- }
1180
-
1181
- async function sendTxAndWait(options, contractName, methodName, ...args) {
1182
- // console.log({
1183
- // options, contractName, methodName, args
1184
- // });
1185
- let from = options.from;
1186
- let ethersSigner;
1187
- if(from.length >= 64) {
1188
- if(from.length == 64) {
1189
- from = '0x' + from;
1190
- }
1191
- ethersSigner = new Wallet(from);
1192
- from = ethersSigner.address;
1193
- } else {
1194
- try {
1195
- ethersSigner = getEthersSigner(from);
1196
- } catch{}
1197
- }
1198
-
1199
- let tx;
1200
- if (contractName) {
1201
- const deployment = rocketh.deployment(contractName);
1202
- const abi = deployment.contractInfo.abi
1203
- const overrides = {
1204
- gasLimit: options.gas,
1205
- gasPrice: options.gasPrice ? BigNumber.from(options.gasPrice) : undefined, // TODO cinfig
1206
- value: options.value ? BigNumber.from(options.value) : undefined,
1207
- nonce: options.nonce,
1208
- chainId: options.chainId,
1209
- }
1210
- if (!ethersSigner) { // ethers.js : would be nice to be able to estimate even if not access to signer (see below)
1211
- console.error('no signer for ' + from);
1212
- console.log('Please execute the following as ' + from);
1213
- const ethersContract = new ethers.Contract(deployment.address, abi, ethersProvider);
1214
- const ethersArgs = args ? args.concat([overrides]) : [overrides];
1215
- const {data} = await ethersContract.populateTransaction[methodName](...ethersArgs);
1216
- console.log(JSON.stringify({
1217
- to: deployment.address,
1218
- data,
1219
- }, null, ' '));
1220
- console.log('if you have an interface use the following');
1221
- console.log(JSON.stringify({
1222
- to: deployment.address,
1223
- method: methodName,
1224
- args,
1225
- }, null, ' '));
1226
- throw new Error('ABORT, ACTION REQUIRED, see above')
1227
- } else {
1228
- const ethersContract = new ethers.Contract(deployment.address, abi, ethersSigner);
1229
- if (!overrides.gasLimit) {
1230
- overrides.gasLimit = options.estimateGasLimit;
1231
- const ethersArgs = args ? args.concat([overrides]) : [overrides];
1232
- // console.log(ethersContract.estimate);
1233
- overrides.gasLimit = (await ethersContract.estimate[methodName](...ethersArgs)).toNumber();
1234
- if (options.estimateGasExtra) {
1235
- overrides.gasLimit = overrides.gasLimit + options.estimateGasExtra;
1236
- if (options.estimateGasLimit) {
1237
- overrides.gasLimit = Math.min(overrides.gasLimit, options.estimateGasLimit);
1238
- }
1239
- }
1240
- }
1241
- const ethersArgs = args ? args.concat([overrides]) : [overrides];
1242
- tx = await ethersContract.functions[methodName](...ethersArgs);
1243
- }
1244
- } else {
1245
- if (!ethersSigner) { // ethers.js : would be nice to be able to estimate even if not access to signer (see below)
1246
- console.error('no signer for ' + from);
1247
- } else {
1248
- const transactionData = {
1249
- to: options.to,
1250
- gasLimit: options.gas,
1251
- gasPrice: options.gasPrice ? BigNumber.from(options.gasPrice) : undefined, // TODO cinfig
1252
- value: options.value ? BigNumber.from(options.value) : undefined,
1253
- nonce: options.nonce,
1254
- data: options.data,
1255
- chainId: options.chainId,
1256
- }
1257
- tx = await ethersSigner.sendTransaction(transactionData);
1258
- }
1259
- }
1260
- if (options.dev_forceMine) {
1261
- try {
1262
- await ethersProvider.send('evm_mine', []);
1263
- } catch(e) {}
1264
- }
1265
- return tx.wait();
1266
- }
1267
-
1268
- async function getEvents(contract, sig, options) {
1269
- const ethersContract = new ethers.Contract(contract.address, contract.abi, ethersProvider);
1270
- let topic = ethers.utils.id(sig);
1271
- let fromBlock = 0;
1272
- let toBlock = 'latest';
1273
- if (options && options.receipt) {
1274
- fromBlock = options.receipt.blockNumber;
1275
- toBlock = options.receipt.blockNumber;
1276
- }
1277
- let filter = {
1278
- address: contract.address,
1279
- fromBlock,
1280
- toBlock,
1281
- topics: [ topic ]
1282
- }
1283
-
1284
- const logs = await ethersProvider.getLogs(filter);
1285
- return logs.map(l => ethersContract.interface.parseLog(l));
1286
- }
1287
-
1288
- async function rawCall(to, data) { // TODO call it eth_call?
1289
- return ethersProvider.send('eth_call', [{
1290
- to,
1291
- data
1292
- }, 'latest']); // TODO overrides
1293
- }
1294
-
1295
- async function call(options, contractName, methodName, ...args) {
1296
- if (typeof options === 'string') {
1297
- if(typeof methodName !== 'undefined') {
1298
- args.unshift(methodName);
1299
- }
1300
- methodName = contractName;
1301
- contractName = options;
1302
- options = {};
1303
- }
1304
- if (typeof args === 'undefined') {
1305
- args = [];
1306
- }
1307
- let from = options.from;
1308
- let ethersSigner;
1309
- if(from && from.length >= 64) {
1310
- if(from.length == 64) {
1311
- from = '0x' + from;
1312
- }
1313
- ethersSigner = new Wallet(from);
1314
- from = ethersSigner.address;
1315
- }
1316
- if(!ethersSigner) {
1317
- ethersSigner = ethersProvider; // TODO rename ethersSigner
1318
- }
1319
- const deployment = rocketh.deployment(contractName);
1320
- if (!deployment) {
1321
- throw new Error(`no contract named "${contractName}"`);
1322
- }
1323
- const abi = deployment.contractInfo.abi
1324
- const overrides = {
1325
- gasLimit: options.gas,
1326
- gasPrice: options.gasPrice,
1327
- value: options.value,
1328
- nonce: options.nonce,
1329
- chainId: options.chainId,
1330
- }
1331
- const ethersContract = new ethers.Contract(deployment.address, abi, ethersSigner);
1332
- if (options.outputTx) {
1333
- const method = ethersContract.populateTransaction[methodName];
1334
- if (!method) {
1335
- throw new Error(`no method named "${methodName}" on contract "${contractName}"`);
1336
- }
1337
- if(args.length > 0) {
1338
- return method(...args, overrides);
1339
- } else {
1340
- return method(overrides);
1341
- }
1342
- }
1343
- const method = ethersContract.callStatic[methodName];
1344
- if (!method) {
1345
- throw new Error(`no method named "${methodName}" on contract "${contractName}"`);
1346
- }
1347
- if(args.length > 0) {
1348
- return method(...args, overrides);
1349
- } else {
1350
- return method(overrides);
1351
- }
1352
- }
1353
-
1354
- async function estimateGas(options, contractName, methodName, ...args) {
1355
- if (typeof options == 'string') {
1356
- if (typeof args == 'undefined') {
1357
- args = [];
1358
- if(typeof methodName != 'undefined') {
1359
- args.push(methodName);
1360
- }
1361
- }
1362
- methodName = contractName;
1363
- contractName = options;
1364
- options = {};
1365
- }
1366
- if (typeof args == 'undefined') {
1367
- args = [];
1368
- }
1369
- let from = options.from;
1370
- let ethersSigner;
1371
- if(from && from.length >= 64) {
1372
- if(from.length == 64) {
1373
- from = '0x' + from;
1374
- }
1375
- ethersSigner = new Wallet(from);
1376
- from = ethersSigner.address;
1377
- } else if (from) {
1378
- ethersSigner = getEthersSigner(from);
1379
- }
1380
- const deployment = rocketh.deployment(contractName);
1381
- const abi = deployment.contractInfo.abi
1382
- const overrides = {
1383
- gasLimit: options.gas,
1384
- gasPrice: options.gasPrice,
1385
- value: options.value,
1386
- nonce: options.nonce,
1387
- chainId: options.chainId,
1388
- }
1389
- const ethersContract = new ethers.Contract(deployment.address, abi, ethersSigner);
1390
- return ethersContract.estimate[methodName](...args, overrides);
1391
- }
1392
-
1393
- async function deploy(name, options, contractName, ...args) {
1394
- let register = true;
1395
- if (typeof name != 'string') {
1396
- register = false;
1397
- args.unshift(contractName);
1398
- contractName = options;
1399
- options = name;
1400
- }
1401
- let from = options.from;
1402
- let ethersSigner;
1403
- if (!from) {
1404
- throw new Error('no from specified');
1405
- }
1406
- if(from.length >= 64) {
1407
- if(from.length == 64) {
1408
- from = '0x' + from;
1409
- }
1410
- ethersSigner = new Wallet(from);
1411
- from = ethersSigner.address;
1412
- } else {
1413
- ethersSigner = getEthersSigner(from);
1414
- }
1415
- const ContractInfo = rocketh.contractInfo(contractName);
1416
- const abi = ContractInfo.abi;
1417
-
1418
- let byteCode = '0x' + ContractInfo.evm.bytecode.object;
1419
- if (options && options.libraries) {
1420
- for (const libName of Object.keys(options.libraries)) {
1421
- const libAddress = options.libraries[libName];
1422
- byteCode = linkLibrary(byteCode, libName, libAddress);
1423
- }
1424
- }
1425
- const factory = new ethers.ContractFactory(abi, byteCode, ethersSigner);
1426
-
1427
- const overrides = {
1428
- gasLimit: options.gas,
1429
- gasPrice: options.gasPrice,
1430
- value: options.value,
1431
- nonce: options.nonce,
1432
- chainId: options.chainId,
1433
- }
1434
- const ethersContract = await factory.deploy(...args, overrides);
1435
- const tx = ethersContract.deployTransaction;
1436
- const transactionHash = tx.hash;
1437
- if (register) {
1438
- rocketh.registerDeployment(name, {
1439
- contractInfo: ContractInfo,
1440
- transactionHash,
1441
- args,
1442
- data: options.associatedData,
1443
- });
1444
- }
1445
- if (options.dev_forceMine) {
1446
- try {
1447
- await ethersProvider.send('evm_mine', []);
1448
- } catch(e) {}
1449
- }
1450
- const receipt = await tx.wait(); // TODO return tx.wait
1451
- const address = receipt.contractAddress;
1452
- const contract = {address, abi};
1453
-
1454
- if (register) {
1455
- rocketh.registerDeployment(name, {
1456
- contractInfo: ContractInfo,
1457
- address: contract.address,
1458
- transactionHash,
1459
- args,
1460
- data: options.associatedData
1461
- });
1462
- }
1463
- return {
1464
- contract,
1465
- transactionHash,
1466
- receipt,
1467
- newlyDeployed: true,
1468
- };
1469
- }
1470
-
1471
- async function deployIfNeverDeployed(name, options, contractName, ...args) {
1472
- const deployment = rocketh.deployment(name);
1473
- if (!deployment) {
1474
- return deploy(name, options, contractName, ...args);
1475
- } else {
1476
- return getDeployedContractWithTransactionHash(name);
1477
- }
1478
- }
1479
-
1480
- async function fetchIfDifferent(fieldsToCompare, name, options, contractName, ...args) {
1481
- const deployment = rocketh.deployment(name);
1482
- if (deployment) {
1483
- const transaction = await ethersProvider.getTransaction(deployment.transactionHash);
1484
- if (transaction) {
1485
- const ContractInfo = rocketh.contractInfo(contractName);
1486
- const abi = ContractInfo.abi;
1487
- const factory = new ethers.ContractFactory(abi, '0x' + ContractInfo.evm.bytecode.object, getEthersSigner(options.from));
1488
-
1489
- const compareOnData = fieldsToCompare.indexOf('data') != -1;
1490
- const compareOnInput = fieldsToCompare.indexOf('input') != -1;
1491
-
1492
- let data;
1493
- if (compareOnData || compareOnInput) {
1494
- const deployStruct = factory.getDeployTransaction(...args);
1495
- data = deployStruct.data;
1496
- // console.log(JSON.stringify(data, null, ' '));
1497
- }
1498
- const newTransaction = {
1499
- data: compareOnData ? data : undefined,
1500
- input: compareOnInput ? data : undefined,
1501
- gas: options.gas,
1502
- gasPrice: options.gasPrice,
1503
- value: options.value,
1504
- from: options.from
1505
- };
1506
-
1507
- transaction.data = transaction.data || transaction.input;
1508
- transaction.input = transaction.input || transaction.data;
1509
- for (let i = 0; i < fieldsToCompare.length; i++) {
1510
- const field = fieldsToCompare[i];
1511
- if (typeof newTransaction[field] == 'undefined') {
1512
- throw new Error('field ' + field + ' not specified in new transaction, cant compare');
1513
- }
1514
- if (transaction[field] != newTransaction[field]) {
1515
- return true;
1516
- }
1517
- }
1518
- return false;
1519
- }
1520
- }
1521
- return true;
1522
- }
1523
-
1524
- async function deployIfDifferent(fieldsToCompare, name, options, contractName, ...args) {
1525
- const differences = await fetchIfDifferent(fieldsToCompare, name, options, contractName, ...args);
1526
- if (differences) {
1527
- return deploy(name, options, contractName, ...args);
1528
- } else {
1529
- return getDeployedContractWithTransactionHash(name);
1530
- }
1531
-
1532
- };
1533
-
1534
- function fromDeployment(deployment) {
1535
- return { address: deployment.address, abi: deployment.contractInfo.abi};
1536
- }
1537
-
1538
- async function getDeployedContractWithTransactionHash(name) {
1539
- const deployment = rocketh.deployment(name);
1540
- if (!deployment) {
1541
- return null;
1542
- }
1543
- const receipt = await fetchReceiptViaWeb3Provider(global.ethereum, deployment.transactionHash);
1544
- return { contract: fromDeployment(deployment), transactionHash: deployment.transactionHash, receipt };
1545
- }
1546
-
1547
- function getDeployedContract(name) {
1548
- const deployment = rocketh.deployment(name);
1549
- if (!deployment) {
1550
- return null;
1551
- }
1552
- return fromDeployment(deployment)
1553
- }
1554
-
1555
- function registerContract(name, address, txHash, contractName, ...args) {
1556
- const ContractInfo = rocketh.contractInfo(contractName);
1557
- rocketh.registerDeployment(name, {
1558
- contractInfo: ContractInfo,
1559
- address,
1560
- transactionHash: txHash,
1561
- args
1562
- });
1563
- return { address, abi: ContractInfo.abi };
1564
- }
1565
-
1566
- module.exports = {
1567
- runStages,
1568
- runNode,
1569
- compile,
1570
- attach,
1571
- rocketh,
1572
- cleanDeployments,
1573
- extractDeployments,
1574
- resetDeployments,
1575
- }