From 25ebb52896522bc97ff61436865768617f6c27b3 Mon Sep 17 00:00:00 2001 From: simbadMarino Date: Mon, 9 Feb 2026 16:30:15 -0600 Subject: [PATCH 1/3] refactor: update compile command for Solidity 0.8.25 - Update Solidity compiler input/output to match v0.8.25: - Remove legacyAST - Add metadata, methodIdentifiers, userdoc, devdoc, storageLayout - Make paths relative for reproducible builds - Add per-contract compilation capability to produce single JSON inputs - Update contractSchema to include metadata and drop legacyAST - Prepares for future verification, debugging, and automation features - Matches latest Solidity documentation: https://docs.soliditylang.org/en/v0.8.25/using-the-compiler.html --- package-lock.json | 36 +++++++++++ package.json | 1 + src/components/Compile/index.js | 41 ++++++++++-- src/components/Config.js | 3 + src/components/ContractSchema/index.js | 20 +++--- .../spec/contract-object.spec.json | 10 ++- src/components/WorkflowCompile.js | 62 ++++++++++++++++--- src/lib/commands/compile.js | 26 +++++++- 8 files changed, 162 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0114db84..b1ea1c2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "graphlib": "2.1.8", "homedir": "0.6.0", "lodash": "4.17.23", + "md5": "^2.3.0", "mkdirp": "0.5.6", "mocha": "11.7.4", "node-dir": "0.1.17", @@ -2692,6 +2693,15 @@ "node": ">=4" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -2839,6 +2849,15 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -3949,6 +3968,12 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -4306,6 +4331,17 @@ "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", diff --git a/package.json b/package.json index e6da8485..f3412abe 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "graphlib": "2.1.8", "homedir": "0.6.0", "lodash": "4.17.23", + "md5": "^2.3.0", "mkdirp": "0.5.6", "mocha": "11.7.4", "node-dir": "0.1.17", diff --git a/src/components/Compile/index.js b/src/components/Compile/index.js index 74dbd500..6364033a 100755 --- a/src/components/Compile/index.js +++ b/src/components/Compile/index.js @@ -52,6 +52,8 @@ const compile = function (sources, options, callback) { replacement = '/' + replacement; replacement = replacement.replace(':', ''); } + //Convert absolute paths to relative for reproducible builds + replacement = makeRelative(replacement, options); // Save the result operatingSystemIndependentSources[replacement] = sources[source]; @@ -69,14 +71,19 @@ const compile = function (sources, options, callback) { ...settings, outputSelection: { '*': { - '': ['legacyAST', 'ast'], + '': ['ast'], '*': [ 'abi', + 'devdoc', + 'userdoc', + 'metadata', + 'storageLayout', 'evm.bytecode.object', 'evm.bytecode.sourceMap', 'evm.bytecode.linkReferences', 'evm.deployedBytecode.object', - 'evm.deployedBytecode.sourceMap' + 'evm.deployedBytecode.sourceMap', + 'evm.methodIdentifiers', ] } } @@ -85,7 +92,7 @@ const compile = function (sources, options, callback) { // Nothing to compile? Bail. if (!Object.keys(sources).length) { - return callback(null, [], []); + return callback(null, [], [], {}); } Object.keys(operatingSystemIndependentSources).forEach(function (file_path) { @@ -158,9 +165,9 @@ const compile = function (sources, options, callback) { source: operatingSystemIndependentSources[source_path], sourceMap: contract.evm.bytecode.sourceMap, deployedSourceMap: contract.evm.deployedBytecode.sourceMap, - legacyAST: standardOutput.sources[source_path].legacyAST, ast: standardOutput.sources[source_path].ast, abi: contract.abi, + metadata: contract.metadata, bytecode: '0x' + contract.evm.bytecode.object, deployedBytecode: '0x' + contract.evm.deployedBytecode.object, unlinked_binary: '0x' + contract.evm.bytecode.object, // deprecated @@ -217,7 +224,7 @@ const compile = function (sources, options, callback) { }); }); - callback(null, returnVal, files); + callback(null, returnVal, files, solcStandardInput); }; function replaceLinkReferences(bytecode, linkReferences, libraryName) { @@ -237,6 +244,21 @@ function replaceLinkReferences(bytecode, linkReferences, libraryName) { return bytecode; } +//Takes an absolute path and return a relative path to ensure reproducibility +const makeRelative = (contractPath, options) => { + const absolutePath = contractPath + + const projectRoot = path.resolve(options.working_directory); + // Normalize for comparison (important on Windows) + const normalizedAbsolute = path.resolve(absolutePath); + const normalizedRoot = path.resolve(projectRoot); + + // Convert to project-relative path + let relativePath = path.relative(normalizedRoot, normalizedAbsolute); + + return relativePath; +}; + function orderABI(contract) { const { abi, contractName, ast } = contract; @@ -289,6 +311,13 @@ compile.all = function (options, callback) { }); }; +//Compile an specific set of contracts or a single one +compile.specific = function (options, callback) { + + options.paths = options.compileTargets; + compile.with_dependencies(options, callback); + +}; // contracts_directory: String. Directory where .sol files can be found. // build_directory: String. Optional. Directory where .sol.js files can be found. Only required if `all` is false. // all: Boolean. Compile all sources found. Defaults to true. If false, will compare sources against built files @@ -300,7 +329,7 @@ compile.necessary = function (options, callback) { if (err) return callback(err); if (updated.length === 0) { - return callback(null, [], {}); + return callback(null, [], {}, {}); } options.paths = updated; diff --git a/src/components/Config.js b/src/components/Config.js index b15b7d99..c436a46f 100755 --- a/src/components/Config.js +++ b/src/components/Config.js @@ -60,6 +60,9 @@ function Config() { return path.resolve(self.working_directory, value); } }, + build_info_directory: function () { + return path.join(self.working_directory, 'build-info'); + }, contracts_directory: { default: function () { return path.join(self.working_directory, 'contracts'); diff --git a/src/components/ContractSchema/index.js b/src/components/ContractSchema/index.js index 02c497f7..c68a63ab 100755 --- a/src/components/ContractSchema/index.js +++ b/src/components/ContractSchema/index.js @@ -1,4 +1,4 @@ -const pkgVersion = '2.0.1'; +const pkgVersion = '2.0.2'; const Ajv = require('ajv'); const contractObjectSchema = require('./spec/contract-object.spec.json'); @@ -62,6 +62,12 @@ const properties = { return value; } }, + metadata: { + sources: ['metadata'], + transform(value) { + return typeof value == 'string' ? value : undefined; + } + }, sourceMap: { sources: ['sourceMap', 'srcmap', 'evm.bytecode.sourceMap'] }, @@ -71,18 +77,6 @@ const properties = { source: {}, sourcePath: {}, ast: {}, - legacyAST: { - transform: function (value, obj) { - const schemaVersion = obj.schemaVersion || '0.0.0'; - - // legacyAST introduced in v2.0.0 - if (schemaVersion[0] < 2) { - return obj.ast; - } else { - return value; - } - } - }, compiler: {}, networks: { transform: function (value) { diff --git a/src/components/ContractSchema/spec/contract-object.spec.json b/src/components/ContractSchema/spec/contract-object.spec.json index b3c324ab..85eae23f 100755 --- a/src/components/ContractSchema/spec/contract-object.spec.json +++ b/src/components/ContractSchema/spec/contract-object.spec.json @@ -46,6 +46,10 @@ } ] }, + "metadata": { + "type": "string", + "description": "Solidity compiler metadata JSON" + }, "sourceMap": { "allOf": [ { @@ -75,9 +79,6 @@ "ast": { "$ref": "#/definitions/AST" }, - "legacyAST": { - "$ref": "#/definitions/LegacyAST" - }, "compiler": { "type": "object", "properties": { @@ -151,9 +152,6 @@ "AST": { "type": "object" }, - "LegacyAST": { - "type": "object" - }, "SchemaVersion": { "type": "string", "pattern": "[0-9]+\\.[0-9]+\\.[0-9]+" diff --git a/src/components/WorkflowCompile.js b/src/components/WorkflowCompile.js index b1c2e26e..44858c8f 100755 --- a/src/components/WorkflowCompile.js +++ b/src/components/WorkflowCompile.js @@ -6,6 +6,8 @@ const { expect } = require('../lib/utils'); const Resolver = require('./Resolver'); const Artifactor = require('./Artifactor'); const TronWrap = require('./TronWrap'); +const fs = require("fs"); +const md5 = require('md5'); async function getCompilerVersion(options) { const config = Config.detect(options); @@ -40,7 +42,7 @@ const Contracts = { const self = this; expect.options(options, ['contracts_build_directory']); - + expect.options(options, ['build_info_directory']); expect.one(options, ['contracts_directory', 'files']); // Use a config object to ensure we get the default sources. @@ -54,10 +56,20 @@ const Contracts = { config.artifactor = new Artifactor(config.contracts_build_directory); } - function finished(err, contracts, paths) { + // Normalize compile targets + if (Array.isArray(config.compileTargets) && config.compileTargets.length > 0) { + config.compileTargets = config.compileTargets.map(p => + path.resolve(config.working_directory, p) + ); + } + + function finished(err, contracts, paths, solcStandardInput) { if (err) return callback(err); if (contracts != null && Object.keys(contracts).length > 0) { + // Create a hash of the standard JSON input to use as a unique identifier for this compilation. + const inputFileName = md5(JSON.stringify(solcStandardInput)); + // Write contract artifacts self.write_contracts(contracts, config, async function (err, abstractions) { options.logger.log(''); options.logger.log(`> Compiled successfully using:`); @@ -65,11 +77,12 @@ const Contracts = { ? options.networks?.compilers?.solc?.version : options.compilers?.solc?.version; options.logger.log(` - solc${options.evm ? '(EVM)' : ''}: ${solcVersion}`); - callback(err, abstractions, paths); + callback(err, abstractions, paths, solcStandardInput); }); + self.write_buildInfo(solcStandardInput, config, inputFileName) } else { options.logger.log('> Everything is up to date, there is nothing to compile.'); - callback(null, [], paths); + callback(null, [], paths, solcStandardInput); } } @@ -77,11 +90,17 @@ const Contracts = { options.logger.log('Compiling your contracts...'); options.logger.log('==========================='); + // Compile specific contracts + if (config.compileTargets && config.compileTargets.length > 0) { + return compile.specific(config, finished); + } + + //If ALL option is selected compile all contracts if (config.all === true || config.compileAll === true) { - compile.all(config, finished); - } else { - compile.necessary(config, finished); + return compile.all(config, finished); } + //Compile modified contracts if none of the above is true + return compile.necessary(config, finished); } getCompilerVersion(options) @@ -102,8 +121,8 @@ const Contracts = { if (!options.quietWrite) { options.logger.log( 'Writing artifacts to .' + - path.sep + - path.relative(options.working_directory, options.contracts_build_directory) + path.sep + + path.relative(options.working_directory, options.contracts_build_directory) ); } @@ -118,7 +137,32 @@ const Contracts = { }) .catch(callback); }); + }, + //Write the standard JSON input to a file in the build info directory. This can be used for verification or debugging purposes. + write_buildInfo: function (solcStandardInput, options, inputFileName) { + + mkdirp(options.build_info_directory, function (err) { + if (err != null) { + callback(err); + return; + } + + if (!options.quietWrite) { + options.logger.log( + 'Writing jsnoninput file to .' + + path.sep + + path.relative(options.working_directory, options.build_info_directory) + ); + } + console.log("Writing build-info..."); + console.log(inputFileName); + fs.writeFileSync(path.relative(options.working_directory, options.build_info_directory) + path.sep + `${inputFileName}.json`, JSON.stringify(solcStandardInput)); + console.log( + `Standard JSON Input ${inputFileName}.json saved in...` + ); + }) } + }; module.exports = Contracts; diff --git a/src/lib/commands/compile.js b/src/lib/commands/compile.js index 0af83167..e37713f5 100755 --- a/src/lib/commands/compile.js +++ b/src/lib/commands/compile.js @@ -2,14 +2,19 @@ const version = require('../version'); const describe = 'Compile contract source files'; const command = { - command: 'compile', + command: 'compile [contracts...]', describe, builder: yargs => { yargs .usage( `TronBox v${version.bundle}\n\n${describe}\n -Usage: $0 compile [--all] [--evm] [--quiet] ` +Usage: $0 compile [--all] [--evm] [--quiet] + $0 compile [contracts...] [options]` ) + .positional('contracts', { + describe: 'Specific contract source files to compile', + type: 'string' + }) .version(false) .options({ all: { @@ -26,6 +31,14 @@ Usage: $0 compile [--all] [--evm] [--quiet] ` } }) .example('$0 compile', 'Compile all contracts in the project') + .example( + '$0 compile contracts/MyContract.sol', + 'Compile a specific contract and its dependencies' + ) + .example( + '$0 compile contracts/A.sol contracts/B.sol', + 'Compile multiple specific contracts' + ) .example('$0 compile --all', 'Compile all contracts, even if not changed') .example('$0 compile --evm', 'Compile using EVM configuration') .group(['all', 'evm', 'quiet', 'help'], 'Options:'); @@ -37,11 +50,18 @@ Usage: $0 compile [--all] [--evm] [--quiet] ` if (options.quiet || options.silent) { options.logger = { - log: function () {} + log: function () { } }; } const config = Config.detect(options); + //console.debug(config); + + if (options.contracts && options.contracts.length > 0) { + config.compileTargets = options.contracts; + console.debug(options.contracts); + } + Contracts.compile(config, done); } }; From c2e841d9294474309cdf72db343414c914f7a247 Mon Sep 17 00:00:00 2001 From: simbadMarino Date: Tue, 10 Feb 2026 18:57:07 -0600 Subject: [PATCH 2/3] test: add solcjsoninput & compiling test cases Improved test coverage for : - Compiling separate contracts (compile command upgrade) - Deploying contracts based on new artifact schema - Updated compiler outputSelection format --- src/components/WorkflowCompile.js | 6 +- src/lib/commands/compile.js | 2 - test/abiv2/.gitignore | 1 + test/consolelogs/.gitignore | 1 + test/evm/.gitignore | 1 + test/runTest.sh | 12 + test/solcjsoninput/.gitignore | 143 +++++++++ test/solcjsoninput/.sample.env | 4 + test/solcjsoninput/contracts/Greeter.sol | 63 ++++ test/solcjsoninput/contracts/Migrations.sol | 19 ++ test/solcjsoninput/contracts/SimpleToken.sol | 41 +++ .../migrations/1_initial_migration.js | 5 + .../migrations/2_deploy_contracts.js | 5 + test/solcjsoninput/package-lock.json | 133 ++++++++ test/solcjsoninput/package.json | 22 ++ test/solcjsoninput/test/solcjson_test.js | 296 ++++++++++++++++++ test/solcjsoninput/tronbox.js | 63 ++++ test/tre/.gitignore | 1 + 18 files changed, 811 insertions(+), 7 deletions(-) create mode 100644 test/solcjsoninput/.gitignore create mode 100644 test/solcjsoninput/.sample.env create mode 100644 test/solcjsoninput/contracts/Greeter.sol create mode 100644 test/solcjsoninput/contracts/Migrations.sol create mode 100644 test/solcjsoninput/contracts/SimpleToken.sol create mode 100644 test/solcjsoninput/migrations/1_initial_migration.js create mode 100644 test/solcjsoninput/migrations/2_deploy_contracts.js create mode 100644 test/solcjsoninput/package-lock.json create mode 100644 test/solcjsoninput/package.json create mode 100644 test/solcjsoninput/test/solcjson_test.js create mode 100644 test/solcjsoninput/tronbox.js diff --git a/src/components/WorkflowCompile.js b/src/components/WorkflowCompile.js index 44858c8f..993a5f04 100755 --- a/src/components/WorkflowCompile.js +++ b/src/components/WorkflowCompile.js @@ -154,12 +154,8 @@ const Contracts = { path.relative(options.working_directory, options.build_info_directory) ); } - console.log("Writing build-info..."); - console.log(inputFileName); fs.writeFileSync(path.relative(options.working_directory, options.build_info_directory) + path.sep + `${inputFileName}.json`, JSON.stringify(solcStandardInput)); - console.log( - `Standard JSON Input ${inputFileName}.json saved in...` - ); + }) } diff --git a/src/lib/commands/compile.js b/src/lib/commands/compile.js index e37713f5..3db83e1c 100755 --- a/src/lib/commands/compile.js +++ b/src/lib/commands/compile.js @@ -55,11 +55,9 @@ Usage: $0 compile [--all] [--evm] [--quiet] } const config = Config.detect(options); - //console.debug(config); if (options.contracts && options.contracts.length > 0) { config.compileTargets = options.contracts; - console.debug(options.contracts); } Contracts.compile(config, done); diff --git a/test/abiv2/.gitignore b/test/abiv2/.gitignore index 710eb82d..43214565 100644 --- a/test/abiv2/.gitignore +++ b/test/abiv2/.gitignore @@ -2,5 +2,6 @@ src/js/metacoin-config.js node_modules build +build-info .env diff --git a/test/consolelogs/.gitignore b/test/consolelogs/.gitignore index c7c57403..6efd1981 100644 --- a/test/consolelogs/.gitignore +++ b/test/consolelogs/.gitignore @@ -2,6 +2,7 @@ src/js/metacoin-config.js node_modules build +build-info .env actual.log diff --git a/test/evm/.gitignore b/test/evm/.gitignore index 710eb82d..43214565 100644 --- a/test/evm/.gitignore +++ b/test/evm/.gitignore @@ -2,5 +2,6 @@ src/js/metacoin-config.js node_modules build +build-info .env diff --git a/test/runTest.sh b/test/runTest.sh index 900b4b9f..88ce025d 100755 --- a/test/runTest.sh +++ b/test/runTest.sh @@ -3,17 +3,20 @@ echo 'Test abiv2' cd abiv2 rm -rf build +rm -rf build-info ../../tronbox.dev test cd .. echo 'Test tre' cd tre rm -rf build +rm -rf build-info ../../tronbox.dev test cd .. echo 'Test init' rm -rf build +rm -rf build-info mkdir build cd build TRONBOX_CREATE_JAVASCRIPT_PROJECT_WITH_DEFAULTS=true ../../tronbox.dev init @@ -22,6 +25,7 @@ cd .. echo 'Test init metacoin' rm -rf build +rm -rf build-info mkdir build cd build TRONBOX_CREATE_JAVASCRIPT_METACOIN_PROJECT_WITH_DEFAULTS=true ../../tronbox.dev init @@ -30,6 +34,7 @@ cd .. echo 'Test unbox metacoin' rm -rf build +rm -rf build-info mkdir build cd build ../../tronbox.dev unbox metacoin-box @@ -44,5 +49,12 @@ cd .. echo 'Test evm' cd evm rm -rf build +rm -rf build-info ../../tronbox.dev test --evm cd .. + +echo 'Test compile & solcjson input file generation' +cd solcjsoninput +rm -rf build +rm -rf build-info +../../tronbox.dev test \ No newline at end of file diff --git a/test/solcjsoninput/.gitignore b/test/solcjsoninput/.gitignore new file mode 100644 index 00000000..5450a728 --- /dev/null +++ b/test/solcjsoninput/.gitignore @@ -0,0 +1,143 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# OSX +.DS_Store + +# TronBox build directory +build +build-info \ No newline at end of file diff --git a/test/solcjsoninput/.sample.env b/test/solcjsoninput/.sample.env new file mode 100644 index 00000000..4ff2b2d0 --- /dev/null +++ b/test/solcjsoninput/.sample.env @@ -0,0 +1,4 @@ +PRIVATE_KEY_SHASTA= +PRIVATE_KEY_NILE= +PRIVATE_KEY_MAINNET= +PRIVATE_KEY_DEVELOPMENT=0000000000000000000000000000000000000000000000000000000000000001 \ No newline at end of file diff --git a/test/solcjsoninput/contracts/Greeter.sol b/test/solcjsoninput/contracts/Greeter.sol new file mode 100644 index 00000000..7a222d2f --- /dev/null +++ b/test/solcjsoninput/contracts/Greeter.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +/** + * @title Greeter + * @dev A simple contract that stores and returns a greeting message + * This is a standalone contract with no external dependencies + */ +contract Greeter { + string private greeting; + address public owner; + uint256 public greetingChangeCount; + + event GreetingChanged(string oldGreeting, string newGreeting, address changedBy); + + /** + * @dev Constructor sets the initial greeting + * @param _greeting The initial greeting message + */ + constructor(string memory _greeting) { + greeting = _greeting; + owner = msg.sender; + greetingChangeCount = 0; + } + + /** + * @dev Get the current greeting + * @return The current greeting string + */ + function greet() public view returns (string memory) { + return greeting; + } + + /** + * @dev Set a new greeting (anyone can call) + * @param _greeting The new greeting message + */ + function setGreeting(string memory _greeting) public { + string memory oldGreeting = greeting; + greeting = _greeting; + greetingChangeCount++; + + emit GreetingChanged(oldGreeting, _greeting, msg.sender); + } + + /** + * @dev Reset greeting to default (only owner) + */ + function resetGreeting() public { + require(msg.sender == owner, 'Only owner can reset'); + setGreeting('Hello, World!'); + } + + /** + * @dev Get greeting statistics + * @return message The current greeting + * @return changes Number of times greeting has been changed + * @return currentOwner The contract owner + */ + function getGreetingInfo() public view returns (string memory message, uint256 changes, address currentOwner) { + return (greeting, greetingChangeCount, owner); + } +} diff --git a/test/solcjsoninput/contracts/Migrations.sol b/test/solcjsoninput/contracts/Migrations.sol new file mode 100644 index 00000000..9aac9750 --- /dev/null +++ b/test/solcjsoninput/contracts/Migrations.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +contract Migrations { + address public owner = msg.sender; + uint public last_completed_migration; + + modifier restricted() { + require( + msg.sender == owner, + "This function is restricted to the contract's owner" + ); + _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/test/solcjsoninput/contracts/SimpleToken.sol b/test/solcjsoninput/contracts/SimpleToken.sol new file mode 100644 index 00000000..c99bd7ae --- /dev/null +++ b/test/solcjsoninput/contracts/SimpleToken.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; + +/** + * @title SimpleToken + * @dev A basic ERC20 token with minting capability + * This contract demonstrates OpenZeppelin imports for testing TronBox compilation and JSON input file generation + */ +contract SimpleToken is ERC20, Ownable { + uint256 public constant MAX_SUPPLY = 1000000 * 10 ** 18; // 1 million tokens + + /** + * @dev Constructor that gives msg.sender all of initial supply + * @param initialSupply The initial token supply to mint + */ + constructor(uint256 initialSupply) ERC20('Simple Token', 'SMPL') Ownable(msg.sender) { + require(initialSupply <= MAX_SUPPLY, 'Initial supply exceeds max supply'); + _mint(msg.sender, initialSupply); + } + + /** + * @dev Mint new tokens (only owner) + * @param to Address to receive the minted tokens + * @param amount Amount of tokens to mint + */ + function mint(address to, uint256 amount) external onlyOwner { + require(totalSupply() + amount <= MAX_SUPPLY, 'Would exceed max supply'); + _mint(to, amount); + } + + /** + * @dev Burn tokens from caller's balance + * @param amount Amount of tokens to burn + */ + function burn(uint256 amount) external { + _burn(msg.sender, amount); + } +} diff --git a/test/solcjsoninput/migrations/1_initial_migration.js b/test/solcjsoninput/migrations/1_initial_migration.js new file mode 100644 index 00000000..037d9982 --- /dev/null +++ b/test/solcjsoninput/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require('./Migrations.sol'); + +module.exports = function (deployer) { + deployer.deploy(Migrations); +}; diff --git a/test/solcjsoninput/migrations/2_deploy_contracts.js b/test/solcjsoninput/migrations/2_deploy_contracts.js new file mode 100644 index 00000000..1d63f3f6 --- /dev/null +++ b/test/solcjsoninput/migrations/2_deploy_contracts.js @@ -0,0 +1,5 @@ +const Greeter = artifacts.require('./Greeter.sol'); + +module.exports = function (deployer) { + deployer.deploy(Greeter, "Hello, Tronbox!"); +}; diff --git a/test/solcjsoninput/package-lock.json b/test/solcjsoninput/package-lock.json new file mode 100644 index 00000000..d4bef5ab --- /dev/null +++ b/test/solcjsoninput/package-lock.json @@ -0,0 +1,133 @@ +{ + "name": "jsoninputdemo", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "jsoninputdemo", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@openzeppelin/contracts": "5.4.0", + "chai": "^6.2.2", + "picocolors": "^1.1.1", + "sinon": "^21.0.1" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", + "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.0.tgz", + "integrity": "sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/sinon": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.1.tgz", + "integrity": "sha512-Z0NVCW45W8Mg5oC/27/+fCqIHFnW8kpkFOq0j9XJIev4Ld0mKmERaZv5DMLAb9fGCevjKwaEeIQz5+MBXfZcDw==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^15.1.0", + "@sinonjs/samsam": "^8.0.3", + "diff": "^8.0.2", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + } + } +} diff --git a/test/solcjsoninput/package.json b/test/solcjsoninput/package.json new file mode 100644 index 00000000..48827725 --- /dev/null +++ b/test/solcjsoninput/package.json @@ -0,0 +1,22 @@ +{ + "name": "jsoninputdemo", + "version": "0.0.1", + "description": "Json input file generation tests", + "keywords": [ + "tron", + "tronbox", + "contracts", + "solidity", + "verification" + ], + "author": { + "name": "Simbad Marino" + }, + "license": "MIT", + "dependencies": { + "@openzeppelin/contracts": "5.4.0", + "chai": "^6.2.2", + "picocolors": "^1.1.1", + "sinon": "^21.0.1" + } +} diff --git a/test/solcjsoninput/test/solcjson_test.js b/test/solcjsoninput/test/solcjson_test.js new file mode 100644 index 00000000..d10647aa --- /dev/null +++ b/test/solcjsoninput/test/solcjson_test.js @@ -0,0 +1,296 @@ +const { assert, expect } = require('chai'); +const fs = require('fs'); +const path = require('path'); +const command = require('../../../src/lib/commands/compile'); + +contract('####### solcjsonInput test #######', function () { + const testContractsDir = path.join(__dirname, '../contracts'); + const buildInfoDir = path.join(__dirname, '../build-info'); + const buildDir = path.join(__dirname, '../build'); + const workingDir = path.join(__dirname, '..'); + + before(function () { + // Delete previous build & build-info folder from last test run + console.log('Cleaning up previous build artifacts...'); + if (fs.existsSync(buildInfoDir)) { + try { + const files = fs.readdirSync(buildInfoDir); + files.forEach(f => { + if (f.endsWith('.json')) { + fs.unlinkSync(path.join(buildInfoDir, f)); + } + }); + } catch (e) { + // Ignore cleanup errors + } + } + + //Clean up generated build-info files + if (fs.existsSync(buildDir)) { + try { + const files = fs.readdirSync(buildDir); + files.forEach(f => { + if (f.endsWith('.json')) { + fs.unlinkSync(path.join(buildDir, f)); + } + }); + } catch (e) { + // Ignore cleanup errors + } + } + }); + + + + it('tc-solc-001: should have correct command structure', function () { + assert.equal(command.command, 'compile [contracts...]'); + assert.include(command.describe, 'Compile contract source files'); + }); + + it('tc-solc-002: should handle compilation errors for non-existent files', function (done) { + this.timeout(30000); + + const options = { + contracts: [path.join(testContractsDir, 'NonExistent.sol')], + working_directory: workingDir + }; + + command.run(options, function (err) { + // Should error on non-existent file + assert.exists(err, 'Should return an error for non-existent contract file'); + assert.isTrue(err instanceof Error, 'Error should be an Error instance'); + + done(); + }); + }); + + it('tc-solc-003: should verify actual contract files exist', function () { + // Ensure test contracts are available + assert.isTrue(fs.existsSync(path.join(testContractsDir, 'SimpleToken.sol')), 'SimpleToken.sol should exist'); + assert.isTrue(fs.existsSync(path.join(testContractsDir, 'Greeter.sol')), 'Greeter.sol should exist'); + }); + + it('tc-solc-004: should properly resolve contract file paths', function (done) { + this.timeout(30000); + + const options = { + // Use relative paths as would be provided by the command + contracts: [ + path.join(testContractsDir, 'SimpleToken.sol') + ], + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + assert.exists(contracts, 'Contracts should compile with absolute paths'); + done(); + }); + }); + + it('tc-solc-005: should compile specific contract', function (done) { + this.timeout(30000); // Compilation can take time + + const contractPath = path.join(testContractsDir, 'Greeter.sol'); + const options = { + contracts: [contractPath], + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + // Verify compilation succeeded with contracts + assert.exists(contracts, 'Contracts should be returned from compilation'); + assert.isObject(contracts, 'Contracts should be an object'); + assert.isTrue(Object.keys(contracts).length > 0, 'Should have compiled contracts'); + + done(); + }); + }); + + it('tc-solc-006: should compile multiple contracts', function (done) { + this.timeout(30000); + + const options = { + contracts: [ + path.join(testContractsDir, 'SimpleToken.sol'), + path.join(testContractsDir, 'Greeter.sol') + ], + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + assert.exists(contracts, 'Multiple contracts should compile'); + assert.isTrue(Object.keys(contracts).length > 0, 'Should have compiled contracts'); + + done(); + }); + }); + + + it('tc-solc-007: should compile all contracts when --all flag is used', function (done) { + this.timeout(30000); + + const options = { + all: true, + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + // Should compile all contracts in the directory + assert.exists(contracts, 'All contracts should be compiled'); + assert.isObject(contracts, 'Should return contracts object'); + + done(); + }); + }); + + it('tc-solc-008: should suppress output with --quiet option', function (done) { + this.timeout(30000); + + const options = { + quiet: true, + all: true, + working_directory: workingDir + }; + + command.run(options, function (err) { + if (err) return done(err); + + // Logger should be a no-op object with a `log` function + assert.isObject(options.logger, 'Logger should be set'); + assert.isFunction(options.logger.log, 'Logger.log should be a function'); + + done(); + }); + }); + + it('tc-solc-009: should suppress output with --silent option', function (done) { + this.timeout(30000); + + const options = { + silent: true, + all: true, + working_directory: workingDir + }; + + command.run(options, function (err) { + if (err) return done(err); + + // Logger should be a no-op object with a `log` function + assert.isObject(options.logger, 'Logger should be set'); + assert.isFunction(options.logger.log, 'Logger.log should be a function'); + + done(); + }); + }); + + it('tc-solc-010: should generate build-info JSON files with compilation data', function (done) { + this.timeout(30000); + + const options = { + all: true, + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + // Check that build-info directory has JSON files + const files = fs.readdirSync(buildInfoDir); + const jsonFiles = files.filter(f => f.endsWith('.json')); + + assert.isTrue(jsonFiles.length > 0, 'Build-info directory should contain JSON files after compilation'); + + // Verify each JSON file contains valid solc standard input format + jsonFiles.forEach(file => { + const content = fs.readFileSync(path.join(buildInfoDir, file), 'utf8'); + const json = JSON.parse(content); + + // Check for solc standard JSON input properties + assert.exists(json.language, `${file} should have language property`); + assert.equal(json.language, 'Solidity', 'Language should be Solidity'); + assert.exists(json.sources, `${file} should have sources property`); + assert.isObject(json.sources, 'sources should be an object'); + assert.exists(json.settings, `${file} should have settings property`); + assert.isObject(json.settings, 'settings should be an object'); + assert.exists(json.settings.outputSelection, 'settings should have outputSelection'); + + // Verify outputSelection has expected structure described in solc documentation (current 0.8.25) + assert.exists(json.settings.outputSelection['*'], 'outputSelection should have * key'); + assert.exists(json.settings.outputSelection['*']['*'], 'outputSelection * should have * key'); + assert.isArray(json.settings.outputSelection['*']['*'], 'outputSelection * * should be an array'); + assert.include(json.settings.outputSelection['*']['*'], 'abi', 'outputSelection should include abi'); + assert.include(json.settings.outputSelection['*']['*'], 'devdoc', 'outputSelection should include devdoc'); + assert.include(json.settings.outputSelection['*']['*'], 'userdoc', 'outputSelection should include userdoc'); + assert.include(json.settings.outputSelection['*']['*'], 'metadata', 'outputSelection should include metadata'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.bytecode.object', 'outputSelection should include evm.bytecode.object'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.bytecode.sourceMap', 'outputSelection should include evm.bytecode.sourceMap'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.bytecode.linkReferences', 'outputSelection should include evm.bytecode.linkReferences'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.deployedBytecode.object', 'outputSelection should include evm.deployedBytecode.object'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.deployedBytecode.sourceMap', 'outputSelection should include evm.deployedBytecode.sourceMap'); + assert.include(json.settings.outputSelection['*']['*'], 'storageLayout', 'outputSelection should include storageLayout'); + assert.include(json.settings.outputSelection['*']['*'], 'evm.methodIdentifiers', 'outputSelection should include evm.methodIdentifiers'); + + }); + + done(); + }); + }); + + it('tc-solc-011: should return compiled contracts with proper ABI and bytecode', function (done) { + this.timeout(30000); + + const options = { + contracts: [path.join(testContractsDir, 'SimpleToken.sol')], + working_directory: workingDir + }; + + command.run(options, function (err, contracts) { + if (err) return done(err); + + // Verify contract structure matches what solc produces + assert.isObject(contracts, 'Contracts should be an object'); + assert.isTrue(Object.keys(contracts).length > 0, 'Should have at least one compiled contract'); + + // Check each contract's structure + Object.keys(contracts).forEach(contractName => { + const contract = contracts[contractName]; + assert.exists(contract.abi, `Contract ${contractName} should have abi`); + assert.isArray(contract.abi, 'abi should be an array'); + assert.exists(contract.bytecode, `Contract ${contractName} should have bytecode`); + }); + + done(); + }); + }); + + it('tc-solc-012: should deploy simple contract using new artifact schema ', async function () { + this.timeout(30000); + + + const Greeter = artifacts.require('./Greeter.sol'); + const c = await Greeter.new("Hello, Tronbox!"); + const greeting = await c.greet(); + assert.equal(greeting, "Hello, Tronbox!", "Greeter should return the correct greeting"); + }); + + it('tc-solc-013: should deploy OpenZeppelin deps contract using new artifact schema ', async function () { + this.timeout(30000); + + + const SimpleToken = artifacts.require('./SimpleToken.sol'); + const c = await SimpleToken.new(7777777); + const minting = await c.mint("TJDMQzjJSh5eC8WezVtnDXDuWXAwjV23eF", 1000); + assert.lengthOf(minting, 64, 'Minting should return a transaction hash of length 64'); + }); + + + +}); diff --git a/test/solcjsoninput/tronbox.js b/test/solcjsoninput/tronbox.js new file mode 100644 index 00000000..d1a49220 --- /dev/null +++ b/test/solcjsoninput/tronbox.js @@ -0,0 +1,63 @@ +module.exports = { + networks: { + mainnet: { + // Don't put your private key here: + privateKey: process.env.PRIVATE_KEY_MAINNET, + /** + * Create a .env file (it must be gitignored) containing something like + * + * export PRIVATE_KEY_MAINNET=4E7FEC...656243 + * + * Then, run the migration with: + * + * source .env && tronbox migrate --network mainnet + */ + userFeePercentage: 100, + feeLimit: 1000 * 1e6, + fullHost: 'https://api.trongrid.io', + network_id: '1' + }, + shasta: { + // Obtain test coin at https://shasta.tronex.io/ + privateKey: process.env.PRIVATE_KEY_SHASTA, + userFeePercentage: 50, + feeLimit: 1000 * 1e6, + fullHost: 'https://api.shasta.trongrid.io', + network_id: '2' + }, + nile: { + // Obtain test coin at https://nileex.io/join/getJoinPage + privateKey: process.env.PRIVATE_KEY_NILE, + userFeePercentage: 100, + feeLimit: 1000 * 1e6, + fullHost: 'https://nile.trongrid.io', + network_id: '3' + }, + development: { + // For tronbox/tre docker image + // See https://hub.docker.com/r/tronbox/tre + privateKey: '0000000000000000000000000000000000000000000000000000000000000001', + userFeePercentage: 0, + feeLimit: 1000 * 1e6, + fullHost: 'http://127.0.0.1:9090', + network_id: '9' + }, + + }, + compilers: { + solc: { + version: '0.8.25', + // An object with the same schema as the settings entry in the Input JSON. + // See https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description + settings: { + optimizer: { + enabled: true, + runs: 200 + }, + evmVersion: 'shanghai', + viaIR: false, + metadata: { bytecodeHash: "ipfs", useLiteralContent: true } + } + } + } +}; \ No newline at end of file diff --git a/test/tre/.gitignore b/test/tre/.gitignore index c27e9ba4..e7209c3c 100644 --- a/test/tre/.gitignore +++ b/test/tre/.gitignore @@ -1,3 +1,4 @@ node_modules build +build-info .env \ No newline at end of file From a07141b90f451a95e145bab6d880d60d0a8194b5 Mon Sep 17 00:00:00 2001 From: simbadMarino Date: Fri, 13 Feb 2026 09:59:01 -0600 Subject: [PATCH 3/3] docs: update README - include compile instructions for specific contracts --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d2ec54a9..702ac567 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,12 @@ $ tronbox compile To compile all contracts, use the `--compile-all` option. +To compile an specific set of contracts: + +```bash +tronbox compile contracts/your_contract.sol contracts/another_contract.sol ... +``` + Specify a network using the `--network` option. Network name must exist in the configuration. For details, see [Compile a Project](https://tronbox.io/docs/guides/compile-contracts). ### Migrate