mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-29 21:47:56 +00:00
7656c6a8b4
- 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%' │ └─────────┴──────────────────────────────────────────────────────────────────────────┴───────────────────────────────┴────────────┴────────────┴───────────┘ ```
227 lines
5.3 KiB
TypeScript
227 lines
5.3 KiB
TypeScript
import solc from 'solc'
|
|
import { spawn } from 'child_process'
|
|
import { resolc, version as resolcVersion } from './resolc'
|
|
import path from 'path'
|
|
import { existsSync, readFileSync } from 'fs'
|
|
import resolvePkg from 'resolve-pkg'
|
|
|
|
export type SolcInput = {
|
|
[contractName: string]: {
|
|
content: string
|
|
}
|
|
}
|
|
|
|
export type SolcError = {
|
|
component: string
|
|
errorCode: string
|
|
formattedMessage: string
|
|
message: string
|
|
severity: string
|
|
sourceLocation?: {
|
|
file: string
|
|
start: number
|
|
end: number
|
|
}
|
|
type: string
|
|
}
|
|
|
|
export type SolcOutput = {
|
|
contracts: {
|
|
[contractPath: string]: {
|
|
[contractName: string]: {
|
|
abi: Array<{
|
|
name: string
|
|
inputs: Array<{ name: string; type: string }>
|
|
outputs: Array<{ name: string; type: string }>
|
|
stateMutability: string
|
|
type: string
|
|
}>
|
|
evm: {
|
|
bytecode: { object: string }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
errors?: Array<SolcError>
|
|
}
|
|
|
|
export function resolveInputs(sources: SolcInput): SolcInput {
|
|
const input = {
|
|
language: 'Solidity',
|
|
sources,
|
|
settings: {
|
|
outputSelection: {}, // no outputs requested, only resolves imports
|
|
},
|
|
}
|
|
|
|
const out = solc.compile(JSON.stringify(input), {
|
|
import: (path: string) => {
|
|
return {
|
|
contents: readFileSync(tryResolveImport(path), 'utf8'),
|
|
}
|
|
},
|
|
})
|
|
|
|
const output = JSON.parse(out) as {
|
|
sources: { [fileName: string]: { id: number } }
|
|
errors: Array<SolcError>
|
|
}
|
|
|
|
if (output.errors && Object.keys(output.sources).length === 0) {
|
|
throw new Error(output.errors[0].formattedMessage)
|
|
}
|
|
|
|
return Object.fromEntries(
|
|
Object.keys(output.sources).map((fileName) => {
|
|
return [
|
|
fileName,
|
|
sources[fileName] ?? {
|
|
content: readFileSync(tryResolveImport(fileName), 'utf8'),
|
|
},
|
|
]
|
|
})
|
|
)
|
|
}
|
|
|
|
export function version(): string {
|
|
const v = resolcVersion()
|
|
return v.split(' ').pop() ?? v
|
|
}
|
|
|
|
export async function compile(
|
|
sources: SolcInput,
|
|
option: {
|
|
optimizer?: Record<string, unknown>
|
|
bin?: string
|
|
} = {}
|
|
): Promise<SolcOutput> {
|
|
const {
|
|
optimizer = {
|
|
mode: 'z',
|
|
fallback_to_optimizing_for_size: true,
|
|
enabled: true,
|
|
runs: 200,
|
|
},
|
|
bin = process.env.RESOLC_BIN,
|
|
} = option
|
|
|
|
const input = JSON.stringify({
|
|
language: 'Solidity',
|
|
sources: resolveInputs(sources),
|
|
settings: {
|
|
optimizer,
|
|
outputSelection: {
|
|
'*': {
|
|
'*': ['abi'],
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
if (bin) {
|
|
return compileWithBin(input, bin)
|
|
}
|
|
|
|
return resolc(input)
|
|
}
|
|
|
|
/**
|
|
* resolve the package root
|
|
* use resolve-pkg to find the package root, and fallback to using require.resolve if the package defines an exports field
|
|
* see https://github.com/sindresorhus/resolve-pkg/issues/9
|
|
**/
|
|
function resolvePkgRoot(basePackage: string) {
|
|
const packageRoot = resolvePkg(basePackage)
|
|
if (packageRoot) {
|
|
return packageRoot
|
|
}
|
|
|
|
try {
|
|
const packageExport = require.resolve(basePackage)
|
|
const endIndex = packageExport.indexOf(basePackage) + basePackage.length
|
|
return packageExport.substring(0, endIndex)
|
|
} catch {}
|
|
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Resolve an import path to a file path.
|
|
* @param importPath - The import path to resolve.
|
|
*/
|
|
export function tryResolveImport(importPath: string) {
|
|
// resolve local path
|
|
if (existsSync(importPath)) {
|
|
return path.resolve(importPath)
|
|
}
|
|
|
|
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/
|
|
const match = importPath.match(importRegex)
|
|
|
|
if (!match) {
|
|
throw new Error('Invalid import path format.')
|
|
}
|
|
|
|
const basePackage = match[1] // "foo", "@scope/foo"
|
|
const specifiedVersion = match[2] // "1.2.3" (optional)
|
|
const relativePath = match[3] // "/path/to/file.sol"
|
|
|
|
const packageRoot = resolvePkgRoot(basePackage)
|
|
if (!packageRoot) {
|
|
throw new Error(`Package ${basePackage} not found.`)
|
|
}
|
|
|
|
// Check if a version was specified and compare with the installed version
|
|
if (specifiedVersion) {
|
|
const installedVersion = JSON.parse(
|
|
readFileSync(path.join(packageRoot, 'package.json'), 'utf-8')
|
|
).version
|
|
|
|
if (installedVersion !== specifiedVersion) {
|
|
throw new Error(
|
|
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
|
|
)
|
|
}
|
|
}
|
|
|
|
// Construct full path to the requested file
|
|
const resolvedPath = path.join(packageRoot, relativePath)
|
|
if (existsSync(resolvedPath)) {
|
|
return resolvedPath
|
|
} else {
|
|
throw new Error(`Resolved path ${resolvedPath} does not exist.`)
|
|
}
|
|
}
|
|
function compileWithBin(input: string, bin: string): PromiseLike<SolcOutput> {
|
|
return new Promise((resolve, reject) => {
|
|
const process = spawn(bin, ['--standard-json'])
|
|
|
|
let output = ''
|
|
let error = ''
|
|
|
|
process.stdin.write(input)
|
|
process.stdin.end()
|
|
|
|
process.stdout.on('data', (data) => {
|
|
output += data.toString()
|
|
})
|
|
|
|
process.stderr.on('data', (data) => {
|
|
error += data.toString()
|
|
})
|
|
|
|
process.on('close', (code) => {
|
|
if (code === 0) {
|
|
try {
|
|
const result: SolcOutput = JSON.parse(output)
|
|
resolve(result)
|
|
} catch {
|
|
reject(new Error(`Failed to parse output`))
|
|
}
|
|
} else {
|
|
reject(new Error(`Process exited with code ${code}: ${error}`))
|
|
}
|
|
})
|
|
})
|
|
}
|