truffle-plugin-stdjsonin 0.5.14

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Mehrdad Salehi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # truffle-plugin-stdjsonin
2
+
3
+ A [Truffle](https://trufflesuite.com/index.html) plugin for generating a flat Solidity Json Input file.
4
+
5
+ The [Solidity Json Input](https://docs.soliditylang.org/en/v0.8.10/using-the-compiler.html#compiler-input-and-output-json-description) format is preferred over [flattening](https://www.npmjs.com/package/truffle-flattener) your files during verification on Etherscan as it :
6
+ - preserves code formatting
7
+ - maintains multipart files
8
+ - embeds compiler settings, including optimization and bytecodehash
9
+
10
+ ## Installation
11
+ 1. Install the plugin using npm
12
+ ```
13
+ npm install -D https://github.com/nhancv/truffle-plugin-stdjsonin/
14
+ ```
15
+ 2. Add the plugin to your `truffle-config.js` file
16
+ ```javascript
17
+ module.exports = {
18
+ /* ... rest of truffle-config */
19
+
20
+ plugins: [
21
+ 'truffle-plugin-stdjsonin'
22
+ ]
23
+ }
24
+ ```
25
+ ## Usage
26
+ 1. Run the plugin on your specified contract name
27
+ ```
28
+ truffle run stdjsonin ContractName
29
+ ```
30
+ A `ContractName-Input.json` file is generated in your project directory.
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "truffle-plugin-stdjsonin",
3
+ "version": "0.5.14",
4
+ "description": "generate Standrad JSON Input from the Truffle CLI",
5
+ "repository": "https://github.com/nhancv/truffle-plugin-stdjsonin",
6
+ "main": "stdjsonin.js",
7
+ "files": [
8
+ "util.js",
9
+ "stdjsonin.js",
10
+ "truffle-plugin.json"
11
+ ],
12
+ "scripts": {
13
+ "test": "echo \"Error: no test specified\" && exit 1",
14
+ "lint": "eslint ."
15
+ },
16
+ "keywords": [
17
+ "Standard JSON Input",
18
+ "truffle",
19
+ "plugin",
20
+ "ethereum",
21
+ "etherscan",
22
+ "verify"
23
+ ],
24
+ "author": "Mehrdad Salehi",
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "cli-logger": "^0.5.40"
28
+ },
29
+ "devDependencies": {
30
+ "eslint": "^7.32.0",
31
+ "eslint-config-standard": "^16.0.3",
32
+ "eslint-plugin-import": "^2.24.2",
33
+ "eslint-plugin-node": "^11.1.0",
34
+ "eslint-plugin-promise": "^5.1.0",
35
+ "eslint-plugin-standard": "^5.0.0"
36
+ }
37
+ }
package/stdjsonin.js ADDED
@@ -0,0 +1,149 @@
1
+ const cliLogger = require("cli-logger");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const { enforce, enforceOrThrow, normaliseContractPath } = require("./util");
5
+ const { version } = require("./package.json");
6
+
7
+ const logger = cliLogger({ level: "info" });
8
+
9
+ module.exports = async (config) => {
10
+ // Set debug logging
11
+ if (config.debug) logger.level("debug");
12
+ logger.debug("DEBUG logging is turned ON");
13
+ logger.debug(`Running truffle-plugin-stdjsonin v${version}`);
14
+
15
+ const options = parseConfig(config);
16
+ const contractNameAddressPairs = config._.slice(1);
17
+
18
+ for (const contractNameAddressPair of contractNameAddressPairs) {
19
+ logger.info(`Generating Standard JSON Input ${contractNameAddressPair}`);
20
+ try {
21
+ const contractName = contractNameAddressPair.split("@")[0];
22
+ const artifact = getArtifact(contractName, options);
23
+ const inputJSON = getInputJSON(artifact, options);
24
+ fs.writeFileSync(`${contractName}-input.json`, JSON.stringify(inputJSON));
25
+ console.log(
26
+ `Standard JSON Input for ${contractName} is saved in ${contractName}-input.json`
27
+ );
28
+ console.log(
29
+ "[Optional step] trying to compile and check with generated Standard JSON Input..."
30
+ );
31
+ tryCompileAndMatch(artifact, inputJSON, contractName);
32
+ } catch (error) {
33
+ logger.error(error.message);
34
+ }
35
+ logger.info();
36
+ }
37
+ };
38
+
39
+ const parseConfig = (config) => {
40
+ enforce(config._.length > 1, "No contract name(s) specified", logger);
41
+
42
+ const workingDir = config.working_directory;
43
+ const contractsBuildDir = config.contracts_build_directory;
44
+ const contractsDir = config.contracts_directory;
45
+
46
+ let forceConstructorArgsType, forceConstructorArgs;
47
+ if (config.forceConstructorArgs) {
48
+ [forceConstructorArgsType, forceConstructorArgs] =
49
+ config.forceConstructorArgs.split(":");
50
+ enforce(
51
+ forceConstructorArgsType === "string",
52
+ "Force constructor args must be string type",
53
+ logger
54
+ );
55
+ logger.debug(`Force custructor args provided: 0x${forceConstructorArgs}`);
56
+ }
57
+
58
+ return {
59
+ workingDir,
60
+ contractsBuildDir,
61
+ contractsDir,
62
+ forceConstructorArgs,
63
+ };
64
+ };
65
+
66
+ const getArtifact = (contractName, options) => {
67
+ const artifactPath = path.resolve(
68
+ options.contractsBuildDir,
69
+ `${contractName}.json`
70
+ );
71
+ logger.debug(`Reading artifact file at ${artifactPath}`);
72
+ enforceOrThrow(
73
+ fs.existsSync(artifactPath),
74
+ `Could not find ${contractName} artifact at ${artifactPath}`
75
+ );
76
+ // Stringify + parse to make a deep copy (to avoid bugs with PR #19)
77
+ return JSON.parse(JSON.stringify(require(artifactPath)));
78
+ };
79
+
80
+ const getInputJSON = (artifact, options) => {
81
+ const metadata = JSON.parse(artifact.metadata);
82
+ // const libraries = getLibraries(artifact, options)
83
+
84
+ const sources = {};
85
+ for (const contractPath in metadata.sources) {
86
+ // If we're on Windows we need to de-Unixify the path so that Windows can read the file
87
+ // We also need to replace the 'project:' prefix so that the file can be read
88
+ const normalisedContractPath = normaliseContractPath(
89
+ contractPath,
90
+ options.contractsDir
91
+ );
92
+ const absolutePath = require.resolve(normalisedContractPath);
93
+ const content = fs.readFileSync(absolutePath, "utf8");
94
+
95
+ // Remove the 'project:' prefix that was added in Truffle v5.3.14
96
+ const relativeContractPath = contractPath;
97
+
98
+ sources[relativeContractPath] = { content };
99
+ }
100
+
101
+ const inputJSON = {
102
+ language: metadata.language,
103
+ sources,
104
+ settings: {
105
+ remappings: metadata.settings.remappings,
106
+ optimizer: metadata.settings.optimizer,
107
+ evmVersion: metadata.settings.evmVersion,
108
+ },
109
+ };
110
+
111
+ return inputJSON;
112
+ };
113
+
114
+ const tryCompileAndMatch = (artifact, jsonInput, contractName) => {
115
+ const solc = require("solc");
116
+ const i = JSON.stringify(jsonInput);
117
+ const iObj = JSON.parse(i);
118
+ iObj.settings.outputSelection = { "*": { "*": ["*", "*"] } };
119
+ const o = solc.compile(JSON.stringify(iObj));
120
+ const oObj = JSON.parse(o);
121
+ if (oObj.errors?.length > 0) {
122
+ console.warn("tryCompileAndMatch ERROR:", oObj.errors[0].message);
123
+ return;
124
+ }
125
+ const metadataOld = artifact.metadata;
126
+ const metadataOldObj = JSON.parse(metadataOld);
127
+
128
+ const path = Object.keys(metadataOldObj.settings.compilationTarget)[0];
129
+ const metadataNew =
130
+ oObj.contracts[path][metadataOldObj.settings.compilationTarget[path]]
131
+ .metadata;
132
+
133
+ const bytecodeNew = oObj.contracts[path][
134
+ metadataOldObj.settings.compilationTarget[path]
135
+ ].evm.bytecode.object.replace("0x", "");
136
+ const bytecodeOld = artifact.bytecode.replace("0x", "");
137
+
138
+ if (metadataOld === metadataNew) {
139
+ console.log("\u2713 metadata matches EXACTLY!");
140
+ } else {
141
+ console.log("ERROR: metadata does not match");
142
+ }
143
+
144
+ if (bytecodeOld === bytecodeNew) {
145
+ console.log("\u2713 bytecode matches EXACTLY!");
146
+ } else {
147
+ console.log("ERROR: bytecode does not match");
148
+ }
149
+ };
@@ -0,0 +1,5 @@
1
+ {
2
+ "commands": {
3
+ "stdjsonin": "stdjsonin.js"
4
+ }
5
+ }
package/util.js ADDED
@@ -0,0 +1,79 @@
1
+ const path = require('path')
2
+
3
+ const abort = (message, logger = console, code = 1) => {
4
+ logger.error(message)
5
+ process.exit(code)
6
+ }
7
+
8
+ const enforce = (condition, message, logger, code) => {
9
+ if (!condition) abort(message, logger, code)
10
+ }
11
+
12
+ const enforceOrThrow = (condition, message) => {
13
+ if (!condition) throw new Error(message)
14
+ }
15
+
16
+ /**
17
+ * The metadata in the Truffle artifact file changes source paths on Windows. Instead of
18
+ * D:\Hello\World.sol, it looks like /D/Hello/World.sol. When trying to read this path,
19
+ * Windows cannot find it, since it is not a valid path. This function changes
20
+ * /D/Hello/World.sol to D:\Hello\World.sol. This way, Windows is able to read these source
21
+ * files. It does not change regular Unix paths, only Unixified Windows paths. It also does
22
+ * not make any changes on platforms that aren't Windows.
23
+ *
24
+ * @param {string} contractPath path to a contract file in any format.
25
+ * @param {any} options Options object containing the parsed truffle-plugin-verify options
26
+ * @returns {string} path to the contract in Windows format when on Windows, or Unix format otherwise.
27
+ */
28
+ const normaliseContractPath = (contractPath, options) => {
29
+ // Replace the 'project:' prefix that was added in Truffle v5.3.14 with the actual project path
30
+ const absolutePath = getAbsolutePath(contractPath, options)
31
+
32
+ // If the current platform is not Windows, the path does not need to be changed
33
+ if (process.platform !== 'win32') return absolutePath
34
+
35
+ // If the contract path doesn't start with '/[A-Z]/' it is not a Unixified Windows path
36
+ if (!absolutePath.match(/^\/[A-Z]\//i)) return absolutePath
37
+
38
+ const driveLetter = absolutePath.substring(1, 2)
39
+ const normalisedContractPath = path.resolve(`${driveLetter}:/${absolutePath.substring(3)}`)
40
+
41
+ return normalisedContractPath
42
+ }
43
+
44
+ /**
45
+ * @param {string} contractPath path to a contract file in any format.
46
+ * @param {string} contractsDir path to the directory containing contract source files.
47
+ * @returns {string} absolute path to the contract source file
48
+ */
49
+ const getAbsolutePath = (contractPath, contractsDir) => {
50
+ // Older versions of truffle already used the absolute path
51
+ if (!contractPath.startsWith('project:/')) return contractPath
52
+
53
+ // Figure out the project path and use it to construct the absolute path
54
+ const relativeContractPath = contractPath.replace('project:/', '')
55
+ const projectPath = findProjectPath(relativeContractPath, contractsDir)
56
+ const absolutePath = path.join(projectPath, relativeContractPath)
57
+
58
+ return absolutePath
59
+ }
60
+
61
+ /**
62
+ * @param {string} relativeContractPath path to a contract file in any format.
63
+ * @param {string} contractsPath path to the directory containing contract source files.
64
+ * @returns {string} project path
65
+ */
66
+ const findProjectPath = (relativeContractPath, contractsDir) => {
67
+ for (let currentPath = relativeContractPath; currentPath.length > 0; currentPath = currentPath.slice(0, -1)) {
68
+ if (contractsDir.endsWith(currentPath)) {
69
+ return contractsDir.slice(0, -1 * currentPath.length)
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = {
75
+ abort,
76
+ enforce,
77
+ enforceOrThrow,
78
+ normaliseContractPath
79
+ }