This commit is contained in:
pgherveou
2025-04-24 16:33:50 +02:00
parent f6a412eef4
commit f44b2485bd
66 changed files with 2969 additions and 747 deletions
+18 -19
View File
@@ -1,19 +1,18 @@
name: "Get Emscripten SDK" name: "Get Emscripten SDK"
inputs: inputs:
version: version:
description: "" description: ""
required: false required: false
default: "3.1.64" default: "3.1.64"
runs:
runs: using: "composite"
using: "composite" steps:
steps: - name: install emsdk
- name: install emsdk shell: bash
shell: bash run: |
run: | git clone https://github.com/emscripten-core/emsdk.git ./emsdk/
git clone https://github.com/emscripten-core/emsdk.git ./emsdk/ cd emsdk
cd emsdk git checkout tags/${{ inputs.version }}
git checkout tags/${{ inputs.version }} ./emsdk install ${{ inputs.version }}
./emsdk install ${{ inputs.version }} ./emsdk activate ${{ inputs.version }}
./emsdk activate ${{ inputs.version }}
+70 -70
View File
@@ -1,70 +1,70 @@
# example: # example:
# - uses: ./.github/actions/get-llvm # - uses: ./.github/actions/get-llvm
# with: # with:
# target: x86_64-unknown-linux-gnu # target: x86_64-unknown-linux-gnu
name: "Download LLVM" name: "Download LLVM"
inputs: inputs:
target: target:
required: true required: true
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: find asset - name: find asset
id: find id: find
uses: actions/github-script@v7 uses: actions/github-script@v7
env: env:
target: ${{ inputs.target }} target: ${{ inputs.target }}
with: with:
result-encoding: string result-encoding: string
script: | script: |
let page = 1; let page = 1;
let releases = []; let releases = [];
let releasePrefix = "llvm-" let releasePrefix = "llvm-"
let target = process.env.target let target = process.env.target
do { do {
const res = await github.rest.repos.listReleases({ const res = await github.rest.repos.listReleases({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
per_page: 50, per_page: 50,
page, page,
}); });
releases = res.data releases = res.data
releases.sort((a, b) => { releases.sort((a, b) => {
return (a.published_at < b.published_at) ? 1 : ((a.published_at > b.published_at) ? -1 : 0); return (a.published_at < b.published_at) ? 1 : ((a.published_at > b.published_at) ? -1 : 0);
}); });
let llvmLatestRelease = releases.find(release => { let llvmLatestRelease = releases.find(release => {
return release.tag_name.startsWith(releasePrefix); return release.tag_name.startsWith(releasePrefix);
}); });
if (llvmLatestRelease){ if (llvmLatestRelease){
let asset = llvmLatestRelease.assets.find(asset =>{ let asset = llvmLatestRelease.assets.find(asset =>{
return asset.name.includes(target); return asset.name.includes(target);
}); });
if (!asset){ if (!asset){
core.setFailed(`Artifact for '${target}' not found in release ${llvmLatestRelease.tag_name} (${llvmLatestRelease.html_url})`); core.setFailed(`Artifact for '${target}' not found in release ${llvmLatestRelease.tag_name} (${llvmLatestRelease.html_url})`);
process.exit(); process.exit();
} }
return asset.browser_download_url; return asset.browser_download_url;
} }
page++; page++;
} while(releases.length > 0); } while(releases.length > 0);
core.setFailed(`No LLVM releases with '${releasePrefix}' atifacts found! Please release LLVM before running this workflow.`); core.setFailed(`No LLVM releases with '${releasePrefix}' atifacts found! Please release LLVM before running this workflow.`);
process.exit(); process.exit();
- name: download - name: download
shell: bash shell: bash
run: | run: |
curl -sSLo llvm.tar.gz ${{ steps.find.outputs.result }} curl -sSLo llvm.tar.gz ${{ steps.find.outputs.result }}
- name: unpack - name: unpack
shell: bash shell: bash
run: | run: |
tar -xf llvm.tar.gz tar -xf llvm.tar.gz
rm llvm.tar.gz rm llvm.tar.gz
+9 -1
View File
@@ -37,7 +37,15 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-emscripten, aarch64-apple-darwin, x86_64-apple-darwin, x86_64-pc-windows-msvc] target:
[
x86_64-unknown-linux-gnu,
x86_64-unknown-linux-musl,
wasm32-unknown-emscripten,
aarch64-apple-darwin,
x86_64-apple-darwin,
x86_64-pc-windows-msvc,
]
include: include:
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
builder-arg: gnu builder-arg: gnu
+3 -3
View File
@@ -4,9 +4,9 @@ on:
branches: ["main"] branches: ["main"]
types: [opened, synchronize] types: [opened, synchronize]
paths: paths:
- 'LLVM.lock' - "LLVM.lock"
- 'crates/llvm-builder/**' - "crates/llvm-builder/**"
- '.github/workflows/test-llvm-builder.yml' - ".github/workflows/test-llvm-builder.yml"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+1
View File
@@ -1,4 +1,5 @@
/target /target
/js/resolc/dist
target-llvm target-llvm
*.dot *.dot
.vscode/ .vscode/
+29 -3
View File
@@ -19,13 +19,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 +38,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 +60,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 +81,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 +92,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 +107,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 +121,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 +133,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 +154,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 +177,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 +185,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 +212,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.
+7 -3
View File
@@ -14,6 +14,7 @@ This is experimental software in active development and not ready just yet for p
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1). Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
## Installation ## Installation
Please consult [the documentation](https://contracts.polkadot.io/revive_compiler/installation) for installation instructions. Please consult [the documentation](https://contracts.polkadot.io/revive_compiler/installation) for installation instructions.
## Building from source ## Building from source
@@ -30,11 +31,13 @@ Building revive requires a [stable Rust installation](https://rustup.rs/) and a
Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q=LLVM+binaries+release&expanded=true) from our releases. Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q=LLVM+binaries+release&expanded=true) from our releases.
> **MacOS** users need to clear the `downloaded` attribute from all binaries after extracting the archive: > **MacOS** users need to clear the `downloaded` attribute from all binaries after extracting the archive:
>
> ```sh > ```sh
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/* > xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
> ``` > ```
After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it: After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it:
```sh ```sh
export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
``` ```
@@ -44,7 +47,7 @@ export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/targ
<details> <details>
<summary>Building from source</summary> <summary>Building from source</summary>
Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards. Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards.
The `Makefile` provides a shortcut target to obtain a compatible LLVM build: The `Makefile` provides a shortcut target to obtain a compatible LLVM build:
@@ -93,11 +96,12 @@ make test-wasm
## Development ## Development
Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks. Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks.
Ensure that your branch passes `make test` locally when submitting a pull request. Ensure that your branch passes `make test` locally when submitting a pull request.
### Design overview ### Design overview
See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works.
See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works.
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc` (the project started as a fork of the era compiler). [Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc` (the project started as a fork of the era compiler).
+1 -1
View File
@@ -18,4 +18,4 @@ To create a new pre-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`.
Version suffix will be resolved automatically. Version suffix will be resolved automatically.
The workflows will create new GitHub release, and upload LLVM binaries. The workflows will create new GitHub release, and upload LLVM binaries.
Next release of resolc will use newly created binaries. Next release of resolc will use newly created binaries.
+1 -1
View File
@@ -1 +1 @@
https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/SECURITY.md https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/SECURITY.md
+42 -42
View File
@@ -3,71 +3,71 @@
## Table of Contents ## Table of Contents
- [Benchmark Results](#benchmark-results) - [Benchmark Results](#benchmark-results)
- [Baseline](#baseline) - [Baseline](#baseline)
- [OddPorduct](#oddporduct) - [OddPorduct](#oddporduct)
- [TriangleNumber](#trianglenumber) - [TriangleNumber](#trianglenumber)
- [FibonacciRecursive](#fibonaccirecursive) - [FibonacciRecursive](#fibonaccirecursive)
- [FibonacciIterative](#fibonacciiterative) - [FibonacciIterative](#fibonacciiterative)
- [FibonacciBinet](#fibonaccibinet) - [FibonacciBinet](#fibonaccibinet)
- [SHA1](#sha1) - [SHA1](#sha1)
## Benchmark Results ## Benchmark Results
### Baseline ### Baseline
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:--------|:-------------------------|:-------------------------------- | | :------ | :------------------------ | :------------------------------- |
| **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) | | **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) |
### OddPorduct ### OddPorduct
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:--------------------------|:-------------------------------- | | :----------- | :------------------------- | :------------------------------- |
| **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) | | **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) |
| **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) | | **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) |
| **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) | | **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) |
### TriangleNumber ### TriangleNumber
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- | | :----------- | :------------------------ | :------------------------------- |
| **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) | | **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) |
| **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) | | **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) |
| **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) | | **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) |
### FibonacciRecursive ### FibonacciRecursive
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:---------|:--------------------------|:--------------------------------- | | :------- | :------------------------- | :-------------------------------- |
| **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) | | **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) |
| **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) | | **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) |
| **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) | | **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) |
| **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) | | **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) |
### FibonacciIterative ### FibonacciIterative
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | | :-------- | :------------------------ | :------------------------------- |
| **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) | | **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ _1.39x slower_) |
| **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) | | **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ _1.20x slower_) |
| **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) | | **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) |
### FibonacciBinet ### FibonacciBinet
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | | :-------- | :------------------------ | :----------------------------- |
| **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) | | **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ _2.07x slower_) |
| **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) | | **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ _1.95x slower_) |
| **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) | | **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ _1.85x slower_) |
### SHA1 ### SHA1
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:--------------------------|:--------------------------------- | | :-------- | :------------------------- | :------------------------------ |
| **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) | | **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ _1.79x slower_) |
| **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) | | **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ _1.38x slower_) |
| **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) | | **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) |
--- ---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+45 -45
View File
@@ -1,47 +1,47 @@
{ {
"config": { "config": {
"chainId": 420420420, "chainId": 420420420,
"homesteadBlock": 0, "homesteadBlock": 0,
"eip150Block": 0, "eip150Block": 0,
"eip155Block": 0, "eip155Block": 0,
"eip158Block": 0, "eip158Block": 0,
"byzantiumBlock": 0, "byzantiumBlock": 0,
"constantinopleBlock": 0, "constantinopleBlock": 0,
"petersburgBlock": 0, "petersburgBlock": 0,
"istanbulBlock": 0, "istanbulBlock": 0,
"berlinBlock": 0, "berlinBlock": 0,
"londonBlock": 0, "londonBlock": 0,
"arrowGlacierBlock": 0, "arrowGlacierBlock": 0,
"grayGlacierBlock": 0, "grayGlacierBlock": 0,
"shanghaiTime": 0, "shanghaiTime": 0,
"cancunTime": 0, "cancunTime": 0,
"terminalTotalDifficulty": 0, "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true, "terminalTotalDifficultyPassed": true,
"blobSchedule": { "blobSchedule": {
"cancun": { "cancun": {
"target": 3, "target": 3,
"max": 6, "max": 6,
"baseFeeUpdateFraction": 3338477 "baseFeeUpdateFraction": 3338477
} }
}
},
"coinbase": "0xffffffffffffffffffffffffffffffffffffffff",
"difficulty": "0x20000",
"extraData": "",
"gasLimit": "0xffffffff",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"alloc": {
"0101010101010101010101010101010101010101": {
"balance": "1000000000"
},
"0202020202020202020202020202020202020202": {
"balance": "1000000000"
},
"0303030303030303030303030303030303030303": {
"balance": "1000000000"
}
} }
} },
"coinbase": "0xffffffffffffffffffffffffffffffffffffffff",
"difficulty": "0x20000",
"extraData": "",
"gasLimit": "0xffffffff",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"alloc": {
"0101010101010101010101010101010101010101": {
"balance": "1000000000"
},
"0202020202020202020202020202020202020202": {
"balance": "1000000000"
},
"0303030303030303030303030303030303030303": {
"balance": "1000000000"
}
}
}
+1 -1
View File
@@ -7,4 +7,4 @@
"FibonacciIterative": 1497, "FibonacciIterative": 1497,
"Flipper": 2099, "Flipper": 2099,
"SHA1": 8243 "SHA1": 8243
} }
+64 -55
View File
@@ -6,6 +6,7 @@ Parity fork of the [Matter Labs zksync LLVM builder](https://github.com/matter-l
The LLVM compiler framework for revive must be built with our tool called `revive-llvm`. The LLVM compiler framework for revive must be built with our tool called `revive-llvm`.
This is because the revive compiler has requirements not fullfilled in upstream builds: This is because the revive compiler has requirements not fullfilled in upstream builds:
- Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables - Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables
- The RISC-V target (the PolkaVM target) - The RISC-V target (the PolkaVM target)
- The compiler-rt builtins for the PolkaVM target - The compiler-rt builtins for the PolkaVM target
@@ -17,85 +18,92 @@ Obtain a compatible build for your host platform from the release section of thi
<details> <details>
<summary>1. Install the system prerequisites.</summary> <summary>1. Install the system prerequisites.</summary>
* Linux (Debian): - Linux (Debian):
Install the following packages: Install the following packages:
```shell
apt install cmake ninja-build curl git libssl-dev pkg-config clang lld
```
* Linux (Arch):
Install the following packages: ```shell
```shell apt install cmake ninja-build curl git libssl-dev pkg-config clang lld
pacman -Syu which cmake ninja curl git pkg-config clang lld ```
```
* MacOS: - Linux (Arch):
* Install the [HomeBrew](https://brew.sh) package manager. Install the following packages:
* Install the following packages:
```shell ```shell
brew install cmake ninja coreutils pacman -Syu which cmake ninja curl git pkg-config clang lld
``` ```
* Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apples Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. - MacOS:
</details>
* Install the [HomeBrew](https://brew.sh) package manager.
* Install the following packages:
```shell
brew install cmake ninja coreutils
```
* Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apples Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager.
</details>
<details> <details>
<summary>2. Install Rust.</summary> <summary>2. Install Rust.</summary>
* Follow the latest [official instructions](https://www.rust-lang.org/tools/install: - Follow the latest [official instructions](https://www.rust-lang.org/tools/install:
```shell `shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. ${HOME}/.cargo/env . ${HOME}/.cargo/env
``` `
> Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. > Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform.
</details>
</details>
<details> <details>
<summary>3. Install the revive LLVM framework builder.</summary> <summary>3. Install the revive LLVM framework builder.</summary>
* Install the builder using `cargo`: - Install the builder using `cargo`:
```shell
cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked
```
> The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`. ```shell
cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked
```
> The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`.
</details> </details>
<details> <details>
<summary>4. (Optional) Create the `LLVM.lock` file.</summary> <summary>4. (Optional) Create the `LLVM.lock` file.</summary>
* The `LLVM.lock` dictates the LLVM source tree being used. - The `LLVM.lock` dictates the LLVM source tree being used.
A default `./LLVM.lock` pointing to the release used for development is already provided. A default `./LLVM.lock` pointing to the release used for development is already provided.
</details> </details>
<details> <details>
<summary>5. Build LLVM.</summary> <summary>5. Build LLVM.</summary>
* Clone and build the LLVM framework using the `revive-llvm` tool. - Clone and build the LLVM framework using the `revive-llvm` tool.
The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default. The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default.
```shell ```shell
revive-llvm clone revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang revive-llvm build --llvm-projects lld --llvm-projects clang
``` ```
Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default. Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default.
The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`. The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`.
You now need to export the final target directory `$LLVM_SYS_181_PREFIX`: `export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final` You now need to export the final target directory `$LLVM_SYS_181_PREFIX`: `export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final`
If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`. If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`.
</details> </details>
## Supported target architectures ## Supported target architectures
The following target platforms are supported: The following target platforms are supported:
- Linux GNU (x86) - Linux GNU (x86)
- Linux MUSL (x86) - Linux MUSL (x86)
- MacOS (aarch64) - MacOS (aarch64)
@@ -105,28 +113,29 @@ The following target platforms are supported:
<details> <details>
<summary>Building for MUSL</summary> <summary>Building for MUSL</summary>
* Via a musl build we can build revive into fully static ELF binaries. - Via a musl build we can build revive into fully static ELF binaries.
Which is desirable for reproducible Solidity contracts builds. Which is desirable for reproducible Solidity contracts builds.
The resulting binary is also very portable, akin to the`solc` frontend binary distribution. The resulting binary is also very portable, akin to the`solc` frontend binary distribution.
Clone and build the LLVM framework using the `revive-llvm` tool: Clone and build the LLVM framework using the `revive-llvm` tool:
```shell
revive-llvm --target-env musl clone ```shell
revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld revive-llvm --target-env musl clone
``` revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld
```
</details> </details>
<details> <details>
<summary>Building for Emscripten</summary> <summary>Building for Emscripten</summary>
* Via an emsdk build we can run revive in the browser and on node.js. - Via an emsdk build we can run revive in the browser and on node.js.
Clone and build the LLVM framework using the `revive-llvm` tool: Clone and build the LLVM framework using the `revive-llvm` tool:
```shell
revive-llvm --target-env emscripten clone ```shell
revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld revive-llvm --target-env emscripten clone
``` revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld
```
</details> </details>
+2 -1
View File
@@ -16,7 +16,7 @@ Inside the root `revive` repository directory, execute:
make install-revive-runner make install-revive-runner
``` ```
Which will install the `revive-runner` using `cargo`. Which will install the `revive-runner` using `cargo`.
## Usage ## Usage
@@ -24,3 +24,4 @@ Set the `RUST_LOG` environment varibale to the `trace` level to see the full Pol
```bash ```bash
RUST_LOG=trace revive-runner -f mycontract.pvm -c a9059cbb000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac0000000000000000000000000000000000000000000000000000000000000000 RUST_LOG=trace revive-runner -f mycontract.pvm -c a9059cbb000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac0000000000000000000000000000000000000000000000000000000000000000
```
@@ -23,4 +23,4 @@
"ts-jest": "^29.2.5", "ts-jest": "^29.2.5",
"typescript": "^5.7.3" "typescript": "^5.7.3"
} }
} }
File diff suppressed because one or more lines are too long
@@ -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,
@@ -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,12 +1,11 @@
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", () => {
@@ -28,16 +27,18 @@ describe("Run with --asm by default", () => {
}); });
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", () => { it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1); expect(resultInvalid.exitCode).toBe(1);
}); });
it("Invalid solc exit code == Invalid resolc exit code", () => { it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --asm'; const command = "solc --asm";
const solcResult = executeCommand(command); const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode); 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", () => { it("solc exit code == resolc exit code", () => {
const command = 'solc'; const command = "solc";
const solcResult = executeCommand(command); const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode); 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("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 files are created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true); // a bug on windows
}); expect(
xit("Output files are created", () => { // a bug on windows isFileExist(
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true); paths.pathToOutputDir,
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true); paths.contractSolFilename,
}); paths.binExtension,
it("the output files are not empty", () => { ),
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false); ).toBe(true);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false); expect(
}); isFileExist(
it("No 'Error'/'Warning'/'Fail' in the output", () => { paths.pathToOutputDir,
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i); 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", () => { describe("Run resolc with source debug information", () => {
const commands = [ const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"` `resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
]; // potential issue on resolc with full path on Windows cmd`; ]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { for (var idx in commands) {
const command = commands[idx]; const command = commands[idx];
const result = executeCommand(command); const result = executeCommand(command);
it("Compiler run successful", () => { it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); expect(result.output).toMatch(/(Compiler run successful.)/i);
}); });
it("Exit code = 0", () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
it("Output dir is created", () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true); expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}); });
it("Output files are created", () => { // a bug on windows it("Output files are created", () => {
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true); // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true); expect(
}); isFileExist(
it("the output files are not empty", () => { paths.pathToOutputDir,
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false); paths.contractSolFilename,
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false); paths.binExtension,
}); ),
it("No 'Error'/'Fail' in the output", () => { ).toBe(true);
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i); 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", () => { describe("Run resolc with source debug information, check LLVM debug-info", () => {
const commands = [ const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"` `resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
]; // potential issue on resolc with full path on Windows cmd`; ]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { for (var idx in commands) {
const command = commands[idx]; const command = commands[idx];
const result = executeCommand(command); const result = executeCommand(command);
it("Compiler run successful", () => { it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); expect(result.output).toMatch(/(Compiler run successful.)/i);
}); });
it("Exit code = 0", () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
it("Output dir is created", () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true); expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}); });
it("Output files are created", () => { // a bug on windows it("Output files are created", () => {
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true); // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true); expect(
}); isFileExist(
it("the output files are not empty", () => { paths.pathToOutputDir,
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false); paths.contractOptimizedLLVMFilename,
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false); paths.llvmExtension,
}); ),
it("No 'Error'/'Fail' in the output", () => { ).toBe(true);
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i); 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);
});
}
}); });
describe("Standard JSON compilation with path options", () => { describe("Standard JSON compilation with path options", () => {
const contractsDir = path.join(shell.tempdir(), 'contracts-test'); const contractsDir = path.join(shell.tempdir(), "contracts-test");
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json'); const inputFile = path.join(__dirname, "..", "src/contracts/compiled/1.json");
beforeAll(() => {
shell.mkdir("-p", contractsDir);
const input = JSON.parse(shell.cat(inputFile).toString());
Object.entries(input.sources).forEach(
([sourcePath, source]: [string, any]) => {
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.rm(tempInputFile);
shell.ShellString(source.content).to(filePath);
});
}); });
afterAll(() => { it("Compiler run successful without emiting warnings", () => {
shell.rm('-rf', contractsDir); const parsedResults = JSON.parse(result.output);
expect(
parsedResults.errors.filter(
(error: { type: string }) => error.type != "Warning",
),
).toEqual([]);
}); });
});
describe("Output with all path options", () => { });
let result: { exitCode: number; output: string };
beforeAll(() => {
const tempInputFile = path.join(contractsDir, 'temp-input.json');
shell.cp(inputFile, tempInputFile);
const inputContent = shell.cat(inputFile).toString();
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,12 +1,11 @@
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", () => {
@@ -18,11 +17,11 @@ describe("Run with --yul by default", () => {
expect(result.output).toMatch(/(No output requested)/i); expect(result.output).toMatch(/(No output requested)/i);
}); });
xit("solc exit code == resolc exit code", () => {
xit("solc exit code == resolc exit code", () => { // unknown solc issue for datatype of the contract // unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`; const command = `solc ${paths.pathToBasicSolContract} --yul`;
const solcResult = executeCommand(command); const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode); expect(solcResult.exitCode).toBe(result.exitCode);
}); });
it("run invalid: resolc --yul", () => { it("run invalid: resolc --yul", () => {
@@ -33,7 +32,7 @@ describe("Run with --yul by default", () => {
}); });
it("Invalid solc exit code == Invalid resolc exit code", () => { it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --yul'; const command = "solc --yul";
const solcResult = executeCommand(command); const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode); expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
}); });
@@ -2,6 +2,6 @@
"compilerOptions": { "compilerOptions": {
"target": "ES6", "target": "ES6",
"module": "CommonJS", "module": "CommonJS",
"outDir": "./dist", "outDir": "./dist"
} }
} }
+163 -124
View File
@@ -1,153 +1,192 @@
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", () => { describe("Set of --combined-json tests", () => {
const zksolcCommand = 'zksolc'; const zksolcCommand = "zksolc";
const solcCommand = 'solc'; const solcCommand = "solc";
const json_args: string[] = [`abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`]; const json_args: string[] = [
`abi`,
`hashes`,
`metadata`,
`devdoc`,
`userdoc`,
`storage-layout`,
`ast`,
`asm`,
`bin`,
`bin-runtime`,
];
//id1742:I //id1742:I
describe(`Run ${zksolcCommand} with just --combined-json`, () => { describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const args = [`--combined-json`]; const args = [`--combined-json`];
const result = executeCommand(zksolcCommand, args); const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1); expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
}); });
//id1742:II it("--combined-json error is presented", () => {
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => { expect(result.output).toMatch(
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]; /(requires a value but none was supplied)/i,
const result = executeCommand(zksolcCommand, args); );
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
}); });
//id1742:III it("solc exit code == zksolc exit code", () => {
for (let i = 0; i < json_args.length; i++) { const solcResult = executeCommand(solcCommand, args);
describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => { expect(solcResult.exitCode).toBe(result.exitCode);
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`]; });
const result = executeCommand(zksolcCommand, args); });
it("Valid command exit code = 0", () => { //id1742:II
expect(result.exitCode).toBe(0); describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
}); const args = [`${paths.pathToBasicSolContract}`, `--combined-json`];
const result = executeCommand(zksolcCommand, args);
it("--combined-json error is presented", () => { it("Valid command exit code = 1", () => {
expect(result.output).toMatch(/(contracts)/i); expect(result.exitCode).toBe(1);
}); });
it("solc exit code == zksolc exit code", () => { it("--combined-json error is presented", () => {
const solcResult = executeCommand(solcCommand, args); expect(result.output).toMatch(
expect(solcResult.exitCode).toBe(result.exitCode); /(requires a value but none was supplied)/i,
}); );
}); });
}
//id1829:I it("solc exit code == zksolc exit code", () => {
for (let i = 0; i < json_args.length; i++) { const solcResult = executeCommand(solcCommand, args);
describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => { expect(solcResult.exitCode).toBe(result.exitCode);
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `--${json_args[i]}`]; });
const result = executeCommand(zksolcCommand, args); });
it("Valid command exit code = 1", () => { //id1742:III
expect(result.exitCode).toBe(1); for (let i = 0; i < json_args.length; i++) {
}); 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("--combined-json error is presented", () => { it("Valid command exit code = 0", () => {
expect(result.output).toMatch(/(Invalid option|error)/i); expect(result.exitCode).toBe(0);
}); });
it("solc exit code == zksolc exit code", () => { it("--combined-json error is presented", () => {
const solcResult = executeCommand(solcCommand, args); expect(result.output).toMatch(/(contracts)/i);
expect(solcResult.exitCode).toBe(result.exitCode); });
});
});
}
//id1829:II it("solc exit code == zksolc exit code", () => {
for (let i = 0; i < json_args.length; i++) { const solcResult = executeCommand(solcCommand, args);
describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => { expect(solcResult.exitCode).toBe(result.exitCode);
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `${json_args[i]}`]; });
const result = executeCommand(zksolcCommand, args); });
}
xit("Valid command exit code = 1", () => { //id1829:I
expect(result.exitCode).toBe(1); for (let i = 0; i < json_args.length; i++) {
}); 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("--combined-json error is presented", () => { it("Valid command exit code = 1", () => {
expect(result.output).toMatch(/(No such file or directory|cannot find the file specified)/i); // Hopefully we should have more precise message here! expect(result.exitCode).toBe(1);
}); });
xit("solc exit code == zksolc exit code", () => { it("--combined-json error is presented", () => {
const solcResult = executeCommand(solcCommand, args); expect(result.output).toMatch(/(Invalid option|error)/i);
expect(solcResult.exitCode).toBe(result.exitCode); });
});
});
}
//id1829:III it("solc exit code == zksolc exit code", () => {
for (let i = 0; i < json_args.length; i++) { const solcResult = executeCommand(solcCommand, args);
describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => { expect(solcResult.exitCode).toBe(result.exitCode);
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", () => { //id1829:II
expect(result.exitCode).toBe(1); for (let i = 0; i < json_args.length; i++) {
}); 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);
it("--combined-json error is presented", () => { xit("Valid command exit code = 1", () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i); expect(result.exitCode).toBe(1);
}); });
it("solc exit code == zksolc exit code", () => { it("--combined-json error is presented", () => {
const solcResult = executeCommand(solcCommand, args); expect(result.output).toMatch(
expect(solcResult.exitCode).toBe(result.exitCode); /(No such file or directory|cannot find the file specified)/i,
}); ); // Hopefully we should have more precise message here!
}); });
}
//id1830 xit("solc exit code == zksolc exit code", () => {
for (let i = 0; i < json_args.length; i++) { const solcResult = executeCommand(solcCommand, args);
describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => { expect(solcResult.exitCode).toBe(result.exitCode);
const args = [`${paths.pathToBasicYulContract}`, `--combined-json`, `${json_args[i]}`]; });
const result = executeCommand(zksolcCommand, args); });
}
it("Valid command exit code = 1", () => { //id1829:III
expect(result.exitCode).toBe(1); for (let i = 0; i < json_args.length; i++) {
}); 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("--combined-json error is presented", () => { it("Valid command exit code = 1", () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i); expect(result.exitCode).toBe(1);
}); });
asd
it("solc exit code == zksolc exit code", () => { it("--combined-json error is presented", () => {
const solcResult = executeCommand(solcCommand, args); expect(result.output).toMatch(/(cannot be used multiple times)/i);
expect(solcResult.exitCode).toBe(result.exitCode); });
});
}); it("solc exit code == zksolc exit code", () => {
} const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1830
for (let i = 0; i < json_args.length; i++) {
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", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i);
});
asd;
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
}); });
-1
View File
@@ -1 +0,0 @@
../../../target/wasm32-unknown-emscripten/release/resolc.js
-1
View File
@@ -1 +0,0 @@
../../../target/wasm32-unknown-emscripten/release/resolc.wasm
-20
View File
@@ -1,20 +0,0 @@
{
"language": "Solidity",
"sources": {
"fixtures/instantiate.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.8.2 <0.9.0;\ncontract ChildContract {\n constructor() {\n }\n}\ncontract MainContract {\n constructor() {\n ChildContract newContract = new ChildContract();\n }\n}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": ["abi"]
}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
+20
View File
@@ -0,0 +1,20 @@
# @parity/revive
Node.js module to compile Solidity contracts to Polkavm bytecode, using [Revive](https://github.com/paritytech/revive)
# Usage from Node.js
```typescript
const sources = {
["contracts/1_Storage.sol"]: {
content: readFileSync("fixtures/storage.sol", "utf8"),
}
const out = await compile(sources);
```
# Usage from shell
```bash
npx @parity/revive@latest --bin contracts/1_Storage.sol
```
+11
View File
@@ -0,0 +1,11 @@
import globals from 'globals'
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ['**/*.{mjs,ts}'] },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
]
+10
View File
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^9999;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract BadPragma {
}
+28
View File
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
+26
View File
@@ -0,0 +1,26 @@
// missing license and pragama
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract MyToken is ERC20, Ownable, ERC20Permit {
constructor(address initialOwner)
ERC20("MyToken", "MTK")
Ownable(initialOwner)
ERC20Permit("MyToken")
{
_mint(msg.sender, 100 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
+42
View File
@@ -0,0 +1,42 @@
{
"name": "@parity/resolc",
"license": "Apache-2.0",
"version": "0.0.0-updated-via-gh-releases",
"author": "Parity <admin@parity.io> (https://parity.io)",
"module": "index.ts",
"types": "./dist/index.d.ts",
"main": "./dist/index.js",
"bin": {
"resolc": "./dist/bin.js"
},
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && cp src/resolc/** dist/resolc",
"test": "npm run build && node ./dist/index.test.js"
},
"devDependencies": {
"@eslint/js": "^9.14.0",
"@openzeppelin/contracts": "5.1.0",
"eslint": "^9.14.0",
"globals": "^15.12.0",
"tar": "^7.4.3",
"typescript": "^5.6.3",
"typescript-eslint": "^8.13.0"
},
"dependencies": {
"@types/node": "^22.9.0",
"commander": "^13.1.0",
"package-json": "^10.0.1",
"solc": "^0.8.29"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env node
import * as commander from 'commander'
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as resolc from '.'
import { SolcInput } from '.'
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
const originalUncaughtExceptionListeners =
process.listeners('uncaughtException')
// FIXME: remove annoying exception catcher of Emscripten
// see https://github.com/chriseth/browser-solidity/issues/167
process.removeAllListeners('uncaughtException')
const program = new commander.Command()
program.name('solcjs')
program.version(resolc.version())
program
.option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.')
.option(
'--base-path <path>',
'Root of the project source tree. ' +
'The import callback will attempt to interpret all import paths as relative to this directory.'
)
.option(
'--include-path <path...>',
'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. ' +
'Can be used multiple times to provide multiple locations.'
)
.option(
'-o, --output-dir <output-directory>',
'Output directory for the contracts.'
)
.option('-p, --pretty-json', 'Pretty-print all JSON output.', false)
.option('-v, --verbose', 'More detailed console output.', false)
.argument('<files...>')
program.parse(process.argv)
const options = program.opts<{
verbose: boolean
abi: boolean
bin: boolean
outputDir?: string
prettyJson: boolean
basePath?: string
includePath?: string[]
}>()
const files: string[] = program.args
const destination = options.outputDir ?? '.'
function abort(msg: string) {
console.error(msg || 'Error occurred')
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
}
return filePath.replace(/\\/g, '/')
}
function makeSourcePathRelativeIfPossible(sourcePath: string) {
const absoluteBasePath = options.basePath
? path.resolve(options.basePath)
: path.resolve('.')
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)
}
})
}
for (const fileName in output.contracts) {
for (const contractName in output.contracts[fileName]) {
let contractFileName = fileName + ':' + contractName
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) {
writeFile(
contractFileName + '.polkavm',
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode
.object,
'hex'
)
)
}
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) => {
console.error('Error:', err)
process.exit(1)
})
+132
View File
@@ -0,0 +1,132 @@
import { test } from 'node:test'
import { readFileSync } from 'node:fs'
import assert from 'node:assert'
import { compile, tryResolveImport } from '.'
import { resolve } from 'node:path'
import { execSync } from 'node:child_process'
function isResolcInPath() {
try {
execSync('resolc --version', { stdio: 'ignore' })
return true
} catch {
return false
}
}
const compileOptions = [{}]
if (isResolcInPath()) {
compileOptions.push({ bin: 'resolc' })
}
for (const options of compileOptions) {
test(`check Ok output with option ${JSON.stringify(options)}`, async () => {
const contract = 'fixtures/token.sol'
const sources = {
[contract]: {
content: readFileSync('fixtures/storage.sol', 'utf8'),
},
}
const out = await compile(sources, options)
assert(out.contracts[contract].Storage.abi != null)
assert(out.contracts[contract].Storage.evm.bytecode != null)
})
}
test('check Err output', async () => {
const sources = {
bad: {
content: readFileSync('fixtures/storage_bad.sol', 'utf8'),
},
}
const out = await compile(sources)
assert(
out?.errors?.[0].message.includes(
'SPDX license identifier not provided in source file'
)
)
assert(
out?.errors?.[1].message.includes(
'Source file does not specify required compiler version'
)
)
})
test('check Err from stderr', async () => {
const sources = {
bad: {
content: readFileSync('fixtures/bad_pragma.sol', 'utf8'),
},
}
try {
await compile(sources)
assert(false, 'Expected error')
} catch (error) {
assert(
String(error).includes(
'Source file requires different compiler version'
)
)
}
})
test('resolve import', () => {
const cases = [
// local
{
file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'),
},
// scopped module with version
{
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
expected: resolve(
'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'
),
},
// scopped module without version
{
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
expected: resolve(
'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'
),
},
// scopped module with wrong version
{
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`,
},
// module without version
{
file: 'acorn/package.json',
expected: resolve('node_modules/acorn/package.json'),
},
// scopped module with version
{
file: 'acorn@8.14.0/package.json',
expected: resolve('node_modules/acorn/package.json'),
},
{
file: 'acorn@8.14.1/package.json',
expected: `Error: Version mismatch: Specified acorn@8.14.1, but installed version is 8.14.0`,
},
]
for (const { file, expected } of cases) {
try {
const resolved = tryResolveImport(file)
assert(
resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}`
)
} catch (error) {
assert(
String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}`
)
}
}
})
+202
View File
@@ -0,0 +1,202 @@
import solc from 'solc'
import { spawn } from 'child_process'
import { resolc, version as resolcVersion } from './resolc'
import path from 'path'
import { existsSync, readFileSync } from 'fs'
export type SolcInput = {
[contractName: string]: {
content: string
}
}
export type SolcError = {
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}
export type SolcOutput = {
contracts: {
[contractPath: string]: {
[contractName: string]: {
abi: Array<{
name: string
inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }>
stateMutability: string
type: string
}>
evm: {
bytecode: { object: string }
}
}
}
}
errors?: Array<SolcError>
}
export function resolveInputs(sources: SolcInput): SolcInput {
const input = {
language: 'Solidity',
sources,
settings: {
outputSelection: {
'*': {
'*': ['evm.bytecode.object'],
},
},
},
}
const out = solc.compile(JSON.stringify(input), {
import: (path: string) => {
return {
contents: readFileSync(tryResolveImport(path), 'utf8'),
}
},
})
const output = JSON.parse(out) as {
sources: { [fileName: string]: { id: number } }
errors: Array<SolcError>
}
if (output.errors && Object.keys(output.sources).length === 0) {
throw new Error(output.errors[0].formattedMessage)
}
return Object.fromEntries(
Object.keys(output.sources).map((fileName) => {
return [
fileName,
sources[fileName] ?? {
content: readFileSync(tryResolveImport(fileName), 'utf8'),
},
]
})
)
}
export function version(): string {
const v = resolcVersion()
return v.split(' ').pop() ?? v
}
export async function compile(
sources: SolcInput,
option: { bin?: string } = {}
): Promise<SolcOutput> {
const input = JSON.stringify({
language: 'Solidity',
sources: resolveInputs(sources),
settings: {
optimizer: { enabled: true, runs: 200 },
outputSelection: {
'*': {
'*': ['abi'],
},
},
},
})
if (option.bin) {
return compileWithBin(input, option.bin)
}
return resolc(input)
}
/**
* Resolve an import path to a file path.
* @param importPath - The import path to resolve.
*/
export function tryResolveImport(importPath: string) {
// resolve local path
if (existsSync(importPath)) {
return path.resolve(importPath)
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/
const match = importPath.match(importRegex)
if (!match) {
throw new Error('Invalid import path format.')
}
const basePackage = match[1] // "foo", "@scope/foo"
const specifiedVersion = match[2] // "1.2.3" (optional)
const relativePath = match[3] // "/path/to/file.sol"
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> {
return new Promise((resolve, reject) => {
const process = spawn(bin, ['--standard-json'])
let output = ''
let error = ''
process.stdin.write(input)
process.stdin.end()
process.stdout.on('data', (data) => {
output += data.toString()
})
process.stderr.on('data', (data) => {
error += data.toString()
})
process.on('close', (code) => {
if (code === 0) {
try {
const result: SolcOutput = JSON.parse(output)
resolve(result)
} catch {
reject(new Error(`Failed to parse output`))
}
} else {
reject(new Error(`Process exited with code ${code}: ${error}`))
}
})
})
}
+30
View File
@@ -0,0 +1,30 @@
import soljson from 'solc/soljson'
import Resolc from './resolc/resolc'
import type { SolcOutput } from '.'
export function resolc(input: string): SolcOutput {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.writeToStdin(input)
m.callMain(['--standard-json'])
const err = m.readFromStderr()
if (err) {
throw new Error(err)
}
return JSON.parse(m.readFromStdout()) as SolcOutput
}
export function version(): string {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson
m.callMain(['--version'])
const err = m.readFromStderr()
if (err) {
throw new Error(err)
}
return m.readFromStdout()
}
View File
+1
View File
@@ -0,0 +1 @@
../../../../target/wasm32-unknown-emscripten/release/resolc.js
+1
View File
@@ -0,0 +1 @@
../../../../target/wasm32-unknown-emscripten/release/resolc.wasm
+95
View File
@@ -0,0 +1,95 @@
declare module 'solc/soljson' {}
declare module 'solc' {
// Basic types for input/output handling
export interface CompileInput {
language: string
sources: {
[fileName: string]: {
content: string
}
}
settings?: {
optimizer?: {
enabled: boolean
runs: number
}
outputSelection: {
[fileName: string]: {
[contractName: string]: string[]
}
}
}
}
export interface CompileOutput {
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]: {
[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
export function compile(
input: string | CompileInput,
options?: {
import: (path: string) =>
| {
contents: string
error?: undefined
}
| {
error: string
contents?: undefined
}
}
): string
}
+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"sourceMap": true,
"declaration": true,
"noFallthroughCasesInSwitch": true,
"allowJs": true,
"moduleResolution": "node",
"module": "commonjs",
"target": "ES2022",
"lib": ["ES2022"],
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"useUnknownInCatchVariables": true,
"types": ["node"],
"typeRoots": ["./node_modules/@types", "./src"],
"paths": {
"#src/*": ["./src/*"]
},
"plugins": []
},
"include": ["src/**/*"]
}
+1213
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,7 +8,7 @@ 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(
__dirname, __dirname,
"../target/wasm32-unknown-emscripten/release", "../../target/wasm32-unknown-emscripten/release",
); );
const RESOLC_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc.js"); const RESOLC_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc.js");
const RESOLC_WEB_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc_web.js"); const RESOLC_WEB_JS = path.join(RESOLC_WASM_TARGET_DIR, "resolc_web.js");
+1
View File
@@ -0,0 +1 @@
../../../../target/wasm32-unknown-emscripten/release/resolc.js
+1
View File
@@ -0,0 +1 @@
../../../../target/wasm32-unknown-emscripten/release/resolc.wasm
+19
View File
@@ -0,0 +1,19 @@
{
"language": "Solidity",
"sources": {
"fixtures/instantiate.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.8.2 <0.9.0;\ncontract ChildContract {\n constructor() {\n }\n}\ncontract MainContract {\n constructor() {\n ChildContract newContract = new ChildContract();\n }\n}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": ["abi"]
}
}
}
}
@@ -72,9 +72,7 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"*": [ "*": ["abi"]
"abi"
]
} }
} }
} }
@@ -65,15 +65,15 @@ describe("Compile Function Tests", function () {
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_tokens.sol"]).to.have.property( expect(
"TokensFactory", output.contracts["fixtures/instantiate_tokens.sol"],
); ).to.have.property("TokensFactory");
expect(output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory).to.have.property( expect(
"abi", output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
); ).to.have.property("abi");
expect(output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory).to.have.property( expect(
"evm", output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
); ).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");
@@ -117,27 +117,26 @@ describe("Compile Function Tests", function () {
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property( expect(output.contracts["fixtures/instantiate.sol"]).to.have.property(
"ChildContract", "ChildContract",
); );
expect(output.contracts["fixtures/instantiate.sol"].ChildContract).to.have.property( expect(
"abi", output.contracts["fixtures/instantiate.sol"].ChildContract,
); ).to.have.property("abi");
expect(output.contracts["fixtures/instantiate.sol"].ChildContract).to.have.property( expect(
"evm", output.contracts["fixtures/instantiate.sol"].ChildContract,
); ).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(output.contracts["fixtures/instantiate.sol"].MainContract).to.have.property( expect(
"abi", output.contracts["fixtures/instantiate.sol"].MainContract,
); ).to.have.property("abi");
expect(output.contracts["fixtures/instantiate.sol"].MainContract).to.have.property( expect(
"evm", output.contracts["fixtures/instantiate.sol"].MainContract,
); ).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");
}); });
}); });
+15 -12
View File
@@ -1,13 +1,16 @@
{ {
"name": "root", "name": "root",
"private": true, "private": true,
"scripts": { "scripts": {
"test:cli": "npm run test -w crates/solidity/src/tests/cli-tests", "test:cli": "npm run test -w crates/solidity/src/tests/cli-tests",
"test:wasm": "npm run test:node -w js", "test:wasm": "npm run test:node -w js/wasm",
"build:package": "npm run build:package -w js" "build:package": "npm run build:package -w js/wasm",
}, "lint": "npx eslint 'js/**/*.{cjs,mjs,ts}' && npx prettier --check '**/*.{ts,json,yml,md}'",
"workspaces": [ "lint:fix": "npx prettier --write '**/*.{mjs,cjs,ts,json,yml,md}'"
"crates/solidity/src/tests/cli-tests", },
"js" "workspaces": [
] "crates/solidity/src/tests/cli-tests",
} "js/wasm",
"js/resolc"
]
}