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 +21 -0
- package/README.md +30 -0
- package/package.json +37 -0
- package/stdjsonin.js +149 -0
- package/truffle-plugin.json +5 -0
- package/util.js +79 -0
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
|
+
};
|
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
|
+
}
|