├── CODE_OF_CONDUCT.md ├── .ci └── package.sh ├── .clippy.toml ├── .cursorignore ├── .devcontainer ├── Dockerfile ├── devcontainer.json ├── install_llvm16.sh └── install_rust.sh ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── enhancement.md │ ├── feature-request.md │ └── general-question.md ├── pull_request_template.md └── workflows │ ├── auto-assign.yml │ ├── commit-lint.yml │ ├── dtvm_sol_dev_docker_release.yml │ ├── dtvm_sol_test.yml │ ├── nightly_release.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LEGAL.md ├── LICENSE ├── README.md ├── build.rs ├── commitlint.config.js ├── coverage.sh ├── dev.makefile ├── docker ├── Dockerfile ├── cargo_config ├── demo_to_download_deps.Cargo.toml ├── docker_build.sh ├── install_llvm16.sh └── install_rust.sh ├── docs ├── COMMIT_CONVENTION.md ├── VERSIONING.md ├── architecture.md ├── cli-reference.md ├── compilation-guide.md ├── contributing.md ├── developer-guide.md ├── release_docs │ └── quick-start.md └── user-guide.md ├── download_deps.sh ├── examples ├── counter │ ├── .gitignore │ ├── build.sh │ └── counter.sol ├── erc20 │ ├── .gitignore │ ├── GLDToken.sol │ ├── build_erc20.sh │ ├── build_gld_token.sh │ ├── package-lock.json │ ├── package.json │ ├── simple_erc20.sol │ ├── test_gld_token.sh │ └── test_simple_token.sh ├── fibonacci │ ├── .gitignore │ ├── build.sh │ ├── fib.sol │ ├── fib_recur.sol │ └── test.sh ├── foundry_erc20 │ ├── .gitignore │ ├── build.sh │ ├── build_forge_test.sh │ ├── foundry.toml │ ├── install_deps.sh │ ├── src │ │ ├── MyToken.sol │ │ └── TokenFactory.sol │ ├── test │ │ └── TestContract.sol │ ├── test_forge_test.sh │ ├── test_my_token.sh │ └── test_token_factory.sh ├── perf_example │ ├── .gitignore │ ├── build.sh │ ├── foundry.toml │ ├── install_deps.sh │ ├── package.sh │ ├── src │ │ ├── GLDToken.sol │ │ ├── MyERC1155.sol │ │ ├── MyERC721.sol │ │ ├── counter.sol │ │ ├── fib_recur.sol │ │ └── test_to_string_store.sol │ ├── test_erc1155.sh │ ├── test_erc721.sh │ ├── test_fib.sh │ ├── test_gldtoken.sh │ └── test_to_string_store.sh ├── scripts │ ├── README.md │ ├── WasmTestVM.sol │ ├── abi_encode.py │ ├── bin_to_hex.py │ ├── common.sh │ ├── requirements.txt │ ├── test_abi_encode.sh │ ├── test_erc20.sh │ ├── test_erc20_by_address.sh │ └── test_yul_erc20.sh └── test_cases │ ├── .gitignore │ ├── build_test_init_code_hash.sh │ ├── test_init_code_hash.sh │ └── test_init_code_hash.sol ├── src ├── main.rs ├── test.rs ├── tests │ ├── arithmetic_tests.rs │ ├── bool_tests.rs │ ├── byte_tests.rs │ ├── chain_context_tests.rs │ ├── create_tests.rs │ ├── function_optimize_tests.rs │ ├── hostapi_tests.rs │ ├── int_cast_tests.rs │ ├── int_constant_tests.rs │ ├── linkersymbol_tests.rs │ ├── mod.rs │ ├── mod_arithmetic_tests.rs │ ├── mstore_tests.rs │ ├── openzepplin_strings_full.sol │ ├── shift_tests.rs │ ├── signed_arithmetic_tests.rs │ ├── solidity_strings.rs │ ├── string_tests.rs │ ├── syntax_tests.rs │ ├── test.rs │ ├── test_helper.rs │ ├── transfer_tests.rs │ ├── tstore_tload_tests.rs │ ├── tuple_tests.rs │ └── var_redefine_tests.rs ├── yul.lalrpop └── yul2ir │ ├── ast.rs │ ├── config.rs │ ├── context.rs │ ├── errors.rs │ ├── function_deduplicator.rs │ ├── infer.rs │ ├── instruction.rs │ ├── linker.cpp │ ├── mod.rs │ ├── stdlib.rs │ ├── transform.rs │ ├── usage.rs │ ├── utils.rs │ ├── var_scope.rs │ ├── wasm.rs │ └── yul_instruction.rs ├── stdlib ├── .gitignore ├── Makefile ├── chain.c ├── chain.h ├── chain_math.c ├── chain_math.h ├── debug.h ├── debug_in_release.c ├── evm_memory.c ├── evm_memory.h ├── hostapi.h ├── stdlib.c ├── stdlib.h ├── utils.c ├── utils.h └── wasm │ └── .gitkeep └── tools ├── build_utils.sh ├── chain_mockcli ├── linux_x86 │ └── chain_mockcli-linux-ubuntu22.04-0.1.0.zip └── mac_arm │ └── chain_mockcli-mac-arm-0.1.0.zip ├── forge_test_utils.sh └── format.sh / CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at {{ email }}. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /.ci/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Check if lib directory exists 5 | if [ ! -d "lib" ]; then 6 | echo "lib directory not found, downloading dependencies..." 7 | ./download_deps.sh 8 | fi 9 | 10 | # Build based on platform 11 | platform=$(uname) 12 | if [ "$platform" = "Linux" ]; then 13 | echo "Building on Linux..." 14 | make -f dev.makefile release 15 | elif [ "$platform" = "Darwin" ]; then 16 | echo "Building on macOS..." 17 | make -f dev.makefile release 18 | else 19 | echo "Unsupported platform: $platform" 20 | exit 1 21 | fi 22 | 23 | git config --global --add safe.directory $(pwd) 24 | 25 | # Get git commit hash and branch name 26 | git_commit=$(git rev-parse --short HEAD) 27 | git_branch=$(git rev-parse --abbrev-ref HEAD) 28 | 29 | # Replace forward slashes with underscores in git_branch 30 | git_branch=$(echo "$git_branch" | tr '/' '_') 31 | 32 | 33 | # Create package directory 34 | package_name="DTVM_SolSDK-${git_commit}-${platform}" 35 | package_dir="/tmp/${package_name}" 36 | mkdir -p "$package_dir" 37 | 38 | # Copy required files to package directory 39 | cp -r target/release/lib "$package_dir" 40 | cp target/release/yul2wasm "$package_dir" 2>/dev/null || echo "Warning: yul2wasm not found" 41 | cp docs/release_docs/* "$package_dir" 2>/dev/null || echo "Warning: release_docs not found" 42 | 43 | # Create tarball in target/release directory 44 | mkdir -p "target/release" 45 | output_file="target/release/${package_name}.tar.gz" 46 | 47 | # Remove existing file if it exists 48 | if [ -f "$output_file" ]; then 49 | echo "Removing existing package file: $output_file" 50 | rm -f "$output_file" 51 | fi 52 | 53 | # Create the compressed package 54 | tar -czf "$output_file" -C /tmp "$package_name" 55 | 56 | cp -f $output_file target/release/DTVM_SolSDK-${platform}-nightly.tar.gz 57 | 58 | echo "Package created: $output_file and target/release/DTVM_SolSDK-${platform}-${git_commit}.tar.gz" 59 | 60 | # Clean up temporary directory 61 | rm -rf "$package_dir" 62 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DTVMStack/DTVM_SolSDK/c5ba420cfb66384ec22fb2435e4033e3d2f08c6c/.clippy.toml -------------------------------------------------------------------------------- /.cursorignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.wat 3 | *.yul 4 | *.out.ll 5 | *.out.s 6 | *.out.hex 7 | /target 8 | /idir 9 | /cov-analysis-* 10 | /.vscode 11 | /.idea 12 | /__MACOSX 13 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VARIANT="16" 2 | FROM mcr.microsoft.com/devcontainers/javascript-node:1-${VARIANT} 3 | 4 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | && apt-get -y install --no-install-recommends build-essential ca-certificates clang-format-15 clang-tidy-15 cmake curl git libzstd-dev ninja-build python3 python3-pip ssh sudo wabt wget zlib1g-dev wget git-lfs zlib1g-dev wget libffi-dev libncurses5-dev libncursesw5-dev libxml2-dev binaryen unzip 6 | 7 | # RUN pip3 install cmake-format lit --no-cache-dir 8 | # RUN cd /usr/bin/ && ln -s python3 python && ln -s clang-format-15 clang-format && ln -s clang-tidy-15 clang-tidy && ln -s run-clang-tidy-15 run-clang-tidy 9 | 10 | RUN mkdir -p /opt 11 | WORKDIR /opt 12 | 13 | COPY install_llvm16.sh /opt/install_llvm16.sh 14 | RUN chmod +x /opt/install_llvm16.sh 15 | 16 | RUN wget https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz && /opt/install_llvm16.sh 17 | 18 | RUN git clone https://github.com/emscripten-core/emsdk.git 19 | WORKDIR /opt/emsdk 20 | RUN ./emsdk install 3.1.69 21 | RUN ./emsdk activate 3.1.69 22 | 23 | RUN curl -sSf https://mirrors.ustc.edu.cn/misc/rustup-install.sh | sh -s -- -y 24 | RUN bash -c ". /root/.cargo/env" 25 | RUN bash -c ". ~/.cargo/env && rustup install 1.81.0 && rustup default 1.81.0" 26 | WORKDIR /home/admin 27 | 28 | WORKDIR /opt 29 | 30 | RUN echo "export PATH=/opt/llvm16/bin:/opt:\$PATH" >> $HOME/.profile 31 | RUN echo "export LLVM_SYS_160_PREFIX=/opt/llvm16" >> $HOME/.profile 32 | ENV PATH=/opt/llvm16/bin:/opt:$PATH 33 | ENV LLVM_SYS_160_PREFIX=/opt/llvm16 34 | 35 | ENV RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static 36 | ENV RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup 37 | COPY install_rust.sh /opt/install_rust.sh 38 | RUN chmod +x /opt/install_rust.sh 39 | RUN /opt/install_rust.sh 40 | 41 | RUN mkdir -p /root/.cargo && touch /root/.cargo/env 42 | RUN echo "source \"$HOME/.cargo/env\"" >> $HOME/.profile 43 | 44 | # install foundry 45 | RUN curl -L https://foundry.paradigm.xyz | bash 46 | ENV PATH=~/.foundry/bin:$PATH 47 | RUN bash -c "source ~/.bashrc && foundryup" 48 | 49 | WORKDIR /opt 50 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | // ... 3 | // "build": { "dockerfile": "Dockerfile" }, 4 | "image": "dtvmdev1/dtvm-sol-dev-x64:main" 5 | // ... 6 | } 7 | -------------------------------------------------------------------------------- /.devcontainer/install_llvm16.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd /opt 4 | ls 5 | tar -xvf clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz 6 | rm -rf clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz 7 | mv clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04 llvm16 8 | -------------------------------------------------------------------------------- /.devcontainer/install_rust.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | curl -sSf https://mirrors.ustc.edu.cn/misc/rustup-install.sh | sh -s -- -y 4 | . "$HOME/.cargo/env" 5 | rustup install 1.81.0 6 | rustup default 1.81.0 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | tools/chain_mockcli/linux_x86/chain_mockcli-linux-ubuntu22.04-0.1.0.zip filter=lfs diff=lfs merge=lfs -text 2 | tools/chain_mockcli/mac_arm/chain_mockcli-mac-arm-0.1.0.zip filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: As a User, I want to report a Bug. 4 | labels: type/bug 5 | --- 6 | 7 | ## Bug Report 8 | 9 | Please answer these questions before submitting your issue. Thanks! 10 | 11 | ### 1. Minimal reproduce step (Required) 12 | 13 | 14 | 15 | ### 2. What did you expect to see? (Required) 16 | 17 | ### 3. What did you see instead (Required) 18 | 19 | ### 4. What is the version of this project you are using? (Required) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Enhancement" 3 | about: As a developer, I want to make an enhancement. 4 | labels: type/enhancement 5 | --- 6 | 7 | ## Enhancement 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: As a user, I want to request a New Feature on the product. 4 | labels: type/feature-request 5 | --- 6 | 7 | ## Feature Request 8 | 9 | **Is your feature request related to a problem? Please describe:** 10 | 11 | 12 | **Describe the feature you'd like:** 13 | 14 | 15 | **Describe alternatives you've considered:** 16 | 17 | 18 | **Teachability, Documentation, Adoption, Migration Strategy:** 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F914 Ask a Question" 3 | about: I want to ask a question. 4 | labels: type/question 5 | --- 6 | 7 | ## General Question 8 | 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 18 | 19 | #### 1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".): 20 | 21 | - [ ] N 22 | - [ ] Y 23 | 24 | 29 | 30 | #### 2. What is the scope of this PR (e.g. component or file name): 31 | 32 | 37 | 38 | #### 3. Provide a description of the PR(e.g. more details, effects, motivations or doc link): 39 | 40 | 41 | - [ ] Affects user behaviors 42 | - [ ] Contains CI/CD configuration changes 43 | - [ ] Contains documentation changes 44 | - [ ] Contains experimental features 45 | - [ ] Performance regression: Consumes more CPU 46 | - [ ] Performance regression: Consumes more Memory 47 | - [ ] Other 48 | 49 | 54 | 55 | #### 4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link): 56 | 57 | - [ ] N 58 | - [ ] Y 59 | 60 | 65 | 66 | #### 5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links: 67 | 68 | 69 | - [ ] Unit test 70 | - [ ] Integration test 71 | - [ ] Benchmark (add benchmark stats below) 72 | - [ ] Manual test (add detailed scripts or steps below) 73 | - [ ] Other 74 | 75 | 82 | 83 | #### 6. Does this PR require a version bump according to [Semantic Versioning](../docs/VERSIONING.md)?(Y/N): 84 | 85 | - [ ] N 86 | - [ ] Y - Patch version (bug fix) 87 | - [ ] Y - Minor version (new feature, backwards compatible) 88 | - [ ] Y - Major version (breaking change, not backwards compatible) 89 | 90 | 91 | 92 | #### 7. Release note 93 | 94 | 95 | 96 | ```release-note 97 | None 98 | ``` -------------------------------------------------------------------------------- /.github/workflows/auto-assign.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign 2 | on: 3 | issues: 4 | types: [opened] 5 | pull_request: 6 | types: [opened] 7 | jobs: 8 | run: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - name: 'Auto-assign issue' 15 | uses: pozil/auto-assign-issue@v1 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | assignees: zoowii 19 | numOfAssignee: 1 20 | -------------------------------------------------------------------------------- /.github/workflows/commit-lint.yml: -------------------------------------------------------------------------------- 1 | name: Commit Lint 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened, edited] 6 | push: 7 | branches: 8 | - '**' 9 | 10 | jobs: 11 | commitlint: 12 | name: Lint Commit Messages 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: '16' 24 | 25 | - name: Install dependencies 26 | run: | 27 | npm install --save-dev @commitlint/cli @commitlint/config-conventional 28 | 29 | - name: Create commitlint config 30 | run: | 31 | cat > commitlint.config.js << 'EOL' 32 | module.exports = { 33 | extends: ['@commitlint/config-conventional'], 34 | rules: { 35 | 'type-enum': [2, 'always', [ 36 | 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore' 37 | ]], 38 | 'scope-enum': [2, 'always', [ 39 | 'core', 'stdlib', 'examples', 'docs', 'tools', 'deps', 'ci', 'test', 'lint', 'other', 40 | ]], 41 | 'body-max-line-length': [1, 'always', 100], 42 | 'subject-case': [0, 'never'], 43 | }, 44 | helpUrl: 'https://github.com/DTVMStack/DTVM_SolSDK/blob/main/docs/COMMIT_CONVENTION.md', 45 | }; 46 | EOL 47 | 48 | - name: Lint commit messages 49 | run: | 50 | if [ "${{ github.event_name }}" = "pull_request" ]; then 51 | # For PR events, check all commits in the PR 52 | npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose 53 | else 54 | # For push events, check only the new commits 55 | npx commitlint --from ${{ github.event.before }} --to ${{ github.event.after }} --verbose 56 | fi 57 | -------------------------------------------------------------------------------- /.github/workflows/dtvm_sol_dev_docker_release.yml: -------------------------------------------------------------------------------- 1 | name: DTVM_SolSDK development Image Release CI 2 | 3 | on: 4 | # push: 5 | workflow_dispatch: 6 | branches: 7 | - main 8 | paths: 9 | - 'docker/**' 10 | permissions: 11 | contents: write 12 | 13 | jobs: 14 | devm-sol-dev-docker-release: 15 | name: Build and release DTVM_SolSDK development docker image on linux 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Check out code 19 | uses: actions/checkout@v3 20 | # Prerequisite 21 | - name: Prepare Dockerfile and docker build dependencies 22 | run: ./docker/docker_build.sh prepare 23 | shell: bash 24 | - name: Login to Docker Hub 25 | uses: docker/login-action@v2 26 | with: 27 | username: ${{ secrets.DOCKER_USERNAME }} 28 | password: ${{ secrets.DOCKER_PASSWORD }} 29 | - name: Extract metadata (tags, labels) for Docker 30 | id: meta 31 | uses: docker/metadata-action@v4 32 | with: 33 | images: dtvmdev1/dtvm-sol-dev-x64 34 | - name: Build and push Docker image 35 | uses: docker/build-push-action@v3 36 | with: 37 | context: docker 38 | push: ${{ github.event_name != 'pull_request' }} 39 | tags: ${{ steps.meta.outputs.tags }} 40 | labels: ${{ steps.meta.outputs.labels }} 41 | -------------------------------------------------------------------------------- /.github/workflows/dtvm_sol_test.yml: -------------------------------------------------------------------------------- 1 | name: DTVM_SolSDK test CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'docs/**' 7 | - "*.md" 8 | - ".gitignore" 9 | pull_request: 10 | paths-ignore: 11 | - 'docs/**' 12 | - "*.md" 13 | - ".gitignore" 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | build_test_on_x86: 19 | name: Build and test DTVM_SolSDK on x86 20 | runs-on: ubuntu-latest 21 | container: 22 | image: dtvmdev1/dtvm-sol-dev-x64:main 23 | steps: 24 | - name: Check out code 25 | uses: actions/checkout@v3 26 | with: 27 | lfs: true 28 | submodules: "true" 29 | - name: Run Tests 30 | run: | 31 | export CUR_PATH=$(pwd) 32 | cd /opt 33 | cp $CUR_PATH/tools/chain_mockcli/linux_x86/chain_mockcli-linux-ubuntu22.04-0.1.0.zip chain_mockcli.zip 34 | unzip chain_mockcli.zip 35 | chmod +x chain_mockcli 36 | 37 | # install rust 38 | /opt/install_rust.sh 39 | 40 | # Install foundry 41 | curl -L https://foundry.paradigm.xyz | bash 42 | export PATH=$HOME/.foundry/bin:$PATH 43 | foundryup 44 | 45 | . "$HOME/.cargo/env" 46 | export PATH=$HOME/.cargo/bin:$PATH 47 | 48 | cd $CUR_PATH 49 | export LLVM_SYS_160_PREFIX=/opt/llvm16 50 | export LLVM_DIR=$LLVM_SYS_160_PREFIX/lib/cmake/llvm 51 | export PATH=$LLVM_SYS_160_PREFIX/bin:$PATH 52 | 53 | ./download_deps.sh 54 | make -f dev.makefile debug 55 | cd examples/scripts 56 | python3 -m pip install -r requirements.txt 57 | ./test_abi_encode.sh 58 | 59 | cd $CUR_PATH 60 | 61 | ./tools/format.sh check 62 | 63 | wget -O /opt/solc https://github.com/ethereum/solidity/releases/download/v0.8.29/solc-static-linux 64 | chmod +x /opt/solc 65 | 66 | export PATH=/opt:$PATH 67 | cd $CUR_PATH 68 | 69 | cargo test -- --nocapture 70 | 71 | cargo build 72 | 73 | echo "testing examples/erc20" 74 | cd $CUR_PATH/examples/erc20 75 | ./build_erc20.sh debug 76 | ./test_simple_token.sh 77 | 78 | echo "testing examples/foundry_erc20" 79 | cd $CUR_PATH/examples/foundry_erc20 80 | ./build.sh debug 81 | ./test_my_token.sh 82 | ./test_token_factory.sh 83 | ./build_forge_test.sh debug 84 | ./test_forge_test.sh 85 | 86 | echo "testing examples/perf_example" 87 | cd $CUR_PATH/examples/perf_example 88 | ./build.sh debug 89 | ./test_gldtoken.sh 90 | ./test_erc721.sh 91 | ./test_erc1155.sh 92 | ./test_fib.sh 93 | ./test_to_string_store.sh 94 | ./package.sh 95 | 96 | echo "testing examples/test_cases" 97 | cd $CUR_PATH/examples/test_cases 98 | ./build_test_init_code_hash.sh debug 99 | ./test_init_code_hash.sh 100 | 101 | cd $CUR_PATH 102 | cd stdlib 103 | make clean 104 | make release 105 | cd .. 106 | cargo build --release 107 | -------------------------------------------------------------------------------- /.github/workflows/nightly_release.yml: -------------------------------------------------------------------------------- 1 | name: DTVM_SolSDK Nightly Release 2 | 3 | on: 4 | workflow_dispatch: 5 | paths: 6 | - '**' 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | build_and_package: # Renamed job for clarity 12 | name: Build and Package DTVM_SolSDK Nightly # Updated name 13 | runs-on: ubuntu-latest 14 | container: 15 | image: dtvmdev1/dtvm-sol-dev-x64:main 16 | steps: 17 | - name: Check out code 18 | uses: actions/checkout@v4 # Use v4 19 | with: 20 | lfs: true 21 | submodules: "true" 22 | - name: Build the Release 23 | working-directory: . 24 | run: | 25 | export CUR_PATH=$(pwd) 26 | # install rust 27 | /opt/install_rust.sh 28 | 29 | . "$HOME/.cargo/env" 30 | export PATH=$HOME/.cargo/bin:$PATH 31 | 32 | export LLVM_SYS_160_PREFIX=/opt/llvm16 33 | export LLVM_DIR=$LLVM_SYS_160_PREFIX/lib/cmake/llvm 34 | export PATH=$LLVM_SYS_160_PREFIX/bin:$PATH 35 | 36 | ./download_deps.sh 37 | .ci/package.sh 38 | # Assuming package.sh creates DTVM_SolSDK-*-nightly.tar.gz in target/release 39 | ls target/release 40 | - name: Upload Nightly Artifact # Renamed step 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: DTVM_SolSDK-nightly-latest # Updated artifact name 44 | path: target/release/DTVM_SolSDK-*-nightly.tar.gz # Updated path assuming this name 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: DTVM_SolSDK Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | build_release_and_upload: 13 | name: Build, Create Release, and Upload Artifact 14 | runs-on: ubuntu-latest 15 | container: 16 | image: dtvmdev1/dtvm-sol-dev-x64:main 17 | steps: 18 | - name: Check out code 19 | uses: actions/checkout@v4 20 | with: 21 | lfs: true 22 | submodules: "true" 23 | - name: Build the Release 24 | working-directory: . 25 | run: | 26 | export CUR_PATH=$(pwd) 27 | # install rust 28 | /opt/install_rust.sh 29 | 30 | . "$HOME/.cargo/env" 31 | export PATH=$HOME/.cargo/bin:$PATH 32 | 33 | export LLVM_SYS_160_PREFIX=/opt/llvm16 34 | export LLVM_DIR=$LLVM_SYS_160_PREFIX/lib/cmake/llvm 35 | export PATH=$LLVM_SYS_160_PREFIX/bin:$PATH 36 | 37 | ./download_deps.sh 38 | .ci/package.sh 39 | # Assuming package.sh creates DTVM_SolSDK-*-nightly.tar.gz in target/release 40 | ls target/release 41 | - name: Prepare Release Artifact 42 | id: prepare_artifact 43 | working-directory: target/release 44 | run: | 45 | RELEASE_VERSION=${{ github.ref_name }} 46 | ARTIFACT_NAME="DTVM_SolSDK-${RELEASE_VERSION}-ubuntu22.04.tar.gz" 47 | # Assuming the script generates a file like DTVM_SolSDK.nightly.latest.tar.gz or similar 48 | # If the script generates a versioned file directly, adjust this mv command 49 | GENERATED_ARTIFACT=$(ls DTVM_SolSDK-*-nightly.tar.gz | head -n 1) 50 | echo "Found artifact: $GENERATED_ARTIFACT" 51 | mv "${GENERATED_ARTIFACT}" "${ARTIFACT_NAME}" 52 | echo "artifact_path=$(pwd)/${ARTIFACT_NAME}" >> $GITHUB_OUTPUT 53 | echo "artifact_name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT 54 | 55 | - name: Create GitHub Release 56 | uses: softprops/action-gh-release@v2 57 | with: 58 | files: ${{ steps.prepare_artifact.outputs.artifact_path }} 59 | tag_name: ${{ github.ref_name }} 60 | name: Release ${{ github.ref_name }} 61 | body: "Release for DTVM_SolSDK version ${{ github.ref_name }}" 62 | prerelease: true 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /*.iml 3 | /*.idea 4 | /logs 5 | /*.out.ll 6 | /*.out.s 7 | /*.out.wasm 8 | /*.out.cbin 9 | /*.out.cbin.hex 10 | /*.out.wat 11 | /*.wasm 12 | /*.wat 13 | /SimpleToken.yul 14 | /my_erc20.wasm 15 | /test.db 16 | .DS_Store 17 | /.vscode 18 | 19 | /testsuites 20 | 21 | # scan tmp 22 | /cov-analysis-* 23 | /tmp_build 24 | /idir 25 | /__MACOSX 26 | 27 | /lib 28 | /venv 29 | /node_modules 30 | 31 | # coverage 32 | *.profraw 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples/foundry_erc20/lib/forge-std"] 2 | path = examples/foundry_erc20/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "examples/foundry_erc20/lib/openzeppelin-contracts"] 5 | path = examples/foundry_erc20/lib/openzeppelin-contracts 6 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 7 | [submodule "examples/foundry_erc20/lib/openzeppelin-contracts-upgradeable"] 8 | path = examples/foundry_erc20/lib/openzeppelin-contracts-upgradeable 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 10 | [submodule "examples/perf_example/lib/forge-std"] 11 | path = examples/perf_example/lib/forge-std 12 | url = https://github.com/foundry-rs/forge-std 13 | [submodule "examples/perf_example/lib/openzeppelin-contracts"] 14 | path = examples/perf_example/lib/openzeppelin-contracts 15 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 16 | [submodule "examples/perf_example/lib/openzeppelin-contracts-upgradeable"] 17 | path = examples/perf_example/lib/openzeppelin-contracts-upgradeable 18 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to DTVM_SolSDK will be documented in this file. 4 | 5 | ## [0.1.0] - 2025-04-22 6 | 7 | ### Added 8 | 9 | - Initial release of the Solidity Yul to WebAssembly compiler 10 | - Complete support for all syntax and instructions in Solidity yul 11 | - Foundry build system integration 12 | - Support for Ethereum ABI compatibility 13 | - Example contracts showcasing capabilities: 14 | - ERC20 token implementation 15 | - Counter contract 16 | - Fibonacci calculator 17 | - Foundry-based ERC20 token 18 | - Comprehensive test suite for validation 19 | - Command-line interface with multiple options for compilation control 20 | - Documentation including user guide, architecture overview, and CLI reference 21 | 22 | ### Changed 23 | 24 | ### Fixed 25 | 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yul2wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | lalrpop = "0.22.0" 8 | cc = "1.0" 9 | 10 | [dependencies] 11 | inkwell = { git = "https://github.com/TheDan64/inkwell", rev = "9f64611f4bf0151370093ac40361fadc08fb2c21", features = [ 12 | "target-webassembly", 13 | "llvm16-0-force-static", 14 | ] } 15 | num-bigint = "0.4.6" 16 | num-integer = "0.1.46" 17 | num-traits = "0.2.19" 18 | num-derive = "0.4.2" 19 | regex = "1.11.1" 20 | indexmap = "2.7.0" 21 | byteorder = "1.5.0" 22 | keccak-hash = "0.11.0" 23 | libsecp256k1 = "0.7.1" 24 | libc = "0.2.167" 25 | hex = "0.4.3" 26 | lalrpop-util = { version = "0.22.0", features = ["lexer", "unicode"] } 27 | clap = { version = "4.5.27", features = ["derive"] } 28 | once_cell = "1.20.2" 29 | tempfile = "3.16.0" 30 | parity-wasm = "0.45.0" 31 | rand = "0.9.0" 32 | home = "0.5.11" 33 | ethabi = "18.0.0" 34 | ethereum-types = "0.14.1" 35 | tokio = "1.44.2" 36 | wizer = "8.0.0" 37 | 38 | [features] 39 | default = ["inkwell/llvm16-0-force-static"] 40 | release = [] 41 | -------------------------------------------------------------------------------- /LEGAL.md: -------------------------------------------------------------------------------- 1 | Legal Disclaimer 2 | 3 | Within this source code, the comments in Chinese shall be the original, governing version. Any comment in other languages are for reference only. In the event of any conflict between the Chinese language version comments and other language version comments, the Chinese language version shall prevail. 4 | 5 | 法律免责声明 6 | 7 | 关于代码注释部分,中文注释为官方版本,其它语言注释仅做参考。中文注释可能与其它语言注释存在不一致,当中文注释与其它语言注释存在不一致时,请以中文注释为准。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DTVM_SolSDK - Solidity to WebAssembly Compiler 2 | 3 | ![GitHub license](https://img.shields.io/badge/license-Apache--2.0-blue.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/DTVMStack/DTVM_SolSDK?style=social) 5 | ![Solidity](https://img.shields.io/badge/solidity-0.8-blue.svg) 6 | 7 | DTVM_SolSDK is an open-source compiler that enables Ethereum Solidity smart contracts to run on WebAssembly (Wasm) based blockchains. The compiler translates Solidity's Yul intermediate representation into optimized WebAssembly bytecode, maintaining compatibility with the Ethereum smart contract model. 8 | 9 | Please visit the [project documentation](docs/user-guide.md) for more details. 10 | 11 | ## Background 12 | 13 | In the blockchain ecosystem, WebAssembly (Wasm) has emerged as a fast, efficient, and portable execution environment. However, the vast majority of smart contracts are written in Solidity for the Ethereum Virtual Machine (EVM). DTVM_SolSDK bridges this gap by allowing developers to compile Solidity contracts to WebAssembly, enabling them to run on next-generation Wasm-based blockchain platforms while maintaining the familiar Ethereum development experience. 14 | 15 | DTVM_SolSDK works by accepting Solidity's Yul intermediate representation (IR) and compiling it to optimized WebAssembly bytecode, ensuring compatibility with the Ethereum smart contract model and preserving all the original contract logic and behavior. 16 | 17 | ## Features 18 | 19 | - Complete Solidity language support (version 0.8) 20 | - LLVM-based optimization pipeline 21 | - Ethereum ABI compatibility 22 | - Support for complex Solidity patterns including DeFi protocols 23 | - Multiple example contracts demonstrating capabilities 24 | 25 | ## Quick Start 26 | 27 | ### Dependencies 28 | 29 | * Solidity Compiler 0.8.29/(0.8.25+) 30 | * LLVM 16 31 | * Rust 1.81 or later 32 | * Binaryen (`brew install binaryen` on macOS, `apt install -y binaryen` on Ubuntu) 33 | 34 | ### Building the Project 35 | 36 | ```sh 37 | cargo build --release 38 | ``` 39 | 40 | ### Basic Usage 41 | 42 | ```sh 43 | # Step 1: Compile Solidity to Yul IR 44 | solc --ir --optimize-yul -o . --overwrite your_contract.sol 45 | 46 | # Step 2: Compile Yul IR to WebAssembly 47 | yul2wasm --input ContractName.yul --output your_contract.wasm 48 | ``` 49 | 50 | For complete usage instructions, see the [User Guide](docs/user-guide.md). 51 | 52 | ## Examples 53 | 54 | The `examples/` directory contains various Solidity contracts and compilation scripts: 55 | 56 | - [ERC20 Token](examples/erc20/) - Basic ERC20 token implementation 57 | - [NFT Implementation](examples/nft/) - NFT (ERC721) implementation 58 | - [Foundry ERC20](examples/foundry_erc20/) - ERC20 token using the Foundry framework 59 | - [Counter](examples/counter/) - Simple counter contract 60 | - [Fibonacci](examples/fibonacci/) - Fibonacci sequence calculator 61 | 62 | ### Compiling an Example 63 | 64 | ```sh 65 | cd examples/erc20 66 | ./build_erc20.sh 67 | ``` 68 | 69 | This script will: 70 | - Compile the `simple_erc20.sol` Solidity file to Yul IR 71 | - Use DTVM_SolSDK to compile the Yul file to WebAssembly 72 | - Convert the Wasm file to human-readable WebAssembly text format (WAT) 73 | 74 | ### Testing an Example 75 | 76 | ```sh 77 | cd examples/erc20 78 | ./test_simple_token.sh 79 | ``` 80 | 81 | ## Documentation 82 | 83 | - [User Guide](docs/user-guide.md) - Instructions for using DTVM_SolSDK 84 | - [Developer Guide](docs/developer-guide.md) - Information for developers contributing to the project 85 | 86 | ## Project Status 87 | 88 | DTVM_SolSDK is actively developed and supports most Solidity language features. The compiler has been tested with various real-world smart contracts including DeFi protocols like Uniswap. 89 | 90 | ## Community 91 | 92 | * [GitHub Issues](https://github.com/DTVMStack/DTVM_SolSDK/issues) - Bug reports and feature requests 93 | * [Pull Requests](https://github.com/DTVMStack/DTVM_SolSDK/pulls) - Contribute to the project 94 | 95 | ## Contributing 96 | 97 | Contributions are welcome! Please see our [Developer Guide](docs/developer-guide.md) for details on how to contribute. 98 | 99 | 1. Fork the repository 100 | 2. Create a feature branch (`git checkout -b feature-branch`) 101 | 3. Commit your changes (`git commit -am 'Add new feature'`) 102 | 4. Push to the branch (`git push origin feature-branch`) 103 | 5. Create a new Pull Request 104 | 105 | Please adhere to our [Commit Convention](docs/COMMIT_CONVENTION.md) when making commits. All PRs are automatically validated against this standard. 106 | 107 | ## Versioning 108 | 109 | This project follows [Semantic Versioning](https://semver.org/). For details on our version numbering scheme and release process, please see our [Versioning Guide](docs/VERSIONING.md). 110 | 111 | ## License 112 | 113 | This project is licensed under the Apache-2.0 License. 114 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // Copyright (c) The Smart Intermediate Representation Contributors 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | extern crate lalrpop; 6 | 7 | use std::path::Path; 8 | use std::{fs, io}; 9 | 10 | /// Copy all files in a folder from `src` to `dst`. 11 | fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { 12 | fs::create_dir_all(&dst)?; 13 | for entry in fs::read_dir(src)? { 14 | let entry = entry?; 15 | let ty = entry.file_type()?; 16 | if ty.is_dir() { 17 | copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; 18 | } else { 19 | fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; 20 | } 21 | } 22 | Ok(()) 23 | } 24 | 25 | /// Copy all std lib deps 26 | pub fn copy_libs() -> io::Result<()> { 27 | // Copy static libs to the target folder. 28 | copy_dir_all("./lib", "./target/lib")?; 29 | copy_dir_all("./lib", "./target/debug/lib")?; 30 | copy_dir_all("./lib", "./target/release/lib")?; 31 | copy_dir_all("./lib", "./target/llvm-cov-target/release/lib")?; 32 | Ok(()) 33 | } 34 | 35 | fn main() { 36 | use std::process::Command; 37 | Command::new("make").status().unwrap(); 38 | 39 | copy_libs().unwrap(); 40 | 41 | let output = Command::new("git") 42 | .args(["describe", "--tags"]) 43 | .output() 44 | .unwrap(); 45 | let git_hash = String::from_utf8(output.stdout).unwrap(); 46 | println!("cargo:rustc-env=GIT_HASH={git_hash}"); 47 | 48 | // Static link LLVM libs 49 | #[cfg(feature = "release")] 50 | static_link_llvm(); 51 | 52 | lalrpop::process_root().unwrap(); 53 | } 54 | 55 | #[allow(dead_code)] 56 | fn static_link_llvm() { 57 | use std::process::Command; 58 | 59 | println!("Use Static Link"); 60 | // compile our linker 61 | let cxxflags = Command::new("llvm-config") 62 | .args(["--cxxflags"]) 63 | .output() 64 | .expect("could not execute llvm-config"); 65 | 66 | let cxxflags = String::from_utf8(cxxflags.stdout).unwrap(); 67 | 68 | let mut build = cc::Build::new(); 69 | 70 | build.file("src/yul2ir/linker.cpp").cpp(true); 71 | 72 | if !cfg!(target_os = "windows") { 73 | build.flag("-Wno-unused-parameter"); 74 | } 75 | 76 | for flag in cxxflags.split_whitespace() { 77 | build.flag(flag); 78 | } 79 | 80 | build.compile("liblinker.a"); 81 | 82 | // add the llvm linker 83 | let libdir = Command::new("llvm-config") 84 | .args(["--libdir"]) 85 | .output() 86 | .unwrap(); 87 | let libdir = String::from_utf8(libdir.stdout).unwrap(); 88 | 89 | println!("cargo:libdir={libdir}"); 90 | for lib in &["lldELF", "lldCommon", "lldWasm"] { 91 | // "lldCore", "lldDriver", in llvm-12 92 | println!("cargo:rustc-link-lib=static={lib}"); 93 | } 94 | 95 | // Add all the symbols were not using, needed by Windows and debug builds 96 | for lib in &["lldMachO", "lldMinGW", "lldCOFF"] { 97 | // "lldReaderWriter", "lldYAML", in llvm-12 98 | println!("cargo:rustc-link-lib=static={lib}"); 99 | } 100 | 101 | // static link lldCommon must be added the last, in order to static link in linux 102 | { 103 | let lib = &"lldCommon"; 104 | println!("cargo:rustc-link-lib=static={lib}"); 105 | } 106 | 107 | // Make sure we have an 8MiB stack on Windows. Windows defaults to a 1MB 108 | // stack, which is not big enough for debug builds 109 | #[cfg(windows)] 110 | println!("cargo:rustc-link-arg=/STACK:8388608"); 111 | } 112 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: 'conventional-changelog-conventionalcommits', 3 | rules: { 4 | 'body-leading-blank': [1, 'always'], 5 | 'body-max-line-length': [2, 'always', 100], 6 | 'footer-leading-blank': [1, 'always'], 7 | 'footer-max-line-length': [2, 'always', 100], 8 | 'header-max-length': [2, 'always', 100], 9 | 'header-trim': [2, 'always'], 10 | 'subject-case': [0, 'never'], 11 | 'subject-empty': [2, 'never'], 12 | 'subject-full-stop': [2, 'never', '.'], 13 | 'type-case': [2, 'always', 'lower-case'], 14 | 'type-empty': [2, 'never'], 15 | 'type-enum': [ 16 | 2, 17 | 'always', 18 | [ 19 | 'feat', 20 | 'fix', 21 | 'docs', 22 | 'style', 23 | 'refactor', 24 | 'perf', 25 | 'test', 26 | 'build', 27 | 'ci', 28 | 'chore' 29 | ] 30 | ], 31 | 'scope-enum': [ 32 | 2, 33 | 'always', 34 | [ 35 | 'core', 36 | 'stdlib', 37 | 'examples', 38 | 'docs', 39 | 'tools', 40 | 'deps', 41 | 'ci', 42 | 'test', 43 | 'other', 44 | '' 45 | ] 46 | ] 47 | }, 48 | prompt: { 49 | questions: { 50 | type: { 51 | description: 'Select the type of change that you\'re committing', 52 | enum: { 53 | feat: { 54 | description: 'A new feature', 55 | title: 'Features', 56 | emoji: '✨', 57 | }, 58 | fix: { 59 | description: 'A bug fix', 60 | title: 'Bug Fixes', 61 | emoji: '🐛', 62 | }, 63 | docs: { 64 | description: 'Documentation only changes', 65 | title: 'Documentation', 66 | emoji: '📚', 67 | }, 68 | style: { 69 | description: 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', 70 | title: 'Styles', 71 | emoji: '💎', 72 | }, 73 | refactor: { 74 | description: 'A code change that neither fixes a bug nor adds a feature', 75 | title: 'Code Refactoring', 76 | emoji: '📦', 77 | }, 78 | perf: { 79 | description: 'A code change that improves performance', 80 | title: 'Performance Improvements', 81 | emoji: '🚀', 82 | }, 83 | test: { 84 | description: 'Adding missing tests or correcting existing tests', 85 | title: 'Tests', 86 | emoji: '🚨', 87 | }, 88 | build: { 89 | description: 'Changes that affect the build system or external dependencies (example: cargo, github actions)', 90 | title: 'Builds', 91 | emoji: '🛠', 92 | }, 93 | ci: { 94 | description: 'Changes to CI configuration files and scripts', 95 | title: 'Continuous Integrations', 96 | emoji: '⚙️', 97 | }, 98 | chore: { 99 | description: 'Other changes that don\'t modify src or test files', 100 | title: 'Chores', 101 | emoji: '♻️', 102 | } 103 | } 104 | }, 105 | scope: { 106 | description: 'What is the scope of this change (e.g. core, stdlib, examples)', 107 | enum: { 108 | core: { 109 | description: 'Core library code', 110 | }, 111 | stdlib: { 112 | description: 'Standard library', 113 | }, 114 | examples: { 115 | description: 'Example code', 116 | }, 117 | docs: { 118 | description: 'Documentation related', 119 | }, 120 | tools: { 121 | description: 'Tool related', 122 | }, 123 | deps: { 124 | description: 'Dependency related', 125 | }, 126 | ci: { 127 | description: 'CI related', 128 | }, 129 | test: { 130 | description: 'Test related', 131 | }, 132 | other: { 133 | description: 'Other changes', 134 | } 135 | } 136 | }, 137 | subject: { 138 | description: 'Write a short, imperative tense description of the change' 139 | }, 140 | body: { 141 | description: 'Provide a longer description of the change' 142 | }, 143 | isBreaking: { 144 | description: 'Are there any breaking changes?' 145 | }, 146 | breaking: { 147 | description: 'Describe the breaking changes' 148 | }, 149 | isIssueAffected: { 150 | description: 'Does this change affect any open issues?' 151 | }, 152 | issues: { 153 | description: 'Add issue references (e.g. "Closes #123, #456")' 154 | } 155 | } 156 | } 157 | }; 158 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Prerequisites: 5 | # rustup component add llvm-tools-preview 6 | # cargo install rustfilt 7 | # cargo install grcov@0.5.1 8 | # or download grcov from https://github.com/mozilla/grcov/releases 9 | 10 | export RUSTC_BOOTSTRAP=1 11 | 12 | # Create the directory for coverage reports 13 | mkdir -p target/coverage 14 | 15 | # Clean any previous coverage data 16 | rm -rf target/coverage/* 17 | 18 | # Clean previous build artifacts to ensure coverage instrumentation is applied 19 | cargo clean 20 | rm -f *.profraw 21 | 22 | export RUSTFLAGS="-Cinstrument-coverage" 23 | 24 | # Build the project with coverage instrumentation 25 | 26 | cargo build --verbose 27 | 28 | # Set environment variables for coverage 29 | export CARGO_INCREMENTAL=0 30 | export RUSTFLAGS="-C instrument-coverage -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" 31 | export RUSTDOCFLAGS="-Cpanic=abort" 32 | export LLVM_PROFILE_FILE="target/coverage/coverage-%p-%m.profraw" 33 | 34 | # Run tests with coverage instrumentation 35 | cargo test --no-fail-fast --verbose 36 | 37 | # List all the profraw files to verify they were created 38 | echo "Generated profraw files:" 39 | # ls -la target/coverage/ 40 | 41 | # Generate coverage report 42 | # target/coverage 43 | grcov . --binary-path ./target/debug -s . -t html --branch --ignore-not-existing --ignore "/*" --ignore "target/*" --ignore "tests/*" --ignore "examples/*" -o target/coverage/html 44 | 45 | # Generate a summary report 46 | grcov . --binary-path ./target/debug -s . -t lcov --branch --ignore-not-existing --ignore "/*" --ignore "target/*" --ignore "tests/*" --ignore "examples/*" -o target/coverage/lcov.info 47 | 48 | # Print a message with the location of the coverage report 49 | echo "Coverage report generated at target/coverage/html/index.html" 50 | 51 | ls -la *.profraw 52 | rm -rf *.profraw 53 | 54 | # Start a local web server to view the coverage report 55 | cd target/coverage/html && python3 -m http.server 8080 56 | # open http://localhost:8080/src/yul2ir/index.html 57 | -------------------------------------------------------------------------------- /dev.makefile: -------------------------------------------------------------------------------- 1 | # if use makefile, the wizer lib will not work well 2 | # Default target 3 | all: fmt_check test 4 | 5 | # Format code using format.sh script 6 | fmt: 7 | ./tools/format.sh format 8 | 9 | # Check code format using format.sh script 10 | fmt_check: 11 | ./tools/format.sh check 12 | 13 | # Run unit tests 14 | test: 15 | cargo test -- --nocapture 16 | 17 | # Build release version 18 | release: 19 | cd stdlib && make release 20 | cargo build --release --features release 21 | 22 | # Build debug version 23 | debug: 24 | cd stdlib && make debug 25 | cargo build 26 | 27 | # Clean build artifacts 28 | clean: 29 | cargo clean 30 | cd stdlib && make clean 31 | 32 | .PHONY: all fmt fmt_check test release debug clean 33 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | RUN sed -i s/archive.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list 3 | RUN sed -i s/security.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list 4 | RUN apt update -y && apt install -y --no-install-recommends build-essential ca-certificates clang-format-15 clang-tidy-15 cmake curl git libzstd-dev ninja-build python3 python3-pip ssh sudo wabt wget zlib1g-dev wget git-lfs zlib1g-dev wget libffi-dev libncurses5-dev libncursesw5-dev libxml2-dev binaryen unzip && rm -rf /var/lib/apt/lists/* 5 | RUN pip3 install cmake-format lit --no-cache-dir 6 | RUN cd /usr/bin/ && ln -s python3 python && ln -s clang-format-15 clang-format && ln -s clang-tidy-15 clang-tidy && ln -s run-clang-tidy-15 run-clang-tidy 7 | RUN useradd -m -u 500 -U -G sudo -s /bin/bash admin && passwd -d admin 8 | RUN mkdir -p /opt 9 | WORKDIR /opt 10 | 11 | COPY install_llvm16.sh /opt/install_llvm16.sh 12 | RUN chmod +x /opt/install_llvm16.sh 13 | # COPY install_llvm15.sh /opt/install_llvm15.sh 14 | # RUN chmod +x /opt/install_llvm15.sh 15 | 16 | RUN wget https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz && /opt/install_llvm16.sh 17 | # RUN wget https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.0/clang+llvm-15.0.0-x86_64-linux-gnu-rhel-8.4.tar.xz 18 | 19 | RUN git clone https://github.com/emscripten-core/emsdk.git 20 | WORKDIR /opt/emsdk 21 | RUN ./emsdk install 3.1.69 22 | RUN ./emsdk activate 3.1.69 23 | USER root 24 | RUN curl -sSf https://mirrors.ustc.edu.cn/misc/rustup-install.sh | sh -s -- -y 25 | RUN bash -c ". /root/.cargo/env" 26 | COPY cargo_config /root/.cargo/config 27 | RUN bash -c ". ~/.cargo/env && rustup install 1.81.0 && rustup default 1.81.0" 28 | WORKDIR /home/admin 29 | USER root 30 | WORKDIR /opt 31 | 32 | RUN echo "export PATH=/opt/llvm16/bin:/opt:\$PATH" >> ~/.bash_profile 33 | RUN echo "export LLVM_SYS_160_PREFIX=/opt/llvm16" >> ~/.bash_profile 34 | ENV PATH=/opt/llvm16/bin:/opt:$PATH 35 | ENV LLVM_SYS_160_PREFIX=/opt/llvm16 36 | 37 | ENV RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static 38 | ENV RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup 39 | COPY install_rust.sh /opt/install_rust.sh 40 | RUN chmod +x /opt/install_rust.sh 41 | 42 | RUN mkdir -p /root/.cargo && touch /root/.cargo/env 43 | 44 | # cache cargo dependencies 45 | RUN mkdir /tmp/cargo_dep 46 | WORKDIR /tmp/cargo_dep 47 | 48 | COPY demo_to_download_deps.Cargo.toml /tmp/cargo_dep/Cargo.toml 49 | RUN bash -c "export PATH=$HOME/.cargo/bin:$PATH && cargo update" 50 | 51 | # install foundry 52 | RUN curl -L https://foundry.paradigm.xyz | bash 53 | ENV PATH=~/.foundry/bin:$PATH 54 | RUN bash -c "source ~/.bashrc && foundryup" 55 | 56 | WORKDIR /opt 57 | -------------------------------------------------------------------------------- /docker/cargo_config: -------------------------------------------------------------------------------- 1 | [source.crates-io] 2 | replace-with = 'ustc' 3 | [source.ustc] 4 | registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/" 5 | -------------------------------------------------------------------------------- /docker/demo_to_download_deps.Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "demo" 3 | version = "0.10.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | cc = "1.0" 8 | lalrpop = "0.19.9" 9 | vergen = "7.4.3" 10 | 11 | [dependencies] 12 | inkwell = { git = "https://github.com/TheDan64/inkwell", rev = "9f64611f4bf0151370093ac40361fadc08fb2c21", features = [ 13 | "target-webassembly", 14 | "llvm16-0-force-static", 15 | ] } 16 | num-bigint = "0.4.6" 17 | num-integer = "0.1.46" 18 | num-traits = "0.2.19" 19 | num-derive = "0.4.2" 20 | regex = "1.11.1" 21 | indexmap = "2.7.0" 22 | byteorder = "1.5.0" 23 | keccak-hash = "0.11.0" 24 | libsecp256k1 = "0.7.1" 25 | libc = "0.2.167" 26 | hex = "0.4.3" 27 | lalrpop-util = { version = "0.22.0", features = ["lexer", "unicode"] } 28 | clap = { version = "4.5.27", features = ["derive"] } 29 | once_cell = "1.20.2" 30 | tempfile = "3.16.0" 31 | parity-wasm = "0.45.0" 32 | rand = "0.9.0" 33 | home = "0.5.11" 34 | ethabi = "18.0.0" 35 | ethereum-types = "0.14.1" 36 | wizer = "8.0.0" 37 | 38 | [dev-dependencies] 39 | 40 | 41 | [[bin]] 42 | name = "demo" 43 | path = "src/bin/main.rs" 44 | 45 | [profile.release] 46 | rpath = true 47 | lto = true 48 | 49 | [features] 50 | default = ["inkwell/llvm16-0-force-static"] 51 | release = [] 52 | -------------------------------------------------------------------------------- /docker/docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | if [ "$1" = "prepare" ]; then 5 | echo "done prepare docker build context" 6 | ls 7 | else 8 | cd docker 9 | docker build . --platform linux/x86_64 -f Dockerfile -t dtvmdev1/dtvm-sol-dev-x64:latest 10 | cd .. 11 | fi 12 | -------------------------------------------------------------------------------- /docker/install_llvm16.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd /opt 4 | ls 5 | tar -xvf clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz 6 | rm -rf clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz 7 | mv clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04 llvm16 8 | -------------------------------------------------------------------------------- /docker/install_rust.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | curl -sSf https://mirrors.ustc.edu.cn/misc/rustup-install.sh | sh -s -- -y 4 | . "$HOME/.cargo/env" 5 | rustup install 1.81.0 6 | rustup default 1.81.0 7 | -------------------------------------------------------------------------------- /docs/COMMIT_CONVENTION.md: -------------------------------------------------------------------------------- 1 | # Commit Convention 2 | 3 | To maintain consistency and readability in the codebase, all commits to this project should follow the convention below. These conventions are based on [Conventional Commits](https://www.conventionalcommits.org/). 4 | 5 | ## Commit Format 6 | 7 | ``` 8 | [optional scope]: 9 | 10 | [optional body] 11 | 12 | [optional footer] 13 | ``` 14 | 15 | ### Type 16 | 17 | Must be one of the following: 18 | 19 | - **feat**: A new feature 20 | - **fix**: A bug fix 21 | - **docs**: Documentation only changes 22 | - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 23 | - **refactor**: A code change that neither fixes a bug nor adds a feature 24 | - **perf**: A code change that improves performance 25 | - **test**: Adding missing or correcting existing tests 26 | - **build**: Changes that affect the build system or external dependencies (example: cargo, github actions) 27 | - **ci**: Changes to CI configuration files and scripts 28 | - **chore**: Other changes that don't modify src or test files 29 | 30 | ### Scope 31 | 32 | The scope should specify the area of the change, for example: 33 | 34 | - **core**: Core library code 35 | - **stdlib**: Standard library 36 | - **examples**: Example code 37 | - **docs**: Documentation related 38 | - **tools**: Tool related 39 | - **deps**: Dependency related 40 | - **ci**: CI related 41 | - **test**: Test related 42 | - **other**: Other 43 | 44 | ### Description 45 | 46 | - Use the imperative, present tense: "change" not "changed" or "changes" 47 | - Don't capitalize the first letter 48 | - No period at the end 49 | 50 | ### Body 51 | 52 | - Use the imperative, present tense 53 | - Should include the motivation for the change and contrast this with previous behavior 54 | 55 | ### Footer 56 | 57 | - **BREAKING CHANGE**: Should start with "BREAKING CHANGE:" followed by a space or two newlines, then a description of the change 58 | - References to issues: Can reference related issues, e.g., "Closes #123, #456" 59 | 60 | ## Examples 61 | 62 | ``` 63 | feat(core): add WebAssembly export function support 64 | 65 | Add support for WebAssembly export functions, making it easier for developers to interact with external systems. 66 | 67 | BREAKING CHANGE: Export functions now need to be explicitly marked, previously implicit exports are no longer supported. 68 | Closes #123 69 | ``` 70 | 71 | ``` 72 | fix(stdlib): fix heap overflow in memory allocator 73 | 74 | Fix heap overflow that could occur when allocating memory too large. 75 | 76 | Closes #456 77 | ``` 78 | 79 | ``` 80 | docs: update WebAssembly interoperability documentation 81 | ``` 82 | 83 | ## Automatic Validation 84 | 85 | All commits will be automatically validated using GitHub Actions to ensure they comply with the above conventions. Commits that do not comply with the convention may be automatically rejected. -------------------------------------------------------------------------------- /docs/VERSIONING.md: -------------------------------------------------------------------------------- 1 | # Versioning Guidelines 2 | 3 | DTVM_SolSDK project follows the [Semantic Versioning (SemVer)](https://semver.org/) specification. 4 | 5 | ## Version Format 6 | 7 | Version numbers are formatted as: **X.Y.Z**, where X, Y, and Z are non-negative integers without leading zeros. 8 | 9 | - **X** represents the major version 10 | - **Y** represents the minor version 11 | - **Z** represents the patch version 12 | 13 | Additional labels can be used as extensions for pre-releases and version metadata, formatted as: **X.Y.Z-label+metadata** 14 | 15 | ## Version Increment Rules 16 | 17 | 1. **Major version (X)**: Increment when making incompatible API changes 18 | 2. **Minor version (Y)**: Increment when adding functionality in a backward-compatible manner 19 | 3. **Patch version (Z)**: Increment when making backward-compatible bug fixes 20 | 21 | ## Pre-release Versions 22 | 23 | Pre-release versions can be indicated by adding a hyphen and an identifier after the version number, for example: 24 | 25 | - **1.0.0-alpha.1** 26 | - **1.0.0-beta.2** 27 | - **1.0.0-rc.1** 28 | 29 | Pre-release versions have lower precedence than the associated normal version. 30 | 31 | ## Release Process 32 | 33 | 1. Each significant version should be developed and tested on a `dev` or feature branch before release 34 | 2. Prior to version release, update the `CHANGELOG.md` file to record all changes for that version 35 | 3. Tag versions with Git tags in the format `v1.0.0` 36 | 4. Release versions through GitHub Releases, including the corresponding CHANGELOG 37 | 38 | ## CHANGELOG Requirements 39 | 40 | The CHANGELOG should list versions from newest to oldest, with each version including the following sections: 41 | 42 | - **Added**: New features 43 | - **Changed**: Changes to existing functionality 44 | - **Deprecated**: Features that are still supported but will be removed in upcoming releases 45 | - **Removed**: Features that have been removed 46 | - **Fixed**: Bug fixes 47 | - **Security**: Security-related fixes 48 | 49 | ## Version Examples 50 | 51 | ### Major Version Upgrade (Incompatible Changes) 52 | ``` 53 | 1.0.0 -> 2.0.0 54 | ``` 55 | 56 | ### Minor Version Upgrade (Backward-compatible New Features) 57 | ``` 58 | 1.0.0 -> 1.1.0 59 | ``` 60 | 61 | ### Patch Version Upgrade (Backward-compatible Bug Fixes) 62 | ``` 63 | 1.1.0 -> 1.1.1 64 | ``` 65 | 66 | ## Tagging Release Versions 67 | 68 | Use Git tags to mark each release version: 69 | 70 | ```bash 71 | git tag -a v1.0.0 -m "Release version 1.0.0" 72 | git push origin v1.0.0 73 | ``` -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # Architecture Document 2 | 3 | This document describes the architecture design, compilation process, and core components of DTVM_SolSDK, helping developers understand the internal workings of the project. 4 | 5 | ## Project Overview 6 | 7 | yul2wasm is the core tool of DTVM_SolSDK, used to compile Ethereum Solidity smart contracts into WebAssembly, enabling them to run on Wasm-based blockchain platforms. The project maintains compatibility with the Ethereum smart contract model while leveraging WebAssembly's high performance and portability features. 8 | 9 | ## Compilation Process 10 | 11 | The complete compilation process from Solidity to WebAssembly is as follows: 12 | 13 | ``` 14 | Solidity Source → Yul IR → LLVM IR → LLVM Optimization → WebAssembly → (Optional) Wasm Optimization 15 | ``` 16 | 17 | Detailed steps: 18 | 19 | 1. **Solidity to Yul IR** 20 | - Use Solidity compiler (solc) to compile Solidity code into Yul intermediate representation 21 | - Command: `solc --ir --optimize-yul -o output_directory --overwrite your_contract.sol` 22 | 23 | - Or use the Foundry framework for compilation (recommended for complex projects) 24 | - Configure foundry.toml: 25 | ```toml 26 | [profile.default] 27 | optimizer = true 28 | yul = true 29 | via_ir = true 30 | ``` 31 | - Compilation command: `forge build --extra-output-files ir-optimized` 32 | - The generated IR file is located at `out/ContractName.sol/ContractName.iropt` 33 | 34 | 2. **Yul IR to LLVM IR** 35 | - Parse Yul IR and convert it to LLVM IR 36 | - This step is the core functionality of yul2wasm 37 | 38 | 3. **LLVM Optimization** 39 | - Apply various optimizations to LLVM IR 40 | - Use `-O2` optimization level by default 41 | 42 | 4. **LLVM IR to WebAssembly** 43 | - Generate WebAssembly binary code 44 | - Support various WebAssembly features and limitations 45 | 46 | 5. **Wasm Optimization (Optional)** 47 | - Use Binaryen tools to further optimize WebAssembly code 48 | - Reduce code size and improve execution efficiency 49 | 50 | ## System Architecture 51 | 52 | yul2wasm consists of the following main components: 53 | 54 | ### 1. Yul Parser 55 | 56 | - Located in `src/yul.lalrpop` 57 | - Built using the LALRPOP parser generator 58 | - Parses Yul IR text into an in-memory Abstract Syntax Tree (AST) 59 | 60 | ### 2. Yul2IR Module 61 | 62 | - Located in `src/yul2ir/` 63 | - Responsible for converting Yul AST to LLVM IR 64 | - Main components: 65 | - `context.rs`: Maintains compilation context 66 | - `config.rs`: Handles compilation configuration options 67 | - `yul_instruction.rs`: Processes Yul instructions and types 68 | 69 | ### 3. Code Generator 70 | 71 | - Utilizes the inkwell library (a Rust wrapper for LLVM) 72 | - Generates optimized LLVM IR 73 | - Applies various LLVM optimizations 74 | 75 | ### 4. Standard Library 76 | 77 | - Located in the `stdlib/` directory 78 | - Provides Ethereum-compatible runtime functionality 79 | - Includes memory management, hash functions, EVM opcode simulations, and more 80 | 81 | ## Data Flow 82 | 83 | During the compilation process, data flows in the following forms: 84 | 85 | 1. Yul IR text → Yul AST (via parser) 86 | 2. Yul AST → LLVM IR (via Yul2IR module) 87 | 3. LLVM IR → Optimized LLVM IR (via LLVM optimization) 88 | 4. Optimized LLVM IR → WebAssembly binary (via LLVM backend) 89 | 90 | ## Key Design Decisions 91 | 92 | ### 1. Memory Model 93 | 94 | yul2wasm uses a linear memory model, consistent with WebAssembly's memory model. It includes: 95 | 96 | - Using WebAssembly linear memory to store dynamic data 97 | - A compatibility layer with Ethereum's EVM memory model 98 | - Memory management to ensure proper memory allocation and deallocation 99 | 100 | ### 2. Type System 101 | 102 | To bridge the gap between Yul and WebAssembly type systems: 103 | 104 | - Yul operations primarily use 256-bit integers (U256) 105 | - WebAssembly only natively supports 32-bit and 64-bit integers 106 | - DTVM_SolSDK implements 256-bit operations through simulation 107 | 108 | ### 3. Optimization Strategy 109 | 110 | The project employs multi-level optimization: 111 | 112 | - Yul-level optimization (via Solidity compiler) 113 | - LLVM optimization (via LLVM optimization pipeline) 114 | - WebAssembly-specific optimization (via Binaryen) 115 | - Custom optimization passes targeting common smart contract patterns 116 | 117 | ### 4. ABI Compatibility 118 | 119 | To ensure compatibility with Ethereum ABI: 120 | 121 | - Support for Ethereum ABI encoding and decoding standards 122 | - Compatibility with Ethereum function calling conventions and slot rules 123 | - Support for event logging functionality 124 | -------------------------------------------------------------------------------- /docs/cli-reference.md: -------------------------------------------------------------------------------- 1 | # CLI Reference 2 | 3 | This document provides a complete reference for the DTVM_SolSDK command-line tool yul2wasm, including detailed descriptions of all available parameters and options. 4 | 5 | ## Basic Usage 6 | 7 | The basic usage of yul2wasm is as follows: 8 | 9 | ``` 10 | yul2wasm --input --output [options] 11 | ``` 12 | 13 | ## Required Parameters 14 | 15 | | Parameter | Description | 16 | |------|------| 17 | | `--input ` | Specifies the path to the input Yul file | 18 | | `--output ` | Specifies the path to the output WebAssembly file | 19 | 20 | ## Optional Parameters 21 | 22 | ### Basic Options 23 | 24 | | Option | Default | Description | 25 | |------|--------|------| 26 | | `--verbose` | No | Enables verbose output mode, displaying detailed information about the compilation process | 27 | | `--debug` | No | Enables debug mode, generating debug information and intermediate files | 28 | | `--opt-level ` | default | Sets the LLVM optimization level (available values: default, none, less, more, aggressive) | 29 | 30 | ### Contract-Related Options 31 | 32 | | Option | Default | Description | 33 | |------|--------|------| 34 | | `--main-contract ` | (Auto-detect) | Specifies the main contract name; if not specified, uses the top-level object name in the file | 35 | | `--symbol ` | None | Defines symbol path and address mapping; can be used multiple times to define multiple mappings | 36 | | `--default_ret_type ` | u256 | Sets the default return type, available values: u256, bytes32 | 37 | 38 | ### Optimization Options 39 | 40 | | Option | Default | Description | 41 | |------|--------|------| 42 | | `--disable-all-optimizers` | No | Disables all optimizers, used for debugging purposes | 43 | | `--enable-all-optimizers` | No | Enables all possible optimizations to maximize performance | 44 | | `--no-binaryen-optimize` | Yes | Disables Binaryen optimization | 45 | | `--minify-wasm-size` | No | Enables additional WebAssembly size optimizations | 46 | 47 | ### Platform-Specific Options 48 | 49 | | Option | Default | Description | 50 | |------|--------|------| 51 | | `--enable-little-endian-storage-load-store` | No | Enables little-endian storage load/store functionality | 52 | | `--ignore-unknown-linker-library` | No | Ignores unknown linker library errors | 53 | 54 | ### Other Options 55 | 56 | | Option | Description | 57 | |------|------| 58 | | `-h, --help` | Displays help information | 59 | | `-V, --version` | Displays version information | 60 | 61 | ## Example Usage 62 | 63 | ### Basic Compilation 64 | 65 | Compile a Solidity contract to WebAssembly: 66 | 67 | ```sh 68 | # First use solc to compile Solidity to Yul 69 | solc --ir --optimize-yul -o output_dir --overwrite MyContract.sol 70 | 71 | # Then use yul2wasm to compile Yul to WebAssembly 72 | yul2wasm --input output_dir/MyContract.yul --output MyContract.wasm 73 | ``` 74 | 75 | ### Enable Verbose Output and Debug Information 76 | 77 | ```sh 78 | yul2wasm --input MyContract.yul --output MyContract.wasm --verbose --debug 79 | ``` 80 | 81 | ### Adjust Optimization Level 82 | 83 | ```sh 84 | yul2wasm --input MyContract.yul --output MyContract.wasm --opt-level aggressive 85 | ``` 86 | 87 | ### Specify Main Contract Name 88 | 89 | ```sh 90 | yul2wasm --input MultipleContracts.yul --output MainContract.wasm --main-contract MainContract 91 | ``` 92 | 93 | ### Define Symbol Mappings 94 | 95 | ```sh 96 | yul2wasm --input MyContract.yul --output MyContract.wasm --symbol "lib.sol=0x1234..." --symbol "utils.sol=0xabcd..." 97 | ``` 98 | 99 | ### Enable All Optimizations 100 | 101 | ```sh 102 | yul2wasm --input MyContract.yul --output MyContract.wasm --enable-all-optimizers 103 | ``` 104 | 105 | ### Minimize WebAssembly Size 106 | 107 | ```sh 108 | yul2wasm --input MyContract.yul --output MyContract.wasm --minify-wasm-size 109 | ``` 110 | 111 | ## Output Files 112 | 113 | When yul2wasm compiles successfully, it generates the following files: 114 | 115 | 1. **Main WebAssembly Binary File** (file name specified by `--output`) 116 | - Contains the compiled WebAssembly code 117 | 118 | 2. **Contract Binary File** (`.cbin` extension) 119 | - Contains the complete contract code for deployment, including the Wasm length prefix 120 | 121 | 3. **Contract Hexadecimal File** (`.cbin.hex` extension) 122 | - Hexadecimal representation of the contract binary file 123 | 124 | 4. **Additional Debug Mode Files** (generated only when using the `--debug` option) 125 | - LLVM IR files (`.ll`) 126 | - Assembly files (`.s`) 127 | - Other intermediate files 128 | 129 | ## Return Codes 130 | 131 | | Code | Description | 132 | |------|------| 133 | | 0 | Success | 134 | | 1 | General error (file read/write errors, parameter parsing errors, etc.) | 135 | | > 1 | Specific error codes, see error message for details | 136 | 137 | ## Environment Variables 138 | 139 | DTVM_SolSDK does not depend on any specific environment variables, but it requires that related tools (such as LLVM) are available in the system PATH. 140 | -------------------------------------------------------------------------------- /docs/compilation-guide.md: -------------------------------------------------------------------------------- 1 | # Compilation Guide 2 | 3 | This guide provides detailed steps for compiling DTVM_SolSDK from source, including all necessary dependencies and environment setup. 4 | 5 | ## System Requirements 6 | 7 | DTVM_SolSDK can be built on the following operating systems: 8 | - Linux (Ubuntu/Debian recommended) 9 | - macOS 10 | - Windows (via WSL) 11 | 12 | ## Dependencies 13 | 14 | Before compiling DTVM_SolSDK, you need to install the following dependencies: 15 | 16 | ### Required Dependencies 17 | 18 | 1. **Rust 1.81+** 19 | - Rust language and its package manager Cargo 20 | 21 | 2. **LLVM 16** 22 | - Used for compilation and optimization 23 | 24 | 3. **Solidity Compiler 0.8.25+** 25 | - Used to compile Solidity code to Yul intermediate representation 26 | 27 | ## Installing Dependencies 28 | 29 | ### Ubuntu/Debian 30 | 31 | ```sh 32 | # Install Rust 33 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 34 | source $HOME/.cargo/env 35 | 36 | # Install LLVM 16 37 | apt update 38 | apt install -y llvm-16 llvm-16-dev 39 | 40 | # Install Solidity Compiler 41 | apt-get install software-properties-common 42 | add-apt-repository ppa:ethereum/ethereum 43 | apt-get update 44 | apt-get install solc 45 | 46 | # Install Binaryen 47 | apt install -y binaryen 48 | ``` 49 | 50 | ### macOS 51 | 52 | ```sh 53 | # Install Rust 54 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 55 | source $HOME/.cargo/env 56 | 57 | # Install LLVM 16 58 | brew install llvm@16 59 | # Add LLVM to PATH 60 | echo 'export LLVM_SYS_160_PREFIX=/opt/homebrew/opt/llvm@16' >> ~/.bash_profile 61 | echo 'export PATH="$LLVM_SYS_160_PREFIX/bin:$PATH"' >> ~/.bash_profile 62 | 63 | # Install Solidity Compiler 64 | brew tap ethereum/ethereum 65 | brew install solidity 66 | 67 | # Install Binaryen 68 | brew install binaryen 69 | ``` 70 | 71 | ## Download Source Code 72 | 73 | Clone the repository from GitHub: 74 | 75 | ```sh 76 | git clone https://github.com/DTVMStack/DTVM_SolSDK.git 77 | cd DTVM_SolSDK 78 | ``` 79 | 80 | ## Compile the Project 81 | 82 | ### Development Mode Compilation 83 | 84 | ```sh 85 | make -f dev.makefile debug 86 | ``` 87 | 88 | The compiled binary will be located at `target/debug/yul2wasm`. 89 | 90 | ### Release Mode Compilation 91 | 92 | ```sh 93 | make -f dev.makefile release 94 | ``` 95 | 96 | The compiled binary will be located at `target/release/yul2wasm`. 97 | 98 | ## Verify Installation 99 | 100 | Verify that yul2wasm is compiled correctly: 101 | 102 | ```sh 103 | ./target/release/yul2wasm --help 104 | ``` 105 | 106 | You should see output similar to the following: 107 | 108 | ``` 109 | Compile Yul source to wasm 110 | 111 | Usage: yul2wasm [OPTIONS] --input --output 112 | 113 | Options: 114 | --input Input file path 115 | --output Output wasm path 116 | --verbose Verbose output 117 | --debug Debug output 118 | --opt-level Optimization level [default: default] 119 | --main-contract Main contract name 120 | --symbol Symbol path=address 121 | --ignore-unknown-linker-library Ignore unknown linker library 122 | --no-binaryen-optimize No binaryen optimize [default: true] 123 | --minify-wasm-size Minify wasm size 124 | --disable-all-optimizers Disable all optimizers 125 | --enable-all-optimizers Enable all optimizers 126 | --enable-little-endian-storage-load-store Enable little endian storage load/store 127 | --default_ret_type Default return type [default: u256] 128 | -h, --help Print help 129 | -V, --version Print version 130 | ``` 131 | 132 | ## Run Tests 133 | 134 | Ensure all tests pass: 135 | 136 | ```sh 137 | cargo test 138 | ``` 139 | 140 | ## Compile Example Project 141 | 142 | yul2wasm provides multiple examples, you can familiarize yourself with the tool's usage by compiling these examples: 143 | 144 | ```sh 145 | cd examples/erc20 146 | ./build_erc20.sh 147 | ``` 148 | 149 | If everything goes well, you should see the generated WebAssembly file (`my_erc20.wasm`) and the readable text format (`my_erc20.wat`). 150 | 151 | ## Troubleshooting 152 | 153 | ### Common Issues 154 | 155 | 1. **LLVM Not Found** 156 | - Ensure LLVM 16 is correctly installed and added to PATH 157 | - For macOS: You can find the path using the command `brew --prefix llvm@16` and make sure the llvm16 bin directory is added to the PATH environment variable 158 | - For Linux: Check if the llvm-16-dev package is installed 159 | 160 | 2. **Solidity Compiler Issues** 161 | - Verify solc version: `solc --version` (should be 0.8.25 or higher) 162 | - If the version is too low, update to the latest version 163 | 164 | 3. **Dependency Conflicts** 165 | - Try cleaning the build: `cargo clean` and then rebuild 166 | 167 | ### Submitting Issues 168 | 169 | If you encounter problems that you cannot resolve, please submit a detailed issue report on GitHub, including: 170 | - Operating system and version 171 | - Dependency versions (Rust, LLVM, Solidity) 172 | - Error messages and logs 173 | - Steps to reproduce the issue 174 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Thank you for your interest in the DTVM_SolSDK project! This guide will help you understand how to effectively contribute to the project. 4 | 5 | ## Code of Conduct 6 | 7 | All participants in the project must adhere to our code of conduct. Please read [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md) for details. 8 | 9 | ## Ways to Contribute 10 | 11 | You can contribute to DTVM_SolSDK in the following ways: 12 | 13 | 1. Report bugs and suggest features 14 | 2. Submit code changes 15 | 3. Improve documentation 16 | 4. Share use cases and feedback 17 | 18 | ## Development Environment Setup 19 | 20 | Before starting to contribute code, please ensure you have set up your development environment according to the [Compilation Guide](compilation-guide.md). 21 | 22 | ## Reporting Bugs 23 | 24 | If you discover an issue, please create an Issue on GitHub and provide the following information: 25 | 26 | 1. A brief description of the bug 27 | 2. Steps to reproduce 28 | 3. Expected behavior 29 | 4. Actual behavior 30 | 5. Environment information (operating system, Rust version, dependency versions, etc.) 31 | 6. If possible, provide relevant logs or screenshots 32 | 33 | ## Contributing Code 34 | 35 | ### Branch Strategy 36 | 37 | - The `main` branch contains the latest stable version 38 | - Feature development should use new branches created from the `main` branch 39 | - Branch naming conventions: 40 | - Feature branches: `feature/description` 41 | - Bug fixes: `bugfix/description` 42 | - Documentation updates: `docs/description` 43 | 44 | ### Development Workflow 45 | 46 | 1. Fork the project repository to your own GitHub account 47 | 2. Clone the forked repository locally 48 | ```sh 49 | git clone https://github.com//DTVM_SolSDK.git 50 | cd DTVM_SolSDK 51 | ``` 52 | 3. Add the main repository as a remote source 53 | ```sh 54 | git remote add upstream https://github.com/DTVMStack/DTVM_SolSDK.git 55 | ``` 56 | 4. Create a new branch 57 | ```sh 58 | git checkout -b feature/your-feature-name 59 | ``` 60 | 5. Implement your changes 61 | 6. Run tests to ensure code quality 62 | ```sh 63 | cargo test 64 | ``` 65 | 7. Commit your changes 66 | ```sh 67 | git commit -m "Descriptive commit message" 68 | ``` 69 | 8. Push to your fork 70 | ```sh 71 | git push origin feature/your-feature-name 72 | ``` 73 | 9. Create a Pull Request (PR) 74 | 75 | ### Commit Message Guidelines 76 | 77 | Please follow this commit message format: 78 | 79 | ``` 80 | : 81 | 82 | [Optional detailed description] 83 | 84 | [Optional issue closure: Fixes #123] 85 | ``` 86 | 87 | Types can be: 88 | - `feat`: New feature 89 | - `fix`: Bug fix 90 | - `docs`: Documentation changes 91 | - `style`: Code style changes (not affecting code functionality) 92 | - `refactor`: Code refactoring 93 | - `perf`: Performance improvements 94 | - `test`: Adding or modifying tests 95 | - `chore`: Build process or auxiliary tool changes 96 | 97 | ### Code Review Process 98 | 99 | After submitting a PR: 100 | 1. Maintainers will review your code 101 | 2. Changes or clarifications may be requested 102 | 3. Once the review is approved, the PR will be merged into the main branch 103 | 104 | ## Code Style Guide 105 | 106 | We use the standard Rust code style, enforced by `rustfmt`: 107 | 108 | ```sh 109 | # Format code 110 | make fmt -f dev.makefile 111 | 112 | # Run clippy for code quality checks 113 | make fmt_check -f dev.makefile 114 | ``` 115 | 116 | Please ensure you run these commands before submitting code to ensure the code complies with the project style. 117 | 118 | ## Testing Guidelines 119 | 120 | For new features or bug fixes, we encourage adding appropriate tests: 121 | 122 | - Unit tests are located in the `src/tests/` directory 123 | - Integration tests use example contracts in the `examples/` directory 124 | 125 | Running tests: 126 | 127 | ```sh 128 | # Run all tests 129 | cargo test 130 | 131 | # Run a specific test 132 | cargo test test_name 133 | 134 | # Run integration test examples 135 | cd examples/erc20 136 | ./test_simple_token.sh 137 | ``` 138 | 139 | ## Documentation Contributions 140 | 141 | Improving documentation is equally important: 142 | 143 | 1. Fix errors or inaccuracies in the documentation 144 | 2. Add missing information or examples 145 | 3. Improve clarity and usability of existing documentation 146 | 147 | ## Release Process 148 | 149 | Project maintainers are responsible for version releases: 150 | 151 | 1. Version numbers follow [Semantic Versioning](https://semver.org/) 152 | 2. Each release version will update CHANGELOG.md 153 | 3. Release versions will be tagged and pushed to GitHub 154 | 155 | ## License 156 | 157 | By contributing code, you agree that your contributions will be distributed under the project's Apache-2.0 license. Please see the [LICENSE](../LICENSE) file for details. 158 | 159 | ## Getting Help 160 | 161 | If you need help during the contribution process, you can: 162 | 163 | - Ask questions on GitHub Issues 164 | - Check existing documentation 165 | - Contact project maintainers 166 | 167 | ## Acknowledgements 168 | 169 | We appreciate all contributors, regardless of the size of the contribution! 170 | -------------------------------------------------------------------------------- /docs/developer-guide.md: -------------------------------------------------------------------------------- 1 | # Developer Guide for DTVM_SolSDK 2 | 3 | ## Project Overview 4 | DTVM_SolSDK is a compiler that translates Solidity's Yul intermediate representation into WebAssembly (Wasm), enabling Ethereum smart contracts to run in Wasm environments. 5 | 6 | ## Development Environment Setup 7 | 8 | ### Prerequisites 9 | - Rust 1.81 or newer 10 | - LLVM 16 11 | - Solidity Compiler 0.8.25 12 | - Binaryen (for wasm tools like wasm2wat) 13 | 14 | ### Installation Instructions 15 | 16 | #### Ubuntu/Debian 17 | ```sh 18 | # Install Rust 19 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 20 | source $HOME/.cargo/env 21 | 22 | # Install LLVM 16 23 | apt update 24 | apt install -y llvm-16 llvm-16-dev 25 | 26 | # Install Solidity Compiler 27 | apt-get install software-properties-common 28 | add-apt-repository ppa:ethereum/ethereum 29 | apt-get update 30 | apt-get install solc 31 | 32 | # Install Binaryen 33 | apt install -y binaryen 34 | ``` 35 | 36 | #### macOS 37 | ```sh 38 | # Install Rust 39 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 40 | source $HOME/.cargo/env 41 | 42 | # Install LLVM 16 43 | brew install llvm@16 44 | 45 | # Install Solidity Compiler 46 | brew tap ethereum/ethereum 47 | brew install solidity 48 | 49 | # Install Binaryen 50 | brew install binaryen 51 | ``` 52 | 53 | ## Project Structure 54 | - `docker/` - Dockerfile and related scripts for building the compiler 55 | - `src/` - Core compiler source code 56 | - `tests/` - Compiler test suite 57 | - `yul2ir/` - Yul IR parser and LLVM IR generator 58 | - `stdlib/` - Standard library modules that provide common functionality and utilities 59 | - `lib` - Additional libraries or external dependencies 60 | - `tools` - Scripts for various tasks like formatting 61 | - `examples/` - Example Solidity contracts and compilation scripts 62 | - `docs/` - Project documentation 63 | 64 | ## Build Process 65 | 66 | 1. **Compilation Pipeline:** 67 | - Solidity source → Yul IR (using solc compiler) 68 | - Yul IR → WebAssembly (using yul2wasm) 69 | 70 | 2. **Compilation Stages:** 71 | - Parsing Yul IR 72 | - Generating LLVM IR 73 | - Optimizing LLVM IR 74 | - Generating WebAssembly binary 75 | 76 | ## Adding Features 77 | 78 | When adding new features to yul2wasm: 79 | 80 | 1. **Understanding Yul IR**: 81 | - Familiarize yourself with Solidity's Yul Intermediate Representation syntax and semantics 82 | - Reference: https://docs.soliditylang.org/en/latest/yul.html 83 | 84 | 2. **Making Compiler Changes**: 85 | - Modify the parser for new Yul syntax support 86 | - Update LLVM IR generation for new features 87 | - Add appropriate tests to verify the functionality 88 | 89 | 3. **Testing**: 90 | - Unit tests: `cargo test` 91 | - Integration tests: Use example contracts in `examples/` directory 92 | 93 | ## Debugging 94 | 95 | 1. **Debug Logging**: 96 | - Use the `--verbose` flag when running yul2wasm 97 | - For advanced debugging, use the `--debug` flag to generate additional debug information 98 | 99 | 2. **Examining LLVM IR and Assembly**: 100 | - Intermediate files are generated during compilation with proper flags 101 | - Check `.ll` files for LLVM IR and `.s` files for assembly output 102 | 103 | 3. **Testing with Example Contracts**: 104 | - The `examples/` directory contains various smart contracts to test against 105 | - Use these as benchmarks for your changes 106 | 107 | ## Contributing Guidelines 108 | 109 | 1. **Code Style**: 110 | - Follow Rust code conventions 111 | - Run `cargo fmt` before submitting code 112 | - Use `cargo clippy` for linting 113 | 114 | 2. **Pull Request Process**: 115 | - Create a new branch for your feature/fix 116 | - Include tests for new functionality 117 | - Update documentation as needed 118 | - Submit a PR with a clear description of changes 119 | 120 | 3. **Documentation**: 121 | - Update relevant documentation in the `docs/` directory 122 | - Include examples for new features 123 | - Comment complex code sections 124 | 125 | ## Performance Considerations 126 | 127 | When optimizing the compiler: 128 | 129 | 1. **LLVM Optimization Levels**: 130 | - Default is `-O2` for maximum optimization 131 | - Consider compilation time vs. performance trade-offs 132 | 133 | 2. **Wasm Size Optimization**: 134 | - Binaryen's `wasm-opt` can further optimize the generated Wasm 135 | 136 | ## Common Issues and Solutions 137 | 138 | - **Memory Management**: WebAssembly has specific memory models; ensure proper memory handling 139 | - **Stack Limitations**: Watch for stack usage in complex contracts 140 | - **ABI Compatibility**: Ensure compatibility with Ethereum ABI encoding/decoding 141 | -------------------------------------------------------------------------------- /docs/release_docs/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide for DTVM_SolSDK 2 | 3 | ## Prerequisites 4 | 5 | The fastest way to set up the compilation environment is to use a Docker image or build it based on the provided Dockerfile: 6 | 7 | ```bash 8 | docker pull dtvmdev1/dtvm-sol-dev-x64:main 9 | ``` 10 | 11 | Before using DTVM_SolSDK, ensure the following dependencies are installed on your system: 12 | 13 | - **solc** (Solidity compiler) or **Foundry** 14 | - **Binaryen** (Optional) 15 | - **zstd** (on Mac) 16 | 17 | ### Installing Solidity Compiler (solc) 18 | 19 | Download the Solidity compiler from: 20 | [https://github.com/ethereum/solidity/releases](https://github.com/ethereum/solidity/releases) 21 | 22 | ### Installing zstd (on Mac) 23 | 24 | If you are using this tool on Mac, you need to install the **zstd** library. The simplest installation method is to first install `homebrew`, then run `brew install zstd` 25 | 26 | ### Installing Foundry 27 | 28 | Install Foundry from: 29 | [https://getfoundry.sh/](https://getfoundry.sh/) 30 | 31 | ## Basic Usage 32 | 33 | ### Compiling a Solidity Contract to WebAssembly 34 | 35 | The compilation process involves two main steps: 36 | 37 | 1. Compile Solidity to Yul IR using the Solidity compiler 38 | 2. Compile Yul IR to WebAssembly using yul2wasm 39 | 40 | Here's a basic example: 41 | 42 | ```bash 43 | # Step 1: Compile Solidity to Yul IR 44 | solc --ir --optimize-yul -o output_directory --overwrite your_contract.sol 45 | 46 | # Step 2: Compile Yul IR to WebAssembly 47 | yul2wasm --input output_directory/ContractName.yul --output your_contract.wasm 48 | ``` 49 | 50 | If you're using Foundry for your project, here's how to compile your Solidity contract to WebAssembly: 51 | 52 | ```bash 53 | # Step1: Compile foundry project to Yul IR 54 | forge build --extra-output-files ir-optimized 55 | 56 | # If your solidity filename and contract name is ExampleContract 57 | 58 | # Step2: Compile Yul IR to WebAssembly 59 | yul2wasm --input out/ExampleContract.sol/ExampleContract.iropt --output out/ExampleContract.wasm 60 | ``` 61 | 62 | ### Command Line Options 63 | 64 | yul2wasm provides several command-line options: 65 | 66 | | Option | Description | 67 | |--------|-------------| 68 | | `--input ` | Specify the input Yul file (required) | 69 | | `--output ` | Specify the output WebAssembly file (required) | 70 | | `--verbose` | Enable verbose output for debugging | 71 | | `--debug` | Generate debug information | 72 | | `--opt-level ` | Set LLVM optimization level (0-3, default: 3) | 73 | 74 | ### Output File Types 75 | 76 | When working with yul2wasm, you'll encounter several file types: 77 | 78 | - `.wasm`: WebAssembly binary format - the final compiled contract that can be deployed on Wasm-based blockchains 79 | - `.cbin`: Contract binary format - contains the compiled bytecode of the contract 80 | - `.cbin.hex`: Hexadecimal representation of the contract binary - useful for deployment and verification 81 | 82 | ### Converting WebAssembly to Text Format (WAT) 83 | 84 | For inspection or debugging, you can convert the binary WebAssembly to text format: 85 | 86 | ```bash 87 | wasm2wat -o your_contract.wat your_contract.wasm 88 | ``` 89 | 90 | ## Troubleshooting 91 | 92 | For common issues, security best practices, and more detailed information, please contact us through: 93 | [https://github.com/DTVMStack/DTVM_SolSDK/issues](https://github.com/DTVMStack/DTVM_SolSDK/issues) 94 | -------------------------------------------------------------------------------- /docs/user-guide.md: -------------------------------------------------------------------------------- 1 | # User Guide for DTVM_SolSDK 2 | 3 | ## Introduction 4 | 5 | DTVM_SolSDK is a tool that compiles Ethereum Solidity smart contracts to WebAssembly (Wasm), enabling deployment on Wasm-based blockchains. This guide will walk you through how to use DTVM_SolSDK to compile your Solidity contracts into WebAssembly. 6 | 7 | Now the core tool of DTVM_SolSDK is yul2wasm. 8 | 9 | ## Installation 10 | 11 | Before using DTVM_SolSDK, ensure you have the following prerequisites installed: 12 | 13 | - Solidity Compiler 0.8.29(0.8.25+) 14 | - LLVM 16 15 | - Rust 1.81 or later 16 | - Binaryen (for wasm tools) 17 | 18 | For detailed installation instructions, refer to the [Developer Guide](developer-guide.md). 19 | 20 | ## Docker image 21 | 22 | The fastest way to set up the compilation environment is to use a Docker image or build it based on docker/Dockerfile. 23 | 24 | ``` 25 | docker pull dtvmdev1/dtvm-sol-dev-x64:main 26 | ``` 27 | 28 | ## Basic Usage 29 | 30 | ### Compiling a Solidity Contract to WebAssembly 31 | 32 | The compilation process involves two main steps: 33 | 34 | 1. Compile Solidity to Yul IR using the Solidity compiler 35 | 2. Compile Yul IR to WebAssembly using yul2wasm 36 | 37 | Here's a basic example: 38 | 39 | ```sh 40 | # Step 1: Compile Solidity to Yul IR 41 | solc --ir --optimize-yul -o output_directory --overwrite your_contract.sol 42 | 43 | # Step 2: Compile Yul IR to WebAssembly 44 | yul2wasm --input output_directory/ContractName.yul --output your_contract.wasm 45 | ``` 46 | 47 | ### Command Line Options 48 | 49 | yul2wasm provides several command-line options: 50 | 51 | - `--input `: Specify the input Yul file (required) 52 | - `--output `: Specify the output WebAssembly file (required) 53 | - `--verbose`: Enable verbose output for debugging 54 | - `--debug`: Generate debug information 55 | - `--opt-level `: Set LLVM optimization level (0-3, default: 3) 56 | 57 | ### Converting WebAssembly to Text Format (WAT) 58 | 59 | For inspection or debugging, you can convert the binary WebAssembly to text format: 60 | 61 | ```sh 62 | wasm2wat -o your_contract.wat your_contract.wasm 63 | ``` 64 | 65 | ## Working with Examples 66 | 67 | The `examples/` directory contains various Solidity contracts and scripts to help you understand how to use yul2wasm. 68 | 69 | ### ERC20 Token Example 70 | 71 | To compile and test the ERC20 token example: 72 | 73 | ```sh 74 | cd examples/erc20 75 | ./build_erc20.sh # Compile the contract 76 | ./test_simple_token.sh # Run tests 77 | ``` 78 | 79 | ### Other Examples 80 | 81 | The `examples/` directory includes several other examples: 82 | 83 | - `foundry_erc20/`: ERC20 token using Foundry framework 84 | - `nft/`: NFT (ERC721) implementation 85 | 86 | ## Integration with Development Workflows 87 | 88 | ### Automating Compilation 89 | 90 | You can create a script similar to `examples/erc20/build_erc20.sh` to automate the compilation process: 91 | 92 | ```sh 93 | #!/bin/bash 94 | set -e 95 | 96 | # Compile Solidity to Yul IR 97 | solc --ir --optimize-yul -o . --overwrite your_contract.sol 98 | 99 | # Compile Yul IR to WebAssembly 100 | yul2wasm --input ContractName.yul --output your_contract.wasm 101 | 102 | # Optional: Convert to WAT format for inspection 103 | wasm2wat -o your_contract.wat your_contract.wasm 104 | ``` 105 | 106 | ### Testing Compiled Contracts 107 | 108 | The `examples/scripts/` directory contains various testing scripts that you can use as templates for testing your compiled contracts. 109 | 110 | ## Troubleshooting 111 | 112 | ### Common Issues 113 | 114 | 1. **Missing Dependencies** 115 | - Ensure all prerequisites are installed correctly. 116 | - Check version compatibility (especially for Solidity and LLVM). 117 | 118 | 2. **Compilation Errors** 119 | - Check Solidity syntax and version compatibility. 120 | - Examine verbose output with `--verbose` flag. 121 | - For complex errors, use `--debug` flag to generate more information. 122 | 123 | 3. **Execution Errors** 124 | - Verify memory management in your contract. 125 | - Check for stack overflow in complex functions. 126 | - Ensure proper ABI encoding/decoding for function calls. 127 | 128 | ### Getting Help 129 | 130 | If you encounter issues not covered in this guide: 131 | 132 | 1. Check the GitHub repository issues section 133 | 2. Examine the test cases in the `examples/` directory 134 | 3. Refer to the developer guide for more technical details 135 | 136 | ### Security Best Practices 137 | 138 | When developing contracts for deployment: 139 | 140 | 1. Follow standard Solidity security practices 141 | 2. Test extensively before deployment 142 | 3. Consider formal verification for critical contracts 143 | -------------------------------------------------------------------------------- /download_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # get libclang_rt.builtins-wasm32.a from https://github.com/WebAssembly/wasi-sdk/releases/tag/wasi-sdk-12 5 | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/libclang_rt.builtins-wasm32-wasi-12.0.tar.gz 6 | tar -xzf libclang_rt.builtins-wasm32-wasi-12.0.tar.gz 7 | rm libclang_rt.builtins-wasm32-wasi-12.0.tar.gz 8 | -------------------------------------------------------------------------------- /examples/counter/.gitignore: -------------------------------------------------------------------------------- 1 | /*.out.ll 2 | /*.out.s 3 | /*.wasm 4 | /*.cbin 5 | /*.cbin.hex 6 | /*.wat 7 | /*.yul 8 | /test.db 9 | -------------------------------------------------------------------------------- /examples/counter/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # Determine the build mode 8 | BUILD_MODE=${1:-release} 9 | 10 | echo "Building in $BUILD_MODE mode" 11 | 12 | YUL2WASM_EXTRA_ARGS="--verbose" 13 | 14 | # Set the yul2wasm path based on the build mode 15 | if [ "$BUILD_MODE" == "release" ]; then 16 | YUL2WASM_PATH="../../target/release/yul2wasm" 17 | else 18 | YUL2WASM_PATH="../../target/debug/yul2wasm" 19 | YUL2WASM_EXTRA_ARGS="--verbose --debug" 20 | fi 21 | 22 | solc --ir --optimize-yul -o . --overwrite counter.sol 23 | 24 | $YUL2WASM_PATH --input Counter.yul --output counter.wasm $YUL2WASM_EXTRA_ARGS 25 | wasm2wat -o counter.wat counter.wasm 26 | -------------------------------------------------------------------------------- /examples/counter/counter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract counter{ 4 | 5 | uint public count; 6 | 7 | function increase() public { 8 | count++; 9 | } 10 | 11 | function decrease() public{ 12 | count--; 13 | } 14 | } -------------------------------------------------------------------------------- /examples/erc20/.gitignore: -------------------------------------------------------------------------------- 1 | /*.out.ll 2 | /*.out.s 3 | /*.wasm 4 | /*.cbin 5 | /*.cbin.hex 6 | /*.wat 7 | /*.yul 8 | /test.db 9 | /*.bin 10 | /out 11 | 12 | /node_modules 13 | -------------------------------------------------------------------------------- /examples/erc20/GLDToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Compatible with OpenZeppelin Contracts ^5.0.0 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract GLDToken is ERC20 { 8 | constructor(uint256 initialSupply) ERC20("Gold", "GLD") { 9 | _mint(msg.sender, initialSupply); 10 | 11 | } 12 | function mint(address to, uint256 amount) public { 13 | _mint(to, amount); 14 | } 15 | } -------------------------------------------------------------------------------- /examples/erc20/build_erc20.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # Determine the build mode 8 | BUILD_MODE=${1:-release} 9 | 10 | echo "Building in $BUILD_MODE mode" 11 | 12 | YUL2WASM_EXTRA_ARGS="--verbose" 13 | 14 | # Set the yul2wasm path based on the build mode 15 | if [ "$BUILD_MODE" == "release" ]; then 16 | YUL2WASM_PATH="../../target/release/yul2wasm" 17 | else 18 | YUL2WASM_PATH="../../target/debug/yul2wasm" 19 | YUL2WASM_EXTRA_ARGS="--verbose --debug" 20 | fi 21 | 22 | solc --ir --optimize-yul -o ./out --overwrite simple_erc20.sol 23 | 24 | $YUL2WASM_PATH --input out/SimpleToken.yul --output my_erc20.wasm $YUL2WASM_EXTRA_ARGS 25 | wasm2wat -o my_erc20.wat my_erc20.wasm 26 | -------------------------------------------------------------------------------- /examples/erc20/build_gld_token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 0.8.29 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # Determine the build mode 8 | BUILD_MODE=${1:-release} 9 | 10 | echo "Building in $BUILD_MODE mode" 11 | 12 | YUL2WASM_EXTRA_ARGS="--verbose" 13 | 14 | # Set the yul2wasm path based on the build mode 15 | if [ "$BUILD_MODE" == "release" ]; then 16 | YUL2WASM_PATH="../../target/release/yul2wasm" 17 | else 18 | YUL2WASM_PATH="../../target/debug/yul2wasm" 19 | YUL2WASM_EXTRA_ARGS="--verbose --debug" 20 | fi 21 | 22 | solc --ir --via-ir --optimize-yul --optimize-runs 200 --optimize -o ./out --overwrite --bin @openzeppelin/contracts=./node_modules/@openzeppelin/contracts/ GLDToken.sol 23 | 24 | $YUL2WASM_PATH --input ./out/GLDToken.yul --output GLDToken.wasm $YUL2WASM_EXTRA_ARGS 25 | wasm2wat -o GLDToken.wat GLDToken.wasm 26 | -------------------------------------------------------------------------------- /examples/erc20/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20-example-project", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "erc20-example-project", 8 | "dependencies": { 9 | "@openzeppelin/contracts": "^4.9.3", 10 | "@openzeppelin/contracts-upgradeable": "^4.9.3" 11 | } 12 | }, 13 | "node_modules/@openzeppelin/contracts": { 14 | "version": "4.9.6", 15 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.6.tgz", 16 | "integrity": "sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==", 17 | "license": "MIT" 18 | }, 19 | "node_modules/@openzeppelin/contracts-upgradeable": { 20 | "version": "4.9.6", 21 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz", 22 | "integrity": "sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA==", 23 | "license": "MIT" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/erc20/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20-example-project", 3 | "dependencies": { 4 | "@openzeppelin/contracts": "^4.9.3", 5 | "@openzeppelin/contracts-upgradeable": "^4.9.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/erc20/simple_erc20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract SimpleToken { 5 | string public name; 6 | string public symbol; 7 | uint8 public decimals; 8 | uint256 public totalSupply; 9 | 10 | mapping(address => uint256) private balances; 11 | mapping(address => mapping(address => uint256)) private allowances; 12 | 13 | event Transfer(address indexed from, address indexed to, uint256 value); 14 | event Mint(address indexed to, uint256 value); 15 | 16 | constructor(uint256 _totalSupply) { 17 | name = "SimpleToken"; 18 | symbol = "STK"; 19 | decimals = 18; 20 | totalSupply = _totalSupply; 21 | if (_totalSupply > 0) { 22 | balances[msg.sender] = _totalSupply; 23 | emit Transfer(address(0), msg.sender, _totalSupply); 24 | } 25 | } 26 | 27 | function balanceOf(address account) public view returns (uint256) { 28 | return balances[account]; 29 | } 30 | 31 | function mint(address to, uint256 amount) public { 32 | require(to != address(0), "Invalid recipient address"); 33 | 34 | totalSupply += amount; 35 | balances[to] += amount; 36 | 37 | emit Mint(to, amount); 38 | emit Transfer(address(0), to, amount); 39 | } 40 | 41 | function transfer(address recipient, uint256 amount) public returns (bool) { 42 | require(recipient != address(0), "Invalid recipient address"); 43 | require(balances[msg.sender] >= amount, "Insufficient balance"); 44 | 45 | balances[msg.sender] -= amount; 46 | balances[recipient] += amount; 47 | 48 | emit Transfer(msg.sender, recipient, amount); 49 | return true; 50 | } 51 | 52 | function approve(address spender, uint256 amount) public returns (bool) { 53 | allowances[msg.sender][spender] = amount; 54 | return true; 55 | } 56 | 57 | function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { 58 | require(recipient != address(0), "Invalid recipient address"); 59 | require(balances[sender] >= amount, "Insufficient balance"); 60 | require(allowances[sender][msg.sender] >= amount, "Insufficient allowance"); 61 | 62 | balances[sender] -= amount; 63 | balances[recipient] += amount; 64 | allowances[sender][msg.sender] -= amount; 65 | 66 | emit Transfer(sender, recipient, amount); 67 | return true; 68 | } 69 | 70 | function allowance(address owner, address spender) public view returns (uint256) { 71 | return allowances[owner][spender]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/erc20/test_gld_token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f test.db 5 | 6 | ../scripts/test_erc20.sh GLDToken.wasm 7 | -------------------------------------------------------------------------------- /examples/erc20/test_simple_token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f test.db 5 | 6 | ../scripts/test_erc20.sh my_erc20.wasm 7 | -------------------------------------------------------------------------------- /examples/fibonacci/.gitignore: -------------------------------------------------------------------------------- 1 | /*.out.ll 2 | /*.out.s 3 | /*.wasm 4 | /*.cbin 5 | /*.cbin.hex 6 | /*.wat 7 | /*.yul 8 | /test.db 9 | -------------------------------------------------------------------------------- /examples/fibonacci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # Determine the build mode 8 | BUILD_MODE=${1:-release} 9 | 10 | echo "Building in $BUILD_MODE mode" 11 | 12 | YUL2WASM_EXTRA_ARGS="--verbose" 13 | 14 | # Set the yul2wasm path based on the build mode 15 | if [ "$BUILD_MODE" == "release" ]; then 16 | YUL2WASM_PATH="../../target/release/yul2wasm" 17 | else 18 | YUL2WASM_PATH="../../target/debug/yul2wasm" 19 | YUL2WASM_EXTRA_ARGS="--verbose --debug" 20 | fi 21 | 22 | solc --ir --optimize-yul -o . --overwrite fib.sol 23 | solc --ir --optimize-yul -o . --overwrite fib_recur.sol 24 | 25 | $YUL2WASM_PATH --input FibonacciTest.yul --output fib.wasm $YUL2WASM_EXTRA_ARGS 26 | wasm2wat -o fib.wat fib.wasm 27 | 28 | $YUL2WASM_PATH --input FibonacciRecurTest.yul --output fib_recur.wasm $YUL2WASM_EXTRA_ARGS 29 | wasm2wat -o fib_recur.wat fib_recur.wasm 30 | -------------------------------------------------------------------------------- /examples/fibonacci/fib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract FibonacciTest { 6 | 7 | function fibonacci(uint n) public pure returns (uint) { 8 | if (n == 0) return 0; 9 | if (n == 1) return 1; 10 | 11 | uint a = 0; 12 | uint b = 1; 13 | uint result; 14 | 15 | for (uint i = 2; i <= n; i++) { 16 | result = a + b; 17 | a = b; 18 | b = result; 19 | } 20 | 21 | return result; 22 | } 23 | 24 | function fibonacciTailOptimized(uint n) public pure returns (uint) { 25 | if (n == 0) return 0; 26 | 27 | uint a = 0; 28 | uint b = 1; 29 | 30 | while (n > 1) { 31 | uint temp = b; 32 | b = a + b; 33 | a = temp; 34 | n--; 35 | } 36 | 37 | return b; 38 | } 39 | } -------------------------------------------------------------------------------- /examples/fibonacci/fib_recur.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract FibonacciRecurTest { 4 | 5 | function fibonacci(uint n) public pure returns (uint) { 6 | if (n == 0) return 0; 7 | if (n == 1) return 1; 8 | return fibonacci(n - 1) + fibonacci(n - 2); 9 | } 10 | 11 | function fibonacciTailOptimized(uint n) public pure returns (uint) { 12 | return fibonacciTailRecursive(n, 0, 1); 13 | } 14 | 15 | function fibonacciTailRecursive(uint n, uint a, uint b) internal pure returns (uint) { 16 | if (n == 0) return a; 17 | if (n == 1) return b; 18 | return fibonacciTailRecursive(n - 1, b, a + b); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/fibonacci/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | ABI_ENCODE="../scripts/abi_encode.py" 7 | 8 | rm -rf test.db 9 | 10 | echo 'test deploy fib_recur contract' 11 | # deploy contract 12 | /opt/chain_mockcli -f fib_recur.wasm --action deploy --print-time --enable-gas-meter -i 0x 13 | 14 | echo 'test fibonacci(30)' 15 | # query fibonacci(uint256) 16 | FIB1_ABI_DATA=$($ABI_ENCODE "fibonacciTailOptimized(uint256)" "30") 17 | output=$(/opt/chain_mockcli -f fib_recur.wasm --action call --print-time --enable-gas-meter -i $FIB1_ABI_DATA) 18 | run_cmd_and_grep "$output" 'evm finish with result hex: 00000000000000000000000000000000000000000000000000000000000cb228' 19 | 20 | 21 | echo 'test deploy fib contract' 22 | # deploy contract 23 | /opt/chain_mockcli -f fib.wasm --action deploy --print-time --enable-gas-meter -i 0x 24 | 25 | echo 'test fibonacci(30)' 26 | # query fibonacci(uint256) 27 | FIB1_ABI_DATA=$($ABI_ENCODE "fibonacciTailOptimized(uint256)" "30") 28 | output=$(/opt/chain_mockcli -f fib.wasm --action call --print-time --enable-gas-meter -i $FIB1_ABI_DATA) 29 | run_cmd_and_grep "$output" 'evm finish with result hex: 00000000000000000000000000000000000000000000000000000000000cb228' 30 | -------------------------------------------------------------------------------- /examples/foundry_erc20/.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /lib 3 | /node_modules 4 | /cache 5 | /test.db 6 | .DS_Store 7 | /*.wasm 8 | /*.cbin 9 | /*.cbin.hex 10 | /*.wat 11 | /*.tar.gz 12 | -------------------------------------------------------------------------------- /examples/foundry_erc20/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../../tools/build_utils.sh 5 | 6 | # install solidity 7 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 8 | 9 | # install foundry 10 | # curl -L https://foundry.paradigm.xyz | bash 11 | 12 | setup_build_mode ${1:-release} 13 | 14 | forge clean 15 | forge build --extra-output-files ir-optimized 16 | # for debug: forge build --extra-output-files ir 17 | # ir generated in out/TokenFactory.sol/TokenFactory.ir 18 | 19 | YUL_IR_PATH="out" 20 | # contracts to compile 21 | CONTRACTS=( 22 | "MyToken" 23 | "TokenFactory" 24 | ) 25 | 26 | compile_all_contracts CONTRACTS[@] "$YUL_IR_PATH" 27 | -------------------------------------------------------------------------------- /examples/foundry_erc20/build_forge_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../../tools/build_utils.sh 5 | 6 | # install solidity 7 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 8 | 9 | # install foundry 10 | # curl -L https://foundry.paradigm.xyz | bash 11 | 12 | setup_build_mode ${1:-release} 13 | 14 | forge clean 15 | cp ../scripts/WasmTestVM.sol src/WasmTestVM.sol 16 | forge test --extra-output-files ir-optimized 17 | rm src/WasmTestVM.sol 18 | 19 | YUL_IR_PATH="out" 20 | 21 | # contracts to compile 22 | CONTRACTS=( 23 | "WasmTestVM" 24 | "TestContract" 25 | ) 26 | 27 | compile_all_contracts CONTRACTS[@] "$YUL_IR_PATH" 28 | -------------------------------------------------------------------------------- /examples/foundry_erc20/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | # now 0.8.25 is supported best 3 | solc-version = "0.8.25" 4 | optimizer = true 5 | yul = true 6 | via_ir = true 7 | verbosity = 3 8 | 9 | [profile.ci.fuzz] 10 | runs = 10_000 11 | -------------------------------------------------------------------------------- /examples/foundry_erc20/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | forge install --no-commit foundry-rs/forge-std 5 | forge install --no-commit OpenZeppelin/openzeppelin-contracts 6 | forge install --no-commit OpenZeppelin/openzeppelin-contracts-upgradeable 7 | -------------------------------------------------------------------------------- /examples/foundry_erc20/src/MyToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract MyToken is ERC20Burnable, Ownable { 8 | // Constant configuration 9 | uint8 private constant TOKEN_DECIMALS = 18; 10 | string private constant TOKEN_NAME = "My First Token"; 11 | string private constant TOKEN_SYMBOL = "MFT"; 12 | 13 | // Constructor 14 | constructor(uint256 initialSupply) ERC20(TOKEN_NAME, TOKEN_SYMBOL) Ownable(msg.sender) { 15 | // Initialize token, mint initial supply 16 | _mint(msg.sender, initialSupply); 17 | } 18 | 19 | // Optional mint function (inherited from ERC20Burnable) 20 | function mint(address to, uint256 amount) public onlyOwner { 21 | _mint(to, amount); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /examples/foundry_erc20/src/TokenFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./MyToken.sol"; 5 | 6 | contract TokenFactory { 7 | // Store addresses of deployed tokens 8 | address[] public deployedTokens; 9 | 10 | // Event: New token deployment 11 | event TokenDeployed( 12 | address indexed tokenAddress, 13 | address indexed owner, 14 | uint256 initialSupply 15 | ); 16 | 17 | // Function to deploy a new token 18 | function createToken(uint256 initialSupply) public returns (address) { 19 | // Create new MyToken instance 20 | MyToken newToken = new MyToken(initialSupply); 21 | 22 | // Transfer ownership to creator 23 | newToken.transferOwnership(msg.sender); 24 | 25 | // Record deployed token address 26 | deployedTokens.push(address(newToken)); 27 | 28 | // Emit event 29 | emit TokenDeployed(address(newToken), msg.sender, initialSupply); 30 | 31 | return address(newToken); 32 | } 33 | 34 | // Get total count of deployed tokens 35 | function getDeployedTokensCount() public view returns (uint256) { 36 | return deployedTokens.length; 37 | } 38 | 39 | // Get token address at specific index 40 | function getTokenAt(uint256 index) public view returns (address) { 41 | require(index < deployedTokens.length, "Invalid token index"); 42 | return deployedTokens[index]; 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/foundry_erc20/test/TestContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import "forge-std/Test.sol"; 5 | 6 | import "src/MyToken.sol"; 7 | 8 | contract TestContract is Test { 9 | MyToken c; 10 | 11 | address owner = address(this); 12 | address user2 = address(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4); 13 | address user3 = address(0x5B38Da6a701C568545DcFCb03fcb875F56BEDDc5); 14 | 15 | uint256 constant INITIAL_SUPPLY = 1000; 16 | uint256 constant MINT_AMOUNT = 7; 17 | uint256 constant TRANSFER_AMOUNT = 5; 18 | uint256 constant APPROVE_AMOUNT = 10; 19 | 20 | function setUp() public { 21 | c = new MyToken(INITIAL_SUPPLY); 22 | } 23 | 24 | function testDeployAndTotalSupply() public { 25 | assertEq(c.totalSupply(), INITIAL_SUPPLY, "Initial total supply should match"); 26 | } 27 | 28 | function testMint() public { 29 | uint256 user2_balance = c.balanceOf(user2); 30 | c.mint(user2, MINT_AMOUNT); 31 | assertEq(c.balanceOf(user2), user2_balance + MINT_AMOUNT, "User2 balance should be correct after mint"); 32 | } 33 | 34 | function testApproveAndAllowance() public { 35 | uint256 user2_allowance = c.allowance(owner, user2); 36 | c.approve(user2, APPROVE_AMOUNT); 37 | assertEq(c.allowance(owner, user2), user2_allowance + APPROVE_AMOUNT, "Allowance should be set correctly"); 38 | } 39 | 40 | function testTransfer() public { 41 | assertEq(c.balanceOf(address(this)), INITIAL_SUPPLY, "Owner balance should be 0 before testTransfer"); 42 | c.mint(address(this), MINT_AMOUNT); 43 | assertEq(c.balanceOf(address(this)), INITIAL_SUPPLY + MINT_AMOUNT, "Owner balance should be 5 before transfer"); 44 | 45 | uint256 user2_balance = c.balanceOf(user2); 46 | c.transfer(user2, TRANSFER_AMOUNT); 47 | 48 | assertEq(c.balanceOf(user2), user2_balance + TRANSFER_AMOUNT, "User2 should receive correct amount"); 49 | assertEq(c.balanceOf(address(this)), INITIAL_SUPPLY + MINT_AMOUNT - TRANSFER_AMOUNT, "Owner balance should be decreased"); 50 | } 51 | 52 | event ValueLogged(uint256 value); 53 | 54 | event AddressLogged(address value); 55 | 56 | function testTransferFrom() public { 57 | emit AddressLogged(address(this)); 58 | uint256 user2_allowance = c.allowance(address(this), user2); 59 | vm.startPrank(owner); 60 | uint256 this_balance = c.balanceOf(address(this)); 61 | emit ValueLogged(this_balance); 62 | c.mint(address(this), MINT_AMOUNT); 63 | emit ValueLogged(c.balanceOf(address(this))); 64 | c.approve(user2, APPROVE_AMOUNT); 65 | vm.stopPrank(); 66 | 67 | vm.startPrank(user2); 68 | uint256 user3_balance = c.balanceOf(user3); 69 | emit ValueLogged(user3_balance); 70 | c.transferFrom(address(this), user3, APPROVE_AMOUNT); 71 | vm.stopPrank(); 72 | emit ValueLogged(c.balanceOf(address(this))); 73 | 74 | assertEq(c.balanceOf(user3), user3_balance + APPROVE_AMOUNT, "User3 should receive correct amount"); 75 | assertEq(c.balanceOf(address(this)), this_balance + MINT_AMOUNT - APPROVE_AMOUNT, "Owner balance should be decreased"); 76 | assertEq(c.allowance(address(this), user2), user2_allowance + APPROVE_AMOUNT - APPROVE_AMOUNT, "Allowance should be decreased"); 77 | } 78 | 79 | // CompleteFlow: deploy -> mint -> transfer -> approve -> transferFrom 80 | function testCompleteFlow() public { 81 | uint256 owner_balance = c.balanceOf(owner); 82 | uint256 user2_balance = c.balanceOf(user2); 83 | uint256 user2_allowance_before_approve = c.allowance(owner, user2); 84 | uint256 user3_balance = c.balanceOf(user3); 85 | 86 | vm.startPrank(owner); 87 | 88 | assertEq(c.totalSupply(), INITIAL_SUPPLY, "Initial total supply should match"); 89 | 90 | c.mint(owner, MINT_AMOUNT); 91 | assertEq(c.balanceOf(owner), owner_balance + MINT_AMOUNT, "Owner should receive minted tokens"); 92 | 93 | c.transfer(user2, TRANSFER_AMOUNT); 94 | assertEq(c.balanceOf(user2), user2_balance + TRANSFER_AMOUNT, "User2 should receive transferred tokens"); 95 | 96 | c.approve(user2, APPROVE_AMOUNT); 97 | uint256 user2_allowance_after_approve = c.allowance(owner, user2); 98 | assertEq(c.allowance(owner, user2), user2_allowance_before_approve + APPROVE_AMOUNT, "Allowance should be set correctly"); 99 | 100 | vm.stopPrank(); 101 | 102 | vm.startPrank(user2); 103 | c.transferFrom(owner, user3, APPROVE_AMOUNT); 104 | vm.stopPrank(); 105 | 106 | assertEq(c.balanceOf(user3), user3_balance + APPROVE_AMOUNT, "User3 should receive correct amount"); 107 | assertEq(c.allowance(owner, user2), user2_allowance_after_approve - APPROVE_AMOUNT, "Allowance should be decreased"); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/foundry_erc20/test_forge_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd .. 5 | source ../tools/forge_test_utils.sh 6 | cd foundry_erc20 7 | 8 | YUL_IR_PATH="out" 9 | wasm_vm_file="$YUL_IR_PATH/WasmTestVM.wasm" 10 | contract_file="$YUL_IR_PATH/TestContract.wasm" 11 | # deploy WasmTestVM, Cheat code address from: abstract contract CommonBase, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D 12 | WASM_TEST_VM_DEPLOY_ADDR=0x7109709ecfa91a80626ff3989d68f67f5b1dd12d 13 | DEPLOYER_INITIALIZER_ADDR=0x11bbccddeeffaabbccddeeffaabbccddeeffaa11 14 | 15 | run_single_test() { 16 | init_test "$wasm_vm_file" "$WASM_TEST_VM_DEPLOY_ADDR" "$contract_file" "$DEPLOYER_INITIALIZER_ADDR" 17 | 18 | local wasm_file=$1 19 | local function_name=$2 20 | local expected_result=$3 21 | call_contract_function "$wasm_file" "$DEPLOYER_INITIALIZER_ADDR" "$function_name" "$expected_output" 22 | echo "Test success: $function_name" 23 | } 24 | 25 | # testDeployAndTotalSupply() - 0x39eb0c5c 26 | run_single_test $contract_file "testDeployAndTotalSupply()" 'evm finish with result hex: 00000000000000000000000000000000000000000000000000000000000003e8' 27 | # testMint() - 0x9642ddaf 28 | run_single_test $contract_file "testMint()" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000007' 29 | # testApproveAndAllowance() - 0xba5af22d 30 | run_single_test $contract_file "testApproveAndAllowance()" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 31 | # testTransfer() - 0xd591221f 32 | run_single_test $contract_file "testTransfer()" 'evm finish with result hex: 00000000000000000000000000000000000000000000000000000000000003ea' 33 | # testTransferFrom() - 0x70557298 34 | run_single_test $contract_file "testTransferFrom()" 'evm finish with result hex:' 35 | # testCompleteFlow() - 0xe44962e7 36 | run_single_test $contract_file "testCompleteFlow()" 'evm finish with result hex:' 37 | 38 | echo "All tests success!" 39 | -------------------------------------------------------------------------------- /examples/foundry_erc20/test_my_token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f test.db 5 | 6 | ../scripts/test_erc20.sh out/MyToken.wasm 7 | -------------------------------------------------------------------------------- /examples/foundry_erc20/test_token_factory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | rm -f test.db 7 | 8 | echo 'test deploy TokenFactory contract $wasm_file' 9 | # deploy contract (arg total supply(uint256)) 10 | /opt/chain_mockcli -f out/TokenFactory.wasm --action deploy -s 0x9988776655443322119900112233445566778899 -i 0x 11 | # Call createToken(uint256 initialSupply=0x68656c6c6f000000000000000000000000000000000000000000000000000000) 12 | # to deploy new MyToken contract and return token contract address 13 | # The output will contain "evm finish with result hex: <32-byte hex contract address>", 14 | # which needs to be extracted using grep 15 | 16 | echo 'test createToken function' 17 | output=$(/opt/chain_mockcli -f out/TokenFactory.wasm --action call --print-time -s 0x9988776655443322119900112233445566778899 -i 0x2d571cc468656c6c6f000000000000000000000000000000000000000000000000000000) 18 | run_cmd_and_grep "$output" 'evm finish with result hex:' 19 | 20 | token_contract_address=$(grep_output_last_result_address "$output") 21 | factory_contract_address="aabbccddeeffaabbccddeeffaabbccddeeffaabb" 22 | 23 | echo "deployed ERC20 token contract address: $token_contract_address" 24 | 25 | # Test the generated token contract address 26 | ../scripts/test_erc20_by_address.sh $token_contract_address '9988776655443322119900112233445566778899' 27 | -------------------------------------------------------------------------------- /examples/perf_example/.gitignore: -------------------------------------------------------------------------------- 1 | /out 2 | /lib 3 | /node_modules 4 | /cache 5 | /test.db 6 | .DS_Store 7 | /*.wasm 8 | /*.cbin 9 | /*.cbin.hex 10 | /*.wat 11 | /*.tar.gz 12 | -------------------------------------------------------------------------------- /examples/perf_example/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # install foundry 8 | # curl -L https://foundry.paradigm.xyz | bash 9 | 10 | # Determine the build mode 11 | BUILD_MODE=${1:-release} 12 | 13 | echo "Building in $BUILD_MODE mode" 14 | 15 | # --enable-little-endian-storage-load-store 16 | YUL2WASM_EXTRA_ARGS="--verbose" 17 | 18 | # if env ENABLE_LITTLE_ENDIAN_STORAGE == "ON", then add --enable-little-endian-storage-load-store 19 | if [ "$ENABLE_LITTLE_ENDIAN_STORAGE" == "ON" ]; then 20 | YUL2WASM_EXTRA_ARGS="$YUL2WASM_EXTRA_ARGS --enable-little-endian-storage-load-store" 21 | fi 22 | 23 | # Set the yul2wasm path based on the build mode 24 | if [ "$BUILD_MODE" == "release" ]; then 25 | YUL2WASM_PATH="../../target/release/yul2wasm" 26 | else 27 | YUL2WASM_PATH="../../target/debug/yul2wasm" 28 | YUL2WASM_EXTRA_ARGS="$YUL2WASM_EXTRA_ARGS --debug" 29 | fi 30 | 31 | # npm install @openzeppelin/contracts 32 | 33 | forge build --extra-output-files ir-optimized 34 | 35 | # build MyERC721 36 | $YUL2WASM_PATH --input out/MyERC721.sol/MyERC721.iropt --output out/MyERC721.wasm $YUL2WASM_EXTRA_ARGS 37 | wasm2wat -o out/MyERC721.wat out/MyERC721.wasm 38 | echo 'MyERC721 compiled to wasm in out/MyERC721.wasm' 39 | 40 | # build MyERC1155 41 | $YUL2WASM_PATH --input out/MyERC1155.sol/MyERC1155.iropt --output out/MyERC1155.wasm $YUL2WASM_EXTRA_ARGS 42 | wasm2wat -o out/MyERC1155.wat out/MyERC1155.wasm 43 | echo 'MyERC1155 compiled to wasm in out/MyERC1155.wasm' 44 | 45 | # build GLDToken 46 | $YUL2WASM_PATH --input out/GLDToken.sol/GLDToken.iropt --output out/GLDToken.wasm $YUL2WASM_EXTRA_ARGS --enable-all-optimizers --default_ret_type u256 47 | wasm2wat -o out/GLDToken.wat out/GLDToken.wasm 48 | echo 'GLDToken compiled to wasm in out/GLDToken.wasm' 49 | 50 | # build fib_recur 51 | $YUL2WASM_PATH --input out/fib_recur.sol/FibonacciRecurTest.iropt --output out/fib_recur.wasm $YUL2WASM_EXTRA_ARGS 52 | wasm2wat -o out/fib_recur.wat out/fib_recur.wasm 53 | echo 'fib_recur compiled to wasm in out/fib_recur.wasm' 54 | 55 | # build counter 56 | echo "building counter contract" 57 | $YUL2WASM_PATH --input out/counter.sol/counter.iropt --output out/counter.wasm $YUL2WASM_EXTRA_ARGS 58 | wasm2wat -o out/counter.wat out/counter.wasm 59 | echo 'counter compiled to wasm in out/counter.wasm' 60 | 61 | # build TestToStringStore to test_to_string_store.wasm 62 | $YUL2WASM_PATH --input out/test_to_string_store.sol/TestToStringStore.iropt --output out/test_to_string_store.wasm $YUL2WASM_EXTRA_ARGS 63 | wasm2wat -o out/test_to_string_store.wat out/test_to_string_store.wasm 64 | echo 'test_to_string_store compiled to wasm in out/test_to_string_store.wasm' -------------------------------------------------------------------------------- /examples/perf_example/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | # now 0.8.25 is supported best 3 | solc-version = "0.8.25" 4 | optimizer = true 5 | yul = true 6 | via_ir = true 7 | verbosity = 3 8 | 9 | [profile.ci.fuzz] 10 | runs = 10_000 11 | -------------------------------------------------------------------------------- /examples/perf_example/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | forge install --no-commit foundry-rs/forge-std 5 | forge install --no-commit OpenZeppelin/openzeppelin-contracts 6 | forge install --no-commit OpenZeppelin/openzeppelin-contracts-upgradeable 7 | -------------------------------------------------------------------------------- /examples/perf_example/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CUR_PATH=`pwd` 6 | cd ../../stdlib 7 | make clean 8 | make release 9 | cd $CUR_PATH 10 | cargo build --release 11 | 12 | cd $CUR_PATH 13 | 14 | # build in release mode 15 | ./build.sh release 16 | 17 | # test generated released mode wasm files 18 | echo 'testing generated release mode wasm files...' 19 | ./test_gldtoken.sh 20 | ./test_erc721.sh 21 | ./test_erc1155.sh 22 | ./test_fib.sh 23 | echo 'tests done' 24 | 25 | # package wasm files and source files, test scripts to tar.gz 26 | tar czf test_perf_token_wasm.tar.gz out/MyERC721.wasm out/MyERC1155.wasm out/counter.wasm out/fib_recur.wasm out/GLDToken.wasm test_erc721.sh test_erc1155.sh test_gldtoken.sh test_fib.sh src/MyERC721.sol src/MyERC1155.sol src/counter.sol src/fib_recur.sol src/GLDToken.sol 27 | echo "test_perf_token_wasm.tar.gz created" 28 | -------------------------------------------------------------------------------- /examples/perf_example/src/GLDToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Compatible with OpenZeppelin Contracts ^5.0.0 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract GLDToken is ERC20 { 8 | constructor(uint256 initialSupply) ERC20("Gold", "GLD") { 9 | _mint(msg.sender, initialSupply); 10 | 11 | } 12 | function mint(address to, uint256 amount) public { 13 | _mint(to, amount); 14 | } 15 | } -------------------------------------------------------------------------------- /examples/perf_example/src/MyERC1155.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract MyERC1155 is ERC1155, Ownable { 8 | // Token name 9 | string public name; 10 | // Token symbol 11 | string public symbol; 12 | 13 | constructor() ERC1155("https://game.example/api/item/{id}.json") Ownable(msg.sender) { 14 | name = "My Game Items"; 15 | symbol = "TestGameSymbol"; 16 | } 17 | 18 | // Mint function that only owner can call 19 | function mint( 20 | address account, 21 | uint256 id, 22 | uint256 amount, 23 | bytes memory data 24 | ) public onlyOwner { 25 | _mint(account, id, amount, data); 26 | } 27 | 28 | // Batch mint function that only owner can call 29 | function mintBatch( 30 | address to, 31 | uint256[] memory ids, 32 | uint256[] memory amounts, 33 | bytes memory data 34 | ) public onlyOwner { 35 | _mintBatch(to, ids, amounts, data); 36 | } 37 | } -------------------------------------------------------------------------------- /examples/perf_example/src/MyERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract MyERC721 is ERC721, Ownable { 8 | uint256 private _nextTokenId; 9 | 10 | constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {} 11 | 12 | function mint(address to) public onlyOwner returns (uint256) { 13 | uint256 tokenId = _nextTokenId++; 14 | _safeMint(to, tokenId); 15 | return tokenId; 16 | } 17 | } -------------------------------------------------------------------------------- /examples/perf_example/src/counter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract counter{ 4 | 5 | uint public count; 6 | 7 | function increase() public { 8 | count++; 9 | } 10 | 11 | function decrease() public{ 12 | count--; 13 | } 14 | } -------------------------------------------------------------------------------- /examples/perf_example/src/fib_recur.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract FibonacciRecurTest { 4 | 5 | function fibonacci(uint n) public pure returns (uint) { 6 | if (n == 0) return 0; 7 | if (n == 1) return 1; 8 | return fibonacci(n - 1) + fibonacci(n - 2); 9 | } 10 | 11 | function fibonacciTailOptimized(uint n) public pure returns (uint) { 12 | return fibonacciTailRecursive(n, 0, 1); 13 | } 14 | 15 | function fibonacciTailRecursive(uint n, uint a, uint b) internal pure returns (uint) { 16 | if (n == 0) return a; 17 | if (n == 1) return b; 18 | return fibonacciTailRecursive(n - 1, b, a + b); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/perf_example/test_erc1155.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source ../scripts/common.sh 6 | 7 | # Clean up previous test database 8 | rm -f test.db 9 | 10 | # Set contract address and test accounts 11 | CONTRACT="0x1010000000000000000000000000000000000101" 12 | USER1="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 13 | USER2="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 14 | USER3="cccccccccccccccccccccccccccccccccccccccc" 15 | 16 | # Deploy contract 17 | echo "Deploying ERC1155 contract..." 18 | /opt/chain_mockcli --action deploy -t $CONTRACT -f out/MyERC1155.wasm -i 0x 19 | 20 | echo "Contract deployed at: $CONTRACT" 21 | 22 | # Function: mint(address account, uint256 id, uint256 amount, bytes memory data) 23 | # Parameters: account = $USER1, id = 1, amount = 100, data = 0xdddd 24 | echo "Testing mint..." 25 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x731133e9000000000000000000000000${USER1}0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002dddd000000000000000000000000000000000000000000000000000000000000) 26 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 27 | 28 | # Function: balanceOf(address account, uint256 id) 29 | # Parameters: account = $USER1, id = 1 30 | echo "Checking balance..." 31 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x00fdd58e000000000000000000000000${USER1}0000000000000000000000000000000000000000000000000000000000000001) 32 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000064\ngas used' 33 | 34 | # Function: setApprovalForAll(address operator, bool approved) 35 | # sender = $USER1 36 | # Parameters: operator = $USER2, approved = true 37 | echo "Testing setApprovalForAll..." 38 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -s 0x$USER1 -i 0xa22cb465000000000000000000000000${USER2}0000000000000000000000000000000000000000000000000000000000000001) 39 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 40 | 41 | # Function: safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) 42 | # sender = $USER2 43 | # Parameters: from = $USER1, to = $USER3, id = 1, amount = 50, data = 0x 44 | echo "Testing safeTransferFrom..." 45 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -s 0x$USER2 -i 0xf242432a000000000000000000000000${USER1}000000000000000000000000${USER3}0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002dddd000000000000000000000000000000000000000000000000000000000000) 46 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 47 | 48 | # Check final balances 49 | echo "Verifying final balances..." 50 | # Check USER1's balance 51 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x00fdd58e000000000000000000000000${USER1}0000000000000000000000000000000000000000000000000000000000000001) 52 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000032\ngas used' 53 | 54 | # Check USER3's balance 55 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x00fdd58e000000000000000000000000${USER3}0000000000000000000000000000000000000000000000000000000000000001) 56 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000032\ngas used' 57 | 58 | echo "All tests passed!" 59 | -------------------------------------------------------------------------------- /examples/perf_example/test_erc721.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source ../scripts/common.sh 6 | 7 | # Clean up previous test database 8 | rm -f test.db 9 | 10 | # Set contract address 11 | CONTRACT="0x1010000000000000000000000000000000000101" 12 | USER1="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 13 | USER2="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 14 | USER3="cccccccccccccccccccccccccccccccccccccccc" 15 | 16 | # Deploy contract using --action deploy 17 | echo "Deploying ERC721 contract..." 18 | /opt/chain_mockcli --action deploy -t $CONTRACT -f out/MyERC721.wasm -i 0x 19 | 20 | echo "Contract deployed at: $CONTRACT" 21 | 22 | # Function: mint(address to) 23 | # Parameters: to = $USER1 24 | echo "Testing mint..." 25 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x6a627842000000000000000000000000$USER1) 26 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000000\ngas used' 27 | 28 | # Function: ownerOf(uint256 tokenId) 29 | # Parameters: tokenId = 0 30 | echo "Checking ownership..." 31 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x6352211e0000000000000000000000000000000000000000000000000000000000000000) 32 | run_cmd_and_grep "$output" 'evm finish with result hex: 000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ngas used' 33 | 34 | # Function: balanceOf(address owner) 35 | # Parameters: owner = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 36 | echo "Checking balance..." 37 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x70a08231000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) 38 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001\ngas used' 39 | 40 | # Function: approve(address to, uint256 tokenId) 41 | # sender = $USER1 42 | # Parameters: 43 | # to = $USER2 44 | # tokenId = 0 45 | echo "Testing approve..." 46 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -s 0x$USER1 -i 0x095ea7b3000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0000000000000000000000000000000000000000000000000000000000000000) 47 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 48 | 49 | # Function: transferFrom(address from, address to, uint256 tokenId) 50 | # sender = USER2 51 | # Parameters: 52 | # from = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 53 | # to = $USER3 54 | # tokenId = 0 55 | echo "Testing transfer..." 56 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -s 0x$USER2 -i 0x23b872dd000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000cccccccccccccccccccccccccccccccccccccccc0000000000000000000000000000000000000000000000000000000000000000) 57 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 58 | 59 | # Function: ownerOf(uint256 tokenId) 60 | # Parameters: tokenId = 0 61 | echo "Verifying token final owner..." 62 | output=$(/opt/chain_mockcli --action call --print-time -t $CONTRACT -i 0x6352211e0000000000000000000000000000000000000000000000000000000000000000) 63 | run_cmd_and_grep "$output" 'evm finish with result hex: 000000000000000000000000cccccccccccccccccccccccccccccccccccccccc\ngas used' 64 | 65 | echo "All tests passed!" 66 | -------------------------------------------------------------------------------- /examples/perf_example/test_fib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | ABI_ENCODE="../scripts/abi_encode.py" 7 | 8 | rm -rf test.db 9 | 10 | echo 'test deploy fib_recur contract' 11 | # deploy contract 12 | /opt/chain_mockcli -f out/fib_recur.wasm --action deploy --print-time --enable-gas-meter -i 0x 13 | 14 | echo 'test fibonacci(30)' 15 | # query fibonacci(uint256) 16 | FIB1_ABI_DATA=$($ABI_ENCODE "fibonacciTailOptimized(uint256)" "30") 17 | output=$(/opt/chain_mockcli -f out/fib_recur.wasm --action call --print-time --enable-gas-meter -i $FIB1_ABI_DATA) 18 | run_cmd_and_grep "$output" 'evm finish with result hex: 00000000000000000000000000000000000000000000000000000000000cb228' 19 | -------------------------------------------------------------------------------- /examples/perf_example/test_gldtoken.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | rm -f test.db 5 | 6 | ../scripts/test_erc20.sh out/GLDToken.wasm 7 | -------------------------------------------------------------------------------- /examples/perf_example/test_to_string_store.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | rm -f test.db 7 | 8 | echo 'test deploy test_to_string_store contract' 9 | # deploy contract (arg total supply(uint256)) 10 | /opt/chain_mockcli -f out/test_to_string_store.wasm --action deploy -i 0x 11 | 12 | echo 'test tester(100)' 13 | output=$(/opt/chain_mockcli -f out/test_to_string_store.wasm --action call --print-time -i 0xa667472f0000000000000000000000000000000000000000000000000000000000000064) 14 | run_cmd_and_grep "$output" 'log data: 000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000053132333435000000000000000000000000000000000000000000000000000000' 15 | -------------------------------------------------------------------------------- /examples/scripts/README.md: -------------------------------------------------------------------------------- 1 | Some helper scripts 2 | ====== 3 | -------------------------------------------------------------------------------- /examples/scripts/WasmTestVM.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | contract WasmTestVM { 5 | /// Asserts that two `int256` values are equal and includes error message into revert string on failure. 6 | function assertEq(uint256 left, uint256 right, string memory err) public pure { 7 | require(left == right, err); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/scripts/abi_encode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) the DTVM authors Core Contributors 3 | # SPDX-License-Identifier: Apache-2.0 4 | # need python3 -m pip3 install -i https://mirrors.ustc.edu.cn/pypi/simple -r requirements.txt 5 | 6 | import sys 7 | import argparse 8 | from eth_abi import encode 9 | from eth_utils import function_signature_to_4byte_selector, to_hex 10 | from eth_utils.hexadecimal import remove_0x_prefix 11 | 12 | def parse_signature(sig): 13 | # Extract function name and param types from signature like "transfer(address,uint256)" 14 | name_end = sig.find('(') 15 | if name_end == -1: 16 | raise ValueError("Invalid function signature") 17 | 18 | name = sig[:name_end] 19 | params_str = sig[name_end+1:-1] 20 | param_types = params_str.split(',') if params_str else [] 21 | return name, param_types 22 | 23 | def convert_param(param, param_type): 24 | # Convert string parameters to appropriate Python types 25 | if param_type.startswith('uint') or param_type.startswith('int'): 26 | return int(param) 27 | elif param_type == 'address': 28 | return remove_0x_prefix(param) 29 | elif param_type == 'bool': 30 | return param.lower() == 'true' 31 | elif param_type.endswith('[]'): 32 | # Convert comma-separated string to list for dynamic arrays 33 | return [convert_param(p.strip(), param_type[:-2]) for p in param.split(',')] 34 | return param 35 | 36 | def main(): 37 | parser = argparse.ArgumentParser( 38 | description='Encode Solidity function calls into transaction calldata', 39 | formatter_class=argparse.RawDescriptionHelpFormatter, 40 | epilog=''' 41 | Examples: 42 | %(prog)s "transfer(address,uint256)" 0x1122334455667788990011223344556677889900 100 43 | %(prog)s "balanceOf(address)" 0x1122334455667788990011223344556677889900 44 | ''') 45 | 46 | parser.add_argument('signature', 47 | help='Solidity function signature (e.g., "transfer(address,uint256)")') 48 | parser.add_argument('params', 49 | nargs='*', 50 | help='Function parameters (should match the types in signature)') 51 | 52 | args = parser.parse_args() 53 | 54 | # Parse function signature 55 | func_name, param_types = parse_signature(args.signature) 56 | 57 | if len(args.params) != len(param_types): 58 | parser.error(f"Expected {len(param_types)} parameters for {args.signature}, got {len(args.params)}") 59 | 60 | try: 61 | # Convert parameters to appropriate types 62 | converted_params = [convert_param(p, t) for p, t in zip(args.params, param_types)] 63 | 64 | # Get function selector 65 | selector = function_signature_to_4byte_selector(args.signature) 66 | 67 | # Encode parameters 68 | encoded_params = encode(param_types, converted_params) 69 | 70 | # Combine selector with encoded parameters 71 | calldata = to_hex(selector + encoded_params) 72 | 73 | print(calldata) 74 | 75 | except ValueError as e: 76 | parser.error(f"Parameter conversion error: {str(e)}") 77 | except Exception as e: 78 | parser.error(f"Encoding error: {str(e)}") 79 | 80 | if __name__ == '__main__': 81 | main() 82 | -------------------------------------------------------------------------------- /examples/scripts/bin_to_hex.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import argparse 3 | 4 | def binary_to_hex_file(input_path, output_path): 5 | try: 6 | # Read the binary file 7 | with open(input_path, 'rb') as binary_file: 8 | # Read the entire file content 9 | binary_data = binary_file.read() 10 | 11 | # Convert binary data to hexadecimal string 12 | hex_string = binary_data.hex() 13 | 14 | # Write the hexadecimal string to a file 15 | with open(output_path, 'w') as hex_file: 16 | hex_file.write(hex_string) 17 | 18 | print(f"Successfully converted {input_path} to {output_path}") 19 | 20 | except FileNotFoundError: 21 | print(f"Error: File {input_path} not found") 22 | except PermissionError: 23 | print(f"Error: No permission to read {input_path} or write to {output_path}") 24 | except Exception as e: 25 | print(f"An unknown error occurred: {e}") 26 | 27 | def main(): 28 | # Create argument parser 29 | parser = argparse.ArgumentParser(description='Convert a binary file to a hexadecimal file') 30 | 31 | # Add input and output file arguments 32 | parser.add_argument('input', help='Path to the input binary file') 33 | parser.add_argument('output', help='Path to the output hexadecimal file') 34 | 35 | # Parse command line arguments 36 | args = parser.parse_args() 37 | 38 | # Call the conversion function 39 | binary_to_hex_file(args.input, args.output) 40 | 41 | # Ensure the script runs only when executed directly 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /examples/scripts/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function run_cmd_and_grep() { 4 | # run command, echo result, and grep $grep. if exit, not run continue 5 | local exit_code=$? 6 | local output="$1" 7 | local grep_pattern="$2" 8 | 9 | # Echo the output 10 | echo "$output" 11 | 12 | # Check if the command was successful 13 | if [ $exit_code -ne 0 ]; then 14 | echo "Command failed with exit code $exit_code" 15 | exit $exit_code 16 | fi 17 | 18 | # Check if the output matches the grep pattern 19 | # echo "$output" | grep -E -zo "$grep_pattern" 20 | echo "matching pattern: $grep_pattern" 21 | echo "$output" | awk -v pattern="$grep_pattern" 'BEGIN { RS="\0" } $0 ~ pattern { found=1 } END { if (!found) exit 1 }' 22 | echo "grep pattern matched" 23 | } 24 | 25 | function grep_output_last_result_address() { 26 | # grep the last result address from the output 27 | local output="$1" 28 | # Find the last line with the result hex using awk 29 | local last_line=$(echo "$output" | awk '/evm (finish|revert) with result hex:/ {last_match = $0} END {print last_match}') 30 | # Extract the address (last field, skipping first 24 chars) 31 | local result_hex=$(echo "$last_line" | awk '{print substr($NF, 25)}') 32 | echo "$result_hex" 33 | } 34 | 35 | function grep_output_last_result_hex() { 36 | # grep the last result hex from the output 37 | local output="$1" 38 | # Find the last line with the result hex using awk 39 | local last_line=$(echo "$output" | awk '/evm (finish|revert) with result hex:/ {last_match = $0} END {print last_match}') 40 | # Extract the hex value (last field) from that line 41 | local result_hex=$(echo "$last_line" | awk '{print $NF}') 42 | echo "$result_hex" 43 | } 44 | -------------------------------------------------------------------------------- /examples/scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | eth-abi 2 | eth-utils>=2.1.0 3 | eth-hash[pycryptodome]>=0.5.0 4 | -------------------------------------------------------------------------------- /examples/scripts/test_abi_encode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) the DTVM authors Core Contributors 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # This script is used to test the ABI encoding of ERC20 functions using the abi_encode.py script. 6 | # It requires the abi_encode.py script to be in the same directory as this script. 7 | # The script will test the encoding of various ERC20 functions and print the results to the console. 8 | 9 | set -e 10 | 11 | source ../scripts/common.sh 12 | 13 | # Make script executable 14 | chmod +x abi_encode.py 15 | 16 | echo "Testing ERC20 function encodings..." 17 | 18 | # Test address and values 19 | ADDR1="0x1122334455667788990011223344556677889900" 20 | ADDR2="0x2233445566778899001122334455667788990011" 21 | AMOUNT="1000000000000000000" # 1 token with 18 decimals 22 | 23 | echo -e "\n1. Testing transfer(address,uint256)" 24 | output=$(./abi_encode.py "transfer(address,uint256)" $ADDR1 $AMOUNT) 25 | run_cmd_and_grep "$output" '0xa9059cbb00000000000000000000000011223344556677889900112233445566778899000000000000000000000000000000000000000000000000000de0b6b3a7640000' # Check for transfer function selector 26 | 27 | echo -e "\n2. Testing balanceOf(address)" 28 | output=$(./abi_encode.py "balanceOf(address)" $ADDR1) 29 | run_cmd_and_grep "$output" '0x70a082310000000000000000000000001122334455667788990011223344556677889900' 30 | 31 | echo -e "\n3. Testing totalSupply()" 32 | output=$(./abi_encode.py "totalSupply()") 33 | run_cmd_and_grep "$output" '0x18160ddd' 34 | 35 | echo -e "\n4. Testing approve(address,uint256)" 36 | output=$(./abi_encode.py "approve(address,uint256)" $ADDR2 $AMOUNT) 37 | run_cmd_and_grep "$output" '0x095ea7b300000000000000000000000022334455667788990011223344556677889900110000000000000000000000000000000000000000000000000de0b6b3a7640000' 38 | 39 | echo -e "\n5. Testing allowance(address,address)" 40 | output=$(./abi_encode.py "allowance(address,address)" $ADDR1 $ADDR2) 41 | run_cmd_and_grep "$output" '0xdd62ed3e00000000000000000000000011223344556677889900112233445566778899000000000000000000000000002233445566778899001122334455667788990011' 42 | 43 | echo -e "\n6. Testing swapExactTokensForTokens(uint256,uint256,address[],address,uint256)" 44 | output=$(./abi_encode.py "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)" "80" "0" "0x5100000000000000000000000000000000000005,0x5100000000000000000000000000000000000006" "0x5100000000000000000000000000000000000006" "99999999999999999") 45 | run_cmd_and_grep "$output" '0x38ed17390000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000005100000000000000000000000000000000000006000000000000000000000000000000000000000000000000016345785d89ffff000000000000000000000000000000000000000000000000000000000000000200000000000000000000000051000000000000000000000000000000000000050000000000000000000000005100000000000000000000000000000000000006' 46 | 47 | echo -e "\nAll tests completed." 48 | -------------------------------------------------------------------------------- /examples/scripts/test_erc20.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | # Read input ERC20 wasm file and execute ERC20 test cases similar to test_my_token.sh 7 | wasm_file=$1 8 | # Check if wasm file path is provided and file exists 9 | if [ -z "$wasm_file" ] || [ ! -f "$wasm_file" ]; then 10 | echo "Usage: $0 " 11 | exit 1 12 | fi 13 | 14 | echo 'test deploy ERC20 contract $wasm_file' 15 | # deploy contract (arg total supply(uint256)) 16 | /opt/chain_mockcli -f $wasm_file --action deploy --print-time --enable-gas-meter -s 0x9988776655443322119900112233445566778899 -i 0x68656c6c6f000000000000000000000000000000000000000000000000000000 17 | # total supply is optional here 18 | # /opt/chain_mockcli -f $wasm_file --action deploy -i 0x 19 | echo 'test totalSupply after deploy erc20' 20 | # query totalSupply() 21 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0x18160ddd) 22 | run_cmd_and_grep "$output" 'evm finish with result hex: 68656c6c6f000000000000000000000000000000000000000000000000000000' 23 | 24 | echo 'test mint' 25 | # mint(owner,amount) 26 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -s 0x9988776655443322119900112233445566778899 -i 0x40c10f1900000000000000000000000000112233445566778899001122334455667788990000000000000000000000000000000000000000000000000000000000000007) 27 | run_cmd_and_grep "$output" 'evm finish with result hex: \ngas used' 28 | 29 | echo 'test balanceOf after mint' 30 | # balanceOf(address) after mint 68656c6c6f000000000000000000000000000000000000000000000000000007 when has total_supply big 31 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0x70a082310000000000000000000000000011223344556677889900112233445566778899) 32 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000007' 33 | 34 | echo 'test transfer from owner to user2' 35 | # transfer from owner to user2 36 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000005) 37 | run_cmd_and_grep "$output" 'evm finish with result hex:' 38 | 39 | echo 'test query balanceOf after transfer' 40 | # balanceOf(address) 41 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0x70a082310000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4) 42 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000005' 43 | 44 | # test approve, allowance, transferFrom 45 | echo 'test approve, allowance, transferFrom' 46 | 47 | # approve to user2 48 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0x095ea7b30000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001) 49 | run_cmd_and_grep "$output" 'evm finish with result hex:' 50 | 51 | # query allowance to user2 (sender is 0x0011223344556677889900112233445566778899) 52 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0xdd62ed3e00000000000000000000000000112233445566778899001122334455667788990000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4) 53 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 54 | 55 | # transferFrom from 0x0011223344556677889900112233445566778899 to user3 (send by user2) 56 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter --sender-address-hex 0x5b38da6a701c568545dcfcb03fcb875f56beddc4 -i 0x23b872dd00000000000000000000000000112233445566778899001122334455667788990000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc50000000000000000000000000000000000000000000000000000000000000001) 57 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 58 | 59 | # query balanceOf user3 60 | output=$(/opt/chain_mockcli -f $wasm_file --action call --print-time --enable-gas-meter -i 0x70a082310000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc5) 61 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 62 | 63 | echo 'all ERC20 tests success' 64 | -------------------------------------------------------------------------------- /examples/scripts/test_erc20_by_address.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | # Read input ERC20 contract address and execute ERC20 test cases similar to test_my_token.sh 6 | erc20_contract_addr=$1 7 | # Need to pass in the token owner contract address which has permission to mint tokens 8 | # We use this address to test minting to bypass permission checks 9 | token_owner_addr=$2 10 | 11 | # Exit with error if contract address or owner address is not provided 12 | if [ -z "$erc20_contract_addr" ] || [ -z "$token_owner_addr" ]; then 13 | echo "Usage: $0 " 14 | exit 1 15 | fi 16 | 17 | # This assumes a fixed amount of tokens were minted during deployment 18 | echo 'test totalSupply after deploy erc20' 19 | # query totalSupply() 20 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0x18160ddd) 21 | run_cmd_and_grep "$output" 'evm finish with result hex: 68656c6c6f000000000000000000000000000000000000000000000000000000' 22 | 23 | echo 'test mint' 24 | # mint(token_owner_addr,amount) 25 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -s 0x$token_owner_addr -i 0x40c10f1900000000000000000000000000112233445566778899001122334455667788990000000000000000000000000000000000000000000000000000000000000007) 26 | run_cmd_and_grep "$output" 'evm finish with result hex:' 27 | 28 | echo 'test balanceOf after mint' 29 | # balanceOf(address) after mint 68656c6c6f000000000000000000000000000000000000000000000000000007 when has total_supply big 30 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0x70a082310000000000000000000000000011223344556677889900112233445566778899) 31 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000007' 32 | 33 | echo 'test transfer from owner to user2' 34 | # transfer from owner to user2 35 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000005) 36 | run_cmd_and_grep "$output" 'evm finish with result hex:' 37 | 38 | echo 'test query balanceOf after transfer' 39 | # balanceOf(address) 40 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0x70a082310000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4) 41 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000005' 42 | 43 | # test approve, allowance, transferFrom 44 | echo 'test approve, allowance, transferFrom' 45 | 46 | # approve to user2 47 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0x095ea7b30000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001) 48 | run_cmd_and_grep "$output" 'evm finish with result hex:' 49 | 50 | # query allowance to user2 (sender is 0x0011223344556677889900112233445566778899) 51 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0xdd62ed3e00000000000000000000000000112233445566778899001122334455667788990000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4) 52 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 53 | 54 | # transferFrom from 0x0011223344556677889900112233445566778899 to user3 (send by user2) 55 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time --sender-address-hex 0x5b38da6a701c568545dcfcb03fcb875f56beddc4 -i 0x23b872dd00000000000000000000000000112233445566778899001122334455667788990000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc50000000000000000000000000000000000000000000000000000000000000001) 56 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 57 | 58 | # query balanceOf user3 59 | output=$(/opt/chain_mockcli -t $erc20_contract_addr --action call --print-time -i 0x70a082310000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc5) 60 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 61 | 62 | echo 'all ERC20 tests success' 63 | -------------------------------------------------------------------------------- /examples/scripts/test_yul_erc20.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # read input yul file of erc20, compile it to wasm, then execute test 5 | yul_file=$1 6 | BUILD_MODE=${2:-release} 7 | 8 | echo "Building in $BUILD_MODE mode" 9 | 10 | # Set the yul2wasm path based on the build mode 11 | if [ "$BUILD_MODE" == "release" ]; then 12 | YUL2WASM_PATH="../../target/release/yul2wasm" 13 | else 14 | YUL2WASM_PATH="../../target/debug/yul2wasm" 15 | fi 16 | 17 | # if yul_file is not provided, or file not exist, then error 18 | if [ -z "$yul_file" ] || [ ! -f "$yul_file" ]; then 19 | echo "Usage: $0 " 20 | exit 1 21 | fi 22 | output_wasm_file="${yul_file}.wasm" 23 | 24 | # compile yul file 25 | $YUL2WASM_PATH --input $yul_file --output $output_wasm_file --verbose --debug 26 | echo "WASM file compiled to $output_wasm_file" 27 | 28 | rm -f test.db 29 | 30 | # SCRIPTS_DIR is the directory where the current script is located 31 | SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 32 | 33 | # execute test 34 | $SCRIPTS_DIR/test_erc20.sh $output_wasm_file 35 | -------------------------------------------------------------------------------- /examples/test_cases/.gitignore: -------------------------------------------------------------------------------- 1 | /*.out.ll 2 | /*.out.s 3 | /*.wasm 4 | /*.cbin 5 | /*.cbin.hex 6 | /*.wat 7 | /*.yul 8 | /test.db 9 | -------------------------------------------------------------------------------- /examples/test_cases/build_test_init_code_hash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # install solidity 5 | # https://docs.soliditylang.org/en/latest/installing-solidity.html 6 | 7 | # Determine the build mode 8 | BUILD_MODE=${1:-release} 9 | 10 | echo "Building in $BUILD_MODE mode" 11 | 12 | YUL2WASM_EXTRA_ARGS="--verbose" 13 | 14 | # Set the yul2wasm path based on the build mode 15 | if [ "$BUILD_MODE" == "release" ]; then 16 | YUL2WASM_PATH="../../target/release/yul2wasm" 17 | else 18 | YUL2WASM_PATH="../../target/debug/yul2wasm" 19 | YUL2WASM_EXTRA_ARGS="--verbose --debug" 20 | fi 21 | 22 | solc --ir --optimize-yul -o . --overwrite test_init_code_hash.sol 23 | 24 | $YUL2WASM_PATH --input TestInitCodeHashParent.yul --output test_init_code_hash.wasm $YUL2WASM_EXTRA_ARGS 25 | wasm2wat -o test_init_code_hash.wat test_init_code_hash.wasm 26 | -------------------------------------------------------------------------------- /examples/test_cases/test_init_code_hash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../scripts/common.sh 5 | 6 | ABI_ENCODE="../scripts/abi_encode.py" 7 | 8 | rm -rf test.db 9 | 10 | echo 'test deploy contract' 11 | # deploy contract 12 | /opt/chain_mockcli -f test_init_code_hash.wasm --action deploy --print-time --enable-gas-meter -i 0x 13 | 14 | echo 'test calculate_create2_addr()' 15 | # query fibonacci(uint256) 16 | CALCULATE_CREATE2_ADDR_ABI_DATA=$($ABI_ENCODE "calculate_create2_addr()") 17 | output=$(/opt/chain_mockcli -f test_init_code_hash.wasm --action call --print-time --enable-gas-meter -i $CALCULATE_CREATE2_ADDR_ABI_DATA) 18 | run_cmd_and_grep "$output" 'evm finish with result hex: ' 19 | echo 'test calculate_create2_addr() end' 20 | 21 | echo 'test create_child_by_create2()' 22 | # query fibonacci(uint256) 23 | CREATE_CHILD_BY_CREATE2_ABI_DATA=$($ABI_ENCODE "create_child_by_create2()") 24 | output=$(/opt/chain_mockcli -f test_init_code_hash.wasm --action call --print-time --enable-gas-meter -i $CREATE_CHILD_BY_CREATE2_ABI_DATA) 25 | run_cmd_and_grep "$output" 'evm finish with result hex: 0000000000000000000000000000000000000000000000000000000000000001' 26 | echo 'test create_child_by_create2() end' 27 | -------------------------------------------------------------------------------- /examples/test_cases/test_init_code_hash.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract TestInitCodeHashChild { 4 | function add(uint256 a, uint256 b) public pure returns (uint256) { 5 | return a + b; 6 | } 7 | } 8 | 9 | contract TestInitCodeHashParent { 10 | 11 | function calculate_create2_addr() public returns (address) { 12 | bytes32 childCodeHashFromCreationCode = keccak256(type(TestInitCodeHashChild).creationCode); 13 | emit emitBytes32(childCodeHashFromCreationCode); 14 | bytes32 salt = keccak256(abi.encodePacked(msg.sender)); 15 | address childAddress = address( 16 | uint160( 17 | uint256( 18 | keccak256( 19 | abi.encodePacked( 20 | bytes1(0xff), 21 | address(this), 22 | salt, 23 | childCodeHashFromCreationCode 24 | ) 25 | ) 26 | ) 27 | ) 28 | ); 29 | return childAddress; 30 | } 31 | 32 | event emitAddress(address value); 33 | event emitBytes32(bytes32 value); 34 | 35 | function create_child_by_create2() public returns (bool) { 36 | bytes32 salt = keccak256(abi.encodePacked(msg.sender)); 37 | address child = address(new TestInitCodeHashChild{salt: salt}()); 38 | bytes32 childCodeHashFromExtcodehash; 39 | assembly { 40 | childCodeHashFromExtcodehash := extcodehash(child) 41 | } 42 | emit emitBytes32(childCodeHashFromExtcodehash); 43 | bytes32 childCodeHashFromCreationCode = keccak256(type(TestInitCodeHashChild).creationCode); 44 | emit emitBytes32(childCodeHashFromCreationCode); 45 | emit emitAddress(child); 46 | return childCodeHashFromExtcodehash == childCodeHashFromCreationCode; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/tests/create_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn test_create_2_and_creation_code() { 14 | let mut runtime = TestRuntime::new( 15 | "test_create_2_and_creation_code", 16 | "target/test_create_2_and_creation_code", 17 | ); 18 | runtime.clear_testdata(); 19 | let _emited_bc = runtime 20 | .compile_test_yul( 21 | r#" 22 | object "test_create_2_and_creation_code" { 23 | code { 24 | } 25 | 26 | object "test_create_2_and_creation_code_deployed" { 27 | 28 | code { 29 | function allocate_unbounded() -> memPtr { 30 | memPtr := mload(64) 31 | } 32 | 33 | function round_up_to_mul_of_32(value) -> result { 34 | result := and(add(value, 31), not(31)) 35 | } 36 | 37 | function panic_error_0x41() { 38 | mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) 39 | mstore(4, 0x41) 40 | revert(0, 0x24) 41 | } 42 | 43 | function finalize_allocation(memPtr, size) { 44 | let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) 45 | // protect against overflow 46 | if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } 47 | mstore(64, newFreePtr) 48 | } 49 | 50 | function allocate_memory(size) -> memPtr { 51 | memPtr := allocate_unbounded() 52 | finalize_allocation(memPtr, size) 53 | } 54 | 55 | function array_dataslot_t_bytes_memory_ptr(ptr) -> data { 56 | data := ptr 57 | data := add(ptr, 0x20) 58 | } 59 | 60 | function array_length_t_bytes_memory_ptr(value) -> length { 61 | length := mload(value) 62 | } 63 | 64 | function test_create_2_and_creation_code() -> init_code_hash, created_addr { 65 | let _1 := datasize("SubContract") 66 | let expr_15_mpos := allocate_memory(add(_1, 32)) 67 | mstore(expr_15_mpos, _1) 68 | datacopy(add(expr_15_mpos, 32), dataoffset("SubContract"), _1) 69 | /// "keccak256(type(SubContract).creationCode)" 70 | init_code_hash := keccak256(array_dataslot_t_bytes_memory_ptr(expr_15_mpos), array_length_t_bytes_memory_ptr(expr_15_mpos)) 71 | created_addr := create2(0, array_dataslot_t_bytes_memory_ptr(expr_15_mpos), array_length_t_bytes_memory_ptr(expr_15_mpos), 0) 72 | } 73 | 74 | let init_code_hash, created_addr := test_create_2_and_creation_code() 75 | mstore(0x00, init_code_hash) 76 | mstore(0x20, created_addr) 77 | return(0x00, 0x40) 78 | } 79 | 80 | object "SubContract" { 81 | code { 82 | } 83 | object "SubContract_deployed" { 84 | code { 85 | function test_and() -> r { 86 | let a := 1 87 | let b := 0 88 | r := and(a, b) 89 | } 90 | } 91 | } 92 | } 93 | 94 | } 95 | } 96 | "#, 97 | ) 98 | .unwrap(); 99 | runtime.deploy(&[]).unwrap(); 100 | runtime 101 | .call(&solidity_selector("test_create_2_and_creation_code()"), &[]) 102 | .unwrap(); 103 | // TODO: manualy check the using init code hash: xxx same as the first 32 bytes hex of the last evm finish with result hex: 104 | runtime.assert_success(); 105 | // for AND 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/tests/int_cast_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn test_i32_cast_to_u256() { 14 | let mut runtime = TestRuntime::new("test_i32_cast_to_u256", "target/test_i32_cast_to_u256"); 15 | runtime.clear_testdata(); 16 | let _emited_bc = runtime 17 | .compile_test_yul( 18 | r#" 19 | object "test_i32_cast_to_u256" { 20 | code { 21 | } 22 | 23 | object "test_i32_cast_to_u256_deployed" { 24 | code { 25 | function test_cast(a) -> r { 26 | debug_print(a) 27 | r := a 28 | } 29 | 30 | let result := test_cast(calldatasize()) 31 | mstore(0x00, result) 32 | return(0x00, 0x20) 33 | } 34 | } 35 | } 36 | "#, 37 | ) 38 | .unwrap(); 39 | runtime.deploy(&[]).unwrap(); 40 | runtime 41 | .call(&solidity_selector("test_cast()"), &[]) 42 | .unwrap(); 43 | runtime.assert_result("0000000000000000000000000000000000000000000000000000000000000004"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/tests/linkersymbol_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn test_linker_symbol_1() { 14 | let mut runtime = TestRuntime::new("test_linker_symbol_1", "target/test_linker_symbol_1"); 15 | let _emited_bc = runtime 16 | .compile_test_yul( 17 | r#" 18 | object "test_linker_symbol_1" { 19 | code { 20 | } 21 | object "test_linker_symbol_1_deployed" { 22 | code { 23 | function test_linker_symbol() -> r { 24 | let a := 3 25 | let b := 5 26 | r := lt(a, b) 27 | } 28 | // not used linkersymbol variable 29 | let l1 := linkersymbol("l1") 30 | let r := test_linker_symbol() 31 | mstore(0x00, r) 32 | return(0x00, 0x20) 33 | } 34 | } 35 | } 36 | "#, 37 | ) 38 | .unwrap(); 39 | runtime.deploy(&[]).unwrap(); 40 | runtime 41 | .call(&solidity_selector("test_linker_symbol()"), &[]) 42 | .unwrap(); 43 | runtime.assert_result("0000000000000000000000000000000000000000000000000000000000000001"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | mod arithmetic_tests; 5 | mod bool_tests; 6 | mod byte_tests; 7 | mod chain_context_tests; 8 | mod create_tests; 9 | mod function_optimize_tests; 10 | mod hostapi_tests; 11 | mod int_cast_tests; 12 | mod int_constant_tests; 13 | mod linkersymbol_tests; 14 | mod mod_arithmetic_tests; 15 | mod mstore_tests; 16 | mod shift_tests; 17 | mod signed_arithmetic_tests; 18 | mod solidity_strings; 19 | mod string_tests; 20 | mod syntax_tests; 21 | mod test; 22 | mod test_helper; 23 | mod transfer_tests; 24 | mod tstore_tload_tests; 25 | mod tuple_tests; 26 | mod var_redefine_tests; 27 | -------------------------------------------------------------------------------- /src/tests/mstore_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn test_mstore_after_arith_1() { 14 | let mut runtime = TestRuntime::new( 15 | "test_mstore_after_arith_1", 16 | "target/test_mstore_after_arith_1", 17 | ); 18 | runtime.clear_testdata(); 19 | let emited_bc = runtime 20 | .compile_test_yul( 21 | r#" 22 | object "test_mstore_after_arith_1" { 23 | code { 24 | } 25 | 26 | object "test_mstore_after_arith_1_deployed" { 27 | code { 28 | function test_mstore_after_arith() -> r { 29 | let var_to := 0x1234567890abcdef 30 | let _1 := sub(shl(160, 1), 1) 31 | let _2 := and(var_to, _1) 32 | 33 | if iszero(_2) 34 | { 35 | let _5 := mload(64) 36 | mstore(_5, shl(224, 0xec442f05)) 37 | mstore(add(_5, 4), 0x00) 38 | revert(_5, 36) 39 | } 40 | mstore(0, _2) 41 | mstore(0x20, 0) 42 | r := _2 43 | } 44 | 45 | let result := test_mstore_after_arith() 46 | mstore(0x00, result) 47 | return(0x00, 0x20) 48 | } 49 | } 50 | } 51 | "#, 52 | ) 53 | .unwrap(); 54 | std::fs::write( 55 | "target/test_mstore_after_arith_1/test_mstore_after_arith_1.wasm", 56 | emited_bc, 57 | ) 58 | .unwrap(); 59 | runtime.wasm2wat( 60 | "target/test_mstore_after_arith_1/test_mstore_after_arith_1.wasm", 61 | "target/test_mstore_after_arith_1/test_mstore_after_arith_1.wat", 62 | ); 63 | 64 | runtime.deploy(&[]).unwrap(); 65 | runtime 66 | .call(&solidity_selector("test_mstore_after_arith()"), &[]) 67 | .unwrap(); 68 | runtime.assert_result("0000000000000000000000000000000000000000000000001234567890abcdef"); 69 | } 70 | 71 | #[test] 72 | fn test_yul_mstore8() { 73 | let mut runtime = TestRuntime::new("Mstore8Test", "target/test_yul_mstore8"); 74 | let _emited_bc = runtime 75 | .compile_test_yul( 76 | r#" 77 | object "Mstore8Test" { 78 | code { 79 | } 80 | object "Mstore8Test_deployed" { 81 | code { 82 | // mstore8(p, v): mem[p] := v & 0xff (only modifies a single byte) 83 | function test_mstore8() -> r { 84 | mstore8(0, 0xFFFF) 85 | mstore8(1, 0xAB) 86 | r := mload(0) 87 | } 88 | 89 | let r := test_mstore8() 90 | mstore(0x00, r) 91 | return(0x00, 0x20) 92 | } 93 | } 94 | } 95 | "#, 96 | ) 97 | .unwrap(); 98 | runtime.deploy(&[]).unwrap(); 99 | runtime 100 | .call(&solidity_selector("test_mstore8()"), &[]) 101 | .unwrap(); 102 | runtime.assert_result("ffab000000000000000000000000000000000000000000000000000000000000"); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/tests/solidity_strings.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// This file contains test cases for the solidity strings library usage. 5 | /// 6 | /// The `Strings.sol` library is imported from the `@openzeppelin/contracts` package. 7 | /// Url is https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol. 8 | /// The library provides functions for string operations, such as converting a `uint256` to a string. 9 | /// The test cases in this file are used to test the correctness of the library functions. 10 | 11 | #[allow(unused)] 12 | use super::test_helper::solidity_selector; 13 | #[allow(unused)] 14 | use super::test_helper::TestRuntime; 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | 19 | // Embed the content of openzepplin_strings_full.sol file in the same directory 20 | // into the OPEN_ZEPPLIN_STRINGS_SOL_CODE global variable. 21 | const OPEN_ZEPPLIN_STRINGS_SOL_CODE: &str = include_str!("openzepplin_strings_full.sol"); 22 | 23 | #[test] 24 | fn test_solidity_strings_to_string_1() { 25 | let mut runtime = TestRuntime::new( 26 | "test_solidity_strings_to_string_1", 27 | "target/test_solidity_strings_to_string_1", 28 | ); 29 | runtime.clear_testdata(); 30 | let yul_code = runtime.compile_solidity_to_yul( 31 | &format!( 32 | r#" 33 | pragma solidity ^0.8.0; 34 | {} 35 | 36 | contract TestContract {{ 37 | function test() public returns (string memory) {{ 38 | // test Strings.toString(uint256) 39 | return Strings.toString(123456789); 40 | }} 41 | }} 42 | "#, 43 | OPEN_ZEPPLIN_STRINGS_SOL_CODE 44 | ), 45 | "TestContract", 46 | ); 47 | if let Err(err) = &yul_code { 48 | eprintln!("compile to yul error: {err}"); 49 | } 50 | assert!(yul_code.is_ok()); 51 | let yul_code = yul_code.unwrap(); 52 | let _emited_bc = runtime.compile_test_yul(&yul_code).unwrap(); 53 | runtime.set_enable_gas_meter(false); 54 | runtime.deploy(&[]).unwrap(); 55 | runtime.call(&solidity_selector("test()"), &[]).unwrap(); 56 | 57 | // The string encoding of 123456789 58 | runtime.assert_result("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000093132333435363738390000000000000000000000000000000000000000000000"); 59 | } 60 | 61 | #[test] 62 | fn test_solidity_strings_to_hex_string_uint() { 63 | let mut runtime = TestRuntime::new( 64 | "test_solidity_strings_to_hex_string_uint", 65 | "target/test_solidity_strings_to_hex_string_uint", 66 | ); 67 | runtime.clear_testdata(); 68 | let yul_code = runtime.compile_solidity_to_yul( 69 | &format!( 70 | r#" 71 | pragma solidity ^0.8.0; 72 | {} 73 | contract TestContract {{ 74 | function test() public pure returns (string memory) {{ 75 | return Strings.toHexString(0xDEADBEEF); 76 | }} 77 | }} 78 | "#, 79 | OPEN_ZEPPLIN_STRINGS_SOL_CODE 80 | ), 81 | "TestContract", 82 | ); 83 | assert!(yul_code.is_ok()); 84 | let yul_code = yul_code.unwrap(); 85 | let _emited_bc = runtime.compile_test_yul(&yul_code).unwrap(); 86 | runtime.set_enable_gas_meter(false); 87 | runtime.deploy(&[]).unwrap(); 88 | runtime.call(&solidity_selector("test()"), &[]).unwrap(); 89 | // The string encoding of "0xDEADBEEF" 90 | runtime.assert_result("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a3078646561646265656600000000000000000000000000000000000000000000"); 91 | } 92 | 93 | #[test] 94 | fn test_solidity_strings_to_hex_string_address() { 95 | let mut runtime = TestRuntime::new( 96 | "test_solidity_strings_to_hex_string_address", 97 | "target/test_solidity_strings_to_hex_string_address", 98 | ); 99 | runtime.clear_testdata(); 100 | let yul_code = runtime.compile_solidity_to_yul( 101 | &format!( 102 | r#" 103 | pragma solidity ^0.8.0; 104 | {} 105 | contract TestContract {{ 106 | function test() public pure returns (string memory) {{ 107 | return Strings.toHexString(0x1234567890123456789012345678901234567890); 108 | }} 109 | }} 110 | "#, 111 | OPEN_ZEPPLIN_STRINGS_SOL_CODE 112 | ), 113 | "TestContract", 114 | ); 115 | assert!(yul_code.is_ok()); 116 | let yul_code = yul_code.unwrap(); 117 | let _emited_bc = runtime.compile_test_yul(&yul_code).unwrap(); 118 | runtime.set_enable_gas_meter(false); 119 | runtime.deploy(&[]).unwrap(); 120 | runtime.call(&solidity_selector("test()"), &[]).unwrap(); 121 | // The string encoding of "0x1234567890123456789012345678901234567890" 122 | runtime.assert_result("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002a30783132333435363738393031323334353637383930313233343536373839303132333435363738393000000000000000000000000000000000000000000000"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/tests/string_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | 9 | #[cfg(test)] 10 | mod tests { 11 | use super::*; 12 | 13 | #[test] 14 | fn test_string_constant() { 15 | let mut runtime = TestRuntime::new("test_string_constant", "target/test_string_constant"); 16 | runtime.clear_testdata(); 17 | let _emited_bc = runtime 18 | .compile_test_yul( 19 | r#" 20 | object "test_string_constant" { 21 | code { 22 | } 23 | 24 | object "test_string_constant_deployed" { 25 | code { 26 | function test_string() -> r { 27 | mstore(200, "hello") 28 | r := mload(200) 29 | } 30 | 31 | let result := test_string() 32 | mstore(0x00, result) 33 | return(0x00, 0x20) 34 | } 35 | } 36 | } 37 | "#, 38 | ) 39 | .unwrap(); 40 | runtime.deploy(&[]).unwrap(); 41 | runtime 42 | .call(&solidity_selector("test_string()"), &[]) 43 | .unwrap(); 44 | runtime.assert_result("68656c6c6f000000000000000000000000000000000000000000000000000000"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/tests/syntax_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[allow(unused)] 9 | use crate::yul; 10 | #[allow(unused)] 11 | use crate::yul2ir::config::Yul2IROptions; 12 | #[allow(unused)] 13 | use crate::Yul2IRContext; 14 | #[allow(unused)] 15 | use inkwell::context::Context; 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use crate::yul2ir::utils::remove_comments; 20 | 21 | use super::*; 22 | 23 | #[test] 24 | fn test_multiline_comment_in_expr_syntax() { 25 | let expr = yul::ObjectParser::new() 26 | .parse( 27 | &remove_comments( 28 | r#" 29 | object "test_multiline_comment_in_expr_syntax" { 30 | 31 | code { 32 | function test_comment() { 33 | /** aaa */ 34 | mstore(/** @src */ 0, 123) 35 | mstore(/** @src -1:-1:-1 */ 0, /** @src 46:163:376 "{..." */ shl(224, 0x4e487b71)) 36 | } 37 | test_comment() 38 | } 39 | data ".metadata" hex"aa" 40 | } 41 | "#, 42 | )) 43 | .unwrap(); 44 | println!("{:?}", expr); 45 | let llvm_context = Context::create(); 46 | let opts = Yul2IROptions::test("test_multiline_comment_in_expr_syntax"); 47 | let mut context = Yul2IRContext::new_with_object(&llvm_context, &opts, expr); 48 | let emited_bc = context 49 | .emit("test_multiline_comment_in_expr_syntax") 50 | .unwrap(); 51 | std::fs::write("test.out.wasm", emited_bc).unwrap(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/tests/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | 9 | #[test] 10 | fn test_yul_add() { 11 | let mut runtime = TestRuntime::new("AddTest", "target/test_yul_add"); 12 | runtime.clear_testdata(); 13 | let _emited_bc = runtime 14 | .compile_test_yul( 15 | r#" 16 | object "AddTest" { 17 | code { 18 | } 19 | object "AddTest_deployed" { 20 | code { 21 | function test_add() -> r { 22 | let a := 1 23 | let b := 2 24 | r := add(a, b) 25 | } 26 | 27 | let r := test_add() 28 | mstore(0x00, r) 29 | return(0x00, 0x20) 30 | } 31 | } 32 | } 33 | "#, 34 | ) 35 | .unwrap(); 36 | runtime.deploy(&[]).unwrap(); 37 | runtime.call(&solidity_selector("test_add()"), &[]).unwrap(); 38 | runtime.assert_result("0000000000000000000000000000000000000000000000000000000000000003"); 39 | } 40 | -------------------------------------------------------------------------------- /src/tests/tstore_tload_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[allow(unused)] 5 | use super::test_helper::solidity_selector; 6 | #[allow(unused)] 7 | use super::test_helper::TestRuntime; 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn test_tstore_tload() { 14 | let mut runtime = TestRuntime::new("test_tstore_tload", "target/test_tstore_tload"); 15 | runtime.clear_testdata(); 16 | let emited_bc = runtime 17 | .compile_test_yul( 18 | r#" 19 | object "test_tstore_tload" { 20 | code { 21 | } 22 | 23 | object "test_tstore_tload_deployed" { 24 | code { 25 | function test_tstore_tload() -> result { 26 | // Use tstore for temporary storage 27 | let temp_key := 0x02 // Key for temporary storage 28 | let temp_value := 0xdeadbeef // Value to store temporarily 29 | 30 | tstore(temp_key, temp_value) // Store temp_value using temp_key 31 | let loaded_value := tload(temp_key) // Load value using temp_key 32 | 33 | // Revert if loaded value doesn't match stored value 34 | if iszero(eq(loaded_value, temp_value)) { 35 | let _ptr := mload(64) 36 | mstore(_ptr, shl(224, 0xec442f05)) // Write revert error identifier (e.g. function selector) 37 | mstore(add(_ptr, 4), 0x00) // Error data 38 | revert(_ptr, 36) 39 | } 40 | 41 | // Return loaded value if values match 42 | result := loaded_value 43 | } 44 | 45 | let test_result := test_tstore_tload() 46 | mstore(0x00, test_result) 47 | return(0x00, 0x20) 48 | } 49 | } 50 | } 51 | "#, 52 | ) 53 | .unwrap(); 54 | std::fs::write("target/test_tstore_tload/test_tstore_tload.wasm", emited_bc).unwrap(); 55 | runtime.wasm2wat( 56 | "target/test_tstore_tload/test_tstore_tload.wasm", 57 | "target/test_tstore_tload/test_tstore_tload.wat", 58 | ); 59 | 60 | runtime.deploy(&[]).unwrap(); 61 | runtime 62 | .call(&solidity_selector("test_tstore_tload()"), &[]) 63 | .unwrap(); 64 | runtime.assert_result("00000000000000000000000000000000000000000000000000000000deadbeef"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/yul2ir/ast.rs: -------------------------------------------------------------------------------- 1 | // Specification of Yul 2 | // https://docs.soliditylang.org/en/latest/yul.html#specification-of-yul 3 | 4 | // https://github.com/AntChainOpenLabs/Smart-Intermediate-Representation/blob/main/yul_to_ir/src/ast.rs 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct Object { 8 | pub name: String, 9 | pub code: Block, 10 | pub inner_segments: Vec, 11 | } 12 | 13 | #[derive(Debug, Clone)] 14 | pub enum InnerSegment { 15 | Object(Box), 16 | Data(String, Vec), 17 | } 18 | 19 | #[allow(unused)] 20 | #[derive(Debug, Clone)] 21 | pub enum Comment { 22 | SingleLineComment, 23 | MultiLineComment, 24 | } 25 | 26 | #[allow(dead_code)] 27 | #[derive(Debug, Clone)] 28 | pub enum DataLiteral { 29 | HexLiteral(String), 30 | StringLiteral(String), 31 | } 32 | 33 | // Copyright (c) the DTVM authors Core Contributors 34 | // Copyright (c) The Smart Intermediate Representation Contributors 35 | // SPDX-License-Identifier: Apache-2.0 36 | 37 | /// Block = '{' Statements* '}' 38 | #[derive(Debug, Clone)] 39 | pub struct Block { 40 | pub statements: Vec, 41 | } 42 | 43 | /// Statements = Block | FunctionDefinition | VariableDeclaration | Assign | 44 | /// If | Expression | Switch | Forloop | BreakContinue | Leave 45 | #[allow(dead_code)] 46 | #[derive(Debug, Clone)] 47 | pub enum Statement { 48 | Assignment(Box), 49 | VariableDeclaration(Box), 50 | If(Box), 51 | For(Box), 52 | Switch(Box), 53 | Leave, 54 | Break, 55 | Continue, 56 | Block(Box), 57 | FunctionDefinition(Box), 58 | FunctionCall(Box), 59 | Comment, 60 | } 61 | 62 | /// If = 'if' Expression Block 63 | #[derive(Debug, Clone)] 64 | pub struct If { 65 | pub cond: Expression, 66 | pub body: Block, 67 | } 68 | 69 | /// Assign = Identifier '=' Expression 70 | #[derive(Debug, Clone)] 71 | pub struct Assignment { 72 | pub identifiers: Vec, 73 | pub value: Expression, 74 | } 75 | 76 | /// VariableDeclaration = 'let' TypedIdentifierList (':=' Expression)? 77 | #[derive(Debug, Clone)] 78 | pub struct VariableDeclaration { 79 | pub identifiers: Vec, 80 | pub value: Option, 81 | } 82 | 83 | /// Expressions = Identifier | FunctionCall | Literal 84 | #[derive(Debug, Clone)] 85 | pub enum Expression { 86 | Identifier(Identifier), 87 | FunctionCall(Box), 88 | Literal(Literal), 89 | } 90 | 91 | /// Literal = (NumberLiteral | StringLiteral | TrueLiteral | FalseLiteral ) (':' TypeName)? 92 | /// NumberLiteral = DecimalNumber | HexNumber 93 | #[allow(clippy::enum_variant_names)] 94 | #[derive(Debug, Clone)] 95 | pub enum Literal { 96 | TrueLiteral(Option), 97 | FalseLiteral(Option), 98 | HexNumberLiteral(HexNumber, Option), 99 | DecimalNumberLiteral(DecimalNumber, Option), 100 | StringLiteral(StringLiteral, Option), 101 | } 102 | 103 | /// Switch = 'switch' Expression (Case+ Default? | Default) 104 | #[derive(Debug, Clone)] 105 | pub struct Switch { 106 | pub condition: Expression, 107 | pub opt: SwitchOptions, 108 | } 109 | 110 | #[derive(Debug, Clone)] 111 | pub enum SwitchOptions { 112 | Cases(Vec, Option), 113 | Default(SwitchDefault), 114 | } 115 | 116 | /// Case = 'case' Literal Block 117 | #[derive(Debug, Clone)] 118 | pub struct SwitchCase { 119 | pub case: Literal, 120 | pub body: Block, 121 | } 122 | 123 | /// Default = 'default' Block 124 | #[derive(Debug, Clone)] 125 | pub struct SwitchDefault { 126 | pub body: Block, 127 | } 128 | 129 | /// ForLoop = 'for' Block Expression Block Block 130 | #[derive(Debug, Clone)] 131 | pub struct For { 132 | pub init_block: Block, 133 | pub condition: Expression, 134 | pub post_block: Block, 135 | pub execution_block: Block, 136 | } 137 | 138 | /// FunctionDefinition = 'function' Identifier '(' TypedIdentifierList? ')' ('->' TypedIdentifierList)? Block 139 | #[derive(Debug, Clone)] 140 | pub struct FunctionDefinition { 141 | pub name: Identifier, 142 | pub params: Vec, 143 | pub body: Block, 144 | pub returns: Vec, 145 | } 146 | 147 | #[derive(Debug, Clone)] 148 | pub struct FunctionDeclaration { 149 | #[allow(unused)] 150 | pub name: Identifier, 151 | #[allow(unused)] 152 | pub params: Vec, 153 | pub returns: Vec, 154 | } 155 | 156 | #[allow(unused)] 157 | pub type TypedIdentifierList = Vec; 158 | 159 | #[derive(Debug, Clone)] 160 | pub struct TypedIdentifier { 161 | pub identifier: Identifier, 162 | pub type_name: Option, 163 | } 164 | 165 | #[derive(Debug, Clone)] 166 | pub struct TypeName { 167 | pub type_name: Identifier, 168 | } 169 | 170 | #[derive(Debug, Clone)] 171 | pub struct Identifier { 172 | pub name: String, 173 | } 174 | 175 | /// FunctionCall = Identifier '(' (Expression ( ',' Expression)* )? ')' 176 | #[derive(Debug, Clone)] 177 | pub struct FunctionCall { 178 | pub id: Identifier, 179 | pub arguments: Vec, 180 | } 181 | 182 | #[derive(Debug, Clone)] 183 | pub struct StringLiteral { 184 | pub str: String, 185 | } 186 | 187 | #[derive(Debug, Clone)] 188 | pub struct DecimalNumber { 189 | pub dec: String, 190 | } 191 | 192 | #[derive(Debug, Clone)] 193 | pub struct HexNumber { 194 | pub hex: String, 195 | } 196 | -------------------------------------------------------------------------------- /src/yul2ir/errors.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use std::fmt::Display; 5 | 6 | #[derive(Debug, Clone)] 7 | pub enum ASTLoweringError { 8 | BuilderError(String), 9 | DuplicateVariableDefinition(String), 10 | UnsupportedType(String), 11 | FunctionReturnValueNotFound(String), 12 | } 13 | 14 | impl From for ASTLoweringError { 15 | fn from(err: inkwell::builder::BuilderError) -> ASTLoweringError { 16 | ASTLoweringError::BuilderError(err.to_string()) 17 | } 18 | } 19 | 20 | impl Display for ASTLoweringError { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | match self { 23 | ASTLoweringError::BuilderError(msg) => { 24 | write!(f, "Builder error: {}", msg) 25 | } 26 | ASTLoweringError::DuplicateVariableDefinition(msg) => { 27 | write!(f, "Variable definition error: {}", msg) 28 | } 29 | ASTLoweringError::UnsupportedType(msg) => { 30 | write!(f, "Unsupported type: {}", msg) 31 | } 32 | ASTLoweringError::FunctionReturnValueNotFound(msg) => { 33 | write!(f, "Function return value not found: {}", msg) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/yul2ir/function_deduplicator.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::yul2ir::ast::FunctionDefinition; 5 | use crate::yul2ir::transform::UNIFIED_REVERT_ERROR_ZERO; 6 | use crate::yul2ir::{context::CompileResult, context::Yul2IRContext}; 7 | 8 | use ethereum_types::U256; 9 | use inkwell::module::Linkage; 10 | use std::rc::Rc; 11 | 12 | impl<'a> Yul2IRContext<'a> { 13 | pub fn is_revert_zero_function(&self, function: &FunctionDefinition) -> bool { 14 | if function.body.statements.len() != 1 { 15 | return false; 16 | } 17 | 18 | let stmt = &function.body.statements[0]; 19 | let revert_params = self.matches_function_call(stmt, "revert", 2); 20 | if let Some(params) = revert_params { 21 | return self.matches_constant_literal(¶ms[0], U256::from(0)) 22 | && self.matches_constant_literal(¶ms[1], U256::from(0)); 23 | } 24 | 25 | false 26 | } 27 | 28 | pub fn generate_unified_revert_zero(&self) -> CompileResult<'a> { 29 | if self 30 | .functions_mapping 31 | .borrow() 32 | .contains_key(UNIFIED_REVERT_ERROR_ZERO) 33 | { 34 | return self.ok_result(); 35 | } 36 | 37 | let func_ty = self.llvm_context.void_type().fn_type(&[], false); 38 | let function = self.llvm_module.borrow_mut().add_function( 39 | UNIFIED_REVERT_ERROR_ZERO, 40 | func_ty, 41 | Some(Linkage::External), 42 | ); 43 | let entry_bb = self.llvm_context.append_basic_block(function, "entry"); 44 | self.builder.borrow_mut().position_at_end(entry_bb); 45 | self.build_void_call( 46 | "wrapper_revert", 47 | &[ 48 | self.i32_type().const_zero().into(), 49 | self.i32_type().const_zero().into(), 50 | ], 51 | )?; 52 | self.builder.borrow_mut().build_return(None)?; 53 | 54 | self.functions.borrow_mut().push(Rc::new(function)); 55 | self.functions_mapping 56 | .borrow_mut() 57 | .insert(UNIFIED_REVERT_ERROR_ZERO.to_string(), Rc::new(function)); 58 | 59 | self.ok_result() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/yul2ir/infer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use inkwell::types::BasicTypeEnum; 5 | 6 | use crate::yul2ir::context::Yul2IRContext; 7 | 8 | /// Represents the expected type for a YUL instruction result 9 | #[derive(Debug, Clone, Copy, PartialEq, Default)] 10 | pub enum ExpectedType { 11 | /// No specific type expected, use the default for the instruction 12 | #[default] 13 | Untyped, 14 | /// Expecting a bool result 15 | Bool, 16 | /// Expecting an i32 result 17 | I32, 18 | /// Expecting an i64 result 19 | I64, 20 | /// Expecting a u256 result 21 | U256, 22 | /// Expecting bytes32 result 23 | Bytes32, 24 | /// Expecting bytes32* result 25 | Bytes32Pointer, 26 | } 27 | 28 | impl<'a> Yul2IRContext<'a> { 29 | /// Convert a LLVM BasicTypeEnum to our ExpectedType enum 30 | pub(crate) fn type_to_expected(&self, ty: BasicTypeEnum<'a>) -> ExpectedType { 31 | if ty.is_int_type() { 32 | let int_ty = ty.into_int_type(); 33 | match int_ty.get_bit_width() { 34 | 1 => ExpectedType::Bool, 35 | 32 => ExpectedType::I32, 36 | 64 => ExpectedType::I64, 37 | 256 => ExpectedType::U256, 38 | _ => ExpectedType::Untyped, 39 | } 40 | } else if self.is_bytes32_type(&ty) { 41 | ExpectedType::Bytes32 42 | } else { 43 | ExpectedType::Untyped 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/yul2ir/linker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // Copyright (c) The Smart Intermediate Representation Contributors 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // Call the LLD linker 6 | #include "lld/Common/Driver.h" 7 | 8 | extern "C" bool LLDWasmLink(const char *argv[], size_t length) 9 | { 10 | llvm::ArrayRef args(argv, length); 11 | 12 | return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false); 13 | } 14 | -------------------------------------------------------------------------------- /src/yul2ir/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024-2025 the DTVM authors. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use lalrpop_util::lalrpop_mod; 5 | lalrpop_mod!(pub yul); // synthesized by LALRPOP 6 | 7 | pub mod ast; 8 | pub mod config; 9 | pub mod context; 10 | pub mod errors; 11 | pub mod function_deduplicator; 12 | pub mod infer; 13 | pub mod instruction; 14 | pub mod stdlib; 15 | pub mod transform; 16 | pub mod usage; 17 | pub mod utils; 18 | pub mod var_scope; 19 | pub mod wasm; 20 | pub mod yul_instruction; 21 | -------------------------------------------------------------------------------- /src/yul2ir/stdlib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::yul2ir::config::Yul2IROptions; 5 | use inkwell::context::Context; 6 | use inkwell::memory_buffer::MemoryBuffer; 7 | use inkwell::module::Module; 8 | 9 | /// Load standard libraries. 10 | pub fn load_stdlib<'a>( 11 | opts: &'a Yul2IROptions, 12 | ctx_ref: &'a Context, 13 | a: Vec<&'a [u8]>, 14 | ) -> Module<'a> { 15 | let memory = MemoryBuffer::create_from_memory_range(WASM_IR[0], "wasm_bc"); 16 | 17 | let module: Module<'a> = Module::parse_bitcode_from_buffer(&memory, ctx_ref).unwrap(); 18 | 19 | let mut to_link_modules = WASM_IR.iter().skip(1).collect::>(); 20 | if !opts.debug_mode { 21 | for m in RELEASE_MODE_EXTRA_WASM_IR.iter() { 22 | to_link_modules.push(m); 23 | } 24 | } 25 | 26 | for bc in to_link_modules.iter() { 27 | let memory = MemoryBuffer::create_from_memory_range(bc, "wasm_bc"); 28 | 29 | module 30 | .link_in_module(Module::parse_bitcode_from_buffer(&memory, ctx_ref).unwrap()) 31 | .unwrap(); 32 | } 33 | 34 | for bc in a.iter() { 35 | let memory = MemoryBuffer::create_from_memory_range(bc, "wasm_bc"); 36 | 37 | module 38 | .link_in_module(Module::parse_bitcode_from_buffer(&memory, ctx_ref).unwrap()) 39 | .unwrap(); 40 | } 41 | 42 | module 43 | } 44 | 45 | #[cfg(debug_assertions)] 46 | static WASM_IR: [&[u8]; 5] = [ 47 | include_bytes!("../../stdlib/wasm/debug/stdlib.bc"), 48 | include_bytes!("../../stdlib/wasm/debug/chain.bc"), 49 | include_bytes!("../../stdlib/wasm/debug/utils.bc"), 50 | include_bytes!("../../stdlib/wasm/debug/evm_memory.bc"), 51 | include_bytes!("../../stdlib/wasm/debug/chain_math.bc"), 52 | ]; 53 | 54 | #[cfg(not(debug_assertions))] 55 | static WASM_IR: [&[u8]; 5] = [ 56 | include_bytes!("../../stdlib/wasm/release/stdlib.bc"), 57 | include_bytes!("../../stdlib/wasm/release/chain.bc"), 58 | include_bytes!("../../stdlib/wasm/release/utils.bc"), 59 | include_bytes!("../../stdlib/wasm/release/evm_memory.bc"), 60 | include_bytes!("../../stdlib/wasm/release/chain_math.bc"), 61 | ]; 62 | 63 | #[cfg(debug_assertions)] 64 | static RELEASE_MODE_EXTRA_WASM_IR: [&[u8]; 0] = []; 65 | 66 | #[cfg(not(debug_assertions))] 67 | static RELEASE_MODE_EXTRA_WASM_IR: [&[u8]; 1] = [include_bytes!( 68 | "../../stdlib/wasm/release/debug_in_release.bc" 69 | )]; 70 | -------------------------------------------------------------------------------- /src/yul2ir/var_scope.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | // A struct that represents the scope of a variable. 4 | // It contains a HashMap that maps variable names to their corresponding PointerValue. 5 | 6 | use crate::yul2ir::context::Yul2IRContext; 7 | use inkwell::types::BasicTypeEnum; 8 | use inkwell::values::PointerValue; 9 | use std::cell::RefCell; 10 | use std::collections::HashMap; 11 | 12 | use super::yul_instruction::YulLowLevelValueType; 13 | 14 | #[derive(Debug)] 15 | pub struct VarScope<'ctx> { 16 | pub vars: RefCell< 17 | HashMap< 18 | String, 19 | ( 20 | BasicTypeEnum<'ctx>, 21 | YulLowLevelValueType, 22 | PointerValue<'ctx>, 23 | bool, // is return var 24 | ), 25 | >, 26 | >, 27 | } 28 | 29 | impl<'ctx> VarScope<'ctx> { 30 | pub fn new() -> VarScope<'ctx> { 31 | VarScope { 32 | vars: RefCell::new(Default::default()), 33 | } 34 | } 35 | } 36 | 37 | pub struct ScopeGuard<'a, 'b> { 38 | context: &'b Yul2IRContext<'a>, 39 | } 40 | 41 | impl<'a, 'b> ScopeGuard<'a, 'b> { 42 | pub fn new(context: &'b Yul2IRContext<'a>) -> Self { 43 | context.enter_scope(); 44 | ScopeGuard { context } 45 | } 46 | } 47 | 48 | impl<'a, 'b> Drop for ScopeGuard<'a, 'b> { 49 | fn drop(&mut self) { 50 | self.context.exit_scope(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stdlib/.gitignore: -------------------------------------------------------------------------------- 1 | /wasm/*.bc 2 | /wasm/debug 3 | /wasm/release 4 | /tests/build 5 | -------------------------------------------------------------------------------- /stdlib/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | DEBUG_FLAGS=-g -O0 3 | RELEASE_FLAGS=-O2 -DNDEBUG 4 | COMMON_FLAGS=$(TARGET_FLAGS) --target=wasm32 -emit-llvm -Xclang -fexperimental-max-bitint-width=256 -ffreestanding -fno-builtin -Wall -Wno-unused-function -ftrapv -v 5 | 6 | wasm/debug/%.bc: %.c Makefile | wasm/debug 7 | $(CC) -c $(CFLAGS) $< -o $@ 8 | 9 | wasm/release/%.bc: %.c Makefile | wasm/release 10 | $(CC) -c $(CFLAGS) $< -o $@ 11 | 12 | wasm/debug: 13 | mkdir -p wasm/debug 14 | 15 | wasm/release: 16 | mkdir -p wasm/release 17 | 18 | WASM_DEBUG=$(addprefix wasm/debug/,stdlib.bc) \ 19 | $(addprefix wasm/debug/,chain.bc) \ 20 | $(addprefix wasm/debug/,utils.bc) \ 21 | $(addprefix wasm/debug/,chain_math.bc) \ 22 | $(addprefix wasm/debug/,evm_memory.bc) 23 | 24 | WASM_RELEASE=$(addprefix wasm/release/,stdlib.bc) \ 25 | $(addprefix wasm/release/,chain.bc) \ 26 | $(addprefix wasm/release/,utils.bc) \ 27 | $(addprefix wasm/release/,chain_math.bc) \ 28 | $(addprefix wasm/release/,evm_memory.bc) \ 29 | $(addprefix wasm/release/,debug_in_release.bc) 30 | 31 | all: Makefile echo 32 | 33 | debug: CFLAGS=$(COMMON_FLAGS) $(DEBUG_FLAGS) 34 | debug: $(WASM_DEBUG) Makefile echo 35 | 36 | release: CFLAGS=$(COMMON_FLAGS) $(RELEASE_FLAGS) 37 | release: $(WASM_RELEASE) Makefile echo 38 | 39 | echo: 40 | echo "stdlib make done" 41 | 42 | clean: 43 | rm -rf wasm/debug/*.bc wasm/release/*.bc wasm/debug wasm/release 44 | 45 | fmt: 46 | clang-format -i *.h *.c 47 | 48 | fmt_check: 49 | clang-format --dry-run --Werror *.h *.c 50 | 51 | .DEFAULT_GOAL := error 52 | 53 | error: 54 | @echo "Error: You must specify either 'make debug/release/clean/fmt/clang-format'" 55 | @exit 1 56 | -------------------------------------------------------------------------------- /stdlib/chain_math.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef __CHAIN_MATH_H_ 5 | #define __CHAIN_MATH_H_ 6 | 7 | #include "stdlib.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | BOOL wrapper_bytes32_lt(const bytes32 *a, const bytes32 *b); 14 | 15 | BOOL wrapper_bytes32_gt(const bytes32 *a, const bytes32 *b); 16 | 17 | BOOL wrapper_bytes32_slt(const bytes32 *a, const bytes32 *b); 18 | 19 | BOOL wrapper_bytes32_sgt(const bytes32 *a, const bytes32 *b); 20 | 21 | BOOL wrapper_bytes32_eq(const bytes32 *a, const bytes32 *b); 22 | 23 | BOOL wrapper_bytes32_iszero(const bytes32 *a); 24 | 25 | void wrapper_bytes32_not(const bytes32 *a, bytes32 *result); 26 | 27 | void wrapper_bytes32_and(const bytes32 *a, const bytes32 *b, bytes32 *result); 28 | 29 | void wrapper_bytes32_or(const bytes32 *a, const bytes32 *b, bytes32 *result); 30 | 31 | void wrapper_bytes32_xor(const bytes32 *a, const bytes32 *b, bytes32 *result); 32 | 33 | void wrapper_bytes32_shl(const bytes32 *value, int32_t shift, bytes32 *result); 34 | 35 | void wrapper_u256_shl(const uint256_t *value, int32_t shift, uint256_t *result); 36 | 37 | void wrapper_bytes32_shr(const bytes32 *value, int32_t shift, bytes32 *result); 38 | 39 | void wrapper_u256_shr(const uint256_t *value, int32_t shift, uint256_t *result); 40 | 41 | void wrapper_bytes32_add(const bytes32 *a, const bytes32 *b, bytes32 *result); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | #endif // __CHAIN_MATH_H_ 48 | -------------------------------------------------------------------------------- /stdlib/debug.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef __DEBUG_H_ 5 | #define __DEBUG_H_ 6 | #include "hostapi.h" 7 | 8 | __attribute__((import_module("env"), import_name("debug_bytes"))) void 9 | debug_bytes(ADDRESS_UINT data_offset, int32_t data_length); 10 | 11 | #endif // __DEBUG_H_ 12 | -------------------------------------------------------------------------------- /stdlib/debug_in_release.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "debug.h" 4 | 5 | void debug_bytes(ADDRESS_UINT data_offset, int32_t data_length) { 6 | // do nothing, so that the debug_bytes can be removed by the optimizer 7 | } 8 | -------------------------------------------------------------------------------- /stdlib/evm_memory.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "evm_memory.h" 5 | #include "debug.h" 6 | 7 | static int32_t inited_before = 0; // 1 is true, 0 is false 8 | 9 | // EVM memory start address, 10 | // Once set, it remains unchanged. This is possible because after the WASM 11 | // instance starts (before optimization), the newly grown memory area is treated 12 | // as EVM memory, preventing conflicts with the LLVM constant pool. 13 | static uint8_t *evm_memory_begin = 0; 14 | static uint8_t *evm_memory_end = 0; 15 | static uint32_t evm_memory_size = 0; // accessible memory range 16 | static uint32_t evm_memory_full_size = 0; 17 | 18 | // In wasm-ld mode, it is possible to avoid allocating separate pages and 19 | // instead use the remaining space after the constant data segment on the stack. 20 | // Another approach is to use static allocation(current choice) 21 | // Allocate a data segment and point to it, allowing space without needing to 22 | // grow; in most cases, 16384 (16k) is sufficient. 23 | // TODO: Implement memory relocation and growth when exceeding limits 24 | #define EVM_MEMORY_MAX_SIZE 16384 // 16KB 25 | static uint8_t evm_memory_init_buffer[EVM_MEMORY_MAX_SIZE] = {0}; 26 | 27 | void __init_evm_heap(int32_t try_new_wasm_page_as_evm_heap_bool) { 28 | if (inited_before) { 29 | // disable re-call abi using the same vm(memory not free) 30 | return; 31 | } 32 | 33 | // if current is large module(init memory size > 1 page), we use the 34 | // evm_memory_begin in new page else we use the evm_memory_init_buffer in 35 | // stack 36 | uint32_t init_memory_pages = __builtin_wasm_memory_size(0); 37 | if (init_memory_pages > 1 || try_new_wasm_page_as_evm_heap_bool) { 38 | evm_memory_begin = (uint8_t *)(init_memory_pages * WASM_PAGE_SIZE); 39 | // Preallocate some memory, as it is generally needed. 40 | __builtin_wasm_memory_grow(0, 1); 41 | uint32_t after_pages = __builtin_wasm_memory_size(0); 42 | if (after_pages == init_memory_pages) { 43 | __abort("wasm memory grow failed"); 44 | } 45 | evm_memory_end = (uint8_t *)(after_pages * WASM_PAGE_SIZE); 46 | evm_memory_size = 0; 47 | evm_memory_full_size = 48 | (uint32_t)evm_memory_end - (uint32_t)evm_memory_begin; 49 | } else { 50 | // small contract 51 | evm_memory_begin = evm_memory_init_buffer; 52 | evm_memory_end = evm_memory_begin + EVM_MEMORY_MAX_SIZE; 53 | evm_memory_size = 0; 54 | evm_memory_full_size = EVM_MEMORY_MAX_SIZE; 55 | } 56 | 57 | inited_before = 1; 58 | } 59 | 60 | uint8_t *evm_make_sure_memory(uint32_t size) { 61 | if (evm_memory_full_size < size) { 62 | uint32_t target_evm_pages = (size + WASM_PAGE_SIZE - 1) / WASM_PAGE_SIZE; 63 | uint32_t grow_pages = 64 | target_evm_pages - evm_memory_full_size / WASM_PAGE_SIZE; 65 | if (__builtin_wasm_memory_grow(0, grow_pages) == -1) { 66 | __abort("__malloc: failed"); 67 | }; 68 | evm_memory_end += (grow_pages * WASM_PAGE_SIZE); 69 | evm_memory_size = size; 70 | evm_memory_full_size = target_evm_pages * WASM_PAGE_SIZE; 71 | return evm_memory_begin; 72 | } 73 | if (evm_memory_size < size) { 74 | // EVM memory is allocated but not accessible for the requested size 75 | evm_memory_size = size; 76 | } 77 | 78 | return evm_memory_begin; 79 | } 80 | 81 | uint8_t *evm_get_memory_addr(int32_t offset) { 82 | return evm_memory_begin + offset; 83 | } 84 | 85 | int32_t is_available_evm_memory(uint8_t *ptr, uint32_t size) { 86 | return ptr >= evm_memory_begin && size <= evm_memory_size; 87 | } 88 | -------------------------------------------------------------------------------- /stdlib/evm_memory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef __EVM_MEMORY_H 5 | #define __EVM_MEMORY_H 6 | #include 7 | 8 | #define WASM_PAGE_SIZE (65536) 9 | 10 | // TODO: use revert with not malloc for abort 11 | #define __abort(x) __builtin_unreachable() 12 | 13 | void __init_evm_heap(int32_t try_new_wasm_page_as_evm_heap_bool); 14 | 15 | // Ensures there is memory space of specified size available for EVM use 16 | // Memory beyond the specified size requires additional checks to prevent access 17 | // Returns the starting address of the EVM memory region 18 | // The starting address of the EVM memory region remains unchanged after 19 | // modifications 20 | uint8_t *evm_make_sure_memory(uint32_t size); 21 | 22 | // Returns the memory address at the given offset 23 | // If offset < 0, copies data from non-EVM memory region 24 | uint8_t *evm_get_memory_addr(int32_t offset); 25 | 26 | // Checks if ptr is within the accessible memory range of EVM 27 | // Returns 1(true) if accessible, 0(false) otherwise 28 | int32_t is_available_evm_memory(uint8_t *ptr, uint32_t size); 29 | 30 | #endif // __EVM_MEMORY_H 31 | -------------------------------------------------------------------------------- /stdlib/stdlib.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // Copyright (c) The Smart Intermediate Representation Contributors 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | // clang --target=wasm32 -c -emit-llvm -O3 -ffreestanding -fno-builtin -Wall 6 | // stdlib.c 7 | 8 | #include "stdlib.h" 9 | #define MAX_ITOA_STR_SIZE 64 10 | #define assert(x) (0) 11 | 12 | #ifdef CC_LIB_TEST_MOCK 13 | // revert hostapi mock 14 | extern void revert(const char *error_msg, uint32_t error_msg_len) { 15 | // mock 16 | } 17 | #endif // CC_LIB_TEST_MOCK 18 | 19 | #ifndef CC_LIB_TEST_MOCK 20 | // sometimes llvm optimizers will include llvm.memset to set default value for 21 | // struct which will import memset extern dependency so we add memset 22 | // implementation for link 23 | void *memset(void *dest, uint8_t val, size_t length) { 24 | __memset(dest, val, length); 25 | return dest; 26 | } 27 | #endif 28 | 29 | // revert hostapi 30 | extern void revert(const char *error_msg, uint32_t error_msg_len); 31 | 32 | void __memset(void *_dest, uint8_t val, size_t length) { 33 | if (length == 0) { 34 | return; 35 | } 36 | uint8_t *dest = _dest; 37 | 38 | do { 39 | *dest++ = val; 40 | } while (--length); 41 | } 42 | 43 | #ifndef CC_LIB_TEST_MOCK 44 | 45 | /* 46 | * Our memcpy can only deal with multiples of 8 bytes. This is 47 | * enough for simple allocator below. 48 | */ 49 | void *memcpy(void *_dest, const void *_src, uint32_t length) { 50 | uint8_t *dest = _dest; 51 | const uint8_t *src = _src; 52 | 53 | while (length--) { 54 | *dest++ = *src++; 55 | } 56 | return _dest; 57 | } 58 | 59 | #endif // CC_LIB_TEST_MOCK 60 | 61 | bool __memcmp(uint8_t *left, uint32_t left_len, uint8_t *right, 62 | uint32_t right_len) { 63 | if (left_len != right_len) 64 | return false; 65 | 66 | while (left_len--) { 67 | if (*left++ != *right++) 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | -------------------------------------------------------------------------------- /stdlib/stdlib.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef __STDLIB_H_ 5 | #define __STDLIB_H_ 6 | 7 | #ifdef CC_LIB_TEST_MOCK 8 | #include 9 | #include 10 | #include 11 | #endif // CC_LIB_TEST_MOCK 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #define uint128_t __uint128_t 18 | #define int128_t __int128_t 19 | 20 | // int256 extension needs at least LLVM12 21 | typedef unsigned _BitInt(256) uint256_t; 22 | typedef _BitInt(256) int256_t; 23 | 24 | typedef uint8_t bytes32[32]; 25 | // 0 is false, 1 or others is true 26 | typedef int32_t BOOL; 27 | 28 | #define INT128_MAX \ 29 | (__int128)(((unsigned __int128)1 \ 30 | << ((__SIZEOF_INT128__ * __CHAR_BIT__) - 1)) - \ 31 | 1) 32 | #define INT128_MIN \ 33 | ((__int128_t)0 - ((__int128_t)1 << 126) - ((__int128_t)1 << 126)) 34 | #define UINT128_MAX (((__uint128_t)INT128_MAX << 1) + 1) 35 | 36 | #define INT256_MAX \ 37 | ( \ 38 | int256_t)(((uint256_t)1 \ 39 | << (uint256_t)((2 * __SIZEOF_INT128__ * __CHAR_BIT__) - 1)) - \ 40 | (uint256_t)1) 41 | #define INT256_MIN (-(INT256_MAX) - (int256_t)1) 42 | #define UINT256_MAX (((uint256_t)INT256_MAX << (uint256_t)1) + (uint256_t)1) 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | #ifdef CC_LIB_TEST_MOCK 49 | extern void *memset(void *dest, uint8_t val, size_t length); 50 | #endif 51 | 52 | extern void __memset(void *dest, uint8_t val, size_t length); 53 | extern bool __memcmp(uint8_t *left, uint32_t left_len, uint8_t *right, 54 | uint32_t right_len); 55 | 56 | #ifndef CC_LIB_TEST_MOCK 57 | extern void *memcpy(void *_dest, const void *_src, uint32_t length); 58 | #endif // CC_LIB_TEST_MOCK 59 | 60 | #ifndef CC_LIB_TEST_MOCK 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | 66 | #endif // CC_LIB_TEST_MOCK 67 | 68 | extern void init_global_bytes_length(uint8_t *bytes, uint32_t length); 69 | extern uint32_t get_global_bytes_length(uint8_t *bytes); 70 | 71 | #ifdef __cplusplus 72 | } // end "C" 73 | #endif 74 | 75 | #endif // __STDLIB_H_ 76 | -------------------------------------------------------------------------------- /stdlib/utils.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "utils.h" 5 | #include "debug.h" 6 | // Convert int32 to decimal string 7 | // Returns the string length 8 | int int32_to_str(int32_t value, char *str_buffer, int buffer_size) { 9 | int index = 0; 10 | int is_negative = 0; 11 | 12 | // Handle negative numbers 13 | if (value < 0) { 14 | is_negative = 1; 15 | value = -value; // Convert to positive for processing 16 | } 17 | 18 | // Special case for zero 19 | if (value == 0) { 20 | if (buffer_size > 0) { 21 | str_buffer[index++] = '0'; 22 | } 23 | } else { 24 | // Convert from least significant digit to most significant 25 | while (value > 0 && index < buffer_size - 1) { 26 | str_buffer[index++] = '0' + (value % 10); 27 | value /= 10; 28 | } 29 | 30 | // Add negative sign if needed 31 | if (is_negative && index < buffer_size - 1) { 32 | str_buffer[index++] = '-'; 33 | } 34 | 35 | // Reverse the string 36 | for (int j = 0; j < index / 2; j++) { 37 | char temp = str_buffer[j]; 38 | str_buffer[j] = str_buffer[index - 1 - j]; 39 | str_buffer[index - 1 - j] = temp; 40 | } 41 | } 42 | 43 | // Add string terminator 44 | if (index < buffer_size) { 45 | str_buffer[index] = '\0'; 46 | } 47 | 48 | return index; 49 | } 50 | 51 | void debug_i32(int32_t value) { 52 | #ifndef NDEBUG 53 | char str_buffer[12]; // 32-bit integer max 10 digits + possible minus sign + 54 | // terminator 55 | int len = int32_to_str(value, str_buffer, sizeof(str_buffer)); 56 | 57 | // Print bytes using debug_bytes 58 | debug_bytes((ADDRESS_UINT)&str_buffer, len); 59 | #endif // NDEBUG 60 | } 61 | 62 | void debug_string(const char *str) { 63 | #ifndef NDEBUG 64 | uint32_t str_len = 0; 65 | for (int i = 0;; i++) { 66 | if (str[i] == '\0') { 67 | break; 68 | } 69 | str_len++; 70 | } 71 | debug_bytes((ADDRESS_UINT)str, str_len); 72 | #endif // NDEBUG 73 | } 74 | -------------------------------------------------------------------------------- /stdlib/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) the DTVM authors Core Contributors 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #ifndef __UTILS_H_ 5 | #define __UTILS_H_ 6 | #include "hostapi.h" 7 | 8 | void debug_string(const char *str); 9 | 10 | void debug_i32(int32_t value); 11 | 12 | #endif // __UTILS_H_ 13 | -------------------------------------------------------------------------------- /stdlib/wasm/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DTVMStack/DTVM_SolSDK/c5ba420cfb66384ec22fb2435e4033e3d2f08c6c/stdlib/wasm/.gitkeep -------------------------------------------------------------------------------- /tools/build_utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | setup_build_mode() { 5 | local BUILD_MODE=${1:-release} 6 | echo "Building in $BUILD_MODE mode" 7 | 8 | # --enable-little-endian-storage-load-store 9 | YUL2WASM_EXTRA_ARGS="--verbose" 10 | 11 | # if env ENABLE_LITTLE_ENDIAN_STORAGE == "ON", then add --enable-little-endian-storage-load-store 12 | if [ "$ENABLE_LITTLE_ENDIAN_STORAGE" == "ON" ]; then 13 | YUL2WASM_EXTRA_ARGS="$YUL2WASM_EXTRA_ARGS --enable-little-endian-storage-load-store" 14 | fi 15 | 16 | # Set the yul2wasm path based on the build mode 17 | if [ "$BUILD_MODE" == "release" ]; then 18 | YUL2WASM_PATH="../../target/release/yul2wasm" 19 | else 20 | YUL2WASM_PATH="../../target/debug/yul2wasm" 21 | YUL2WASM_EXTRA_ARGS="$YUL2WASM_EXTRA_ARGS --debug" 22 | fi 23 | 24 | export YUL2WASM_PATH 25 | export YUL2WASM_EXTRA_ARGS 26 | } 27 | 28 | compile_contract() { 29 | local contract=$1 30 | local YUL_IR_PATH=$2 31 | echo "Compiling $contract..." 32 | 33 | $YUL2WASM_PATH --input $YUL_IR_PATH/$contract.sol/$contract.iropt \ 34 | --output $YUL_IR_PATH/$contract.wasm \ 35 | $YUL2WASM_EXTRA_ARGS 36 | 37 | wasm2wat -o $YUL_IR_PATH/$contract.wat $YUL_IR_PATH/$contract.wasm 38 | 39 | if [ -f "$YUL_IR_PATH/$contract.wasm" ]; then 40 | echo "Successfully compiled $contract to $YUL_IR_PATH/$contract.wasm" 41 | else 42 | echo "Error: Failed to compile $contract" >&2 43 | exit 1 44 | fi 45 | } 46 | 47 | compile_all_contracts() { 48 | local contracts=("${!1}") 49 | local YUL_IR_PATH=$2 50 | 51 | for contract in "${contracts[@]}"; do 52 | compile_contract "$contract" "$YUL_IR_PATH" 53 | done 54 | 55 | echo "All contracts compiled successfully" 56 | } 57 | -------------------------------------------------------------------------------- /tools/chain_mockcli/linux_x86/chain_mockcli-linux-ubuntu22.04-0.1.0.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6f14988987a7b7e844bd8584e71f4efdb1ffd6b94f74edae27815501f017498f 3 | size 2546024 4 | -------------------------------------------------------------------------------- /tools/chain_mockcli/mac_arm/chain_mockcli-mac-arm-0.1.0.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b10d0f4461ea3ba666a1eea73b27493f8bef58d7c740b93236060e52161b4df 3 | size 2421265 4 | -------------------------------------------------------------------------------- /tools/forge_test_utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source ../examples/scripts/common.sh 5 | ABI_ENCODE="../scripts/abi_encode.py" 6 | MOCKCLI_PATH="/opt/chain_mockcli" 7 | 8 | DEPLOYER_SENDER=0x9988776655443322119900112233445566778899 9 | 10 | cleanup() { 11 | if [ -f test.db ]; then 12 | rm -f test.db 13 | fi 14 | } 15 | 16 | deploy_contract() { 17 | local wasm_file=$1 18 | local deploy_addr=$2 19 | echo "deploy contract: $wasm_file" 20 | $MOCKCLI_PATH -f $wasm_file --action deploy -s $DEPLOYER_SENDER -t $deploy_addr -i 0x 21 | } 22 | 23 | call_contract_function() { 24 | local wasm_file=$1 25 | local deploy_addr=$2 26 | local function_name=$3 27 | local expected_output=$4 28 | echo "call contract $wasm_file function $function_name" 29 | ABI_DATA=$($ABI_ENCODE "$function_name") 30 | output=$($MOCKCLI_PATH -f $wasm_file -t $deploy_addr --action call --print-time --enable-gas-meter -s $DEPLOYER_SENDER -i $ABI_DATA) 31 | run_cmd_and_grep "$output" "$expected_output" 32 | } 33 | 34 | init_test() { 35 | cleanup 36 | 37 | local wasm_vm_file=$1 38 | local WASM_TEST_VM_DEPLOY_ADDR=$2 39 | local contract_file=$3 40 | local DEPLOYER_INITIALIZER_ADDR=$4 41 | 42 | deploy_contract "$wasm_vm_file" "$WASM_TEST_VM_DEPLOY_ADDR" 43 | deploy_contract "$contract_file" "$DEPLOYER_INITIALIZER_ADDR" 44 | 45 | call_contract_function "$contract_file" "$DEPLOYER_INITIALIZER_ADDR" "setUp()" 'evm finish with result hex:' 46 | } 47 | -------------------------------------------------------------------------------- /tools/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Get the directory where the script is located 6 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )" 8 | 9 | # Function to perform formatting 10 | format() { 11 | echo "Running cargo fmt..." 12 | cargo fmt 13 | 14 | echo "Running cargo clippy --fix..." 15 | cargo clippy --fix --allow-dirty 16 | 17 | echo "Running stdlib make fmt..." 18 | cd "$PROJECT_ROOT/stdlib" 19 | make fmt 20 | cd "$PROJECT_ROOT" 21 | } 22 | 23 | # Function to perform format checking 24 | check() { 25 | echo "Running cargo fmt --check..." 26 | cargo fmt --all -- --check 27 | 28 | echo "Running cargo clippy check..." 29 | cargo clippy --all-targets --all-features -- -D warnings 30 | 31 | echo "Running stdlib make fmt_check..." 32 | cd "$PROJECT_ROOT/stdlib" 33 | make fmt_check 34 | cd "$PROJECT_ROOT" 35 | } 36 | 37 | # Main script logic 38 | case "$1" in 39 | "format") 40 | format 41 | ;; 42 | "check") 43 | check 44 | ;; 45 | *) 46 | echo "Usage: $0 {format|check}" 47 | echo " format: Run formatting tools" 48 | echo " check: Run format checking tools" 49 | exit 1 50 | ;; 51 | esac 52 | --------------------------------------------------------------------------------