From 7656c6a8b4cea4aa8be7a1df2a39540358f34583 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 19 Jun 2025 12:17:09 +0200 Subject: [PATCH] js: Add stats and override options (#348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add `RESOLC_BIN` env variable to force use a binary compiler instead of the wasm - Add `--diff-stats` options to print file path e.g ``` ~/github/redstone-oracles-monorepo main* ❯ npx @parity/resolc@latest --diff-stats \ --base-path . \ --include-path node_modules \ --bin packages/evm-connector/contracts/samples/SampleWithEvents.sol ┌─────────┬──────────────────────────────────────────────────────────────────────────┬───────────────────────────────┬────────────┬────────────┬───────────┐ │ (index) │ file │ contract │ polkavm │ bin │ diff │ ├─────────┼──────────────────────────────────────────────────────────────────────────┼───────────────────────────────┼────────────┼────────────┼───────────┤ │ 0 │ 'packages/evm-connector/contracts/core/CalldataExtractor.sol' │ 'CalldataExtractor' │ '4.31 kB' │ '2.53 kB' │ '70.12%' │ │ 1 │ 'packages/evm-connector/contracts/core/RedstoneConstants.sol' │ 'RedstoneConstants' │ '0.80 kB' │ '0.17 kB' │ '368.18%' │ │ 2 │ 'packages/evm-connector/contracts/core/RedstoneDefaultsLib.sol' │ 'RedstoneDefaultsLib' │ '0.90 kB' │ '0.31 kB' │ '189.06%' │ │ 3 │ 'packages/evm-connector/contracts/libs/BitmapLib.sol' │ 'BitmapLib' │ '0.90 kB' │ '0.31 kB' │ '189.06%' │ │ 4 │ 'packages/evm-connector/contracts/libs/NumericArrayLib.sol' │ 'NumericArrayLib' │ '0.90 kB' │ '0.31 kB' │ '189.06%' │ │ 5 │ 'packages/evm-connector/contracts/libs/SignatureLib.sol' │ 'SignatureLib' │ '0.90 kB' │ '0.31 kB' │ '189.06%' │ │ 6 │ 'packages/evm-connector/contracts/mocks/RedstoneConsumerNumericMock.sol' │ 'RedstoneConsumerNumericMock' │ '13.62 kB' │ '10.17 kB' │ '33.96%' │ │ 7 │ 'packages/evm-connector/contracts/samples/SampleWithEvents.sol' │ 'SampleWithEvents' │ '29.34 kB' │ '15.60 kB' │ '88.03%' │ └─────────┴──────────────────────────────────────────────────────────────────────────┴───────────────────────────────┴────────────┴────────────┴───────────┘ ``` --- js/resolc/src/bin.ts | 65 +++++++++++++++++++++++++++++++++++------- js/resolc/src/index.ts | 8 ++---- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/js/resolc/src/bin.ts b/js/resolc/src/bin.ts index a607ad3..3b64757 100755 --- a/js/resolc/src/bin.ts +++ b/js/resolc/src/bin.ts @@ -6,6 +6,7 @@ import * as os from 'os' import * as path from 'path' import * as resolc from '.' import { SolcInput } from '.' +import { execSync } from 'child_process' async function main() { // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end @@ -17,11 +18,15 @@ async function main() { const program = new commander.Command() - program.name('solcjs') + program.name('resolcjs') program.version(resolc.version()) program .option('--bin', 'Binary of the contracts in hex.') .option('--abi', 'ABI of the contracts.') + .option( + '--diff-stats', + 'Print statistics about Resolc vs Solc compilation.' + ) .option( '--base-path ', 'Root of the project source tree. ' + @@ -49,8 +54,20 @@ async function main() { outputDir?: string prettyJson: boolean basePath?: string + diffStats: boolean includePath?: string[] }>() + + // when using --stats option, we want to run solc as well to compare outputs size + if (options.diffStats) { + const args = process.argv.filter((arg) => !arg.startsWith('--diff-stats')) + try { + execSync(`npx --yes solc@latest ${args.slice(2).join(' ')}`) + } catch (err) { + abort(`Failed to run solc: ${err}`) + } + } + const files: string[] = program.args const destination = options.outputDir ?? '.' @@ -137,7 +154,7 @@ async function main() { if (!output) { abort('No output from compiler') - } else if (output.errors) { + } else if (output.errors && !options.diffStats) { for (const error in output.errors) { const message = output.errors[error] if (message.severity === 'warning') { @@ -160,19 +177,41 @@ async function main() { }) } + const contractStats = [] + for (const fileName in output.contracts) { for (const contractName in output.contracts[fileName]) { let contractFileName = fileName + ':' + contractName contractFileName = contractFileName.replace(/[:./\\]/g, '_') - - if (options.bin) { - writeFile( - contractFileName + '.polkavm', - Buffer.from( - output.contracts[fileName][contractName].evm.bytecode.object, - 'hex' - ) + let polkavmSize = 0 + let binSize = 0 + if ( + options.bin && + output.contracts?.[fileName]?.[contractName]?.evm?.bytecode?.object + ) { + const pvmData = Buffer.from( + output.contracts[fileName][contractName].evm.bytecode.object, + 'hex' ) + writeFile(contractFileName + '.polkavm', pvmData) + polkavmSize = pvmData.length + + const binOutPath = path.join(destination, `${contractFileName}.bin`) + if (fs.existsSync(binOutPath)) { + try { + binSize = fs.statSync(binOutPath).size || 0 + } catch {} + } + contractStats.push({ + file: fileName, + contract: contractName, + ['resolc (kB)']: Number((polkavmSize / 1024).toFixed(2)), + ['solc (kB)']: Number((binSize / 1024).toFixed(2)), + ['diff (%)']: + binSize > 0 + ? Number(((polkavmSize / binSize - 1) * 100).toFixed(2)) + : Number.NaN, + }) } if (options.abi) { @@ -184,6 +223,12 @@ async function main() { } } + if (options.diffStats && contractStats.length > 0) { + console.table( + contractStats.sort((a, b) => b['resolc (kB)'] - a['resolc (kB)']) + ) + } + // Put back original exception handlers. originalUncaughtExceptionListeners.forEach(function (listener) { process.addListener('uncaughtException', listener) diff --git a/js/resolc/src/index.ts b/js/resolc/src/index.ts index 9b3ab60..ea61bf4 100644 --- a/js/resolc/src/index.ts +++ b/js/resolc/src/index.ts @@ -50,11 +50,7 @@ export function resolveInputs(sources: SolcInput): SolcInput { language: 'Solidity', sources, settings: { - outputSelection: { - '*': { - '*': ['evm.bytecode.object'], - }, - }, + outputSelection: {}, // no outputs requested, only resolves imports }, } @@ -106,7 +102,7 @@ export async function compile( enabled: true, runs: 200, }, - bin, + bin = process.env.RESOLC_BIN, } = option const input = JSON.stringify({