Compare commits

..

9 Commits

Author SHA1 Message Date
PG Herveou 0421869e4b Replace release 0.1.0-dev.15 with 0.1.0-dev.16 (#314)
add missing patch and rename release
2025-05-08 15:26:10 +02:00
xermicus 32f55b976c update changelog (#313)
somehow went wrong

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 14:16:46 +02:00
PG Herveou 459a786299 v0.1.0-dev.15 release (#311)
Co-authored-by: xermicus <cyrill@parity.io>
2025-05-08 14:09:55 +02:00
xermicus fbaa45f283 support solc v0.8.30 (#308)
- Add support for solc `v0.8.30`. No changes in YUL or the binary
interface.
- Fix a semver bug in the NPM packages to correctly align their solc
dependencies with our last supported version.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:29:13 +02:00
xermicus f2fac85dae add npm package information to release checklist (#312)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:14:28 +02:00
PG Herveou b8f3073e29 Run lint:fix (#309) 2025-05-08 12:36:11 +02:00
xermicus 11d47d74ac apply size optimizations by default (#298)
So far if no optimization level was specified, optimizations for
execution time were applied. However, we currently are a bit limited on
code size. Add to that, this setting is not available in solc and people
generally ignore the docs, generating a lot of support requests.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 15:35:48 +02:00
xermicus e3a9c95d32 llvm-builder: use the ninja generator for building the builtins on windows (#299)
The builtins build should use the Ninja generator (MSVC does not build a
valid archive).

Tested and verified here:
https://github.com/paritytech/revive-alex-workflowtest/releases/tag/untagged-f02d0f574bab8404fead

Closes #305

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 11:10:58 +02:00
PG Herveou a560b2d919 Fix npm-release job (#297)
follow up from #295
2025-04-30 21:48:11 +02:00
30 changed files with 7882 additions and 1086 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ runs:
shell: bash shell: bash
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.29/${SOLC_NAME} curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.30/${SOLC_NAME}
- name: Make Solc Executable - name: Make Solc Executable
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
+5
View File
@@ -87,6 +87,11 @@ jobs:
run: | run: |
brew install ninja brew install ninja
- name: Install Dependencies
if: ${{ matrix.host == 'windows' }}
run: |
choco install ninja
- name: Install LLVM Builder - name: Install LLVM Builder
run: | run: |
cargo install --path crates/llvm-builder cargo install --path crates/llvm-builder
+20 -7
View File
@@ -181,7 +181,7 @@ jobs:
- name: Basic Sanity Check - name: Basic Sanity Check
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.29/soljson.js curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js
node -e " node -e "
const soljson = require('solc/soljson'); const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js'); const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
@@ -292,24 +292,37 @@ jobs:
runs-on: macos-14 runs-on: macos-14
environment: tags environment: tags
steps: steps:
- name: Build - uses: actions/checkout@v4
run: npm -w js/resolc run build
- name: Set version - name: Download Artifacts
run: npm -w js/resolc version --no-git-tag-version ${{github.event.release.tag_name}} uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- run: npm ci -w js/resolc
- name: Build
run: |
cp -f resolc.{wasm,js} js/resolc/src/resolc
npm -w js/resolc run build
- name: npm pack - name: npm pack
run: npm -w js/resolc pack run: npm -w js/resolc pack
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: package name: npm_package
path: "parity-resolc-*.tgz" path: "parity-resolc-*.tgz"
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83 - uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
with: with:
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
ref: main ref: main
inputs: '${{ format(''{{ "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}' inputs: '${{ format(''{{ "artifact_name": "npm_package", "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
env: env:
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }} GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
-1
View File
@@ -12,7 +12,6 @@ target-llvm
node_modules node_modules
artifacts artifacts
tmp tmp
package-lock.json
/*.html /*.html
/build /build
soljson.js soljson.js
+1 -1
View File
@@ -2,5 +2,5 @@
"trailingComma": "es5", "trailingComma": "es5",
"tabWidth": 2, "tabWidth": 2,
"semi": false, "semi": false,
"singleQuote": false "singleQuote": true
} }
+55 -3
View File
@@ -6,12 +6,38 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
## v0.1.0
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
### Changed ### Changed
### Fixed ### Fixed
## v0.1.0-dev.16
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added
- Move the npm package from paritytech/js-revive, into this repo. The package `@parity/resolc` will be deployed to npm for each release.
- Support for solc v0.8.30
### Changed
- By default, heavy size optimizations are applied.
### Fixed
- @parity/resolc: The solc dependency package is constrained to the latest supported version, preventing breaking the package ever time a new solc package was released.
- The resolc npm package no longer ignores the optimizer settings
## v0.1.0-dev.14 ## v0.1.0-dev.14
This is a development pre-release. This is a development pre-release.
@@ -19,13 +45,16 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- The `revive-runner` helper utility binary which helps to run contracts locally without a blockchain node.
- The `revive-runner` helper utility binary which helps to run contracts locally without a blockchain node.
- Allow configuration of the EVM heap memory size and stack size via CLI flags and JSON input settings. - Allow configuration of the EVM heap memory size and stack size via CLI flags and JSON input settings.
### Changed ### Changed
- The default PVM stack memory size was increased from 16kb to 32kb. - The default PVM stack memory size was increased from 16kb to 32kb.
### Fixed ### Fixed
- Constructors avoid storing zero sized immutable data on exit. - Constructors avoid storing zero sized immutable data on exit.
## v0.1.0-dev.13 ## v0.1.0-dev.13
@@ -35,16 +64,19 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- Support for solc v0.8.29 - Support for solc v0.8.29
- Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate. - Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate.
- `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions. - `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions.
- Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration. - Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration.
### Changed ### Changed
- Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is. - Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is.
- Running `resolc` using webkit is no longer supported. - Running `resolc` using webkit is no longer supported.
### Fixed ### Fixed
- A missing byte swap for the create2 salt value. - A missing byte swap for the create2 salt value.
## v0.1.0-dev.12 ## v0.1.0-dev.12
@@ -54,10 +86,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9` Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9`
### Added ### Added
- Per file output selection for `--standard-json` mode. - Per file output selection for `--standard-json` mode.
- The `ir` output selection option for `--standard-json` mode. - The `ir` output selection option for `--standard-json` mode.
### Changed ### Changed
- Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`). - Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`).
### Fixed ### Fixed
@@ -73,6 +107,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Changed ### Changed
### Fixed ### Fixed
- A bug causing incorrect loads from the emulated EVM linear memory. - A bug causing incorrect loads from the emulated EVM linear memory.
- A missing integer truncate after switching to 64bit. - A missing integer truncate after switching to 64bit.
@@ -83,10 +118,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50` Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added ### Added
- Support for the `coinbase` opcode. - Support for the `coinbase` opcode.
- The resolc web JS version. - The resolc web JS version.
### Changed ### Changed
- Missing the `--overwrite` flag emits an error instead of a warning. - Missing the `--overwrite` flag emits an error instead of a warning.
- The `resolc` executable prints the help by default. - The `resolc` executable prints the help by default.
- Removed support for legacy EVM assembly (EVMLA) translation. - Removed support for legacy EVM assembly (EVMLA) translation.
@@ -96,6 +133,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
If detected, the re-entrant call flag is not set and 0 deposit limit is endowed. If detected, the re-entrant call flag is not set and 0 deposit limit is endowed.
### Fixed ### Fixed
- Solidity: Add the solc `--libraries` files to sources. - Solidity: Add the solc `--libraries` files to sources.
- A data race in tests. - A data race in tests.
- Fix `broken pipe` errors. - Fix `broken pipe` errors.
@@ -109,9 +147,11 @@ This is a development pre-release.
### Added ### Added
### Changed ### Changed
- Syscalls with more than 6 arguments now pack them into registers. - Syscalls with more than 6 arguments now pack them into registers.
### Fixed ### Fixed
- Remove reloading of the resolc.js file (fix issue with relative path in web worker) - Remove reloading of the resolc.js file (fix issue with relative path in web worker)
## v0.1.0-dev.8 ## v0.1.0-dev.8
@@ -119,15 +159,18 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency. - The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency.
- Initial support for running `resolc` in the browser. - Initial support for running `resolc` in the browser.
### Changed ### Changed
- Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`. - Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`.
- The minimum supported Rust version is `1.81.0`. - The minimum supported Rust version is `1.81.0`.
- Error out early instead of invoking `solc` with invalid base or include path flags. - Error out early instead of invoking `solc` with invalid base or include path flags.
### Fixed ### Fixed
- Decouple the LLVM target dependency from the LLVM host dependency. - Decouple the LLVM target dependency from the LLVM host dependency.
- Do not error out if no files and no errors were produced. This aligns resolc closer to solc. - Do not error out if no files and no errors were produced. This aligns resolc closer to solc.
- Fixes input normalization in the Wasm version. - Fixes input normalization in the Wasm version.
@@ -137,17 +180,20 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- Implement the `GASPRICE` opcode. - Implement the `GASPRICE` opcode.
- Implement the `BASEFEE` opcode. - Implement the `BASEFEE` opcode.
- Implement the `GASLIMIT` opcode. - Implement the `GASLIMIT` opcode.
### Changed ### Changed
- The `GAS` opcode now returns the remaining `ref_time`. - The `GAS` opcode now returns the remaining `ref_time`.
- Contracts can now be supplied call data input of arbitrary size. - Contracts can now be supplied call data input of arbitrary size.
- Some syscalls now return the value in a register, slightly improving emitted contract code. - Some syscalls now return the value in a register, slightly improving emitted contract code.
- Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit. - Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit.
### Fixed ### Fixed
- A linker bug which was preventing certain contracts from linking with the PVM linker. - A linker bug which was preventing certain contracts from linking with the PVM linker.
- JS: Fix encoding conversion from JS string (UTF-16) to UTF-8. - JS: Fix encoding conversion from JS string (UTF-16) to UTF-8.
- The git commit hash slug is always displayed in the version string. - The git commit hash slug is always displayed in the version string.
@@ -157,6 +203,7 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
# Added # Added
- Implement the `BLOCKHASH` opcode. - Implement the `BLOCKHASH` opcode.
- Implement delegate calls. - Implement delegate calls.
- Implement the `GASPRICE` opcode. Currently hard-coded to return `1`. - Implement the `GASPRICE` opcode. Currently hard-coded to return `1`.
@@ -164,21 +211,24 @@ This is a development pre-release.
- Initial support for emitting debug info (opt in via the `-g` flag) - Initial support for emitting debug info (opt in via the `-g` flag)
# Changed # Changed
- resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time. - resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time.
- The RISC-V bit-manipulation target feature (`zbb`) is enabled. - The RISC-V bit-manipulation target feature (`zbb`) is enabled.
# Fixed # Fixed
- Compilation to Wasm (for usage in node and web browsers)
- Compilation to Wasm (for usage in node and web browsers)
## v0.1.0-dev.5 ## v0.1.0-dev.5
This is development pre-release. This is development pre-release.
# Added # Added
- Implement the `CODESIZE` and `EXTCODESIZE` opcodes. - Implement the `CODESIZE` and `EXTCODESIZE` opcodes.
# Changed # Changed
- Include the full revive version in the contract metadata. - Include the full revive version in the contract metadata.
# Fixed # Fixed
@@ -188,9 +238,11 @@ This is development pre-release.
This is development pre-release. This is development pre-release.
# Added # Added
- Support the `ORIGIN` opcode. - Support the `ORIGIN` opcode.
# Changed # Changed
- Update polkavm to `v0.14.0`. - Update polkavm to `v0.14.0`.
- Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts. - Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts.
Generated
+15 -15
View File
@@ -4553,7 +4553,7 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]] [[package]]
name = "lld-sys" name = "lld-sys"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -8266,7 +8266,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-benchmarks" name = "revive-benchmarks"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"criterion", "criterion",
@@ -8278,18 +8278,18 @@ dependencies = [
[[package]] [[package]]
name = "revive-build-utils" name = "revive-build-utils"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
[[package]] [[package]]
name = "revive-builtins" name = "revive-builtins"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"revive-build-utils", "revive-build-utils",
] ]
[[package]] [[package]]
name = "revive-common" name = "revive-common"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",
@@ -8299,7 +8299,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-differential" name = "revive-differential"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"alloy-genesis", "alloy-genesis",
"alloy-primitives", "alloy-primitives",
@@ -8312,7 +8312,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-integration" name = "revive-integration"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-sol-types", "alloy-sol-types",
@@ -8328,7 +8328,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-linker" name = "revive-linker"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"libc", "libc",
@@ -8340,7 +8340,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-llvm-builder" name = "revive-llvm-builder"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assert_cmd", "assert_cmd",
@@ -8362,7 +8362,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-llvm-context" name = "revive-llvm-context"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hex", "hex",
@@ -8384,7 +8384,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-runner" name = "revive-runner"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"anyhow", "anyhow",
@@ -8403,7 +8403,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-runtime-api" name = "revive-runtime-api"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"inkwell", "inkwell",
@@ -8413,7 +8413,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-solc-json-interface" name = "revive-solc-json-interface"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"rayon", "rayon",
@@ -8425,7 +8425,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-solidity" name = "revive-solidity"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -8452,7 +8452,7 @@ dependencies = [
[[package]] [[package]]
name = "revive-stdlib" name = "revive-stdlib"
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
dependencies = [ dependencies = [
"inkwell", "inkwell",
"revive-build-utils", "revive-build-utils",
+15 -15
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
authors = [ authors = [
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>", "Parity Technologies <admin@parity.io>",
@@ -14,20 +14,20 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0" rust-version = "1.81.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.14", path = "crates/benchmarks" } revive-benchmarks = { version = "0.1.0-dev.16", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.14", path = "crates/builtins" } revive-builtins = { version = "0.1.0-dev.16", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.14", path = "crates/common" } revive-common = { version = "0.1.0-dev.16", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.14", path = "crates/differential" } revive-differential = { version = "0.1.0-dev.16", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.14", path = "crates/integration" } revive-integration = { version = "0.1.0-dev.16", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.14", path = "crates/linker" } revive-linker = { version = "0.1.0-dev.16", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.14", path = "crates/lld-sys" } lld-sys = { version = "0.1.0-dev.16", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.14", path = "crates/llvm-context" } revive-llvm-context = { version = "0.1.0-dev.16", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.14", path = "crates/runtime-api" } revive-runtime-api = { version = "0.1.0-dev.16", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.14", path = "crates/runner" } revive-runner = { version = "0.1.0-dev.16", path = "crates/runner" }
revive-solc-json-interface = { version = "0.1.0-dev.14", path = "crates/solc-json-interface" } revive-solc-json-interface = { version = "0.1.0-dev.16", path = "crates/solc-json-interface" }
revive-solidity = { version = "0.1.0-dev.14", path = "crates/solidity" } revive-solidity = { version = "0.1.0-dev.16", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.14", path = "crates/stdlib" } revive-stdlib = { version = "0.1.0-dev.16", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.14", path = "crates/build-utils" } revive-build-utils = { version = "0.1.0-dev.16", path = "crates/build-utils" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.2" cc = "1.2"
+4
View File
@@ -13,6 +13,10 @@ To create a new pre-release:
7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes. 7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes.
8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly 8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
# `resolc` NPM package release
Will happen automatically.
# LLVM release # LLVM release
To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`. To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`.
+2 -8
View File
@@ -117,13 +117,7 @@ pub fn build(
log::info!("building compiler-rt for rv64emac"); log::info!("building compiler-rt for rv64emac");
crate::utils::check_presence("cmake")?; crate::utils::check_presence("cmake")?;
crate::utils::check_presence("ninja")?;
let generator = if cfg!(target_os = "windows") {
"Visual Studio 17 2022"
} else {
crate::utils::check_presence("ninja")?;
"Ninja"
};
let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?; let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?;
let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?; let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?;
@@ -136,7 +130,7 @@ pub fn build(
"-B", "-B",
llvm_compiler_rt_build.to_string_lossy().as_ref(), llvm_compiler_rt_build.to_string_lossy().as_ref(),
"-G", "-G",
generator, "Ninja",
]) ])
.args(CMAKE_STATIC_ARGS) .args(CMAKE_STATIC_ARGS)
.args(cmake_dynamic_args(build_type, target_env)?) .args(cmake_dynamic_args(build_type, target_env)?)
@@ -234,7 +234,7 @@ impl TryFrom<&SolcStandardJsonInputSettingsOptimizer> for Settings {
fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> { fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> {
let mut result = match value.mode { let mut result = match value.mode {
Some(mode) => Self::try_from_cli(mode)?, Some(mode) => Self::try_from_cli(mode)?,
None => Self::cycles(), None => Self::size(),
}; };
if value.fallback_to_optimizing_for_size.unwrap_or_default() { if value.fallback_to_optimizing_for_size.unwrap_or_default() {
result.enable_fallback_to_size(); result.enable_fallback_to_size();
+1 -1
View File
@@ -129,7 +129,7 @@ fn main_inner() -> anyhow::Result<()> {
let mut optimizer_settings = match arguments.optimization { let mut optimizer_settings = match arguments.optimization {
Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?, Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?,
None => revive_llvm_context::OptimizerSettings::cycles(), None => revive_llvm_context::OptimizerSettings::size(),
}; };
if arguments.fallback_to_optimizing_for_size { if arguments.fallback_to_optimizing_for_size {
optimizer_settings.enable_fallback_to_size(); optimizer_settings.enable_fallback_to_size();
+1 -1
View File
@@ -19,7 +19,7 @@ use self::version::Version;
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0); pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The last supported version of `solc`. /// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 29); pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 30);
/// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8> /// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8); pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8);
@@ -1,19 +1,33 @@
import * as path from 'path'; import * as path from 'path'
const outputDir = 'artifacts'; const outputDir = 'artifacts'
const binExtension = ':C.pvm'; const binExtension = ':C.pvm'
const asmExtension = ':C.pvmasm'; const asmExtension = ':C.pvmasm'
const llvmExtension = '.ll'; const llvmExtension = '.ll'
const contractSolFilename = 'contract.sol'; const contractSolFilename = 'contract.sol'
const contractYulFilename = 'contract.yul'; const contractYulFilename = 'contract.yul'
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized'; const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized'
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized'; const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized'
const pathToOutputDir = path.join(__dirname, '..', outputDir); const pathToOutputDir = path.join(__dirname, '..', outputDir)
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts'); const pathToContracts = path.join(__dirname, '..', 'src', 'contracts')
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename); const pathToBasicYulContract = path.join(
const pathToBasicSolContract = path.join(pathToContracts, 'solidity', contractSolFilename); pathToContracts,
const pathToSolBinOutputFile = path.join(pathToOutputDir, contractSolFilename + binExtension); 'yul',
const pathToSolAsmOutputFile = path.join(pathToOutputDir, contractSolFilename + asmExtension); contractYulFilename
)
const pathToBasicSolContract = path.join(
pathToContracts,
'solidity',
contractSolFilename
)
const pathToSolBinOutputFile = path.join(
pathToOutputDir,
contractSolFilename + binExtension
)
const pathToSolAsmOutputFile = path.join(
pathToOutputDir,
contractSolFilename + asmExtension
)
export const paths = { export const paths = {
outputDir: outputDir, outputDir: outputDir,
@@ -30,4 +44,4 @@ export const paths = {
pathToBasicYulContract: pathToBasicYulContract, pathToBasicYulContract: pathToBasicYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile, pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile, pathToSolAsmOutputFile: pathToSolAsmOutputFile,
}; }
@@ -1,44 +1,51 @@
import * as shell from 'shelljs'; import * as shell from 'shelljs'
import * as fs from 'fs'; import * as fs from 'fs'
import { spawnSync } from 'child_process'; import { spawnSync } from 'child_process'
interface CommandResult { interface CommandResult {
output: string; output: string
exitCode: number; exitCode: number
} }
export const executeCommand = (command: string, stdin?: string): CommandResult => { export const executeCommand = (
if (stdin) { command: string,
const process = spawnSync(command, [], { stdin?: string
input: stdin, ): CommandResult => {
shell: true, if (stdin) {
encoding: 'utf8', const process = spawnSync(command, [], {
maxBuffer: 30 * 1024 * 1024 input: stdin,
}); shell: true,
encoding: 'utf8',
maxBuffer: 30 * 1024 * 1024,
})
return {
exitCode: process.status || 0,
output: (process.stdout || process.stderr || '').toString()
};
}
const result = shell.exec(command, { silent: true, async: false });
return { return {
exitCode: result.code, exitCode: process.status || 0,
output: result.stdout || result.stderr || '' output: (process.stdout || process.stderr || '').toString(),
}; }
}; }
const result = shell.exec(command, { silent: true, async: false })
return {
exitCode: result.code,
output: result.stdout || result.stderr || '',
}
}
export const isFolderExist = (folder: string): boolean => { export const isFolderExist = (folder: string): boolean => {
return shell.test('-d', folder); return shell.test('-d', folder)
}; }
export const isFileExist = (pathToFileDir: string, fileName: string, fileExtension: string): boolean => { export const isFileExist = (
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension); pathToFileDir: string,
}; fileName: string,
fileExtension: string
): boolean => {
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension)
}
export const isFileEmpty = (file: string): boolean => { export const isFileEmpty = (file: string): boolean => {
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
return (fs.readFileSync(file).length === 0); return fs.readFileSync(file).length === 0
} }
}; }
@@ -1,43 +1,44 @@
import {executeCommand} from "../src/helper"; import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'; import { paths } from '../src/entities'
//id1746 //id1746
describe("Run with --asm by default", () => { describe('Run with --asm by default', () => {
const command = `resolc ${paths.pathToBasicSolContract} --asm`; const command = `resolc ${paths.pathToBasicSolContract} --asm`
const result = executeCommand(command); const result = executeCommand(command)
const commandInvalid = 'resolc --asm'; const commandInvalid = 'resolc --asm'
const resultInvalid = executeCommand(commandInvalid); const resultInvalid = executeCommand(commandInvalid)
it("Valid command exit code = 0", () => { it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0)
}); })
it("--asm output is presented", () => { it('--asm output is presented', () => {
const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i]; const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i]
for (const pattern of expectedPatterns) { for (const pattern of expectedPatterns) {
expect(result.output).toMatch(pattern); expect(result.output).toMatch(pattern)
} }
}); })
it("solc exit code == resolc exit code", () => { it('solc exit code == resolc exit code', () => {
const command = `solc ${paths.pathToBasicSolContract} --asm`; const command = `solc ${paths.pathToBasicSolContract} --asm`
const solcResult = executeCommand(command); const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode); expect(solcResult.exitCode).toBe(result.exitCode)
}); })
it("run invalid: resolc --asm", () => { it('run invalid: resolc --asm', () => {
expect(resultInvalid.output).toMatch(/(No input sources specified|Compilation aborted)/i); expect(resultInvalid.output).toMatch(
}); /(No input sources specified|Compilation aborted)/i
)
it("Invalid command exit code = 1", () => { })
expect(resultInvalid.exitCode).toBe(1);
});
it("Invalid solc exit code == Invalid resolc exit code", () => { it('Invalid command exit code = 1', () => {
const command = 'solc --asm'; expect(resultInvalid.exitCode).toBe(1)
const solcResult = executeCommand(command); })
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
}); it('Invalid solc exit code == Invalid resolc exit code', () => {
}); const command = 'solc --asm'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
})
})
@@ -1,191 +1,241 @@
import { executeCommand, isFolderExist, isFileExist, isFileEmpty } from "../src/helper"; import {
import { paths } from '../src/entities'; executeCommand,
import * as shell from 'shelljs'; isFolderExist,
import * as path from 'path'; isFileExist,
isFileEmpty,
} from '../src/helper'
import { paths } from '../src/entities'
import * as shell from 'shelljs'
import * as path from 'path'
//id1762 //id1762
describe("Run resolc without any options", () => { describe('Run resolc without any options', () => {
const command = 'resolc'; const command = 'resolc'
const result = executeCommand(command); const result = executeCommand(command)
it("Info with help is presented", () => { it('Info with help is presented', () => {
expect(result.output).toMatch(/(Usage: resolc)/i); expect(result.output).toMatch(/(Usage: resolc)/i)
}); })
it("Exit code = 1", () => { it('Exit code = 1', () => {
expect(result.exitCode).toBe(1); expect(result.exitCode).toBe(1)
}); })
it("solc exit code == resolc exit code", () => {
const command = 'solc';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
it('solc exit code == resolc exit code', () => {
const command = 'solc'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
})
//#1713 //#1713
describe("Default run a command from the help", () => { describe('Default run a command from the help', () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"` // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd it('Compiler run successful', () => {
const result = executeCommand(command); expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it("Compiler run successful", () => { it('Exit code = 0', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); expect(result.exitCode).toBe(0)
}); })
it("Exit code = 0", () => { it('Output dir is created', () => {
expect(result.exitCode).toBe(0); expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
}); })
it("Output dir is created", () => { xit('Output file is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true); // a bug on windows
}); expect(
xit("Output file is created", () => { // a bug on windows isFileExist(
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true); paths.pathToOutputDir,
}); paths.contractSolFilename,
it("the output file is not empty", () => { paths.binExtension
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false); )
}); ).toBe(true)
it("No 'Error'/'Warning'/'Fail' in the output", () => { })
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i); it('the output file is not empty', () => {
}); expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
}); })
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
//#1818 //#1818
describe("Default run a command from the help", () => { describe('Default run a command from the help', () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"` // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd it('Compiler run successful', () => {
const result = executeCommand(command); expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
xit('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
it("Compiler run successful", () => { describe('Run resolc with source debug information', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const commands = [
}); `resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
it("Exit code = 0", () => { `resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
expect(result.exitCode).toBe(0); ] // potential issue on resolc with full path on Windows cmd`;
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
describe("Run resolc with source debug information", () => { for (var idx in commands) {
const commands = [ const command = commands[idx]
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`, const result = executeCommand(command)
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { it('Compiler run successful', () => {
const command = commands[idx]; expect(result.output).toMatch(/(Compiler run successful.)/i)
const result = executeCommand(command); })
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => { describe('Run resolc with source debug information, check LLVM debug-info', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const commands = [
}); `resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
it("Exit code = 0", () => { `resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
expect(result.exitCode).toBe(0); ] // potential issue on resolc with full path on Windows cmd`;
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
describe("Run resolc with source debug information, check LLVM debug-info", () => { for (var idx in commands) {
const commands = [ const command = commands[idx]
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`, const result = executeCommand(command)
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { it('Compiler run successful', () => {
const command = commands[idx]; expect(result.output).toMatch(/(Compiler run successful.)/i)
const result = executeCommand(command); })
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractOptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractUnoptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => { describe('Standard JSON compilation with path options', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const contractsDir = path.join(shell.tempdir(), 'contracts-test')
}); const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json')
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
beforeAll(() => {
shell.mkdir('-p', contractsDir)
const input = JSON.parse(shell.cat(inputFile).toString())
describe("Standard JSON compilation with path options", () => { Object.entries(input.sources).forEach(
const contractsDir = path.join(shell.tempdir(), 'contracts-test'); ([sourcePath, source]: [string, any]) => {
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json'); const filePath = path.join(contractsDir, sourcePath)
shell.mkdir('-p', path.dirname(filePath))
shell.ShellString(source.content).to(filePath)
}
)
})
afterAll(() => {
shell.rm('-rf', contractsDir)
})
describe('Output with all path options', () => {
let result: { exitCode: number; output: string }
beforeAll(() => { beforeAll(() => {
shell.mkdir('-p', contractsDir); const tempInputFile = path.join(contractsDir, 'temp-input.json')
shell.cp(inputFile, tempInputFile)
const inputContent = shell.cat(inputFile).toString()
const input = JSON.parse(shell.cat(inputFile).toString()); const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`
Object.entries(input.sources).forEach(([sourcePath, source]: [string, any]) => { result = executeCommand(command, inputContent)
const filePath = path.join(contractsDir, sourcePath);
shell.mkdir('-p', path.dirname(filePath));
shell.ShellString(source.content).to(filePath);
});
});
afterAll(() => { shell.rm(tempInputFile)
shell.rm('-rf', contractsDir); })
});
describe("Output with all path options", () => { it('Compiler run successful without emiting warnings', () => {
let result: { exitCode: number; output: string }; const parsedResults = JSON.parse(result.output)
expect(
beforeAll(() => { parsedResults.errors.filter(
const tempInputFile = path.join(contractsDir, 'temp-input.json'); (error: { type: string }) => error.type != 'Warning'
shell.cp(inputFile, tempInputFile); )
const inputContent = shell.cat(inputFile).toString(); ).toEqual([])
})
const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`; })
})
result = executeCommand(command, inputContent);
shell.rm(tempInputFile);
});
it("Compiler run successful without emiting warnings", () => {
const parsedResults = JSON.parse(result.output)
expect(parsedResults.errors.filter((error: { type: string; }) => error.type != 'Warning')).toEqual([]);
});
});
});
@@ -1,40 +1,39 @@
import {executeCommand} from "../src/helper"; import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'; import { paths } from '../src/entities'
//id1743 //id1743
describe("Run with --yul by default", () => { describe('Run with --yul by default', () => {
const command = `resolc ${paths.pathToBasicYulContract} --yul`; const command = `resolc ${paths.pathToBasicYulContract} --yul`
const result = executeCommand(command); const result = executeCommand(command)
const commandInvalid = 'resolc --yul'; const commandInvalid = 'resolc --yul'
const resultInvalid = executeCommand(commandInvalid); const resultInvalid = executeCommand(commandInvalid)
it("Valid command exit code = 0", () => { it('Valid command exit code = 0', () => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0)
}); })
it("--yul output is presented", () => { it('--yul output is presented', () => {
expect(result.output).toMatch(/(Compiler run successful)/i); expect(result.output).toMatch(/(Compiler run successful)/i)
expect(result.output).toMatch(/(No output requested)/i); expect(result.output).toMatch(/(No output requested)/i)
}); })
xit('solc exit code == resolc exit code', () => {
// unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
xit("solc exit code == resolc exit code", () => { // unknown solc issue for datatype of the contract it('run invalid: resolc --yul', () => {
const command = `solc ${paths.pathToBasicSolContract} --yul`; expect(resultInvalid.output).toMatch(/(The input file is missing)/i)
const solcResult = executeCommand(command); })
expect(solcResult.exitCode).toBe(result.exitCode); it('Invalid command exit code = 1', () => {
}); expect(resultInvalid.exitCode).toBe(1)
})
it("run invalid: resolc --yul", () => { it('Invalid solc exit code == Invalid resolc exit code', () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i); const command = 'solc --yul'
}); const solcResult = executeCommand(command)
it("Invalid command exit code = 1", () => { expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
expect(resultInvalid.exitCode).toBe(1); })
}); })
it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --yul';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
+159 -124
View File
@@ -1,153 +1,188 @@
import { executeCommand } from "../src/helper"; import { executeCommand } from '../src/helper'
import { paths } from '../src/entities'; import { paths } from '../src/entities'
describe('Set of --combined-json tests', () => {
const zksolcCommand = 'zksolc'
const solcCommand = 'solc'
const json_args: string[] = [
`abi`,
`hashes`,
`metadata`,
`devdoc`,
`userdoc`,
`storage-layout`,
`ast`,
`asm`,
`bin`,
`bin-runtime`,
]
describe("Set of --combined-json tests", () => { //id1742:I
const zksolcCommand = 'zksolc'; describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const solcCommand = 'solc'; const args = [`--combined-json`]
const json_args: string[] = [`abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`]; const result = executeCommand(zksolcCommand, args)
//id1742:I it('Valid command exit code = 1', () => {
describe(`Run ${zksolcCommand} with just --combined-json`, () => { expect(result.exitCode).toBe(1)
const args = [`--combined-json`]; })
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(requires a value but none was supplied)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => { //id1742:II
const solcResult = executeCommand(solcCommand, args); describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
expect(solcResult.exitCode).toBe(result.exitCode); const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]
}); const result = executeCommand(zksolcCommand, args)
});
//id1742:II it('Valid command exit code = 1', () => {
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => { expect(result.exitCode).toBe(1)
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]; })
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(requires a value but none was supplied)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => { //id1742:III
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => {
}); const args = [
}); `${paths.pathToBasicSolContract}`,
`--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1742:III it('Valid command exit code = 0', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(0)
describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => { })
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 0", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(0); expect(result.output).toMatch(/(contracts)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(contracts)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1829:I
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => {
}); const args = [
}); `${paths.pathToBasicSolContract}`,
} `--combined-json`,
`--${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:I it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => { })
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `--${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(Invalid option|error)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(Invalid option|error)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1829:II
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => {
}); const args = [
}); `${paths.pathToBasicSolContract}`,
} `--combined-json`,
`${json_args[i]}`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:II xit('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => { })
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
xit("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(
}); /(No such file or directory|cannot find the file specified)/i
) // Hopefully we should have more precise message here!
})
it("--combined-json error is presented", () => { xit('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(No such file or directory|cannot find the file specified)/i); // Hopefully we should have more precise message here! const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
xit("solc exit code == zksolc exit code", () => { //id1829:III
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => {
}); const args = [
}); `${paths.pathToBasicSolContract}`,
} `--combined-json`,
`${json_args[i]}`,
`--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1829:III it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => { })
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(cannot be used multiple times)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1830
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => {
}); const args = [
}); `${paths.pathToBasicYulContract}`,
} `--combined-json`,
`${json_args[i]}`,
]
const result = executeCommand(zksolcCommand, args)
//id1830 it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => { })
const args = [`${paths.pathToBasicYulContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(ParserError: Expected identifier)/i)
}); })
asd
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
asd })
})
it("solc exit code == zksolc exit code", () => { }
const solcResult = executeCommand(solcCommand, args); })
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
});
+4 -4
View File
@@ -4,8 +4,8 @@ import tseslint from 'typescript-eslint'
/** @type {import('eslint').Linter.Config[]} */ /** @type {import('eslint').Linter.Config[]} */
export default [ export default [
{ files: ['**/*.{mjs,ts}'] }, { files: ['**/*.{mjs,ts}'] },
{ languageOptions: { globals: globals.browser } }, { languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended, pluginJs.configs.recommended,
...tseslint.configs.recommended, ...tseslint.configs.recommended,
] ]
+1 -1
View File
@@ -3,7 +3,7 @@ const path = require("path");
const { minify } = require("terser"); const { minify } = require("terser");
const SOLJSON_URI = const SOLJSON_URI =
"https://binaries.soliditylang.org/wasm/soljson-v0.8.29+commit.ab55807c.js"; "https://binaries.soliditylang.org/wasm/soljson-v0.8.30+commit.73712a01.js";
const RESOLC_WASM_URI = const RESOLC_WASM_URI =
process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm"; process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm";
const RESOLC_WASM_TARGET_DIR = path.join( const RESOLC_WASM_TARGET_DIR = path.join(
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "revive", "name": "revive",
"private": true, "private": true,
"dependencies": { "dependencies": {
"solc": "^0.8.29" "solc": ">=0.8.0 <=0.8.30"
}, },
"scripts": { "scripts": {
"example:web": "http-server ./examples/web/", "example:web": "http-server ./examples/web/",
+106 -110
View File
@@ -1,142 +1,138 @@
import { expect } from "chai"; import { expect } from 'chai'
import { compile } from "../examples/node/revive.js"; import { compile } from '../examples/node/revive.js'
import { fileURLToPath } from "url"; import { fileURLToPath } from 'url'
import path from "path"; import path from 'path'
import fs from "fs"; import fs from 'fs'
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename)
function loadFixture(fixture) { function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`); const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`)
return JSON.parse(fs.readFileSync(fixturePath, "utf-8")); return JSON.parse(fs.readFileSync(fixturePath, 'utf-8'))
} }
describe("Compile Function Tests", function () { describe('Compile Function Tests', function () {
it("should successfully compile valid Solidity code", async function () { it('should successfully compile valid Solidity code', async function () {
const standardInput = loadFixture("storage.json"); const standardInput = loadFixture('storage.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/storage.sol"]).to.have.property( expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage')
"Storage", expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
); 'abi'
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property( )
"abi", expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
); 'evm'
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property( )
"evm",
);
expect( expect(
output.contracts["fixtures/storage.sol"].Storage.evm, output.contracts['fixtures/storage.sol'].Storage.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
if (typeof globalThis.Bun == "undefined") { if (typeof globalThis.Bun == 'undefined') {
// Running this test with Bun on a Linux host causes: // Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)') // RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)')
// Once this issue is resolved, the test will be re-enabled. // Once this issue is resolved, the test will be re-enabled.
it("should successfully compile large Solidity code", async function () { it('should successfully compile large Solidity code', async function () {
const standardInput = loadFixture("token.json"); const standardInput = loadFixture('token.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/token.sol"]).to.have.property( expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken')
"MyToken", expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
); 'abi'
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property( )
"abi", expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
); 'evm'
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property( )
"evm",
);
expect( expect(
output.contracts["fixtures/token.sol"].MyToken.evm, output.contracts['fixtures/token.sol'].MyToken.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () { it('should successfully compile a valid Solidity contract that instantiates the token contracts', async function () {
const standardInput = loadFixture("instantiate_tokens.json"); const standardInput = loadFixture('instantiate_tokens.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"], output.contracts['fixtures/instantiate_tokens.sol']
).to.have.property("TokensFactory"); ).to.have.property('TokensFactory')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory.evm, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
} }
it("should throw an error for invalid Solidity code", async function () { it('should throw an error for invalid Solidity code', async function () {
const standardInput = loadFixture("invalid_contract_content.json"); const standardInput = loadFixture('invalid_contract_content.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("errors"); expect(output).to.have.property('errors')
expect(output.errors).to.be.an("array"); expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].type).to.exist; expect(output.errors[0].type).to.exist
expect(output.errors[0].type).to.contain("ParserError"); expect(output.errors[0].type).to.contain('ParserError')
}); })
it("should return not found error for missing imports", async function () { it('should return not found error for missing imports', async function () {
const standardInput = loadFixture("missing_import.json"); const standardInput = loadFixture('missing_import.json')
const result = await compile(standardInput); const result = await compile(standardInput)
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("errors"); expect(output).to.have.property('errors')
expect(output.errors).to.be.an("array"); expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].message).to.exist; expect(output.errors[0].message).to.exist
expect(output.errors[0].message).to.include( expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found', 'Source "nonexistent/console.sol" not found'
); )
}); })
it("should successfully compile a valid Solidity contract that instantiates another contract", async function () { it('should successfully compile a valid Solidity contract that instantiates another contract', async function () {
const standardInput = loadFixture("instantiate.json"); const standardInput = loadFixture('instantiate.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property( expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
"ChildContract", 'ChildContract'
); )
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract, output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract, output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract.evm, output.contracts['fixtures/instantiate.sol'].ChildContract.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property( expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
"MainContract", 'MainContract'
); )
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract, output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract, output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract.evm, output.contracts['fixtures/instantiate.sol'].MainContract.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
}); })
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "@parity/resolc", "name": "@parity/resolc",
"license": "Apache-2.0", "license": "Apache-2.0",
"version": "0.0.0-updated-via-gh-releases", "version": "0.1.0-dev.16",
"author": "Parity <admin@parity.io> (https://parity.io)", "author": "Parity <admin@parity.io> (https://parity.io)",
"module": "index.ts", "module": "index.ts",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -32,6 +32,6 @@
"@types/node": "^22.9.0", "@types/node": "^22.9.0",
"commander": "^13.1.0", "commander": "^13.1.0",
"package-json": "^10.0.1", "package-json": "^10.0.1",
"solc": "^0.8.29" "solc": ">=0.8.0 <=0.8.30"
} }
} }
+169 -172
View File
@@ -8,196 +8,193 @@ import * as resolc from '.'
import { SolcInput } from '.' import { SolcInput } from '.'
async function main() { 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 // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end
const originalUncaughtExceptionListeners = const originalUncaughtExceptionListeners =
process.listeners('uncaughtException') process.listeners('uncaughtException')
// FIXME: remove annoying exception catcher of Emscripten // FIXME: remove annoying exception catcher of Emscripten
// see https://github.com/chriseth/browser-solidity/issues/167 // see https://github.com/chriseth/browser-solidity/issues/167
process.removeAllListeners('uncaughtException') process.removeAllListeners('uncaughtException')
const program = new commander.Command() const program = new commander.Command()
program.name('solcjs') program.name('solcjs')
program.version(resolc.version()) program.version(resolc.version())
program program
.option('--bin', 'Binary of the contracts in hex.') .option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.') .option('--abi', 'ABI of the contracts.')
.option( .option(
'--base-path <path>', '--base-path <path>',
'Root of the project source tree. ' + 'Root of the project source tree. ' +
'The import callback will attempt to interpret all import paths as relative to this directory.' 'The import callback will attempt to interpret all import paths as relative to this directory.'
) )
.option( .option(
'--include-path <path...>', '--include-path <path...>',
'Extra source directories available to the import callback. ' + 'Extra source directories available to the import callback. ' +
'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' + 'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' +
'Can be used multiple times to provide multiple locations.' 'Can be used multiple times to provide multiple locations.'
) )
.option( .option(
'-o, --output-dir <output-directory>', '-o, --output-dir <output-directory>',
'Output directory for the contracts.' 'Output directory for the contracts.'
) )
.option('-p, --pretty-json', 'Pretty-print all JSON output.', false) .option('-p, --pretty-json', 'Pretty-print all JSON output.', false)
.option('-v, --verbose', 'More detailed console output.', false) .option('-v, --verbose', 'More detailed console output.', false)
.argument('<files...>') .argument('<files...>')
program.parse(process.argv) program.parse(process.argv)
const options = program.opts<{ const options = program.opts<{
verbose: boolean verbose: boolean
abi: boolean abi: boolean
bin: boolean bin: boolean
outputDir?: string outputDir?: string
prettyJson: boolean prettyJson: boolean
basePath?: string basePath?: string
includePath?: string[] includePath?: string[]
}>() }>()
const files: string[] = program.args const files: string[] = program.args
const destination = options.outputDir ?? '.' const destination = options.outputDir ?? '.'
function abort(msg: string) { function abort(msg: string) {
console.error(msg || 'Error occurred') console.error(msg || 'Error occurred')
process.exit(1) process.exit(1)
}
function withUnixPathSeparators(filePath: string) {
// On UNIX-like systems forward slashes in paths are just a part of the file name.
if (os.platform() !== 'win32') {
return filePath
} }
function withUnixPathSeparators(filePath: string) { return filePath.replace(/\\/g, '/')
// On UNIX-like systems forward slashes in paths are just a part of the file name. }
if (os.platform() !== 'win32') {
return filePath
}
return filePath.replace(/\\/g, '/') function makeSourcePathRelativeIfPossible(sourcePath: string) {
} const absoluteBasePath = options.basePath
? path.resolve(options.basePath)
function makeSourcePathRelativeIfPossible(sourcePath: string) { : path.resolve('.')
const absoluteBasePath = options.basePath const absoluteIncludePaths = options.includePath
? path.resolve(options.basePath) ? options.includePath.map((prefix: string) => {
: path.resolve('.') return path.resolve(prefix)
const absoluteIncludePaths = options.includePath
? options.includePath.map((prefix: string) => {
return path.resolve(prefix)
})
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
}
// File is not located inside base path or include paths so use its absolute path.
return withUnixPathSeparators(absoluteSourcePath)
}
function toFormattedJson<T>(input: T) {
return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
}
if (files.length === 0) {
console.error('Must provide a file')
process.exit(1)
}
if (!(options.bin || options.abi)) {
abort('Invalid option selected, must specify either --bin or --abi')
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
}
}
if (options.verbose) {
console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
}
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
}) })
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
} }
for (const fileName in output.contracts) { // File is not located inside base path or include paths so use its absolute path.
for (const contractName in output.contracts[fileName]) { return withUnixPathSeparators(absoluteSourcePath)
let contractFileName = fileName + ':' + contractName }
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) { function toFormattedJson<T>(input: T) {
writeFile( return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
contractFileName + '.polkavm', }
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode
.object,
'hex'
)
)
}
if (options.abi) { if (files.length === 0) {
writeFile( console.error('Must provide a file')
contractFileName + '.abi', process.exit(1)
toFormattedJson( }
output.contracts[fileName][contractName].abi
) if (!(options.bin || options.abi)) {
) abort('Invalid option selected, must specify either --bin or --abi')
} }
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
} }
}
// Put back original exception handlers. if (options.verbose) {
originalUncaughtExceptionListeners.forEach(function (listener) { console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
process.addListener('uncaughtException', listener) }
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
}) })
}
if (hasError) { for (const fileName in output.contracts) {
process.exit(1) 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'
)
)
}
if (options.abi) {
writeFile(
contractFileName + '.abi',
toFormattedJson(output.contracts[fileName][contractName].abi)
)
}
} }
}
// Put back original exception handlers.
originalUncaughtExceptionListeners.forEach(function (listener) {
process.addListener('uncaughtException', listener)
})
if (hasError) {
process.exit(1)
}
} }
main().catch((err) => { main().catch((err) => {
console.error('Error:', err) console.error('Error:', err)
process.exit(1) process.exit(1)
}) })
+88 -87
View File
@@ -4,112 +4,113 @@ import assert from 'node:assert'
import { compile, tryResolveImport } from '.' import { compile, tryResolveImport } from '.'
import { resolve } from 'node:path' import { resolve } from 'node:path'
const compileOptions = [{}] const compileOptions = [{}]
if (existsSync('../../target/release/resolc')) { if (existsSync('../../target/release/resolc')) {
compileOptions.push({ bin: '../../target/release/resolc' }) compileOptions.push({ bin: '../../target/release/resolc' })
} }
for (const options of compileOptions) { for (const options of compileOptions) {
test(`check Ok output with option ${JSON.stringify(options)}`, async () => { test(`check Ok output with option ${JSON.stringify(options)}`, async () => {
const contract = 'fixtures/token.sol' const contract = 'fixtures/token.sol'
const sources = { const sources = {
[contract]: { [contract]: {
content: readFileSync('fixtures/storage.sol', 'utf8'), content: readFileSync('fixtures/storage.sol', 'utf8'),
}, },
} }
const out = await compile(sources, options) const out = await compile(sources, options)
assert(out.contracts[contract].Storage.abi != null) assert(out.contracts[contract].Storage.abi != null)
assert(out.contracts[contract].Storage.evm.bytecode != null) assert(out.contracts[contract].Storage.evm.bytecode != null)
}) })
} }
test('check Err output', async () => { test('check Err output', async () => {
const sources = { const sources = {
bad: { bad: {
content: readFileSync('fixtures/storage_bad.sol', 'utf8'), content: readFileSync('fixtures/storage_bad.sol', 'utf8'),
}, },
} }
const out = await compile(sources) const out = await compile(sources)
assert( assert(
out?.errors?.[0].message.includes( out?.errors?.[0].message.includes(
'SPDX license identifier not provided in source file' 'SPDX license identifier not provided in source file'
)
) )
assert( )
out?.errors?.[1].message.includes( assert(
'Source file does not specify required compiler version' out?.errors?.[1].message.includes(
) 'Source file does not specify required compiler version'
) )
)
}) })
test('check Err from stderr', async () => { test('check Err from stderr', async () => {
const sources = { const sources = {
bad: { bad: {
content: readFileSync('fixtures/bad_pragma.sol', 'utf8'), content: readFileSync('fixtures/bad_pragma.sol', 'utf8'),
}, },
} }
try { try {
await compile(sources) await compile(sources)
assert(false, 'Expected error') assert(false, 'Expected error')
} catch (error) { } catch (error) {
assert( assert(
String(error).includes( String(error).includes('Source file requires different compiler version')
'Source file requires different compiler version' )
) }
)
}
}) })
test('resolve import', () => { test('resolve import', () => {
const cases = [ const cases = [
// local // local
{ {
file: './fixtures/storage.sol', file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'), expected: resolve('fixtures/storage.sol'),
}, },
// scopped module with version // scopped module with version
{ {
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol', file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
expected: require.resolve('@openzeppelin/contracts/token/ERC20/ERC20.sol'), expected: require.resolve(
}, '@openzeppelin/contracts/token/ERC20/ERC20.sol'
// scopped module without version ),
{ },
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol', // scopped module without version
expected: require.resolve('@openzeppelin/contracts/token/ERC20/ERC20.sol'), {
}, file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
// scopped module with wrong version expected: require.resolve(
{ '@openzeppelin/contracts/token/ERC20/ERC20.sol'
file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol', ),
expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`, },
}, // scopped module with wrong version
// module without version {
{ file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol',
file: '@openzeppelin/contracts/package.json', expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`,
expected: require.resolve('@openzeppelin/contracts/package.json'), },
}, // module without version
// scopped module with version {
{ file: '@openzeppelin/contracts/package.json',
file: '@openzeppelin/contracts@5.1.0/package.json', expected: require.resolve('@openzeppelin/contracts/package.json'),
expected: require.resolve('@openzeppelin/contracts/package.json'), },
}, // scopped module with version
] {
file: '@openzeppelin/contracts@5.1.0/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
]
for (const { file, expected } of cases) { for (const { file, expected } of cases) {
try { try {
const resolved = tryResolveImport(file) const resolved = tryResolveImport(file)
assert( assert(
resolved === expected, resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}` `\nGot:\n${resolved}\nExpected:\n${expected}`
) )
} catch (error) { } catch (error) {
assert( assert(
String(error) == expected, String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}` `\nGot:\n${String(error)}\nExpected:\n${expected}`
) )
}
} }
}
}) })
+164 -153
View File
@@ -5,114 +5,127 @@ import path from 'path'
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'fs'
export type SolcInput = { export type SolcInput = {
[contractName: string]: { [contractName: string]: {
content: string content: string
} }
} }
export type SolcError = { export type SolcError = {
component: string component: string
errorCode: string errorCode: string
formattedMessage: string formattedMessage: string
message: string message: string
severity: string severity: string
sourceLocation?: { sourceLocation?: {
file: string file: string
start: number start: number
end: number end: number
} }
type: string type: string
} }
export type SolcOutput = { export type SolcOutput = {
contracts: { contracts: {
[contractPath: string]: { [contractPath: string]: {
[contractName: string]: { [contractName: string]: {
abi: Array<{ abi: Array<{
name: string name: string
inputs: Array<{ name: string; type: string }> inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }> outputs: Array<{ name: string; type: string }>
stateMutability: string stateMutability: string
type: string type: string
}> }>
evm: { evm: {
bytecode: { object: string } bytecode: { object: string }
}
}
} }
}
} }
errors?: Array<SolcError> }
errors?: Array<SolcError>
} }
export function resolveInputs(sources: SolcInput): SolcInput { export function resolveInputs(sources: SolcInput): SolcInput {
const input = { const input = {
language: 'Solidity', language: 'Solidity',
sources, sources,
settings: { settings: {
outputSelection: { outputSelection: {
'*': { '*': {
'*': ['evm.bytecode.object'], '*': ['evm.bytecode.object'],
},
},
}, },
} },
},
}
const out = solc.compile(JSON.stringify(input), { const out = solc.compile(JSON.stringify(input), {
import: (path: string) => { import: (path: string) => {
return { return {
contents: readFileSync(tryResolveImport(path), 'utf8'), 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'),
}, },
]
}) })
)
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 { export function version(): string {
const v = resolcVersion() const v = resolcVersion()
return v.split(' ').pop() ?? v return v.split(' ').pop() ?? v
} }
export async function compile( export async function compile(
sources: SolcInput, sources: SolcInput,
option: { bin?: string } = {} option: {
optimizer?: Record<string, unknown>
bin?: string
} = {}
): Promise<SolcOutput> { ): Promise<SolcOutput> {
const input = JSON.stringify({ const {
language: 'Solidity', optimizer = {
sources: resolveInputs(sources), mode: 'z',
settings: { fallback_to_optimizing_for_size: true,
optimizer: { enabled: true, runs: 200 }, enabled: true,
outputSelection: { runs: 200,
'*': { },
'*': ['abi'], bin,
}, } = option
},
const input = JSON.stringify({
language: 'Solidity',
sources: resolveInputs(sources),
settings: {
optimizer,
outputSelection: {
'*': {
'*': ['abi'],
}, },
}) },
},
})
if (option.bin) { if (bin) {
return compileWithBin(input, option.bin) return compileWithBin(input, bin)
} }
return resolc(input) return resolc(input)
} }
/** /**
@@ -120,83 +133,81 @@ export async function compile(
* @param importPath - The import path to resolve. * @param importPath - The import path to resolve.
*/ */
export function tryResolveImport(importPath: string) { export function tryResolveImport(importPath: string) {
// resolve local path // resolve local path
if (existsSync(importPath)) { if (existsSync(importPath)) {
return path.resolve(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"
let packageJsonPath
try {
packageJsonPath = require.resolve(path.join(basePackage, 'package.json'))
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
} }
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/ const packageRoot = path.dirname(packageJsonPath)
const match = importPath.match(importRegex)
if (!match) { // Construct full path to the requested file
throw new Error('Invalid import path format.') const resolvedPath = path.join(packageRoot, relativePath)
} if (existsSync(resolvedPath)) {
return resolvedPath
const basePackage = match[1] // "foo", "@scope/foo" } else {
const specifiedVersion = match[2] // "1.2.3" (optional) throw new Error(`Resolved path ${resolvedPath} does not exist.`)
const relativePath = match[3] // "/path/to/file.sol" }
let packageJsonPath
try {
packageJsonPath = require.resolve(
path.join(basePackage, 'package.json')
)
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
}
}
const packageRoot = path.dirname(packageJsonPath)
// 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> { function compileWithBin(input: string, bin: string): PromiseLike<SolcOutput> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const process = spawn(bin, ['--standard-json']) const process = spawn(bin, ['--standard-json'])
let output = '' let output = ''
let error = '' let error = ''
process.stdin.write(input) process.stdin.write(input)
process.stdin.end() process.stdin.end()
process.stdout.on('data', (data) => { process.stdout.on('data', (data) => {
output += data.toString() 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}`))
}
})
}) })
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}`))
}
})
})
} }
+17 -17
View File
@@ -3,28 +3,28 @@ import Resolc from './resolc/resolc'
import type { SolcOutput } from '.' import type { SolcOutput } from '.'
export function resolc(input: string): SolcOutput { export function resolc(input: string): SolcOutput {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson m.soljson = soljson
m.writeToStdin(input) m.writeToStdin(input)
m.callMain(['--standard-json']) m.callMain(['--standard-json'])
const err = m.readFromStderr() const err = m.readFromStderr()
if (err) { if (err) {
throw new Error(err) throw new Error(err)
} }
return JSON.parse(m.readFromStdout()) as SolcOutput return JSON.parse(m.readFromStdout()) as SolcOutput
} }
export function version(): string { export function version(): string {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson m.soljson = soljson
m.callMain(['--version']) m.callMain(['--version'])
const err = m.readFromStderr() const err = m.readFromStderr()
if (err) { if (err) {
throw new Error(err) throw new Error(err)
} }
return m.readFromStdout() return m.readFromStdout()
} }
+82 -82
View File
@@ -1,95 +1,95 @@
declare module 'solc/soljson' {} declare module 'solc/soljson' {}
declare module 'solc' { declare module 'solc' {
// Basic types for input/output handling // Basic types for input/output handling
export interface CompileInput { export interface CompileInput {
language: string language: string
sources: { sources: {
[fileName: string]: { [fileName: string]: {
content: string content: string
} }
}
settings?: {
optimizer?: {
enabled: boolean
runs: number
}
outputSelection: {
[fileName: string]: {
[contractName: string]: string[]
} }
settings?: { }
optimizer?: { }
enabled: boolean }
runs: number
} export interface CompileOutput {
outputSelection: { errors?: Array<{
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: { [fileName: string]: {
[contractName: string]: string[] [libraryName: string]: Array<{
start: number
length: number
}>
} }
}
} }
} deployedBytecode: {
} object: string
sourceMap: string
export interface CompileOutput { linkReferences: {
errors?: Array<{ [fileName: string]: {
component: string [libraryName: string]: Array<{
errorCode: string start: number
formattedMessage: string length: number
message: string }>
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
deployedBytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
}
} }
}
} }
}
} }
}
} }
}
// Main exported functions // Main exported functions
export function compile( export function compile(
input: string | CompileInput, input: string | CompileInput,
options?: { options?: {
import: (path: string) => import: (path: string) =>
| { | {
contents: string contents: string
error?: undefined error?: undefined
} }
| { | {
error: string error: string
contents?: undefined contents?: undefined
} }
} }
): string ): string
} }
+6618
View File
File diff suppressed because it is too large Load Diff