rocketh 0.4.41 → 0.5.2
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/.prettierignore +3 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +13 -0
- package/README.md +1 -21
- package/dist/chunk-INGRKRCC.js +648 -0
- package/dist/chunk-INGRKRCC.js.map +1 -0
- package/dist/cli.cjs +697 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +684 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -20
- package/src/cli.ts +37 -0
- package/src/environment/deployments.ts +79 -0
- package/src/environment/index.ts +392 -0
- package/src/environment/types.ts +158 -0
- package/src/executor/index.ts +302 -0
- package/src/executor/types.ts +42 -0
- package/src/index.ts +5 -0
- package/src/internal/types.ts +6 -0
- package/src/utils/fs.ts +70 -0
- package/src/utils/json.ts +26 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +5 -0
- package/.gitattributes +0 -1
- package/bitski_subprovider.js +0 -148
- package/geth_test_server.js +0 -194
- package/index.js +0 -424
- package/provider.js +0 -58
- package/providerengine.js +0 -128
- package/run.js +0 -1575
- package/run_ganache.js +0 -27
- package/utils.js +0 -188
- package/walletprovider.js +0 -232
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
|
-
}
|