truffle-plugin-stdjsonin 0.5.14

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/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
+ }