├── .github └── workflows │ ├── docs.yml │ ├── wheels-dev.yml │ └── wheels.yml ├── .gitignore ├── .idea ├── .gitignore ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── tonpy.iml └── vcs.xml ├── LICENSE ├── MANIFEST.in ├── MANIFEST_WIN.in ├── README.md ├── built_requirements.txt ├── docs ├── Makefile └── source │ ├── conf.py │ ├── index.rst │ ├── installation.rst │ ├── main_info.rst │ ├── modules.rst │ ├── tonpy.autogen.rst │ ├── tonpy.fift.rst │ ├── tonpy.rst │ ├── tonpy.tlb_gen.rst │ ├── tonpy.tvm.rst │ ├── tonpy.types.rst │ ├── tonpy.types.tlb_types.rst │ └── tonpy.utils.rst ├── docs_requirements.txt ├── fix_whl_name.py ├── generate_autogen.py ├── playground └── example.ipynb ├── pyproject.toml ├── requirements.txt ├── run_test_in_docker.sh ├── setup.py ├── src └── tonpy │ ├── __init__.py │ ├── autogen │ ├── __init__.py │ └── block.py │ ├── data_for_tests │ ├── __init__.py │ ├── block_boc.py │ ├── dict_test_boc.py │ └── raw_emulator_data_for_test.py │ ├── fift │ ├── __init__.py │ ├── disasm.py │ ├── fift.py │ └── libs │ │ ├── Asm.fif │ │ ├── Color.fif │ │ ├── Disasm.fif │ │ ├── Fift.fif │ │ ├── FiftExt.fif │ │ ├── GetOpt.fif │ │ ├── Lisp.fif │ │ ├── Lists.fif │ │ ├── Stack.fif │ │ └── TonUtil.fif │ ├── libs │ └── .gitkeep │ ├── tests │ ├── test_address.py │ ├── test_aug_vmdict.py │ ├── test_autogen_block.py │ ├── test_cell.py │ ├── test_cellbuilder.py │ ├── test_cellslice.py │ ├── test_disasm.py │ ├── test_fift.py │ ├── test_pickle.py │ ├── test_python_ton.py │ ├── test_raw_emulator.py │ ├── test_stack.py │ ├── test_tlb.py │ ├── test_tlb_redefine.py │ ├── test_tlb_unpack.py │ ├── test_tvm.py │ ├── test_utils.py │ └── test_vmdict.py │ ├── tlb_gen │ ├── __init__.py │ └── py.py │ ├── tlb_sources │ └── block.tlb │ ├── tvm │ ├── __init__.py │ ├── emulator.py │ └── tvm.py │ ├── types │ ├── __init__.py │ ├── address.py │ ├── cell.py │ ├── cellbuilder.py │ ├── cellslice.py │ ├── stack.py │ ├── tlb.py │ ├── tlb_types │ │ ├── __init__.py │ │ ├── builtins.py │ │ ├── complex.py │ │ ├── nat.py │ │ └── reft.py │ └── vmdict.py │ └── utils │ ├── __init__.py │ ├── actions.py │ ├── address_packer.py │ ├── bit_converter.py │ ├── bit_int.py │ ├── global_config.py │ ├── shard_account.py │ └── token.py └── win └── libcrypto-1_1-x64.dll /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs content 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | concurrency: 14 | group: "pages" 15 | cancel-in-progress: false 16 | 17 | jobs: 18 | # Single deploy job since we're just deploying 19 | deploy: 20 | environment: 21 | name: github-pages 22 | url: ${{ steps.deployment.outputs.page_url }} 23 | runs-on: Linux 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v3 27 | - name: Setup Pages 28 | uses: actions/configure-pages@v3 29 | - name: Get Python 30 | uses: actions/setup-python@v3 31 | with: 32 | python-version: "3.10" 33 | - name: Setup build 34 | run: | 35 | curl -Lo ./src/tonpy/libs/python_ton.cpython-310-x86_64-linux-gnu.so https://github.com/disintar/ton/releases/download/ton-cpython-310-x86_64-linux/python_ton.cpython-310-x86_64-linux-gnu.so 36 | pip install -r requirements.txt 37 | pip install -r docs_requirements.txt 38 | - name: Build 39 | run: | 40 | cd docs && make html 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v1 43 | with: 44 | path: './docs/build/html' 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v2 48 | -------------------------------------------------------------------------------- /.github/workflows/wheels-dev.yml: -------------------------------------------------------------------------------- 1 | name: DEV Wheels & Tests & PyPi 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | workflow_call: 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | build: 20 | strategy: 21 | matrix: 22 | include: 23 | - arch: x86_64 24 | os: Linux 25 | python: "3.9" 26 | link: https://github.com/disintar/ton/releases/download/ton-cpython-39-x86_64-linux/ 27 | file: python_ton.cpython-39-x86_64-linux-gnu.so 28 | fix_tag: manylinux2014_x86_64 29 | 30 | runs-on: ${{ matrix.os }} 31 | 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: actions/setup-python@v3 35 | with: 36 | python-version: ${{ matrix.python }} 37 | env: 38 | USER: tvorogme 39 | 40 | - name: Install deps 41 | run: | 42 | python -m pip install -r built_requirements.txt 43 | python -m pip install -r requirements.txt 44 | 45 | - name: Download prebuilt 46 | run: | 47 | curl -Lo ./src/tonpy/libs/${{ matrix.file }} ${{ matrix.link }}${{ matrix.file }} 48 | 49 | - name: Run tests 50 | run: | 51 | pytest 52 | 53 | - name: Build wheel 54 | run: | 55 | python -m build --wheel --outdir dist/ . 56 | python fix_whl_name.py 57 | env: 58 | TAG_FIX: ${{ matrix.fix_tag }} 59 | DEV_PYPI: true 60 | - name: Store the binary wheel 61 | uses: actions/upload-artifact@v2 62 | with: 63 | name: python-package-distributions 64 | path: dist 65 | 66 | deploy: 67 | name: Publish DEV 🐍📦 to PyPI 68 | runs-on: Linux 69 | needs: 70 | - build 71 | 72 | steps: 73 | - uses: actions/checkout@v3 74 | 75 | - name: Download all the dists 76 | uses: actions/download-artifact@v2 77 | with: 78 | name: python-package-distributions 79 | path: dist/ 80 | 81 | - name: Install deps 82 | run: python -m pip install setuptools==68.0.0 build==0.10.0 twine 83 | 84 | - name: Publish distribution 📦 to PyPi 85 | run: | 86 | twine upload dist/* 87 | env: 88 | TWINE_USERNAME: __token__ 89 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 90 | -------------------------------------------------------------------------------- /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Wheels & Tests & PyPi 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | workflow_call: 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | build: 20 | strategy: 21 | matrix: 22 | include: 23 | - arch: x86_64 24 | os: macos-latest 25 | python: "3.10" 26 | link: https://github.com/disintar/ton/releases/download/ton-cpython-310-x86_64-darwin/ 27 | file: python_ton.cpython-310-darwin.so 28 | fix_tag: x86_64 29 | 30 | - arch: x86_64 31 | os: macos-latest 32 | python: "3.11" 33 | link: https://github.com/disintar/ton/releases/download/ton-cpython-311-x86_64-darwin/ 34 | file: python_ton.cpython-311-darwin.so 35 | fix_tag: x86_64 36 | 37 | - arch: x86_64 38 | os: Linux 39 | python: "3.9" 40 | link: https://github.com/disintar/ton/releases/download/ton-cpython-39-x86_64-linux/ 41 | file: python_ton.cpython-39-x86_64-linux-gnu.so 42 | fix_tag: manylinux2014_x86_64 43 | 44 | - arch: x86_64 45 | os: Linux 46 | python: "3.10" 47 | link: https://github.com/disintar/ton/releases/download/ton-cpython-310-x86_64-linux/ 48 | file: python_ton.cpython-310-x86_64-linux-gnu.so 49 | fix_tag: manylinux2014_x86_64 50 | 51 | - arch: x86_64 52 | os: Linux 53 | python: "3.11" 54 | link: https://github.com/disintar/ton/releases/download/ton-cpython-311-x86_64-linux/ 55 | file: python_ton.cpython-311-x86_64-linux-gnu.so 56 | fix_tag: manylinux2014_x86_64 57 | 58 | - arch: aarch64 59 | os: macOS 60 | python: "3.10" 61 | link: https://github.com/disintar/ton/releases/download/ton-cpython-310-aarch64-darwin/ 62 | file: python_ton.cpython-310-darwin.so 63 | fix_tag: arm64 64 | 65 | - arch: aarch64 66 | os: macOS 67 | python: "3.11" 68 | link: https://github.com/disintar/ton/releases/download/ton-cpython-311-aarch64-darwin/ 69 | file: python_ton.cpython-311-darwin.so 70 | fix_tag: arm64 71 | 72 | - arch: x86_64 73 | os: windows-2019 74 | python: "3.9" 75 | link: https://github.com/disintar/ton/releases/download/ton-cpython-39-x86_64-windows/ 76 | file: python_ton.cp39-win_amd64.pyd 77 | fix_tag: 78 | 79 | - arch: x86_64 80 | os: windows-2019 81 | python: "3.10" 82 | link: https://github.com/disintar/ton/releases/download/ton-cpython-310-x86_64-windows/ 83 | file: python_ton.cp310-win_amd64.pyd 84 | fix_tag: 85 | 86 | - arch: x86_64 87 | os: windows-2019 88 | python: "3.11" 89 | link: https://github.com/disintar/ton/releases/download/ton-cpython-311-x86_64-windows/ 90 | file: python_ton.cp311-win_amd64.pyd 91 | fix_tag: 92 | 93 | runs-on: ${{ matrix.os }} 94 | 95 | steps: 96 | - uses: actions/checkout@v3 97 | - uses: actions/setup-python@v3 98 | with: 99 | python-version: ${{ matrix.python }} 100 | env: 101 | USER: tvorogme 102 | 103 | - name: Install deps 104 | run: | 105 | python -m pip install -r built_requirements.txt 106 | python -m pip install -r requirements.txt 107 | 108 | - name: Download prebuilt 109 | run: | 110 | curl -Lo ./src/tonpy/libs/${{ matrix.file }} ${{ matrix.link }}${{ matrix.file }} 111 | 112 | - name: Win fix manifest 113 | run: | 114 | mv win/libcrypto-1_1-x64.dll ./src/tonpy/libs/ 115 | mv MANIFEST.in MANIFEST_LINUX.in 116 | mv MANIFEST_WIN.in MANIFEST.in 117 | if: matrix.os == 'windows-2019' 118 | 119 | - name: Run tests 120 | run: | 121 | pytest 122 | 123 | - name: Build wheel 124 | run: | 125 | python -m build --wheel --outdir dist/ . 126 | python fix_whl_name.py 127 | env: 128 | TAG_FIX: ${{ matrix.fix_tag }} 129 | - name: Store the binary wheel 130 | uses: actions/upload-artifact@v2 131 | with: 132 | name: python-package-distributions 133 | path: dist 134 | 135 | buildMacOs39: 136 | strategy: 137 | matrix: 138 | include: 139 | - arch: x86_64 140 | os: macos-latest 141 | python: "3.9" 142 | link: https://github.com/disintar/ton/releases/download/ton-cpython-39-x86_64-darwin/ 143 | file: python_ton.cpython-39-darwin.so 144 | fix_tag: x86_64 145 | 146 | - arch: aarch64 147 | os: macOS 148 | python: "3.9" 149 | link: https://github.com/disintar/ton/releases/download/ton-cpython-39-aarch64-darwin/ 150 | file: python_ton.cpython-39-darwin.so 151 | fix_tag: arm64 152 | 153 | runs-on: ${{ matrix.os }} 154 | 155 | steps: 156 | - uses: actions/checkout@v3 157 | - name: Install python3.9 158 | run: | 159 | /opt/homebrew/bin/brew install python@3.9 160 | if: matrix.os == 'macOS' 161 | 162 | - name: Install python3.9 163 | run: | 164 | brew install python@3.9 165 | if: matrix.os == 'macos-latest' 166 | 167 | - name: Install deps 168 | run: | 169 | python3.9 -m pip install -r built_requirements.txt 170 | python3.9 -m pip install -r requirements.txt 171 | 172 | - name: Download prebuilt 173 | run: | 174 | curl -Lo ./src/tonpy/libs/${{ matrix.file }} ${{ matrix.link }}${{ matrix.file }} 175 | 176 | - name: Run tests 177 | run: | 178 | python3.9 -m pytest 179 | 180 | - name: Build wheel 181 | run: | 182 | python3.9 -m build --wheel --outdir dist/ . 183 | python3.9 fix_whl_name.py 184 | env: 185 | TAG_FIX: ${{ matrix.fix_tag }} 186 | - name: Store the binary wheel 187 | uses: actions/upload-artifact@v2 188 | with: 189 | name: python-package-distributions 190 | path: dist 191 | 192 | # buildVirtualLinux: 193 | # strategy: 194 | # matrix: 195 | # include: 196 | # - arch: aarch64 197 | # os: Linux 198 | # python: "3.9" 199 | # link: https://github.com/disintar/ton/releases/download/ton-cpython-39-aarch64-linux/ 200 | # file: python_ton.cpython-39-aarch64-linux-gnu.so 201 | # fix_tag: manylinux2014_aarch64 202 | # docker_python_tag: 3.9.17-bookworm 203 | # 204 | # - arch: aarch64 205 | # os: Linux 206 | # python: "3.10" 207 | # link: https://github.com/disintar/ton/releases/download/ton-cpython-310-aarch64-linux/ 208 | # file: python_ton.cpython-310-aarch64-linux-gnu.so 209 | # fix_tag: manylinux2014_aarch64 210 | # docker_python_tag: 3.10.12-bookworm 211 | # 212 | # - arch: aarch64 213 | # os: Linux 214 | # python: "3.11" 215 | # link: https://github.com/disintar/ton/releases/download/ton-cpython-311-aarch64-linux/ 216 | # file: python_ton.cpython-311-aarch64-linux-gnu.so 217 | # fix_tag: manylinux2014_aarch64 218 | # docker_python_tag: 3.11.4-bookworm 219 | # 220 | # runs-on: ${{ matrix.os }} 221 | # needs: 222 | # - build 223 | # 224 | # steps: 225 | # - uses: actions/checkout@v3 226 | # 227 | # - name: Save download prebuilt 228 | # run: | 229 | # curl -Lo ${PWD}/src/tonpy/libs/${{ matrix.file }} ${{ matrix.link }}${{ matrix.file }} 230 | # 231 | # - name: Run tests in virtual 232 | # run: | 233 | # docker run --network host --platform linux/aarch64 -e TAG_FIX=${{ matrix.fix_tag }} --rm --volume "${PWD}:/tmp/tonpy" -t arm64v8/python:${{ matrix.docker_python_tag }} /tmp/tonpy/run_test_in_docker.sh 234 | # if: (matrix.os == 'Linux' && matrix.arch == 'aarch64') 235 | # 236 | # - name: Show wheel 237 | # run: | 238 | # ls -lhta ./dist 239 | # 240 | # - name: Store the binary wheel 241 | # uses: actions/upload-artifact@v2 242 | # with: 243 | # name: python-package-distributions 244 | # path: dist 245 | 246 | deploy: 247 | name: Publish 🐍📦 to PyPI 248 | runs-on: Linux 249 | needs: 250 | # - buildVirtualLinux 251 | - build 252 | - buildMacOs39 253 | 254 | steps: 255 | - uses: actions/checkout@v3 256 | 257 | - name: Download all the dists 258 | uses: actions/download-artifact@v2 259 | with: 260 | name: python-package-distributions 261 | path: dist/ 262 | 263 | - name: Install deps 264 | run: python -m pip install setuptools==68.0.0 build==0.10.0 twine 265 | 266 | - name: Publish distribution 📦 to PyPi 267 | run: | 268 | twine upload dist/* 269 | env: 270 | TWINE_USERNAME: __token__ 271 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 272 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # pdm 106 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 107 | #pdm.lock 108 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 109 | # in version control. 110 | # https://pdm.fming.dev/#use-with-ide 111 | .pdm.toml 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 157 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 158 | 159 | # User-specific stuff 160 | .idea/**/workspace.xml 161 | .idea/**/tasks.xml 162 | .idea/**/usage.statistics.xml 163 | .idea/**/dictionaries 164 | .idea/**/shelf 165 | 166 | # AWS User-specific 167 | .idea/**/aws.xml 168 | 169 | # Generated files 170 | .idea/**/contentModel.xml 171 | 172 | # Sensitive or high-churn files 173 | .idea/**/dataSources/ 174 | .idea/**/dataSources.ids 175 | .idea/**/dataSources.local.xml 176 | .idea/**/sqlDataSources.xml 177 | .idea/**/dynamic.xml 178 | .idea/**/uiDesigner.xml 179 | .idea/**/dbnavigator.xml 180 | 181 | # Gradle 182 | .idea/**/gradle.xml 183 | .idea/**/libraries 184 | 185 | # Gradle and Maven with auto-import 186 | # When using Gradle or Maven with auto-import, you should exclude module files, 187 | # since they will be recreated, and may cause churn. Uncomment if using 188 | # auto-import. 189 | # .idea/artifacts 190 | # .idea/compiler.xml 191 | # .idea/jarRepositories.xml 192 | # .idea/modules.xml 193 | # .idea/*.iml 194 | # .idea/modules 195 | # *.iml 196 | # *.ipr 197 | 198 | # CMake 199 | cmake-build-*/ 200 | 201 | # Mongo Explorer plugin 202 | .idea/**/mongoSettings.xml 203 | 204 | # File-based project format 205 | *.iws 206 | 207 | # IntelliJ 208 | out/ 209 | 210 | # mpeltonen/sbt-idea plugin 211 | .idea_modules/ 212 | 213 | # JIRA plugin 214 | atlassian-ide-plugin.xml 215 | 216 | # Cursive Clojure plugin 217 | .idea/replstate.xml 218 | 219 | # SonarLint plugin 220 | .idea/sonarlint/ 221 | 222 | # Crashlytics plugin (for Android Studio and IntelliJ) 223 | com_crashlytics_export_strings.xml 224 | crashlytics.properties 225 | crashlytics-build.properties 226 | fabric.properties 227 | 228 | # Editor-based Rest Client 229 | .idea/httpRequests 230 | 231 | # Android studio 3.1+ serialized cache file 232 | .idea/caches/build_file_checksums.ser 233 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/tonpy.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 10 | 11 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 12 | 13 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 14 | 15 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 16 | 17 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 18 | 19 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 20 | 21 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 22 | 23 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 24 | 25 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 26 | 27 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 28 | 29 | 2. Grant of Copyright License. 30 | 31 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. 34 | 35 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 36 | 37 | 4. Redistribution. 38 | 39 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 40 | 41 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 42 | You must cause any modified files to carry prominent notices stating that You changed the files; and 43 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 44 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 45 | 46 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 47 | 48 | 5. Submission of Contributions. 49 | 50 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 51 | 52 | 6. Trademarks. 53 | 54 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 55 | 56 | 7. Disclaimer of Warranty. 57 | 58 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 59 | 60 | 8. Limitation of Liability. 61 | 62 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 63 | 64 | 9. Accepting Warranty or Additional Liability. 65 | 66 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 67 | 68 | END OF TERMS AND CONDITIONS 69 | 70 | Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 71 | 72 | Licensed under the Apache License, Version 2.0 (the "License"); 73 | you may not use this file except in compliance with the License. 74 | You may obtain a copy of the License at 75 | 76 | http://www.apache.org/licenses/LICENSE-2.0 77 | 78 | Unless required by applicable law or agreed to in writing, software 79 | distributed under the License is distributed on an "AS IS" BASIS, 80 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 81 | See the License for the specific language governing permissions and 82 | limitations under the License. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | recursive-include src/tonpy/libs *.so 3 | recursive-include src/tonpy/fift/libs *.fif -------------------------------------------------------------------------------- /MANIFEST_WIN.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | recursive-include src\tonpy\libs *.pyd 3 | recursive-include src\tonpy\libs *.dll 4 | recursive-include src\tonpy\fift\libs *.fif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [telegram-tondev-url]: https://t.me/tondev_eng 2 | [ton-svg]: https://img.shields.io/badge/Based%20on-TON-blue 3 | [telegram-tondev-badge]: https://img.shields.io/badge/chat-TONDev-2CA5E0?logo=telegram&logoColor=white&style=flat 4 | [ton]: https://ton.org 5 | 6 | 7 | [![Based on TON][ton-svg]][ton] 8 | ![Python version](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue) 9 | ![Supported OS](https://img.shields.io/badge/os-Linux%20%7C%20MacOS%20%7C%20Windows-green) 10 | ![Supported arch](https://img.shields.io/badge/arch-arm64%20%7C%20x86_64-purple) 11 | [![Telegram Community Chat][telegram-tondev-badge]][telegram-tondev-url] 12 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 13 | [![PyPI version](https://badge.fury.io/py/tonpy.svg)](https://pypi.org/project/tonpy/) 14 | 15 | # tonpy: powerful Python TON toolkit 16 | 17 | ## What is it? 18 | 19 | **tonpy** is a Python package that provides data structures and API to interact 20 | with [TON blockchain](https://github.com/ton-blockchain/ton). Tonpy is separate for two 21 | packages: [C++ bindings](https://github.com/disintar/ton/tree/master/tvm-python) 22 | and [main package](https://github.com/disintar/tonpy) 23 | that [takes](https://github.com/disintar/tonpy/tree/main/.github/workflows) pre-built binaries compiled 24 | by [workflow](https://github.com/disintar/ton/tree/master/.github/workflows) and creates complete python package with 25 | some python code on top of C++ bindings. 26 | 27 | 28 | ## Installation 29 | 30 | One command: `pip install tonpy` 31 | 32 | Complex installation for not supported systems and python versions are described in [documentation](https://tonpy.dton.io/installation.html#development-setup-compile-from-sources) 33 | 34 | ## Documentation 35 | 36 | Documentation can be found on [tonpy.dton.io](https://tonpy.dton.io) 37 | 38 | ## Donation 39 | 40 | If you want to support our work, send any coins to: 41 | 42 | `EQDfmsDtWQP5D_YkXX-XlULvs4HivRaKY38ftT2hS5yAANRf` 43 | 44 | ## License 45 | 46 | Copyright (c) 2023 Disintar LLP Licensed under the Apache License 47 | -------------------------------------------------------------------------------- /built_requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | build 3 | twine 4 | pytest -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SPHINXAPIDOC ?= sphinx-apidoc 9 | SOURCEDIR = source 10 | BUILDDIR = build 11 | 12 | # Put it first so that "make" without argument is like "make help". 13 | help: 14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 15 | 16 | .PHONY: help Makefile 17 | 18 | # Catch-all target: route all unknown targets to Sphinx using the new 19 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 20 | %: Makefile 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | @$(SPHINXAPIDOC) -o $(SOURCEDIR) "../src/tonpy" 23 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | sys.path.insert(0, os.path.abspath('../../src/')) 17 | sys.path.insert(0, os.path.abspath('../../src/tonpy')) 18 | sys.path.insert(0, os.path.abspath('../../src/tonpy/types')) 19 | 20 | # -- Project informa 21 | # tion ----------------------------------------------------- 22 | 23 | project = 'tonpy' 24 | copyright = '2023, Disintar LLP' 25 | author = 'Disintar LLP' 26 | 27 | # -- General configuration --------------------------------------------------- 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = ['sphinx.ext.autodoc'] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # List of patterns, relative to source directory, that match files and 38 | # directories to ignore when looking for source files. 39 | # This pattern also affects html_static_path and html_extra_path. 40 | exclude_patterns = [] 41 | 42 | # -- Options for HTML output ------------------------------------------------- 43 | 44 | # The theme to use for HTML and HTML Help pages. See the documentation for 45 | # a list of builtin themes. 46 | # 47 | html_theme = 'alabaster' 48 | 49 | # Add any paths that contain custom static files (such as style sheets) here, 50 | # relative to this directory. They are copied after the builtin static files, 51 | # so a file named "default.css" will overwrite the builtin "default.css". 52 | html_static_path = ['_static'] 53 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | tonpy: powerful Python TON toolkit 2 | ================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | main_info 9 | installation 10 | tonpy 11 | tonpy.types 12 | tonpy.tlb_gen 13 | 14 | 15 | .. role:: raw-html(raw) 16 | :format: html 17 | 18 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Module setup 2 | =============== 3 | 4 | Setup from PyPi 5 | --------------- 6 | 7 | ``pip install tonpy`` 8 | 9 | 10 | Supported OS: 11 | 12 | - MacOS 13 | - Linux 14 | - Windows 15 | 16 | Supported Python versions: 17 | 18 | - Python 3.9 19 | - Python 3.10 20 | - Python 3.11 21 | 22 | Supported arch: 23 | 24 | - aarch64 (include Mac M1) 25 | - x86_64 26 | 27 | For each ``OS`` / ``Python`` / ``arch`` it's separated ``.whl`` distribution in `pypi`_ 28 | 29 | .. _pypi: https://pypi.org/project/tonpy/#files 30 | 31 | Also you can compile lib from sources for your os or for local development. 32 | 33 | Development setup (pre-built) 34 | ----------------------------- 35 | 36 | 1. Find you ``OS`` / ``Python`` / ``arch`` in `releases`_ 37 | 2. Download ``so`` or ``dll`` from release and put it into ``src/tonpy/libs/`` folder 38 | 39 | .. _releases: https://github.com/disintar/ton/releases 40 | 41 | 42 | Development setup (compile from sources) 43 | ---------------------------------------- 44 | 45 | If you want to setup ``tonpy`` package for local development (including C++ code) you need to: 46 | 47 | 1. Download Disintar TON monorepo fork: 48 | ``git clone https://github.com/disintar/ton`` 49 | 50 | 2. Compile ``python_ton`` target with ``cmake``: 51 | .. code-block:: console 52 | 53 | cd ton && mkdir build && cd build 54 | 55 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DTON_USE_PYTHON=1 \ 56 | -DPYTHON_INCLUDE_DIRECTORIES=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \ 57 | -DPYTHON_LIBRARY=$(python3 -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR'))") \ 58 | -DPYTHON_EXECUTABLE=$(which python3) 59 | 60 | cmake --build . --target python_ton 61 | 62 | Optionally add path to openssl ``-DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3`` 63 | 64 | After compilation ``so`` / ``dll`` file will be in ``build/tvm_python`` folder. Example name: ``python_ton.cpython-39-darwin.so`` 65 | 66 | 3. Link compiled shared object to ``src/tonpy/libs/`` of ``tonpy`` project: 67 | ``ln -s PATH_TO_LIB src/tonpy/libs/`` 68 | 69 | -------------------------------------------------------------------------------- /docs/source/main_info.rst: -------------------------------------------------------------------------------- 1 | Main information 2 | ================ 3 | 4 | 5 | 6 | **tonpy** is a Python package that provides data structures and API to interact 7 | with `TON blockchain`_. Tonpy is separate for two 8 | packages: `C++ bindings`_ 9 | and `main package`_ 10 | that `takes`_ pre-built binaries compiled 11 | by `workflow`_ and creates complete python package with 12 | some python code on top of C++ bindings. 13 | 14 | We try to provide 100% test coverage on all structures and functions. Github workflow automatically run tests on all supported systems (Linux, Windows, MacOS) / (x86_64 / aarch64) / (py39, py310, py311) and publish package to pypi. 15 | 16 | 17 | .. _TON blockchain: https://github.com/ton-blockchain/ton 18 | .. _C++ bindings: https://github.com/disintar/ton/tree/master/tvm-python 19 | .. _main package: https://github.com/disintar/tonpy 20 | .. _takes: https://github.com/disintar/tonpy/tree/main/.github/workflows 21 | .. _workflow: https://github.com/disintar/ton/tree/master/.github/workflows 22 | 23 | What is supported? 24 | ------------------ 25 | 26 | | 27 | 28 | - Basic types: 29 | - `Cell`_ 30 | - `Usage example `_ 31 | - `CellSlice`_ 32 | - `Usage example `_ 33 | - `CellBuilder`_ 34 | - `Usage example `_ 35 | - `VmDict`_ (HashmapE, HashmapAugE) 36 | - `Usage example `_ 37 | - `TypedVmDict`_ (same as VmDict but allow to unpack values / extra with TLB types) 38 | - `Augmented TypedVmDict usage example `_ 39 | - `StackEntry`_ (Tuple included) 40 | - `Usage example `_ 41 | - `Stack`_ 42 | - `Fift usage example `_ 43 | - `TVM usage example `_ 44 | - `Continuation`_ 45 | - `Usage example `_ 46 | - `Augmented data`_ (eval_leaf / skip_extra / eval_fork / eval_empty for HashmapAugE) 47 | - `Usage example `_ 48 | 49 | - TVM 50 | - `Raw TVM`_ 51 | - `Usage example `_ 52 | 53 | - `Detailed TVM step info`_ 54 | - `Usage example `_ 55 | 56 | - `Transaction Emulator`_ 57 | - `Usage example `_ 58 | 59 | 60 | - TLB 61 | - `TLB basic types`_ 62 | - `Basic TLB examples `_ 63 | - `Python codegen`_ 64 | - `Unpack / pack examples `_ 65 | - `Tag examples `_ 66 | 67 | 68 | - Autogen 69 | - `Block TLB types`_ 70 | - `Block parse example `_ 71 | 72 | 73 | - Tools 74 | - `Fift disassembler`_ 75 | - `Usage example `_ 76 | 77 | - `Token metadata parser`_ 78 | 79 | - Fift 80 | - `Fift`_ (with support of Stack / StackEntry) 81 | - `Usage example `_ 82 | 83 | 84 | 85 | .. _Cell: https://tonpy.dton.io/tonpy.types.html#module-tonpy.types.cell 86 | .. _CellSlice: https://tonpy.dton.io/tonpy.types.html#module-tonpy.types.cellslice 87 | .. _CellBuilder: https://tonpy.dton.io/tonpy.types.html#module-tonpy.types.cellbuilder 88 | .. _VmDict: https://tonpy.dton.io/tonpy.types.html#module-tonpy.types.vmdict 89 | .. _TLB basic types: https://tonpy.dton.io/tonpy.types.html#module-tonpy.types.tlb 90 | .. _Python codegen: https://tonpy.dton.io/tonpy.tlb_gen.html#module-tonpy.tlb_gen.py 91 | .. _Fift disassembler: https://tonpy.dton.io/tonpy.fift.html#tonpy.fift.disasm.disassembler 92 | .. _Block TLB types: https://github.com/disintar/tonpy/blob/main/src/tonpy/autogen/block.py 93 | .. _TypedVmDict: https://tonpy.dton.io/tonpy.types.html#tonpy.types.vmdict.TypedVmDict 94 | .. _StackEntry: https://tonpy.dton.io/tonpy.types.html#tonpy.types.stack.StackEntry 95 | .. _Continuation: https://tonpy.dton.io/tonpy.types.html#tonpy.types.stack.Continuation 96 | .. _Stack: https://tonpy.dton.io/tonpy.types.html#tonpy.types.stack.Stack 97 | .. _Augmented data: https://tonpy.dton.io/tonpy.types.html#tonpy.types.vmdict.AugmentedData 98 | .. _Raw TVM: https://tonpy.dton.io/tonpy.tvm.html#tonpy.tvm.tvm.TVM 99 | .. _Detailed TVM step info: https://tonpy.dton.io/tonpy.tvm.html#tonpy.tvm.tvm.StepInfo 100 | .. _Transaction Emulator: https://tonpy.dton.io/tonpy.tvm.html#module-tonpy.tvm.emulator 101 | .. _Token metadata parser: https://tonpy.dton.io/tonpy.utils.html#module-tonpy.utils.token 102 | .. _Fift: https://tonpy.dton.io/tonpy.fift.html#module-tonpy.fift.fift 103 | | 104 | 105 | .. _check tests examples here: https://github.com/disintar/tonpy/tree/main/src/tonpy/tests 106 | 107 | 108 | Test examples may show you how to work with library, `check tests examples here`_ 109 | 110 | 111 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | tonpy 2 | ===== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | tonpy 8 | -------------------------------------------------------------------------------- /docs/source/tonpy.autogen.rst: -------------------------------------------------------------------------------- 1 | tonpy.autogen package 2 | ===================== 3 | 4 | .. |br| raw:: html 5 | 6 | Submodules 7 | ---------- 8 | 9 | tonpy.autogen.block module 10 | -------------------------- 11 | 12 | .. automodule:: tonpy.autogen.block 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | 18 | Module contents 19 | --------------- 20 | 21 | .. automodule:: tonpy.autogen 22 | :members: 23 | :undoc-members: 24 | :show-inheritance: 25 | -------------------------------------------------------------------------------- /docs/source/tonpy.fift.rst: -------------------------------------------------------------------------------- 1 | tonpy.fift package 2 | =================== 3 | 4 | .. |br| raw:: html 5 | 6 |
7 | 8 | 9 | tonpy.fift.disasm module 10 | ------------------------ 11 | 12 | .. autofunction:: tonpy.fift.disasm.disassembler 13 | 14 | 15 | tonpy.fift.fift module 16 | ---------------------- 17 | 18 | .. automodule:: tonpy.fift.fift 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/tonpy.rst: -------------------------------------------------------------------------------- 1 | tonpy package 2 | ============= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | tonpy.types 11 | tonpy.tlb_types 12 | tonpy.tlb_gen 13 | tonpy.autogen 14 | tonpy.tvm 15 | tonpy.fift 16 | tonpy.utils 17 | 18 | -------------------------------------------------------------------------------- /docs/source/tonpy.tlb_gen.rst: -------------------------------------------------------------------------------- 1 | tonpy.tlb\_gen package 2 | ====================== 3 | 4 | .. |br| raw:: html 5 | 6 | Submodules 7 | ---------- 8 | 9 | tonpy.tlb\_gen.py module 10 | ------------------------ 11 | 12 | .. automodule:: tonpy.tlb_gen.py 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | Module contents 18 | --------------- 19 | 20 | .. automodule:: tonpy.tlb_gen 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | -------------------------------------------------------------------------------- /docs/source/tonpy.tvm.rst: -------------------------------------------------------------------------------- 1 | tonpy.tvm package 2 | ================= 3 | 4 | .. |br| raw:: html 5 | 6 | Submodules 7 | ---------- 8 | 9 | tonpy.tvm.emulator module 10 | ------------------------- 11 | 12 | .. automodule:: tonpy.tvm.emulator 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | tonpy.tvm.tvm module 18 | -------------------- 19 | 20 | .. autofunction:: tonpy.tvm.tvm.method_name_to_id 21 | 22 | 23 | .. automodule:: tonpy.tvm.tvm 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | Module contents 29 | --------------- 30 | 31 | .. automodule:: tonpy.tvm 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | -------------------------------------------------------------------------------- /docs/source/tonpy.types.rst: -------------------------------------------------------------------------------- 1 | tonpy.types package 2 | =================== 3 | 4 | .. |br| raw:: html 5 | 6 | Subpackages 7 | ----------- 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | 12 | tonpy.types.tlb_types 13 | 14 | Submodules 15 | ---------- 16 | 17 | tonpy.types.cell module 18 | ----------------------- 19 | 20 | .. automodule:: tonpy.types.cell 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | tonpy.types.cellbuilder module 26 | ------------------------------ 27 | 28 | .. automodule:: tonpy.types.cellbuilder 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | tonpy.types.cellslice module 34 | ---------------------------- 35 | 36 | .. automodule:: tonpy.types.cellslice 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | tonpy.types.stack module 42 | ------------------------ 43 | 44 | .. automodule:: tonpy.types.stack 45 | :members: 46 | :undoc-members: 47 | :show-inheritance: 48 | 49 | tonpy.types.tlb module 50 | ---------------------- 51 | 52 | .. automodule:: tonpy.types.tlb 53 | :members: 54 | :undoc-members: 55 | :show-inheritance: 56 | 57 | tonpy.types.vmdict module 58 | ------------------------- 59 | 60 | .. automodule:: tonpy.types.vmdict 61 | :members: 62 | :undoc-members: 63 | :show-inheritance: 64 | 65 | Module contents 66 | --------------- 67 | 68 | .. automodule:: tonpy.types 69 | :members: 70 | :undoc-members: 71 | :show-inheritance: 72 | -------------------------------------------------------------------------------- /docs/source/tonpy.types.tlb_types.rst: -------------------------------------------------------------------------------- 1 | tonpy.types.tlb\_types package 2 | ============================== 3 | 4 | .. |br| raw:: html 5 | 6 | Submodules 7 | ---------- 8 | 9 | tonpy.types.tlb\_types.builtins module 10 | -------------------------------------- 11 | 12 | .. automodule:: tonpy.types.tlb_types.builtins 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | tonpy.types.tlb\_types.complex module 18 | ------------------------------------- 19 | 20 | .. automodule:: tonpy.types.tlb_types.complex 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | tonpy.types.tlb\_types.nat module 26 | --------------------------------- 27 | 28 | .. automodule:: tonpy.types.tlb_types.nat 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | tonpy.types.tlb\_types.reft module 34 | ---------------------------------- 35 | 36 | .. automodule:: tonpy.types.tlb_types.reft 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | Module contents 42 | --------------- 43 | 44 | .. automodule:: tonpy.types.tlb_types 45 | :members: 46 | :undoc-members: 47 | :show-inheritance: 48 | -------------------------------------------------------------------------------- /docs/source/tonpy.utils.rst: -------------------------------------------------------------------------------- 1 | tonpy.utils package 2 | =================== 3 | 4 | .. |br| raw:: html 5 | 6 | Submodules 7 | ---------- 8 | 9 | tonpy.utils.bit\_converter module 10 | --------------------------------- 11 | 12 | .. automodule:: tonpy.utils.bit_converter 13 | :members: 14 | :undoc-members: 15 | :show-inheritance: 16 | 17 | tonpy.utils.bit\_int module 18 | --------------------------- 19 | 20 | .. automodule:: tonpy.utils.bit_int 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | tonpy.utils.global\_config module 26 | --------------------------------- 27 | 28 | .. automodule:: tonpy.utils.global_config 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | tonpy.utils.shard\_account module 34 | --------------------------------- 35 | 36 | .. automodule:: tonpy.utils.shard_account 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | tonpy.utils.token module 42 | ------------------------ 43 | 44 | .. automodule:: tonpy.utils.token 45 | :members: 46 | :undoc-members: 47 | :show-inheritance: 48 | 49 | Module contents 50 | --------------- 51 | 52 | .. automodule:: tonpy.utils 53 | :members: 54 | :undoc-members: 55 | :show-inheritance: 56 | -------------------------------------------------------------------------------- /docs_requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx >= 3.0.0 2 | docutils 3 | pylons-sphinx-themes >= 1.0.8 4 | pylons_sphinx_latesturl 5 | repoze.sphinx.autointerface 6 | sphinxcontrib-autoprogram 7 | sphinx-rtd-theme -------------------------------------------------------------------------------- /fix_whl_name.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | import os 4 | 5 | if __name__ == "__main__": 6 | dst = os.listdir('dist') 7 | for file in dst: 8 | for tag in ['linux_x86_64', 'universal2', 'linux_aarch64']: 9 | if tag in file: 10 | os.rename(f"dist/{file}", f'dist/{file.replace(tag, os.getenv("TAG_FIX"))}') 11 | -------------------------------------------------------------------------------- /generate_autogen.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | import os 4 | 5 | from tonpy import process_file 6 | 7 | if __name__ == "__main__": 8 | for tlb_file in os.listdir("./src/tonpy/tlb_sources/"): 9 | if '.tlb' in tlb_file: 10 | process_file(f"./src/tonpy/tlb_sources/{tlb_file}", f"./src/tonpy/autogen/{tlb_file[:-4]}.py") 11 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | loguru 2 | pytest 3 | bitstring -------------------------------------------------------------------------------- /run_test_in_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | python -m pip install --default-timeout=100 --upgrade pip 4 | python -m pip install --default-timeout=100 -r /tmp/tonpy/requirements.txt 5 | python -m pip install --default-timeout=100 -r /tmp/tonpy/built_requirements.txt 6 | cd /tmp/tonpy && pytest 7 | cd /tmp/tonpy && python -m build -n --wheel --outdir dist/ . 8 | cd /tmp/tonpy && python fix_whl_name.py 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | import os 4 | from setuptools import setup, find_packages 5 | IS_DEV = os.environ.get('DEV_PYPI', False) 6 | 7 | with open(f"README.md", "r", encoding="utf-8") as fh: 8 | long_description = fh.read() 9 | 10 | with open(f"requirements.txt", encoding="utf-8") as fh: 11 | install_requires = fh.read().split('\n') 12 | 13 | with open(f"docs_requirements.txt", encoding="utf-8") as fh: 14 | docs_extras = fh.read().split('\n') 15 | 16 | with open(f"built_requirements.txt", encoding="utf-8") as fh: 17 | built_extras = fh.read().split('\n') 18 | 19 | try: 20 | from wheel.bdist_wheel import bdist_wheel as _bdist_wheel 21 | 22 | 23 | class bdist_wheel(_bdist_wheel): 24 | plat_name = 'manylinux2014_x86_64' 25 | 26 | def finalize_options(self): 27 | _bdist_wheel.finalize_options(self) 28 | self.root_is_pure = False 29 | except ImportError: 30 | bdist_wheel = None 31 | 32 | setup( 33 | name="tonpy" if not IS_DEV else "tonpy-dev", 34 | version="0.0.0.1.4c0", 35 | author="Disintar LLP", 36 | author_email="andrey@head-labs.com", 37 | description="Types / API for TON blockchain", 38 | long_description=long_description, 39 | long_description_content_type="text/markdown", 40 | url="https://github.com/disintar/tonpy", 41 | project_urls={ 42 | "Bug Tracker": "https://github.com/disintar/tonpy/issues", 43 | }, 44 | classifiers=[ 45 | "License :: OSI Approved :: Apache Software License", 46 | "Operating System :: OS Independent", 47 | 'Intended Audience :: Developers', 48 | 'Programming Language :: Python :: 3', 49 | 'Programming Language :: Python :: Implementation :: PyPy', 50 | ], 51 | setup_requires=install_requires, 52 | install_requires=install_requires, 53 | python_requires=">3.8,<3.12", 54 | packages=find_packages( 55 | where='src', # '.' by default 56 | ), 57 | package_dir={ 58 | "": "src", 59 | }, 60 | extras_require={'docs': docs_extras, 'built': built_extras}, 61 | cmdclass={'bdist_wheel': bdist_wheel}, 62 | include_package_data=True 63 | ) 64 | -------------------------------------------------------------------------------- /src/tonpy/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types import * 4 | from tonpy.tlb_gen import * 5 | from tonpy.tvm import * 6 | from tonpy.utils import * 7 | from tonpy.fift import * 8 | 9 | from tonpy.utils.address_packer import pack_address 10 | -------------------------------------------------------------------------------- /src/tonpy/autogen/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | -------------------------------------------------------------------------------- /src/tonpy/data_for_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | -------------------------------------------------------------------------------- /src/tonpy/fift/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.fift.disasm import disassembler 4 | from tonpy.fift.fift import Fift 5 | -------------------------------------------------------------------------------- /src/tonpy/fift/disasm.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | 5 | from tonpy.libs.python_ton import code_dissemble_str, code_dissemble_cell 6 | from typing import Union 7 | import os 8 | from tonpy.types import Cell 9 | 10 | libs_root = Path(__file__).parents[0] 11 | libs_root = os.path.join(libs_root, 'libs') + os.path.sep 12 | 13 | def disassembler(code: Union[Cell, str]): 14 | if isinstance(code, str): 15 | return code_dissemble_str(code, str(libs_root)) 16 | elif isinstance(code, Cell): 17 | return code_dissemble_cell(code.cell, str(libs_root)) 18 | else: 19 | raise ValueError(f"Type {type(code)} is not supported. " 20 | f"Please provide cell or string") 21 | -------------------------------------------------------------------------------- /src/tonpy/fift/fift.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import os 5 | 6 | from tonpy.libs.python_ton import PyFift 7 | 8 | from tonpy import Cell 9 | from tonpy.types import Stack 10 | 11 | libs_root = Path(__file__).parents[0] 12 | libs_root = os.path.join(libs_root, 'libs') + os.path.sep 13 | 14 | 15 | class Fift: 16 | def __init__(self, base_path=None, silent=True): 17 | """ 18 | 19 | :param base_path: Path to libs folder 20 | :param silent: Print output to std::out 21 | """ 22 | if base_path is None: 23 | base_path = libs_root 24 | 25 | self.fift = PyFift(base_path, silent) 26 | 27 | def add_lib(self, lib: str) -> None: 28 | """Add lib to load""" 29 | self.fift.add_lib(lib) 30 | 31 | def clear_libs(self) -> None: 32 | """Clear all loaded libs""" 33 | self.fift.clear_libs() 34 | 35 | def run(self, code_text: str) -> int: 36 | """Run fift code, return exit_code""" 37 | return self.fift.run(code_text) 38 | 39 | def get_stack(self) -> Stack: 40 | return Stack(prev_stack=self.fift.get_stack()) 41 | 42 | def last(self): 43 | return self.get_stack()[0].get() 44 | 45 | 46 | def convert_assembler(assembler_code: str) -> Cell: 47 | f = Fift() 48 | f.add_lib("Asm.fif") 49 | f.run(assembler_code) 50 | return f.last() 51 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/Color.fif: -------------------------------------------------------------------------------- 1 | library Color 2 | { 27 emit } : esc 3 | { char " word 27 chr swap $+ 1 ' type does create } :_ make-esc" 4 | make-esc"[0m" ^reset 5 | make-esc"[30m" ^black 6 | make-esc"[31m" ^red 7 | make-esc"[32m" ^green 8 | make-esc"[33m" ^yellow 9 | make-esc"[34m" ^blue 10 | make-esc"[35m" ^magenta 11 | make-esc"[36m" ^cyan 12 | make-esc"[37m" ^white 13 | // bold 14 | make-esc"[30;1m" ^Black 15 | make-esc"[31;1m" ^Red 16 | make-esc"[32;1m" ^Green 17 | make-esc"[33;1m" ^Yellow 18 | make-esc"[34;1m" ^Blue 19 | make-esc"[35;1m" ^Magenta 20 | make-esc"[36;1m" ^Cyan 21 | make-esc"[37;1m" ^White 22 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/Disasm.fif: -------------------------------------------------------------------------------- 1 | library TVM_Disasm 2 | // simple TVM Disassembler 3 | "Lists.fif" include 4 | 5 | variable 'disasm 6 | { 'disasm @ execute } : disasm // disassemble a slice 7 | // usage: x{74B0} disasm 8 | 9 | variable @dismode @dismode 0! 10 | { rot over @ and rot xor swap ! } : andxor! 11 | { -2 0 @dismode andxor! } : stack-disasm // output 's1 s4 XCHG' 12 | { -2 1 @dismode andxor! } : std-disasm // output 'XCHG s1, s4' 13 | { -3 2 @dismode andxor! } : show-vm-code 14 | { -3 0 @dismode andxor! } : hide-vm-code 15 | { @dismode @ 1 and 0= } : stack-disasm? 16 | 17 | variable @indent @indent 0! 18 | { ' space @indent @ 2* times } : .indent 19 | { @indent 1+! } : +indent 20 | { @indent 1-! } : -indent 21 | 22 | { " " $pos } : spc-pos 23 | { dup " " $pos swap "," $pos dup 0< { drop } { 24 | over 0< { nip } { min } cond } cond 25 | } : spc-comma-pos 26 | { { dup spc-pos 0= } { 1 $| nip } while } : -leading 27 | { -leading -trailing dup spc-pos dup 0< { 28 | drop dup $len { atom single } { drop nil } cond } { 29 | $| swap atom swap -leading 2 { over spc-comma-pos dup 0>= } { 30 | swap 1+ -rot $| 1 $| nip -leading rot 31 | } while drop tuple 32 | } cond 33 | } : parse-op 34 | { dup "s-1" $= { drop "s(-1)" true } { 35 | dup "s-2" $= { drop "s(-2)" true } { 36 | dup 1 $| swap "x" $= { nip "x{" swap $+ +"}" true } { 37 | 2drop false } cond } cond } cond 38 | } : adj-op-arg 39 | { over count over <= { drop } { 2dup [] adj-op-arg { swap []= } { drop } cond } cond } : adj-arg[] 40 | { 1 adj-arg[] 2 adj-arg[] 3 adj-arg[] 41 | dup first 42 | dup `XCHG eq? { 43 | drop dup count 2 = { tpop swap "s0" , swap , } if } { 44 | dup `LSHIFT eq? { 45 | drop dup count 2 = stack-disasm? and { second `LSHIFT# swap pair } if } { 46 | dup `RSHIFT eq? { 47 | drop dup count 2 = stack-disasm? and { second `RSHIFT# swap pair } if } { 48 | drop 49 | } cond } cond } cond 50 | } : adjust-op 51 | 52 | variable @cp @cp 0! 53 | variable @curop 54 | variable @contX variable @contY variable @cdict 55 | 56 | { atom>$ type } : .atom 57 | { dup first .atom dup count 1 > { space 0 over count 2- { 1+ 2dup [] type .", " } swap times 1+ [] type } { drop } cond } : std-show-op 58 | { 0 over count 1- { 1+ 2dup [] type space } swap times drop first .atom } : stk-show-op 59 | { @dismode @ 2 and { .indent ."// " @curop @ csr. } if } : .curop? 60 | { .curop? .indent @dismode @ 1 and ' std-show-op ' stk-show-op cond cr 61 | } : show-simple-op 62 | { dup 4 u@ 9 = { 8 u@+ swap 15 and 3 << s@ } { 63 | dup 7 u@ 0x47 = { 7 u@+ nip 2 u@+ 7 u@+ -rot 3 << swap sr@ } { 64 | dup 8 u@ 0x8A = { ref@ " cr } : show-cont-op 74 | { swap scont-swap ":<{" show-cont-bodyx scont-swap 75 | "" show-cont-bodyx .indent ."}>" cr } : show-cont2-op 76 | 77 | { @contX @ null? { "CONT" show-cont-op } ifnot 78 | } : flush-contX 79 | { @contY @ null? { scont-swap "CONT" show-cont-op scont-swap } ifnot 80 | } : flush-contY 81 | { flush-contY flush-contX } : flush-cont 82 | { @contX @ null? not } : have-cont? 83 | { @contY @ null? not } : have-cont2? 84 | { flush-contY @contY ! scont-swap } : save-cont-body 85 | 86 | { @cdict ! } : save-const-dict 87 | { @cdict null! } : flush-dict 88 | { @cdict @ null? not } : have-dict? 89 | 90 | { flush-cont .indent type .":<{" cr 91 | @curop @ ref@ " cr 92 | } : show-ref-op 93 | { flush-contY .indent rot type .":<{" cr 94 | @curop @ ref@ " cr 97 | } : show-cont-ref-op 98 | { flush-cont .indent swap type .":<{" cr 99 | @curop @ ref@+ " cr 101 | } : show-ref2-op 102 | 103 | { flush-cont first atom>$ dup 5 $| drop "DICTI" $= swap 104 | .indent type ." {" cr +indent @cdict @ @cdict null! unpair 105 | rot { 106 | swap .indent . ."=> <{" cr +indent disasm -indent .indent ."}>" cr true 107 | } swap ' idictforeach ' dictforeach cond drop 108 | -indent .indent ."}" cr 109 | } : show-const-dict-op 110 | 111 | ( `PUSHCONT `PUSHREFCONT ) constant @PushContL 112 | ( `REPEAT `UNTIL `IF `IFNOT `IFJMP `IFNOTJMP ) constant @CmdC1 113 | ( `IFREF `IFNOTREF `IFJMPREF `IFNOTJMPREF `CALLREF `JMPREF ) constant @CmdR1 114 | ( `DICTIGETJMP `DICTIGETJMPZ `DICTUGETJMP `DICTUGETJMPZ `DICTIGETEXEC `DICTUGETEXEC ) constant @JmpDictL 115 | { dup first `DICTPUSHCONST eq? { 116 | flush-cont @curop @ get-const-dict save-const-dict show-simple-op } { 117 | dup first @JmpDictL list-member? have-dict? and { 118 | flush-cont show-const-dict-op } { 119 | flush-dict 120 | dup first @PushContL list-member? { 121 | drop @curop @ get-cont-body save-cont-body } { 122 | dup first @CmdC1 list-member? have-cont? and { 123 | flush-contY first atom>$ .curop? show-cont-op } { 124 | dup first @CmdR1 list-member? { 125 | flush-cont first atom>$ dup $len 3 - $| drop .curop? show-ref-op } { 126 | dup first `WHILE eq? have-cont2? and { 127 | drop "WHILE" "}>DO<{" .curop? show-cont2-op } { 128 | dup first `IFELSE eq? have-cont2? and { 129 | drop "IF" "}>ELSE<{" .curop? show-cont2-op } { 130 | dup first dup `IFREFELSE eq? swap `IFELSEREF eq? or have-cont? and { 131 | first `IFREFELSE eq? "IF" "}>ELSE<{" rot .curop? show-cont-ref-op } { 132 | dup first `IFREFELSEREF eq? { 133 | drop "IF" "}>ELSE<{" .curop? show-ref2-op } { 134 | flush-cont show-simple-op 135 | } cond } cond } cond } cond } cond } cond } cond } cond } cond 136 | } : show-op 137 | { dup @cp @ (vmoplen) dup 0> { 65536 /mod swap sr@+ swap dup @cp @ (vmopdump) parse-op swap s> true } { drop false } cond } : fetch-one-op 138 | { { fetch-one-op } { swap @curop ! adjust-op show-op } while } : disasm-slice 139 | { { disasm-slice dup sbitrefs 1- or 0= } { ref@ B 1 'nop } ::_ B{ 13 | { swap ({) over 2+ -roll swap (compile) (}) } : does 14 | { 1 'nop does create } : constant 15 | { 2 'nop does create } : 2constant 16 | { hole constant } : variable 17 | 10 constant ten 18 | { bl word 1 { find 0= abort"word not found" } } :: (') 19 | { bl word find not abort"-?" 0 swap } :: [compile] 20 | { bl word 1 { 21 | dup find { " -?" $+ abort } ifnot nip execute 22 | } } :: @' 23 | { bl word 1 { swap 1 'nop does swap 0 (create) } 24 | } :: =: 25 | { bl word 1 { -rot 2 'nop does swap 0 (create) } 26 | } :: 2=: 27 | { } : s>c 28 | { s>c hashB } : shash 29 | // to be more efficiently re-implemented in C++ in the future 30 | { dup 0< ' negate if } : abs 31 | { 2dup > ' swap if } : minmax 32 | { minmax drop } : min 33 | { minmax nip } : max 34 | "" constant <# 35 | ' $reverse : #> 36 | { swap 10 /mod char 0 + rot swap hold } : # 37 | { { # over 0<= } until } : #s 38 | { 0< { char - hold } if } : sign 39 | // { dup abs <# #s rot sign #> nip } : (.) 40 | // { (.) type } : ._ 41 | // { ._ space } : . 42 | { dup 10 < { 48 } { 55 } cond + } : Digit 43 | { dup 10 < { 48 } { 87 } cond + } : digit 44 | // x s b -- x' s' 45 | { rot swap /mod Digit rot swap hold } : B# 46 | { rot swap /mod digit rot swap hold } : b# 47 | { 16 B# } : X# 48 | { 16 b# } : x# 49 | // x s b -- 0 s' 50 | { -rot { 2 pick B# over 0<= } until rot drop } : B#s 51 | { -rot { 2 pick b# over 0<= } until rot drop } : b#s 52 | { 16 B#s } : X#s 53 | { 16 b#s } : x#s 54 | variable base 55 | { 10 base ! } : decimal 56 | { 16 base ! } : hex 57 | { 8 base ! } : octal 58 | { 2 base ! } : binary 59 | { base @ B# } : Base# 60 | { base @ b# } : base# 61 | { base @ B#s } : Base#s 62 | { base @ b#s } : base#s 63 | // x w -- s 64 | { over abs <# rot 1- ' X# swap times X#s rot sign #> nip } : (0X.) 65 | { over abs <# rot 1- ' x# swap times x#s rot sign #> nip } : (0x.) 66 | { (0X.) type } : 0X._ 67 | { 0X._ space } : 0X. 68 | { (0x.) type } : 0x._ 69 | { 0x._ space } : 0x. 70 | { bl (-trailing) } : -trailing 71 | { char 0 (-trailing) } : -trailing0 72 | { char " word 1 ' $+ } ::_ +" 73 | { find 0<> dup ' nip if } : (def?) 74 | { bl word 1 ' (def?) } :: def? 75 | { bl word 1 { (def?) not } } :: undef? 76 | { def? ' skip-to-eof if } : skip-ifdef 77 | { bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library 78 | { bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version 79 | { hole dup 1 'nop does swap 1 { context! } does bl word tuck 0 (create) +"-wordlist" 0 (create) } : namespace 80 | { context@ current! } : definitions 81 | { char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $( 82 | // b s -- ? 83 | { sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits? 84 | // b s x -- ? 85 | { swap sbitrefs -rot + rot brembitrefs -rot <= -rot <= and } : s-fits-with? 86 | { 0 swap ! } : 0! 87 | { tuck @ + swap ! } : +! 88 | { tuck @ swap - swap ! } : -! 89 | { 1 swap +! } : 1+! 90 | { -1 swap +! } : 1-! 91 | { null swap ! } : null! 92 | { not 2 pick @ and xor swap ! } : ~! 93 | 0 tuple constant nil 94 | { 1 tuple } : single 95 | { 2 tuple } : pair 96 | { 3 tuple } : triple 97 | { 1 untuple } : unsingle 98 | { 2 untuple } : unpair 99 | { 3 untuple } : untriple 100 | { over tuple? { swap count = } { 2drop false } cond } : tuple-len? 101 | { 0 tuple-len? } : nil? 102 | { 1 tuple-len? } : single? 103 | { 2 tuple-len? } : pair? 104 | { 3 tuple-len? } : triple? 105 | { 0 [] } : first 106 | { 1 [] } : second 107 | { 2 [] } : third 108 | ' pair : cons 109 | ' unpair : uncons 110 | { 0 [] } : car 111 | { 1 [] } : cdr 112 | { cdr car } : cadr 113 | { cdr cdr } : cddr 114 | { cdr cdr car } : caddr 115 | { null ' cons rot times } : list 116 | { -rot pair swap ! } : 2! 117 | { @ unpair } : 2@ 118 | { true (atom) drop } : atom 119 | { bl word atom 1 'nop } ::_ ` 120 | { hole dup 1 { @ execute } does create } : recursive 121 | { 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n 122 | { 10 hold } : +cr 123 | { 9 hold } : +tab 124 | { "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word 125 | { 0 word -trailing scan-until-word 1 'nop } ::_ $<< 126 | { 0x40 runvmx } : runvmcode 127 | { 0x48 runvmx } : gasrunvmcode 128 | { 0xc8 runvmx } : gas2runvmcode 129 | { 0x43 runvmx } : runvmdict 130 | { 0x4b runvmx } : gasrunvmdict 131 | { 0xcb runvmx } : gas2runvmdict 132 | { 0x45 runvmx } : runvm 133 | { 0x4d runvmx } : gasrunvm 134 | { 0xcd runvmx } : gas2runvm 135 | { 0x55 runvmx } : runvmctx 136 | { 0x5d runvmx } : gasrunvmctx 137 | { 0xdd runvmx } : gas2runvmctx 138 | { 0x75 runvmx } : runvmctxact 139 | { 0x7d runvmx } : gasrunvmctxact 140 | { 0xfd runvmx } : gas2runvmctxact 141 | { 0x35 runvmx } : runvmctxactq 142 | { 0x3d runvmx } : gasrunvmctxactq 143 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/FiftExt.fif: -------------------------------------------------------------------------------- 1 | { ?dup { 1+ { execute } { 0 swap } cond } 2 | { (number) ?dup 0= abort"-?" 'nop } cond 3 | } : (interpret-prepare) 4 | { { include-depth 0= (seekeof?) not } { 5 | (word-prefix-find) (interpret-prepare) (execute) 6 | } while 7 | } : interpret 8 | { ({) 9 | { 0 (seekeof?) abort"no }" (word-prefix-find) (interpret-prepare) (compile) over atom? not } until 10 | (}) swap execute 11 | } : begin-block 12 | { swap 0 'nop } : end-block 13 | { { 1 'nop } `{ begin-block } 14 | { { swap `{ eq? not abort"} without {" swap execute } end-block } 15 | :: } :: { 16 | 17 | // if{ ... }then{ ... }elseif{ ... }then{ ... }else{ ... } 18 | { eq? not abort"unexpected" } : ?pairs 19 | { dup `if eq? swap `ifnot eq? over or not abort"without if{" } : if-ifnot? 20 | // cond then ? -- exec 21 | { { ' if } { ' ifnot } cond rot ({) 0 rot (compile) -rot 1 swap (compile) (}) 22 | } : (make-if) 23 | // cond then else -- exec 24 | { rot ({) 0 rot (compile) -rot 2 ' cond (compile) (}) 25 | } : (make-cond) 26 | { `noelse `if begin-block } :: if{ 27 | { `noelse `ifnot begin-block } :: ifnot{ 28 | { 1 ' end-block does } : end-block-does 29 | { { over `else eq? } { 30 | nip rot if-ifnot? ' swap ifnot (make-cond) 31 | } while 32 | swap `noelse ?pairs 0 swap 33 | } : finish-else-chain 34 | { swap dup if-ifnot? drop `then { 35 | swap `then ?pairs 36 | swap if-ifnot? (make-if) finish-else-chain 37 | } `{ begin-block 38 | } end-block-does :: }then{ 39 | { swap `{ ?pairs nip 40 | swap `then eq? not abort"without }then{" `else 41 | } : ?else-ok 42 | { ?else-ok { finish-else-chain } `{ begin-block } end-block-does :: }else{ 43 | { ?else-ok `if begin-block } end-block-does :: }elseif{ 44 | { ?else-ok `ifnot begin-block } end-block-does :: }elseifnot{ 45 | 46 | // while{ ... }do{ ... } 47 | { 2 ' while does } : (make-while) 48 | { `while begin-block } :: while{ 49 | { swap `while eq? not abort"without while{" `while-do { 50 | swap `while-do ?pairs (make-while) 0 swap 51 | } `{ begin-block 52 | } end-block-does :: }do{ 53 | 54 | // repeat{ ... }until{ ... } 55 | { swap ({) 0 rot (compile) 0 rot (compile) (}) 1 ' until does } : (make-until) 56 | { `repeat begin-block } :: repeat{ 57 | { swap `repeat eq? not abort"without repeat{" `until { 58 | swap `until ?pairs (make-until) 0 swap 59 | } `{ begin-block 60 | } end-block-does :: }until{ 61 | 62 | // def { ... } instead of { ... } : 63 | { bl word swap bl word "{" $cmp abort"{ expected" `def { 64 | swap `def ?pairs -rot 3 ' (create) 65 | } `{ begin-block 66 | } : (def) 67 | { 0 (def) } :: def 68 | { 1 (def) } :: def:: 69 | 70 | // defrec { ... } instead of recursive { ... } swap ! 71 | { recursive bl word "{" $cmp abort"{ expected" `defrec { 72 | swap `defrec ?pairs swap ! 0 'nop 73 | } `{ begin-block 74 | } :: defrec 75 | 76 | def .sgn { 77 | if{ ?dup 0= }then{ 78 | ."zero" 79 | }elseif{ 0> }then{ 80 | ."positive" 81 | }else{ 82 | ."negative" 83 | } 84 | cr 85 | } 86 | // equivalent to: { ?dup 0= { ."zero" } { 0> { ."positive" } { ."negative" } cond } cond cr } : .sgn 87 | 88 | defrec fact { 89 | if{ dup }then{ 90 | dup 1- fact * 91 | }else{ 92 | drop 1 93 | } 94 | } 95 | // equivalent to: recursive fact { dup { dup 1- fact * } { drop 1 } cond } swap ! 96 | 97 | // [[ ... ]] computes arbitrary constants inside definitions 98 | // { [[ 5 dup * ]] + } : add25 99 | // is equivalent to 100 | // { 25 + } : add25 101 | { "without [[" abort } box constant ']] 102 | { ']] @ execute } : ]] 103 | { { ']] @ 2 { ']] ! call/cc } does ']] ! 104 | interpret 'nop ']] ! "]] not found" abort 105 | } call/cc 106 | drop 1 'nop 107 | } :: [[ 108 | 109 | { { over @ swap 2 { call/cc } does swap ! 110 | interpret "literal to eof" abort 111 | } call/cc 112 | drop execute 1 'nop 113 | } : interpret-literal-to 114 | // use next line only if Lists.fif is loaded (or move it to Lists.fif if FiftExt.fif becomes part of Fift.fif) 115 | // { ( ') interpret-literal-to } :: '( 116 | // then you can use list literals '( a b c ... ) inside definitions: 117 | // { '( 1 2 3 ) } : test 118 | // { '( ( `a { ."A" } ) ( `b { ."B" } ) ) assoc { cadr execute } { ."???" } cond } : test2 119 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/GetOpt.fif: -------------------------------------------------------------------------------- 1 | library GetOpt // Simple command-line options parser 2 | "Lists.fif" include 3 | 4 | // May be used as follows: 5 | // begin-options 6 | // "h" { ."Help Message" 0 halt } short-option 7 | // "v" { parse-int =: verbosity } short-option-arg 8 | // "i" "--interactive" { true =: interactive } short-long-option 9 | // parse-options 10 | 11 | // ( l -- l') computes tail of list l if non-empty; else () 12 | { dup null? ' cdr ifnot } : safe-cdr 13 | // ( l c -- l') deletes first c elements from list l 14 | { ' safe-cdr swap times } : list-delete-first 15 | // ( l n c -- l' ) deletes c elements starting from n-th in list l 16 | recursive list-delete-range { 17 | dup 0<= { 2drop } { 18 | over 0<= { nip list-delete-first } { 19 | swap 1- swap rot uncons 2swap list-delete-range cons 20 | } cond } cond 21 | } swap ! 22 | // ( n c -- ) deletes $n .. $(n+c-1) from the argument list $* 23 | { swap 1- $* @ swap rot list-delete-range $* ! } : $*del.. 24 | // ( s s' -- ? ) checks whether s' is a prefix of s 25 | { tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond 26 | } : $pfx? 27 | // ( s -- ? ) checks whether s is an option (a string beginning with '-') 28 | { dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt? 29 | // ( s -- ? ) checks whether s is a digit option 30 | { 2 $| drop 1 $| nip $>B 8 B>u@ dup 57 <= swap 48 >= and } : is-digit-opt? 31 | 0 box constant disable-digit-opts 32 | // ( l -- s i or 0 ) finds first string in l beginning with '-' 33 | { 0 { 1+ over null? { 2drop 0 true } { 34 | swap uncons over is-opt? 35 | { disable-digit-opts @ { over is-digit-opt? not } { true } cond } { false } cond 36 | { drop swap true } { nip swap false } cond 37 | } cond } until 38 | } : list-find-opt 39 | // ( -- s i or 0 ) finds first option in cmdline args 40 | { $* @ list-find-opt } : first-opt 41 | ' second : get-opt-flags 42 | ' first : get-opt-exec 43 | // ( s t -- ? ) checks whether short/long option s matches description t 44 | { third $= } : short-option-matches 45 | { dup get-opt-flags 4 and 0= 3 + [] $= 46 | } : long-option-matches 47 | // ( t -- s -1 or 0 ) extracts help message from description 48 | { dup get-opt-flags 4 and 0= 4 + over count over > 49 | { [] true } { 2drop false } cond 50 | } : get-opt-help 51 | // ( s l -- t -1 or 0 ) finds short/long option s in list l 52 | { swap 1 { swap short-option-matches } does assoc-gen 53 | } : lookup-short-option 54 | { swap 1 { swap long-option-matches } does assoc-gen 55 | } : lookup-long-option 56 | // ( s -- s' null or s' s'' ) Splits long option --opt=arg at '=' 57 | { dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond 58 | } : split-longopt 59 | // ( l -- f or 0 ) Extracts global option flags from first entry of l 60 | { dup null? { drop 0 } { car get-opt-flags -256 and } cond 61 | } : get-global-option-flags 62 | variable options-list 63 | // ( l -- i or 0 ) 64 | // parses command line arguments according to option description list l 65 | // and returns index i of first incorrect option 66 | { dup options-list ! get-global-option-flags 67 | 256 and disable-digit-opts ! 68 | { first-opt dup 0= { true } { 69 | swap dup "--" $pfx? { // i s 70 | dup $len 2 = { drop dup 1 $*del.. 0 true } { 71 | split-longopt swap options-list @ 72 | lookup-long-option not { drop true } { // i s' t f 73 | dup get-opt-exec swap get-opt-flags 3 and // i s' e f' 74 | 2 pick null? { dup 1 = } { dup 0= negate } cond // i s' e f' f'' 75 | dup 1 = { 2drop 2drop true } { 76 | { drop nip over 1+ $() swap execute 2 $*del.. false } { 77 | ' nip ifnot execute 1 $*del.. false 78 | } cond } cond } cond } cond } { // i s 79 | 1 $| nip { 80 | dup $len 0= { drop 1 $*del.. false true } { 81 | 1 $| swap options-list @ // i s' s l 82 | lookup-short-option not { drop true true } { // i s' t 83 | dup get-opt-exec swap get-opt-flags 3 and // i s' e f' 84 | ?dup 0= { execute false } { 85 | 2 pick $len { drop execute "" false } { 86 | 2 = { nip null swap execute "" false } { // i e 87 | nip over 1+ $() swap execute 2 $*del.. false true 88 | } cond } cond } cond } cond } cond } until 89 | } cond 90 | } cond } until 91 | } : getopt 92 | // ( t -- ) Displays help message for one option 93 | { dup get-opt-flags dup 4 and 2 pick third swap { 94 | ."-" type ."/" over 3 [] type } { 95 | dup $len { dup "--" $pfx? { ."-" } ifnot type } { 96 | drop ."usage: " $0 type 97 | } cond } cond 98 | dup 3 and ?dup { 99 | 2 = { ."[=]" } { ."=" } cond 100 | } if 101 | 8 and { 9 emit } ifnot 102 | get-opt-help { type } { ."No help available" } cond cr 103 | } : show-opt-help 104 | // ( -- ) Displays options help message according to options-list 105 | { options-list @ { dup null? not } { 106 | uncons swap show-opt-help 107 | } while drop 108 | } : show-options-help 109 | // ( l -- ) Parses options and throws an error on failure 110 | { getopt ?dup { 111 | $() "cannot parse command line options near `" swap $+ +"`" 112 | show-options-help abort } if 113 | } : run-getopt 114 | anon constant opt-list-marker 115 | ' opt-list-marker : begin-options 116 | { opt-list-marker list-until-marker } : end-options 117 | { end-options run-getopt } : parse-options 118 | // ( s e -- o ) Creates short/long option s with execution token e 119 | { 0 rot triple } dup : short-option : long-option 120 | // ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e 121 | { 4 2swap 4 tuple } : short-long-option 122 | { 1 rot triple } dup : short-option-arg : long-option-arg 123 | { 2 rot triple } dup : short-option-?arg : long-option-?arg 124 | { 5 2swap 4 tuple } : short-long-option-arg 125 | { 6 2swap 4 tuple } : short-long-option-?arg 126 | // ( o s -- s' ) Adds help message to option 127 | ' , : option-help 128 | // ( s f -- o ) Creates a generic help message 129 | { swap 'nop rot "" 3 roll 4 tuple } : generic-help-setopt 130 | { 0 generic-help-setopt } : generic-help 131 | 256 constant disable-digit-options 132 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/Lists.fif: -------------------------------------------------------------------------------- 1 | library Lists // List utilities 2 | // 3 | { hole dup 1 { @ execute } does create } : recursive 4 | // x x' -- ? recursively compares two S-expressions 5 | recursive equal? { 6 | dup tuple? { 7 | over tuple? { 8 | over count over count over = { // t t' l ? 9 | 0 { dup 0>= { 2dup [] 3 pick 2 pick [] equal? { 1+ } { drop -1 } cond 10 | } if } rot times 11 | nip nip 0>= 12 | } { drop 2drop false } cond 13 | } { 2drop false } cond 14 | } { eqv? } cond 15 | } swap ! 16 | // (a1 .. an) -- (an .. a1) 17 | { null swap { dup null? not } { uncons swap rot cons swap } while drop } : list-reverse 18 | // (a1 .. an) -- an Computes last element of non-empty list l 19 | { { uncons dup null? { drop true } { nip false } cond } until } : list-last 20 | // l l' -- l++l' Concatenates two lists 21 | recursive list+ { 22 | over null? { nip } { swap uncons rot list+ cons } cond 23 | } swap ! 24 | // l l' -- l'' -1 or 0, where l = l' ++ l'' 25 | // Removes prefix from list 26 | { { dup null? { drop true true } { 27 | swap dup null? { 2drop false true } { // l' l 28 | uncons swap rot uncons -rot equal? { false } { 29 | 2drop false true 30 | } cond } cond } cond } until 31 | } : list- 32 | // (a1 .. an) -- a1 .. an n Explodes a list 33 | { 0 { over null? not } { swap uncons rot 1+ } while nip } : explode-list 34 | // (a1 .. an) x -- a1 .. an n x Explodes a list under the topmost element 35 | { swap explode-list dup 1+ roll } : explode-list-1 36 | // l -- t Transforms a list into a tuple with the same elements 37 | { explode-list tuple } : list>tuple 38 | // a1 ... an n x -- (a1 .. an) x 39 | { null swap rot { -rot cons swap } swap times } : mklist-1 40 | // (s1 ... sn) -- s1+...+sn Concatenates a list of strings 41 | { "" { over null? not } { swap uncons -rot $+ } while nip 42 | } : concat-string-list 43 | // (x1 ... xn) -- x1+...+xn Sums a list of integers 44 | { 0 { over null? not } { swap uncons -rot + } while nip 45 | } : sum-list 46 | // (a1 ... an) a e -- e(...e(e(a,a1),a2),...),an) 47 | { -rot { over null? not } { swap uncons -rot 3 pick execute } while nip nip 48 | } : foldl 49 | // (a1 ... an) e -- e(...e(e(a1,a2),a3),...),an) 50 | { swap uncons swap rot foldl } : foldl-ne 51 | // (a1 ... an) a e -- e(a1,e(a2,...,e(an,a)...)) 52 | recursive foldr { 53 | rot dup null? { 2drop } { 54 | uncons -rot 2swap swap 3 pick foldr rot execute 55 | } cond 56 | } swap ! 57 | // (a1 ... an) e -- e(a1,e(a2,...,e(a[n-1],an)...)) 58 | recursive foldr-ne { 59 | over cdr null? { drop car } { 60 | swap uncons 2 pick foldr-ne rot execute 61 | } cond 62 | } swap ! 63 | // (l1 ... ln) -- l1++...++ln Concatenates a list of lists 64 | { dup null? { ' list+ foldr-ne } ifnot } : concat-list-lists 65 | // (a1 .. an . t) n -- t Computes the n-th tail of a list 66 | { ' cdr swap times } : list-tail 67 | // (a0 .. an ..) n -- an Computes the n-th element of a list 68 | { list-tail car } : list-ref 69 | // l -- ? 70 | { { dup null? { drop true true } { 71 | dup pair? { cdr false } { 72 | drop false true 73 | } cond } cond } until 74 | } : list? 75 | // l -- n 76 | { 0 { over null? not } { 1+ swap uncons nip swap } while nip 77 | } : list-length 78 | // l e -- t // returns tail of l after first member that satisfies e 79 | { swap { 80 | dup null? { nip true } { 81 | tuck car over execute { drop true } { 82 | swap cdr false 83 | } cond } cond } until 84 | } : list-tail-from 85 | // a l -- t // tail of l after first occurence of a using eq? 86 | { swap 1 ' eq? does list-tail-from } : list-member-eq 87 | { swap 1 ' eqv? does list-tail-from } : list-member-eqv 88 | { swap 1 ' equal? does list-tail-from } : list-member-equal 89 | // a l -- ? 90 | { list-member-eq null? not } : list-member? 91 | { list-member-eqv null? not } : list-member-eqv? 92 | // l -- a -1 or 0 // returns car l if l is non-empty 93 | { dup null? { drop false } { car true } cond 94 | } : safe-car 95 | { dup null? { drop false } { car second true } cond 96 | } : get-first-value 97 | // l e -- v -1 or 0 98 | { list-tail-from safe-car } : assoc-gen 99 | { list-tail-from get-first-value } : assoc-gen-x 100 | // a l -- (a.v) -1 or 0 -- returns first entry (a . v) in l 101 | { swap 1 { swap first eq? } does assoc-gen } : assq 102 | { swap 1 { swap first eqv? } does assoc-gen } : assv 103 | { swap 1 { swap first equal? } does assoc-gen } : assoc 104 | // a l -- v -1 or 0 -- returns v from first entry (a . v) in l 105 | { swap 1 { swap first eq? } does assoc-gen-x } : assq-val 106 | { swap 1 { swap first eqv? } does assoc-gen-x } : assv-val 107 | { swap 1 { swap first equal? } does assoc-gen-x } : assoc-val 108 | // (a1 .. an) e -- (e(a1) .. e(an)) 109 | recursive list-map { 110 | over null? { drop } { 111 | swap uncons -rot over execute -rot list-map cons 112 | } cond 113 | } swap ! 114 | 115 | variable ctxdump variable curctx 116 | // (a1 .. an) e -- executes e for a1, ..., an 117 | { ctxdump @ curctx @ ctxdump 2! curctx 2! 118 | { curctx 2@ over null? not } { swap uncons rot tuck curctx 2! execute } 119 | while 2drop ctxdump 2@ curctx ! ctxdump ! 120 | } : list-foreach 121 | forget ctxdump forget curctx 122 | 123 | // 124 | // Experimental implementation of `for` loops with index 125 | // 126 | variable loopdump variable curloop 127 | { curloop @ loopdump @ loopdump 2! } : push-loop-ctx 128 | { loopdump 2@ loopdump ! curloop ! } : pop-loop-ctx 129 | // ilast i0 e -- executes e for i=i0,i0+1,...,ilast-1 130 | { -rot 2dup > { 131 | push-loop-ctx { 132 | triple dup curloop ! first execute curloop @ untriple 1+ 2dup <= 133 | } until pop-loop-ctx 134 | } if 2drop drop 135 | } : for 136 | // ilast i0 e -- same as 'for', but pushes current index i before executing e 137 | { -rot 2dup > { 138 | push-loop-ctx { 139 | triple dup curloop ! untriple nip swap execute curloop @ untriple 1+ 2dup <= 140 | } until pop-loop-ctx 141 | } if 2drop drop 142 | } : for-i 143 | // ( -- i ) Returns innermost loop index 144 | { curloop @ third } : i 145 | // ( -- j ) Returns outer loop index 146 | { loopdump @ car third } : j 147 | { loopdump @ cadr third } : k 148 | forget curloop forget loopdump 149 | 150 | // 151 | // create Lisp-style lists using words "(" and ")" 152 | // 153 | variable ') 154 | 'nop box constant ', 155 | { ") without (" abort } ') ! 156 | { ') @ execute } : ) 157 | anon constant dot-marker 158 | // m x1 ... xn t m -- (x1 ... xn . t) 159 | { swap 160 | { -rot 2dup eq? not } 161 | { over dot-marker eq? abort"invalid dotted list" 162 | swap rot cons } while 2drop 163 | } : list-tail-until-marker 164 | // m x1 ... xn m -- (x1 ... xn) 165 | { null swap list-tail-until-marker } : list-until-marker 166 | { over dot-marker eq? { nip 2dup eq? abort"invalid dotted list" } 167 | { null swap } cond 168 | list-tail-until-marker 169 | } : list-until-marker-ext 170 | { ') @ ', @ } : ops-get 171 | { ', ! ') ! } : ops-set 172 | { anon dup ops-get 3 { ops-set list-until-marker-ext } does ') ! 'nop ', ! 173 | } : ( 174 | // test of Lisp-style lists 175 | // ( 42 ( `+ 9 ( `* 3 4 ) ) "test" ) .l cr 176 | // ( `eq? ( `* 3 4 ) 3 4 * ) .l cr 177 | // `alpha ( `beta `gamma `delta ) cons .l cr 178 | // { ( `eq? ( `* 3 5 pick ) 3 4 roll * ) } : 3*sample 179 | // 17 3*sample .l cr 180 | 181 | // similar syntax _( x1 .. xn ) for tuples 182 | { 2 { 1+ 2dup pick eq? } until 3 - nip } : count-to-marker 183 | { count-to-marker tuple nip } : tuple-until-marker 184 | { anon dup ops-get 3 { ops-set tuple-until-marker } does ') ! 'nop ', ! } : _( 185 | // test of tuples 186 | // _( _( 2 "two" ) _( 3 "three" ) _( 4 "four" ) ) .dump cr 187 | 188 | // pseudo-Lisp tokenizer 189 | "()[]'" 34 hold constant lisp-delims 190 | { lisp-delims 11 (word) } : lisp-token 191 | { null cons `quote swap cons } : do-quote 192 | { 1 { ', @ 2 { 2 { ', ! execute ', @ execute } does ', ! } 193 | does ', ! } does 194 | } : postpone-prefix 195 | { ', @ 1 { ', ! } does ', ! } : postpone-', 196 | ( `( ' ( pair 197 | `) ' ) pair 198 | `[ ' _( pair 199 | `] ' ) pair 200 | `' ' do-quote postpone-prefix pair 201 | `. ' dot-marker postpone-prefix pair 202 | `" { char " word } pair 203 | `;; { 0 word drop postpone-', } pair 204 | ) constant lisp-token-dict 205 | variable eol 206 | { eol @ eol 0! anon dup ') @ 'nop 3 207 | { ops-set list-until-marker-ext true eol ! } does ') ! rot ', ! 208 | { lisp-token dup (number) dup { roll drop } { 209 | drop atom dup lisp-token-dict assq { nip second execute } if 210 | } cond 211 | ', @ execute 212 | eol @ 213 | } until 214 | -rot eol ! execute 215 | } :_ List-generic( 216 | { 'nop 'nop List-generic( } :_ LIST( 217 | // LIST((lambda (x) (+ x 1)) (* 3 4)) 218 | // LIST('(+ 3 4)) 219 | // LIST(2 3 "test" . 9) 220 | // LIST((process '[plus 3 4])) 221 | -------------------------------------------------------------------------------- /src/tonpy/fift/libs/Stack.fif: -------------------------------------------------------------------------------- 1 | library Stack // advanced stack manupulation library 2 | "Lists.fif" include 3 | // S(a b c - a c 2 a b) would compile to code performing the requested stack manipulation 4 | 5 | // interface to low-level stack manipulation primitives 6 | { (number) 1- abort"index expected" dup 0 < over 255 > or 7 | abort"index 0..255 expected" 8 | } : (idx) 9 | // push(n) : a0 .. an - a0 .. an a0 equivalent to "n pick" 10 | // push(0) = dup, push(1) = over 11 | { 0 char ) word (idx) } ::_ push( 12 | // pop(n) : a0 a1 .. a(n-1) an - an a1 .. a(n-1) 13 | // pop(0) = drop, pop(1) = nip 14 | { 0 char ) word (idx) } ::_ pop( 15 | // xchg(i,j) : equivalent to "i j exch2" 16 | { 0 char , word (idx) char ) word (idx) } ::_ xchg( 17 | // xchg0(i) : equivalent to "i exch" or "xchg(0,i)" 18 | // xchg0(1) = swap 19 | { 0 char ) word (idx) 0 } ::_ xchg0( 20 | forget (idx) 21 | 22 | // parser for stack notation expressions 23 | ")" 34 hold +" -" constant stk-delims 24 | anon constant stk-start 25 | anon constant stk-to 26 | variable stk-mode 27 | { stk-delims 11 (word) } : stk-token 28 | 'nop : mk-lit 29 | // stk-start vn ... v0 -- stk-start ... v0 i where v[i]=v0 30 | { 0 { 31 | 1+ 2dup 2+ pick dup stk-start eq? { 2drop drop 0 true } { eqv? } cond 32 | } until 33 | } : stk-lookup 34 | // stk-start a1 .. an stk-to b1 .. bm -- [a1 .. an] [b1 .. bm] 35 | { stk-mode @ 0= abort"identifier expected" } : chk-lit 36 | { stk-to list-until-marker stk-mode ! 37 | stk-start list-until-marker stk-mode @ 38 | stk-mode 0! 39 | } : build-stk-effect 40 | { stk-start stk-mode 0! { 41 | stk-token dup ")" $= { drop true } { 42 | dup "-" $= { 43 | drop stk-mode @ abort"duplicate -" true stk-mode ! stk-to false } { 44 | dup 34 chr $= { chk-lit drop char " word mk-lit false } { 45 | dup (number) ?dup { chk-lit 1- { swap mk-lit -rot } if mk-lit nip false } { 46 | atom dup `_ eq? { stk-mode @ abort"identifier expected" false } { 47 | stk-lookup 0= stk-mode @ = { 48 | stk-mode @ { atom>$ +" -?" } { atom>$ +" redefined" } cond abort } { 49 | false 50 | } cond } cond } cond } cond } cond } cond } until 51 | stk-mode @ 0= abort"'-' expected" 52 | build-stk-effect 53 | } :_ parse-stk-list( 54 | 55 | // stack operation list construction 56 | variable op-rlist 57 | { op-rlist null! } : clear-op-list 58 | { op-rlist @ list-reverse } : get-op-list 59 | { op-rlist @ cons op-rlist ! } : issue-op 60 | { minmax `xchg -rot triple } : op-xchg 61 | { `push swap pair } : op-push 62 | { `lit swap pair } : op-lit 63 | { `pop swap pair } : op-pop 64 | 0 op-pop constant op-drop 65 | { 2dup <> { op-xchg issue-op } if } : issue-xchg 66 | { op-push issue-op } : issue-push 67 | { op-lit issue-op } : issue-lit 68 | { op-pop issue-op } : issue-pop 69 | { op-drop issue-op } : issue-drop 70 | { ' issue-drop swap times } : issue-drop-# 71 | 72 | // emulated stack contents 73 | variable emul-stk 74 | { emul-stk @ count } : emul-depth 75 | { emul-depth 1- swap - } : adj-i 76 | { emul-depth 1- tuck swap - swap rot - swap } : adj-ij 77 | // i j -- 78 | { adj-ij 2dup emul-stk @ tuck swap [] swap rot [] rot // i sj si j 79 | emul-stk @ -rot []= swap rot []= emul-stk ! 80 | } : emul-xchg 81 | { emul-stk @ tpop drop emul-stk ! } : emul-drop 82 | // i -- 83 | { 0 emul-xchg emul-drop } : emul-pop 84 | // i -- s[i] 85 | { emul-stk @ swap [] } : emul-stk[] 86 | // i -- si 87 | { adj-i emul-stk[] } : emul-get 88 | { 0 emul-get } : emul-tos 89 | // v i -- ? Check whether s[i]=v 90 | { dup emul-depth < { emul-stk[] eqv? } { 2drop false } cond } : emul[]-eq? 91 | // v -- i or -1 Returns maximum i with s[i]=v 92 | { emul-stk @ dup count { // v s i 93 | ?dup 0= { -1 true } { 1- 2dup [] 3 pick eqv? } cond // v s i' ? 94 | } until nip nip 95 | } : emul-stk-lookup-rev 96 | // i -- 97 | { emul-get emul-stk @ swap , emul-stk ! } : emul-push 98 | { emul-stk @ swap , emul-stk ! } : emul-lit 99 | // show emulated stack contents similarly to .s 100 | { emul-stk @ explode dup 1 reverse ' .l swap times cr } : .e 101 | 102 | // both issue an operation and emulate it 103 | { 2dup issue-xchg emul-xchg } : issue-emul-xchg 104 | { dup issue-push emul-push } : issue-emul-push 105 | { dup issue-lit emul-lit } : issue-emul-lit 106 | { dup issue-pop emul-pop } : issue-emul-pop 107 | { issue-drop emul-drop } : issue-emul-drop 108 | { ' issue-emul-drop swap times } : issue-emul-drop-# 109 | 110 | // b.. s -- b.. s moves tos value to stk[s] 111 | { dup emul-stk[] 2 pick cdr list-member-eqv? { 112 | dup adj-i 0 issue-emul-xchg } { dup adj-i issue-emul-pop } cond 113 | } : move-tos-to 114 | 115 | // new s -- ops registered 116 | { { over null? not } { 117 | // .sl .e get-op-list .l cr 118 | // get-op-list list-length 100 > abort"too long" 119 | emul-depth over > 120 | { over emul-tos swap list-member-eqv? not } { false } cond { 121 | // b.. s tos unneeded 122 | issue-emul-drop } { 123 | over car // b.. s b1 124 | 2dup swap emul[]-eq? { drop swap cdr swap 1+ } { 125 | dup emul-stk-lookup-rev // b.. s b1 i 126 | dup 0< { // b.. s b1 i not found, must be a literal 127 | drop dup atom? abort"unavailable value" 128 | issue-emul-lit } { 129 | dup 3 pick < { // b.. s b1 i found in bottom s stack values 130 | nip adj-i issue-emul-push // b.. s 131 | dup emul-depth 1- < { move-tos-to } if 132 | } { 133 | emul-depth 1- over = { // b.. s b1 i found in tos 134 | 2drop move-tos-to 135 | } { // b.. s b1 i 136 | nip over adj-ij issue-emul-xchg 137 | } cond } cond } cond } cond } cond } while 138 | nip emul-depth swap - issue-emul-drop-# 139 | } : generate-reorder-ops 140 | 141 | // old new -- op-list 142 | { emul-stk @ op-rlist @ 2swap 143 | swap list>tuple emul-stk ! clear-op-list 144 | 0 generate-reorder-ops get-op-list 145 | -rot op-rlist ! emul-stk ! 146 | } : generate-reorder 147 | { parse-stk-list( generate-reorder } :_ SG( 148 | 149 | // op-list rewriting according to a ruleset 150 | // l f l1 l2 -- l' -1 or l f with l' = l2 + (l - l1) 151 | { push(3) rot list- { list+ nip nip true } { drop } cond 152 | } : try-rule 153 | // l f ll -- l' -1 or l f 154 | { { dup null? not } { uncons 3 -roll unpair try-rule rot } while drop 155 | } : try-ruleset 156 | // l ll -- l' 157 | { swap { over false swap try-ruleset 0= } until nip 158 | } : try-ruleset* 159 | // l ruleset -- l' 160 | recursive try-ruleset*-everywhere { 161 | tuck try-ruleset* dup null? { nip } { 162 | uncons rot try-ruleset*-everywhere cons } cond 163 | } swap ! 164 | LIST( 165 | [([xchg 0 1] [xchg 0 2]) ([rot])] 166 | [([xchg 0 1] [xchg 1 2]) ([-rot])] 167 | [([xchg 0 2] [xchg 1 2]) ([rot])] 168 | [([xchg 0 2] [xchg 0 1]) ([-rot])] 169 | [([xchg 1 2] [xchg 0 1]) ([rot])] 170 | [([xchg 1 2] [xchg 0 2]) ([-rot])] 171 | [([xchg 0 1] [rot]) ([xchg 0 2])] 172 | [([-rot] [xchg 0 1]) ([xchg 0 2])] 173 | [([xchg 0 2] [xchg 1 3]) ([2swap])] 174 | [([xchg 1 3] [xchg 0 2]) ([2swap])] 175 | [([push 1] [push 1]) ([2dup])] 176 | [([push 3] [push 3]) ([2over])] 177 | [([pop 0] [pop 0]) ([2drop])] 178 | [([pop 1] [pop 0]) ([2drop])] 179 | [([xchg 0 1] [push 1]) ([tuck])] 180 | [([rot] [-rot]) ()] 181 | [([-rot] [rot]) ()] 182 | ) constant fift-stack-ruleset 183 | { fift-stack-ruleset try-ruleset*-everywhere } : fift-ops-rewrite 184 | { SG( fift-ops-rewrite } :_ SGF( 185 | 186 | // helpers for creating Fift source strings for one fift-op 187 | // i j -- s 188 | { minmax over { "xchg(" rot (.) $+ +"," swap (.) $+ +")" } 189 | { nip dup 1 = { drop "swap" } { 190 | ?dup { "xchg0(" swap (.) $+ +")" } { "" } cond 191 | } cond } cond 192 | } : source- 193 | // i -- s 194 | { dup 1 = { drop "over" } { 195 | ?dup { "push(" swap (.) $+ +")" } { "dup" } cond 196 | } cond 197 | } : source- 198 | // i -- s 199 | { dup 1 = { drop "nip" } { 200 | ?dup { "pop(" swap (.) $+ +")" } { "drop" } cond 201 | } cond 202 | } : source- 203 | // lit -- s 204 | { dup string? { char " chr swap $+ char " hold } { (.) } cond 205 | } : source- 206 | 207 | // dictionary with all fift op compilation/source creation 208 | { 0 swap (compile) } : fop-compile 209 | ( _( `xchg 2 { fop-compile } { source- swap cons } ) 210 | _( `push 1 { fop-compile } { source- swap cons } ) 211 | _( `pop 1 { fop-compile } { source- swap cons } ) 212 | _( `lit 1 { 1 'nop (compile) } { source- swap cons } ) 213 | _( `rot 0 { ' rot fop-compile } { "rot" swap cons } ) 214 | _( `-rot 0 { ' -rot fop-compile } { "-rot" swap cons } ) 215 | _( `tuck 0 { ' tuck fop-compile } { "tuck" swap cons } ) 216 | _( `2swap 0 { ' 2swap fop-compile } { "2swap" swap cons } ) 217 | _( `2drop 0 { ' 2drop fop-compile } { "2drop" swap cons } ) 218 | _( `2dup 0 { ' 2dup fop-compile } { "2dup" swap cons } ) 219 | _( `2over 0 { ' 2over fop-compile } { "2over" swap cons } ) 220 | ) box constant fift-op-dict 221 | 222 | { dup atom? { atom>$ } { drop "" } cond 223 | "unknown operation " swap $+ abort 224 | } : report-unknown-op 225 | variable 'fop-entry-exec 226 | // process fift-op according to 'fop-entry-exec 227 | // ... op - ... 228 | { dup first dup fift-op-dict @ assq { report-unknown-op } ifnot 229 | dup second 1+ push(3) count <> abort"incorrect param count" 230 | nip swap explode dup roll drop 1- roll // o2 .. on entry 231 | 'fop-entry-exec @ execute 232 | } : process-fift-op 233 | 234 | // compile op-list into Fift wordlist 235 | // wl op-list -- wl' 236 | { { third execute } 'fop-entry-exec ! 237 | swap ' process-fift-op foldl } : compile-fift-op* 238 | // op-list -- e 239 | { fift-ops-rewrite ({) swap compile-fift-op* (}) } : ops>wdef 240 | 241 | // S( - ) compiles a "word" performing required action 242 | { SG( ops>wdef 0 swap } ::_ S( 243 | // 1 2 3 S(a b c - c a b a) .s would print 3 1 2 1 244 | 245 | // transform op-list into Fift source 246 | // ls op -- ls' 247 | { fift-ops-rewrite 248 | { 3 [] execute } 'fop-entry-exec ! 249 | null ' process-fift-op foldl 250 | dup null? { drop "" } { { +" " swap $+ } foldr-ne } cond 251 | } : ops>$ 252 | { SG( ops>$ 1 'nop } ::_ $S( 253 | { SG( ops>$ type } :_ .$S( 254 | // $S(a b c - b c a c a c) => string "rot 2dup over" 255 | // S(a b c - b c a c a c) => compile/execute block { rot 2dup over } 256 | // $S(_ x y _ - y x) => string "drop pop(2)" 257 | // .$S(x1 x2 - 17 x1) => print string "drop 17 swap" 258 | 259 | // simplify/transform sequences of stack manipulation operations 260 | LIST(. [a b c d e f g h i j]) constant std-stack 261 | { stk-start std-stack explode drop stk-to std-stack explode drop 262 | } : simplify<{ 263 | { build-stk-effect generate-reorder ops>$ } : }>stack 264 | // simplify<{ drop drop over over -13 }>stack => string "2drop 2dup -13" 265 | // simplify<{ 17 rot }>stack => string "swap 17 swap" 266 | // simplify<{ 5 1 reverse }>stack => string "xchg(1,5) xchg(2,4)" 267 | -------------------------------------------------------------------------------- /src/tonpy/libs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disintar/tonpy/31367bc3698501defa54c22152ae4543c1a35ecd/src/tonpy/libs/.gitkeep -------------------------------------------------------------------------------- /src/tonpy/tests/test_address.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.address import Address 4 | import pickle as pck 5 | 6 | 7 | def helper(a: Address): 8 | assert a.serialize() == 'EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA' 9 | assert a.pack().get_hash() == '94804DD004BE8628522901CF117E4B03CF69E78FC0A9E92BC578BCCB1B3FD0D2' 10 | assert a.wc == 0 11 | a.wc = -1 12 | assert a.serialize() == 'Ef_rLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPZsI' 13 | assert a.pack().get_hash() == '4FD6BD06AA7BB5EBC5ACFBB694A7DF00AA3F8C6155E47F11620D9B32ED11B5D0' 14 | assert a.address == 'EB2EAF97EA32993470127208218748758A88374AD2BBD739FC75C9AB3A3F233D' 15 | assert a.bounceable is True 16 | a.bounceable = False 17 | assert a.serialize() == 'Uf_rLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPcbN' 18 | assert a.pack().get_hash() == '4FD6BD06AA7BB5EBC5ACFBB694A7DF00AA3F8C6155E47F11620D9B32ED11B5D0' 19 | assert a.testnet is False 20 | a.testnet = True 21 | # todo: double check 22 | assert a.serialize() == '0f_rLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPX1H' 23 | 24 | 25 | def test_address_string(): 26 | a = Address("EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA") 27 | helper(a) 28 | 29 | 30 | def test_pickle_friendly(): 31 | a = Address("EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA") 32 | assert pck.loads(pck.dumps(a)).serialize() == "EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA" 33 | 34 | 35 | def test_address_string_fail(): 36 | try: 37 | Address("LOLKEK") 38 | except RuntimeError: 39 | pass 40 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_aug_vmdict.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | from random import random 6 | from time import time 7 | 8 | import pytest 9 | 10 | path_root = Path(__file__).parents[2] 11 | sys.path.append(str(path_root)) 12 | 13 | from tonpy.types.vmdict import VmDict, TypedVmDict 14 | from tonpy.types.cellbuilder import CellBuilder, CellSlice, Cell 15 | from tonpy.libs.python_ton import PyAugmentationCheckData, PyCellSlice 16 | from tonpy.types import CellBuilder, CellSlice 17 | from typing import Optional 18 | 19 | from tonpy.types.vmdict import AugmentedData 20 | 21 | 22 | # Example of augment usage 23 | class BasicAug(AugmentedData): 24 | def eval_leaf(self, cs: CellSlice) -> (bool, Optional[CellSlice]): 25 | # on leaf creation automatically extract needed data and store as extra 26 | value = cs.load_uint(32) + 10 27 | cs = CellBuilder().store_uint(value, 64).begin_parse() 28 | return True, cs 29 | 30 | def skip_extra(self, cs: CellSlice) -> (bool, Optional[CellSlice]): 31 | cs.skip_bits(64) 32 | return True, cs 33 | 34 | def eval_fork(self, left: CellSlice, right: CellSlice) -> (bool, Optional[CellSlice]): 35 | a = left.load_uint(64) 36 | b = right.load_uint(64) 37 | return True, CellBuilder().store_uint(a + b, 64).begin_parse() 38 | 39 | def eval_empty(self) -> (bool, Optional[CellSlice]): 40 | cs = CellBuilder().store_uint(0, 64).begin_parse() 41 | return True, cs 42 | 43 | 44 | def test_set_get_item(): 45 | d = VmDict(256, aug=BasicAug()) 46 | d[0] = CellBuilder().store_uint(0, 32).end_cell().begin_parse() 47 | d[1] = CellBuilder().store_uint(1, 32).end_cell().begin_parse() 48 | 49 | v1 = d[0] 50 | assert v1.data.load_uint(32) == 0 51 | assert v1.extra.load_uint(64) == 10 52 | 53 | v2 = d[1] 54 | assert v2.data.load_uint(32) == 1 55 | assert v2.extra.load_uint(64) == 11 56 | 57 | 58 | def test_config(): 59 | from tonpy.utils.global_config import CFG_TEST 60 | from tonpy.autogen.block import ConfigParam, WorkchainDescr 61 | 62 | config_data = VmDict(32, False, CFG_TEST) 63 | 64 | # _ config_addr:bits256 = ConfigParam 0; 65 | config_addr = hex(int(ConfigParam(0).fetch(config_data[0].load_ref()).config_addr, 2))[2:].upper() 66 | assert config_addr == '5555555555555555555555555555555555555555555555555555555555555555' 67 | 68 | # _ elector_addr:bits256 = ConfigParam 1; 69 | elector_addr = hex(int(ConfigParam(1).fetch(config_data[1].load_ref()).elector_addr, 2))[2:].upper() 70 | assert elector_addr == '3333333333333333333333333333333333333333333333333333333333333333' 71 | 72 | # _ minter_addr:bits256 = ConfigParam 2; 73 | minter_addr = hex(int(ConfigParam(2).fetch(config_data[2].load_ref()).minter_addr, 2))[2:].upper() 74 | assert minter_addr == '0' 75 | 76 | # _ dns_root_addr:bits256 = ConfigParam 4; 77 | dns_root_addr = hex(int(ConfigParam(4).fetch(config_data[4].load_ref()).dns_root_addr, 2))[2:].upper() 78 | assert dns_root_addr == 'E56754F83426F69B09267BD876AC97C44821345B7E266BD956A7BFBFB98DF35C' 79 | 80 | # burning_config#01 81 | # blackhole_addr:(Maybe bits256) 82 | # fee_burn_num:# fee_burn_denom:# { fee_burn_num <= fee_burn_denom } { fee_burn_denom >= 1 } = BurningConfig; 83 | # _ BurningConfig = ConfigParam 5; 84 | burning_config = ConfigParam(5).fetch(config_data[5].load_ref()).x 85 | assert burning_config.fee_burn_denom == 2 86 | assert burning_config.fee_burn_num == 1 87 | assert hex(int(burning_config.blackhole_addr.value, 2))[2:].upper() \ 88 | == 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' 89 | 90 | 91 | def test_typed_solo(): 92 | from tonpy.autogen.block import ConfigParam, WorkchainDescr 93 | 94 | # check for iterator 95 | items = list(TypedVmDict(WorkchainDescr(), 32, False, 96 | 'te6ccuEBAQEAXgC8ALfQUy7nTs8AAAJwACrYn7aHDoYaZOELB7fIx0lsFfzu58bxcmSlH++c6KojdwX2/yWZOw/Zr08OxAx1OQZWjQc9ppdrOeJEc5dIgaEAAAAAD/////gAAAAAAAAABEhiGEM=')) 97 | 98 | assert len(items) == 1 99 | a = items[0] 100 | 101 | # check for __getitem__ 102 | b = TypedVmDict(WorkchainDescr(), 32, False, 103 | 'te6ccuEBAQEAXgC8ALfQUy7nTs8AAAJwACrYn7aHDoYaZOELB7fIx0lsFfzu58bxcmSlH++c6KojdwX2/yWZOw/Zr08OxAx1OQZWjQc9ppdrOeJEc5dIgaEAAAAAD/////gAAAAAAAAABEhiGEM=') 104 | b = [0, b[0]] 105 | 106 | for j in [a, b]: 107 | assert j[0] == 0 108 | 109 | from tonpy import RecordBase 110 | assert issubclass(type(j[1]), RecordBase) 111 | assert j[1].to_dict(rec_unpack=True) == {'enabled_since': 1573821854, 'actual_min_split': 0, 'min_split': 0, 112 | 'max_split': 4, 'basic': True, 'active': True, 'accept_msgs': True, 113 | 'flags': 0, 114 | 'zerostate_root_hash': '0101010110110001001111110110110100001110000111010000110000110100110010011100001000010110000011110110111110010001100011101001001011011000001010111111100111011101110011111000110111100010111001001100100101001010001111111101111100111001110100010101010001000110', 115 | 'zerostate_file_hash': '1110111000001011111011011111111001001011001100100111011000011111101100110101111010011110000111011000100000011000111010100111001000001100101011010001101000001110011110110100110100101110110101100111001111000100100010001110011100101110100100010000001101000010', 116 | 'version': 0, 'format': {'vm_version': -1, 'vm_mode': 0}} 117 | 118 | 119 | def test_param_20(): 120 | from tonpy.autogen.block import ConfigParam, WorkchainDescr 121 | a = 'te6ccuEBAQEATACYAJTRAAAAAAAAAGQAAAAAAA9CQN4AAAAAJxAAAAAAAAAAD0JAAAAAAAIWDsAAAAAAAAAnEAAAAAACNJNAAAAAAAX14QAAAAAAO5rKAD0ju+Y=' 122 | c = ConfigParam(20).fetch(Cell(a), rec_unpack=True) 123 | assert c.to_dict(rec_unpack=True) == {'x': {'flat_gas_limit': 100, 'flat_gas_price': 1000000, 124 | 'other': {'gas_price': 655360000, 'gas_limit': 1000000, 125 | 'special_gas_limit': 35000000, 'gas_credit': 10000, 126 | 'block_gas_limit': 37000000, 'freeze_due_limit': 100000000, 127 | 'delete_due_limit': 1000000000}}} 128 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_autogen_block.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | 6 | import pytest 7 | 8 | from tonpy import CellBuilder 9 | 10 | path_root = Path(__file__).parents[2] 11 | sys.path.append(str(path_root)) 12 | 13 | from tonpy.types import CellSlice 14 | from tonpy.data_for_tests.block_boc import block_boc 15 | from tonpy.autogen.block import BlockInfo, ValueFlow, BlockExtra, Block, InMsg, ImportFees 16 | from time import time 17 | from tonpy.types.vmdict import TypedVmDict 18 | 19 | 20 | def test_block_autogen_unpack(): 21 | c = CellSlice(block_boc, True) 22 | 23 | # block#11ef55aa global_id:int32 24 | # info:^BlockInfo value_flow:^ValueFlow 25 | # state_update:^(MERKLE_UPDATE ShardState) 26 | # extra:^BlockExtra = Block; 27 | 28 | parsed_block = Block().fetch(c) 29 | 30 | # block#11ef55aa global_id:int32 31 | # info:^BlockInfo value_flow:^ValueFlow 32 | # state_update:^(MERKLE_UPDATE ShardState) 33 | # extra:^BlockExtra = Block; 34 | assert parsed_block.get_tag() == 0x11ef55aa 35 | assert parsed_block.global_id == -239 36 | 37 | # block_info#9bc7a987 version:uint32 38 | # not_master:(## 1) 39 | # after_merge:(## 1) before_split:(## 1) 40 | # after_split:(## 1) 41 | # want_split:Bool want_merge:Bool 42 | # key_block:Bool vert_seqno_incr:(## 1) 43 | # flags:(## 8) { flags <= 1 } 44 | # seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr } 45 | # { prev_seq_no:# } { ~prev_seq_no + 1 = seq_no } 46 | # shard:ShardIdent gen_utime:uint32 47 | # start_lt:uint64 end_lt:uint64 48 | # gen_validator_list_hash_short:uint32 49 | # gen_catchain_seqno:uint32 50 | # min_ref_mc_seqno:uint32 51 | # prev_key_block_seqno:uint32 52 | # gen_software:flags . 0?GlobalVersion 53 | # master_ref:not_master?^BlkMasterInfo 54 | # prev_ref:^(BlkPrevInfo after_merge) 55 | # prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0) 56 | # = BlockInfo; 57 | 58 | block_info = BlockInfo().fetch(parsed_block.info, rec_unpack=True) 59 | assert block_info.get_tag() == 0x9bc7a987 60 | assert block_info.version == 0 61 | assert block_info.not_master == 1 62 | assert block_info.after_merge == 1 63 | assert block_info.before_split == 0 64 | assert block_info.after_split == 0 65 | assert block_info.want_split == 0 66 | assert block_info.want_merge == 0 67 | assert block_info.key_block == 0 68 | assert block_info.vert_seqno_incr == 0 69 | assert block_info.flags == 1 70 | assert block_info.seq_no == 13516764 71 | assert block_info.vert_seq_no == 1 72 | assert block_info.gen_utime == 1612979175 73 | assert block_info.start_lt == 15441606000000 74 | assert block_info.end_lt == 15441606000117 75 | assert block_info.gen_validator_list_hash_short == 3566337926 76 | assert block_info.gen_catchain_seqno == 150640 77 | assert block_info.min_ref_mc_seqno == 9546494 78 | assert block_info.prev_key_block_seqno == 9535794 79 | 80 | gen_software = block_info.gen_software 81 | assert gen_software.capabilities == 46 82 | assert gen_software.version == 3 83 | 84 | shard = block_info.shard 85 | assert shard.shard_pfx_bits == 1 86 | assert shard.workchain_id == 0 87 | assert shard.shard_prefix == 9223372036854775808 88 | 89 | master_ref = block_info.master_ref 90 | master = master_ref.master 91 | 92 | assert master.end_lt == 15441605000004 93 | assert master.seq_no == 9546494 94 | assert int(master.root_hash, 2) == 0xD98A92A8E06FCE5D2805A447A6DE394060166B3C8D4A0286BFE35740E74FE604 95 | assert int(master.file_hash, 2) == 0xDCF740947D1C467F3DB482CB049292BDA006C1B052076B3A1F6619773AC378B6 96 | 97 | prev_ref = block_info.prev_ref 98 | prev1 = prev_ref.prev1 99 | prev2 = prev_ref.prev2 100 | 101 | assert prev1.end_lt == 15441599000040 102 | assert prev1.seq_no == 13516763 103 | assert int(prev1.root_hash, 2) == 0x617F643F15A42F28018E3E3C89F14B952A0D67FA90968AE5360A51B96C6A1C42 104 | assert int(prev1.file_hash, 2) == 0x563AA5F3D51585B95C0C89BF6C4E39455F4C121269521C1C5B6DC07F03C5D230 105 | 106 | assert int(prev2.root_hash, 2) == 0x032B1BF3016C9B71816C52F207C4CD79D75541F78EACB11CAC2EA7B77D2A603D 107 | assert int(prev2.file_hash, 2) == 0x8DCAB64721513F3DB73A081DD61CDF51D7FEC79347AB348D43FFB8BC052A8DB3 108 | assert prev2.end_lt == 15441602000076 109 | assert prev2.seq_no == 13516699 110 | 111 | # value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection 112 | # to_next_blk:CurrencyCollection 113 | # imported:CurrencyCollection 114 | # exported:CurrencyCollection ] 115 | # fees_collected:CurrencyCollection 116 | # ^[ 117 | # fees_imported:CurrencyCollection 118 | # recovered:CurrencyCollection 119 | # created:CurrencyCollection 120 | # minted:CurrencyCollection 121 | # ] = ValueFlow; 122 | 123 | value_flow = ValueFlow().fetch(parsed_block.value_flow, rec_unpack=True) 124 | assert value_flow.r1.from_prev_blk.grams.amount.value == 316323479379560897 125 | assert value_flow.r1.to_next_blk.grams.amount.value == 316323439152031803 126 | assert value_flow.r1.imported.grams.amount.value == 41877335552 127 | assert value_flow.r1.exported.grams.amount.value == 76204670704 128 | assert value_flow.fees_collected.grams.amount.value == 6400193942 129 | assert value_flow.r2.fees_imported.grams.amount.value == 0 130 | assert value_flow.r2.recovered.grams.amount.value == 0 131 | assert value_flow.r2.created.grams.amount.value == 500000000 132 | assert value_flow.r2.minted.grams.amount.value == 0 133 | 134 | block_extra = BlockExtra().fetch(parsed_block.extra) 135 | 136 | # _ (HashmapAugE 256 InMsg ImportFees) = InMsgDescr; 137 | in_msg_descr = block_extra.in_msg_descr 138 | 139 | # Load augmented typed dictionary with recursively unpack all fields with autogenerated TLB 140 | in_msgs = TypedVmDict((InMsg(), ImportFees()), 141 | 256, False, 142 | in_msg_descr, rec_unpack=True) 143 | 144 | # You can get all messages 145 | # for i in in_msgs: 146 | # print(i) 147 | 148 | # Get one value for test 149 | test_msg = in_msgs[55320535438392264092286370946255453957461499360282925697254340001519613742160] 150 | # You can access extra value 151 | assert test_msg.extra.fees_collected.amount.value == 666672 152 | assert test_msg.extra.value_imported.grams.amount.value == 100666672 153 | 154 | # And any value recursively unpacked by TLB 155 | assert int(test_msg.data.transaction.account_addr, 2) == \ 156 | 0x86e9617280e747235574d07d7b0202cf5a2b3c6d70f583a82dda3e8bb9b1b0c6 157 | assert test_msg.data.transaction.now == 1612979175 158 | assert test_msg.data.transaction.lt == 15441606000010 159 | assert test_msg.data.transaction.orig_status == 2 160 | assert test_msg.data.transaction.end_status == 2 161 | assert test_msg.data.transaction.outmsg_cnt == 0 162 | 163 | # Test very deep value 164 | assert test_msg.data.transaction.description.compute_ph.r1.gas_used.value == 409 165 | 166 | out_msg_descr = block_extra.out_msg_descr 167 | account_blocks = block_extra.account_blocks 168 | assert hex(int(block_extra.rand_seed, 2)).upper()[ 169 | 2:] == '80881F1F06B661A89494CE223BE1A491B8461796F01CB2FDF35A1181AEEE3F16' 170 | assert hex(int(block_extra.created_by, 2)).upper()[ 171 | 2:] == 'D318C6BB5F2114B5E37BE9239CDBC5C6F45C5BDFC25DFC6B6B650F7D961CD801' 172 | custom = block_extra.custom 173 | assert custom.name == 'Record_nothing' 174 | 175 | 176 | if __name__ == "__main__": 177 | test_block_autogen_unpack() 178 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_cell.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | 6 | import pytest 7 | 8 | path_root = Path(__file__).parents[2] 9 | sys.path.append(str(path_root)) 10 | 11 | from tonpy.types import CellSlice, Cell, CellBuilder 12 | 13 | boc_bomb = "te6ccgECTgEAC5UAAm3AA3Hv0IobJQMf+6YZsFQt8ZzuTGEy2Nfngby7/rMDnstinJNGwyJCZVAAAIcbEBnqYMB6EhNAAQIBFP8A9KQT9LzyyAsDBEC4dwmy9iVNUgntm0c7/L8QADFu2qACTbasp/JOiZY6EQQEBAQAWtP4JfgV7UT4ECHIzFIgzFIgzBLMy//J7VRwgBDIywX4KM8WIfoCy2rJgwb7AARA15OO/Ox4iqqzcK6zfXSF/uZ6mwhNqBuI2s+Bi0LJSqkFBQUFBEB2kkDF9skl6NuIV9ThB8asJ6BF5ZKB8XfQsHFLkeXRTAYGBgYEQPggTcUUlHJIxCI/bOluAonjJW7B9DRCDKAHG1V569qrBwcHBwRAe0R9cgRMWRXpKcLkrXW/r221QpWqSQA6uUgddUEw9ukICAgIBEBtM3N1gsZctPjnUF+8RZ8H2EuFZp2v77gkFJNKm/8qPwkJCQkEQKvnq4ZGmNJzUqG1bprtvKHAJaZcywfptQpMJd/vAhiHCgoKCgRA8curH860xWjbf5cXznl2RMTVnGHbdH6AWaLjsM3CKv8LCwsLBEBBQ12e7JSptIZW7tpAyvdElVKQnTiiWVUv6N0bAwiAQQwMDAwEQC7fn60fPr4scD/yquUIrMlM6M1xoF8I92KkE/nxm4v+DQ0NDQRA6IB9qL40oBeDKkDHRUSHNXjNGjs1bSiDsxlOlZg9t+MODg4OBECsZ9hQElS7OCzMyVNYN2QaVcUA7EiBOVeBUw1lHtXGKQ8PDw8EQPludFwOGodrKr9qFBzdD15mPBqLGwmmiAfPX9SELqJQEBAQEARAqHm+CAlCNY0kemVFZILsE0gC8DNWSHtWhcyPo1x6bL4RERERBECwXArfle8DYhDaLQ684SlXfiLDe0vXiUDBddIviPCnXxISEhIEQJ98pr4ZhtFEWtTnAMuwqLpY5X0tUN233zRDAx7TTxVeExMTEwRAxL6qd0xQaatO7I/HW7EbOJBm4w21gxsem79S+XdqG84UFBQUBEAHJDfmjpFP6HJiqlN4zP0A80lcVFJpRHaQucHWFZm+fxUVFRUEQPbq8DRzHnGq0+seseSj2egiE+HPcNdYZqnubh82gyU1FhYWFgRA4RjjHkkNSwP17sDrkzzK8m92xZh8uTqT+Uv/kWoIrGUXFxcXBEBaDQQMBiITp6g7lAlmlL/SdoxgeVuNU2lrvJTRL+PetRgYGBgEQBFpkVWWRz44OfMgIn1NfEPBKY8/cOxKmlR2lBF+9D0sGRkZGQRAAV4Lge2pHuYy3AKg7szay4/TvtjxoRXwLW+J9GOZI5UaGhoaBEC9M5H0Xeb/ANgMWlGPrObz4ZIaQE6oKTJGe3KxsJmeEhsbGxsEQONcEp+FVeRBLmGOe64vCt3EyCDnS0kplgdtSpFt/RkMHBwcHARAjgIt/rnOf6tEyD+82DSTbUFgkSo92BSvCUSDTrIeCHodHR0dBEB72K9Ya2KKdM46UkdyvQBAqnYkjImxtrogEP8KAGQw0x4eHh4EQJhVPESLqB8zbQavHvxJ/zLcfVAb9MZcd7pbi1R2XmIFHx8fHwRAcyvk+7fHIce6HmeTx4/zbgz4xRxD0xMtrlgLICcQRcIgICAgBECg5D4NwM2HmnlP3Ds+llRNfayYhLvBbPP5unFmQ2Q2YSEhISEEQJe1hnSAdg9ctjkRSFB6m7msa1rsaKhTQALzjWHeVBY1IiIiIgRAGjjVK+RTODjVe+mPw8sCrxgWLFXtbmdL8LDAsReTyFEjIyMjBEAr8PEXcfJMLaSLSZw4qGCA/MqqVIOK7+Q2y4SM3aXsdCQkJCQEQMn0JwvLk/+SlWa1WXsId+FwXuctPimq6rbiifagbC3xJSUlJQRA3IIQDz0Nmx4WHNcQhtQ3fIhI2TeiTFK7M1dulMLBI5AmJiYmBEDDjNegQb9peXx99+RUbFAHft14rWt98mrPqk67Oqxa2ScnJycEQP2qwG8/HPWwXwEycIEn7+5MdvXLKHaCXI6aPtd3Q0s6KCgoKARA7eOW6Cc2kDpRCucoEFJkYkTVqdCQqGS99ZlltpD0r4kpKSkpBECD3rXsNYH5LyCmTzj//TMYazJ4fIoxSm8o9zDd0PNr9yoqKioEQIIsd9/o/4PVoFSaW1wWdMc17Nb+nPnGOYwIDhjMOOaaKysrKwRAEiJWn4dxZQiiCnsbK3IvLKCBdN8KlJYvNmw6HinuouYsLCwsBEBF80t1iwQZ76c9/xrLwCWOIISrjj7u5iCWN0Mp9lWiwS0tLS0EQLFLHNzNpZTFEHdoHZDw6Wl9vFj8SJI5aquTza3FtzLMLi4uLgRAdd1hXZR1vU+QMTL9D0MHaeHA5p7xYdYs+bfToy6EWxQvLy8vBEDAZCgsO1XN7fyAo/YijJR3x5yr/hzVx9Kx3xJdnkbWBDAwMDAEQLDs4aKAKyAvpCNNn/bx27abfiWAY/yLXX+NWV2GsKFpMTExMQRApJzt2Wa0IKBCKYBnKZZE0Xy9wWzUkEW13psqqyPqQ+4yMjIyBEBBpL4BV1abicQYod4CfdRc2uZhUGBJCVuIzmGcDE/HLTMzMzMEQITbvHQcFu0v/s18HVWTikKBGWfJwC19Ji/7EUuKYRS3NDQ0NARA0h0jB2Q1rEBslxmzYQIYAatd+KZApUVVl/AjsaQVj/g1NTU1BEARk2zCIPKwvobhnUb5xNXzS5lJwA/r3DK0BL7YuWLnijY2NjYEQIawZbi6VIgekiMnxQVd1mA7iZPVcqPx3vqqZIV9Ngm5Nzc3NwRAPQbFPYH9q+UfUSOy37vDYl9tpFAOcwv1zCu/zbLNqfo4ODg4BECw3Z8Bhh40wWyM2yY3U8HAd7Os5Fqylg8b7B4JyhRjEzk5OTkEQNWo+1tYwJ7ym9NCLT9EYYby1uMxLzye8RHtcavl3eGOOjo6OgRAdeW+yFRGQi2RdhYrIKS8RPZew5CKI9hu/y/975O4BDc7Ozs7BECp8m3ZgwYsezbvcfbGW04TvG7qXU1Z/Kh28uW247vCCjw8PDwEQCxZ0ZmOUlsgz1CalG7DqsTPEWhEkfcq8D4ZDIsJOmc3PT09PQRAlfJTtov7TQi3GlGLulRn6XTxviTC1HeKNslKTXnAEjM+Pj4+BEDCasHk0jCNh7uzOtaC6CtLVH4Y2e83pnL1k2gnW++C0z8/Pz8EQPRg0hqHknHRzVfjYLW2ukHKmUdsR+s2sYKFC11596umQEBAQARAWrhIk22JiJU3H7BuU7iwVrM/fXT8evUM3wdO3p2zDzpBQUFBBEABhKVAtDf9UnelF060a9FAwS4Qb7GuGrOVr1xgrv/KrEJCQkIEQMzeaRP9fBJ+i8Ey7StKEgTsNHzZlhx7Mgw0+SGVyd3wQ0NDQwRAIXDqE0fQr3dKWVhlkWPO7QCMzpADOF9nGwAcIM2Thn9EREREBEB1P/H+3BPerqtkCtubJyt68p6JvrXNHqy2exMyTYiUXEVFRUUEQK8QU1U0fYAYBCiWkoEe5UvQ3vGdML0Nu8iMJUcWeBGrRkZGRgRAp05ORKAnwQWNnPMRz8kE1xgCMHc+2FCluGCZS3LQ+S1HR0dHBECsT9aiJSyPG5PL4Bv8wtmQojS9rQ/HbkrnnHkeJGyW2EhISEgEQM9qUj+qNqOWw3TJigBQpHA3QOZgnigQlcqlCBgow5xkSUlJSQRAPc+PtMvktQlOftXjKQyAGpLrPrjCBf3pPfF7U0zDau5KSkpKBEAhrXnlwgVcrWQuxSS1RR2R2D9RsN5GtuAC8nAIVEIH1ktLS0sEQOU4UkxCwHD4bRxGHS0wkr2qOjpIwkQZ9XWacgluOTZMTExMTARAmwHHgofnTVUb9jR8vHbLZehzTa5ZkG9ZQBVToAhA1VZNTU1NAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGHumtdTw==" 14 | empty = 'te6ccuEBAQEACgAUABAAAAAAAAAAe4EqE1s=' 15 | 16 | 17 | def test_boc(): 18 | cell = Cell(boc_bomb) 19 | assert cell.get_hash() == 'EC89185485A679715A615BCA264BF01B4CB619BD28D9E17FF73D05CF5E01C0E0' 20 | 21 | cell = Cell(empty) 22 | assert cell.to_boc() == empty 23 | 24 | 25 | def test_dump_as_tlb(): 26 | # addr_extern$01 len:(## 9) external_address:(bits len) 27 | # = MsgAddressExt; 28 | 29 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 30 | assert c.dump_as_tlb("MsgAddressExt") == "(addr_extern len:10 external_address:xFFE_)" 31 | 32 | 33 | def test_dump(): 34 | cb = CellBuilder() 35 | cb.store_uint(0, 10) 36 | 37 | cs = Cell(cb.to_boc()) 38 | assert cs.dump()[:-1] == "x{002_}" 39 | 40 | 41 | def test_begin_parse(): 42 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 43 | assert isinstance(c.begin_parse(), CellSlice) 44 | 45 | 46 | def test_null(): 47 | c = Cell() 48 | assert c.is_null() is True 49 | 50 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 51 | assert c.is_null() is False 52 | 53 | 54 | def test_get_hash(): 55 | c = Cell("te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==") 56 | assert c.get_hash() == '94804DD004BE8628522901CF117E4B03CF69E78FC0A9E92BC578BCCB1B3FD0D2' 57 | 58 | 59 | def test_to_boc(): 60 | c = Cell("te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==") 61 | assert c.to_boc() == "te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==" 62 | 63 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 64 | assert c.to_boc() == "te6ccuEBAQEABQAKAAVBX/xo8FXp" 65 | 66 | 67 | special_boc = 'te6ccgECCAEAAZYACUYDrgStkKrCoJbT+LOzwtnwHYu1pPW8WSJMWo0m/dpuxBkAIAEkEBHvVar///8RAgMEBQKgm8ephwAAAACEAQIwXXIAAAABAAAAAAAAAAAAAAAAAGStZTsAACOndPk2QAAAI6d0+TZGvx8hlQAG+a0B2aZhAdmf0MQAAAADAAAAAAAAAC4GByhIAQFKQKbStv6SvSqEfoPgXK5bCn+NHXuYUPbA1ciY+SAQPwADKEgBAZ4wW4B3exJSaUiB1Hy1NBfKS519G+RZu6rIG17vV4jLAB8oSAEBs0TT5lkdKa7umzwtMNVY3bOu9WuMAia4hgBNxgU9geAACwCYAAAjp3Tp9AQB2aZh58HTwBnpdOI4ieRKyBDHoZ1QCWxZF6lCAJRNRaCm2NEoU/0ZqIy9W1WLKoTqLuiyRadYbHBC9Vr8r5voMExOiwCYAAAjp3Tp9AgCMF1xLotqvpwEzhDPQp5pnxsacYiwPX2hQ3vH7250Lm8mfwax4kMBEp/6Dgg9YmqElR7RqVYmYR1A1ZLh8FfsNgMyCA==' 68 | 69 | 70 | def test_special_boc(): 71 | cell = Cell(special_boc) 72 | 73 | with pytest.raises(RuntimeError): 74 | cell.begin_parse(False) 75 | 76 | # allow to load special 77 | cs = cell.begin_parse(True) 78 | 79 | 80 | def test_is_special(): 81 | cs = Cell(special_boc).begin_parse(True) 82 | assert cs.is_special() is True 83 | 84 | 85 | def test_special_type(): 86 | cs = Cell(special_boc).begin_parse(True) 87 | assert cs.special_type() == CellSlice.SpecialType.MerkleProof 88 | 89 | 90 | def test_copy(): 91 | cell = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 92 | cell2 = cell.copy() 93 | 94 | assert cell.get_hash() == cell2.get_hash() 95 | 96 | 97 | def test_repr(): 98 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 99 | print(c) 100 | assert str( 101 | c) == "" 102 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_cellbuilder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | 6 | path_root = Path(__file__).parents[2] 7 | sys.path.append(str(path_root)) 8 | 9 | from tonpy.types.cellbuilder import CellBuilder, Cell 10 | from random import randint 11 | 12 | 13 | def test_store_ref(): 14 | cb = CellBuilder() 15 | c = Cell("te6ccuEBAQEAAgAEAABmLc6k") # empty cell 16 | 17 | assert cb.refs == 0 18 | assert cb.remaining_refs == 4 19 | 20 | cb.store_ref(c) 21 | 22 | assert cb.refs == 1 23 | assert cb.remaining_refs == 3 24 | 25 | 26 | def get_tester_store_builder_or_cellslice(store_builder: bool = False): 27 | cb1 = CellBuilder() 28 | cb1.store_ref(Cell("te6ccuEBAQEAAgAEAABmLc6k")) 29 | cb1.store_uint(32, 64) 30 | 31 | if not store_builder: 32 | cb1 = cb1.begin_parse() 33 | 34 | bits_cb1 = cb1.bits 35 | refs_cb1 = cb1.refs 36 | 37 | assert bits_cb1 == 64 38 | assert refs_cb1 == 1 39 | 40 | cb2 = CellBuilder() 41 | cb2.store_uint(0, 256) 42 | 43 | bits_cb2 = cb2.bits 44 | refs_cb2 = cb2.refs 45 | 46 | assert bits_cb2 == 256 47 | assert refs_cb2 == 0 48 | 49 | if not store_builder: 50 | cb2.store_slice(cb1) 51 | else: 52 | cb2.store_builder(cb1) 53 | 54 | assert cb2.bits == (bits_cb1 + bits_cb2) 55 | assert cb2.refs == (refs_cb1 + refs_cb2) 56 | 57 | 58 | def test_store_builder(): 59 | get_tester_store_builder_or_cellslice(True) 60 | 61 | 62 | def test_store_cell_slice(): 63 | get_tester_store_builder_or_cellslice(False) 64 | 65 | 66 | def test_store_uint(): 67 | cb = CellBuilder() 68 | 69 | cb.store_uint(0, 1) 70 | cb.store_uint(1, 64) 71 | cb.store_uint(int("1" * 64, 2), 64) # Max uint 64 72 | cb.store_uint(int("1" * 256, 2), 256) # Max uint 256 73 | 74 | cs = cb.begin_parse() 75 | assert cs.load_uint(1) == 0 76 | assert cs.load_uint(64) == 1 77 | assert cs.load_uint(64) == int("1" * 64, 2) 78 | assert cs.load_uint(256) == int("1" * 256, 2) 79 | 80 | 81 | def test_store_int(): 82 | cb = CellBuilder() 83 | 84 | cb.store_int(0, 1) 85 | cb.store_int(-1, 2) 86 | cb.store_int(-1 * int("1" * 63, 2), 64) # Min int 64 87 | cb.store_int(int("1" * 63, 2), 64) # Max int 64 88 | cb.store_int(int("1" * 256, 2), 257) # Max int 256 89 | cb.store_int(-1 * int("1" * 256, 2), 257) # Max int 64 90 | 91 | cs = cb.begin_parse() 92 | assert cs.load_int(1) == 0 93 | assert cs.load_int(2) == -1 94 | assert cs.load_int(64) == -1 * int("1" * 63, 2) 95 | assert cs.load_int(64) == int("1" * 63, 2) 96 | assert cs.load_int(257) == int("1" * 256, 2) 97 | assert cs.load_int(257) == -1 * int("1" * 256, 2) 98 | 99 | 100 | def test_store_zeroes(): 101 | cb = CellBuilder() 102 | 103 | cb.store_zeroes(64) 104 | cb.store_zeroes(128) 105 | cb.store_zeroes(64) 106 | 107 | cs = cb.begin_parse() 108 | assert cs.load_uint(64 + 128 + 64) == 0 109 | assert cs.bits == 0 and cs.refs == 0 110 | 111 | 112 | def test_store_ones(): 113 | cb = CellBuilder() 114 | 115 | cb.store_ones(32) 116 | cb.store_ones(64) 117 | cb.store_ones(128) 118 | cb.store_ones(256) 119 | 120 | cs = cb.begin_parse() 121 | assert cs.load_uint(32 + 64 + 128) == int("1" * (32 + 64 + 128), 2) 122 | assert cs.load_uint(256) == int("1" * 256, 2) 123 | assert cs.bits == 0 and cs.refs == 0 124 | 125 | 126 | def test_store_uint_less(): 127 | cb = CellBuilder() 128 | cb.store_uint_less(int("1" * 32, 2), 0) 129 | cb.store_uint_less(int("1" * 32, 2), int("1" * 31, 2)) 130 | cb.store_uint_less(int("1" * 30, 2), 0) 131 | cb.store_uint_less(int("1" * 30, 2), 32123123) 132 | cb.store_uint_less(3, 2) 133 | 134 | cs = cb.begin_parse() 135 | assert cs.load_uint(32) == 0 136 | assert cs.load_uint(32) == int("1" * 31, 2) 137 | assert cs.load_uint(30) == 0 138 | assert cs.load_uint(30) == 32123123 139 | assert cs.load_uint(len(bin(3)[2:])) == 2 140 | 141 | 142 | def test_store_uint_leq(): 143 | cb = CellBuilder() 144 | 145 | cb.store_uint_leq(int("1" * 32, 2), 0) 146 | cb.store_uint_leq(int("1" * 32, 2), int("1" * 32, 2)) 147 | cb.store_uint_leq(int("1" * 30, 2), 0) 148 | cb.store_uint_leq(int("1" * 30, 2), int("1" * 30, 2)) 149 | 150 | x = 100000 151 | y = x - 1 152 | 153 | cb.store_uint_leq(x, y) 154 | 155 | cs = cb.begin_parse() 156 | assert cs.load_uint(32) == 0 157 | assert cs.load_uint(32) == int("1" * 32, 2) 158 | assert cs.load_uint(30) == 0 159 | assert cs.load_uint(30) == int("1" * 30, 2) 160 | assert cs.load_uint(len(bin(x)[2:])) == y 161 | 162 | 163 | def test_store_bitstring(): 164 | cb = CellBuilder() 165 | cb.store_bitstring('1111') 166 | cs = cb.begin_parse() 167 | assert cs.load_uint(4) == int('1' * 4, 2) 168 | 169 | cb = CellBuilder() 170 | cb.store_bitstring(''.join(['0' if x % 2 == 0 else '1' for x in range(1023)])) 171 | cs = cb.begin_parse() 172 | assert cs.bits == 1023 173 | 174 | bits = bin(2268)[2:] 175 | cb = CellBuilder() 176 | cb.store_bitstring(bits) 177 | cs = cb.begin_parse() 178 | assert cs.load_uint(len(bits)) == 2268 179 | 180 | 181 | def test_get_cell(): 182 | cell = Cell("te6ccuEBAQEAAgAEAABmLc6k") 183 | cb = CellBuilder() 184 | assert cb.end_cell().to_boc() == cell.to_boc() 185 | 186 | 187 | def test_dump(): 188 | cb = CellBuilder() 189 | cb.store_uint(0, 10) 190 | assert cb.dump()[:-1] == "x{002_}" 191 | 192 | 193 | def test_dump_as_tlb(): 194 | cb = CellBuilder() 195 | 196 | # addr_extern$01 len:(## 9) external_address:(bits len) 197 | # = MsgAddressExt; 198 | 199 | cb.store_bitstring("01") 200 | cb.store_uint(10, 9) 201 | cb.store_bitstring("1" * 10) 202 | assert cb.dump_as_tlb("MsgAddressExt") == "(addr_extern len:10 external_address:xFFE_)" 203 | 204 | 205 | def test_get_hash(): 206 | cb = CellBuilder() 207 | cb.store_uint(10, 32) 208 | assert cb.get_hash() == "8AE53B8B0178198B1165839BF91623AD7A92E1D074F1C3786A62078542667024" 209 | 210 | cb = CellBuilder() 211 | cb.store_ref(CellBuilder().end_cell()) 212 | assert cb.get_hash() == "6C64B3153333F7AF728149B88CD7B27F5DED7CD17AC88893EE47FC208A15E640" 213 | 214 | 215 | def test_repr(): 216 | cb = CellBuilder() 217 | cb.store_uint(10, 32) 218 | assert str( 219 | cb) == "" 220 | 221 | 222 | def test_store_grams(): 223 | for i in range(30): 224 | cb = CellBuilder() 225 | 226 | grams = randint(0, int("1" * 16, 2)) 227 | cb.store_grams(grams) 228 | boc32_grams = cb.end_cell().to_boc() 229 | 230 | cb = CellBuilder() 231 | cb.store_var_uint(grams, 16) 232 | boc32 = cb.end_cell().to_boc() 233 | 234 | assert boc32_grams == boc32 235 | 236 | cb = CellBuilder() 237 | grams = int("1" * 16, 2) 238 | cb.store_grams(grams) 239 | boc32_grams = cb.end_cell().to_boc() 240 | 241 | cb = CellBuilder() 242 | cb.store_var_uint(grams, 16) 243 | boc32 = cb.end_cell().to_boc() 244 | 245 | assert boc32_grams == boc32 246 | 247 | 248 | def test_store_var_uint(): 249 | cb = CellBuilder() 250 | 251 | x = 32 252 | bits = ((x - 1) * 8) 253 | int_by_bits = int("1" * bits, 2) 254 | 255 | cb.store_var_uint(int_by_bits, x) # max value can be stored 256 | assert cb.bits == (len(bin(x)[2:]) + bits - 1) 257 | 258 | cs = cb.begin_parse() 259 | assert cs.load_var_uint(x) == int_by_bits 260 | 261 | cb = CellBuilder() 262 | cb.store_var_uint(0, 16) 263 | cs = cb.begin_parse() 264 | assert cs.load_var_uint(16) == 0 265 | 266 | cb = CellBuilder() 267 | cb.store_var_uint(352, 16) 268 | cs = cb.begin_parse() 269 | assert cs.load_var_uint(16) == 352 270 | 271 | 272 | def test_store_var_int(): 273 | cb = CellBuilder() 274 | 275 | x = 32 276 | bits = ((x - 1) * 8) 277 | int_by_bits = -1 * int("1" * (bits - 1), 2) 278 | 279 | cb.store_var_int(int_by_bits, x) # max value can be stored 280 | assert cb.bits == (len(bin(x)[2:]) + bits - 1) 281 | 282 | cs = cb.begin_parse() 283 | assert cs.load_var_int(x) == int_by_bits 284 | 285 | cb = CellBuilder() 286 | x = 32 287 | bits = ((x - 1) * 8) 288 | int_by_bits = int("1" * (bits - 1), 2) 289 | 290 | cb.store_var_int(int_by_bits, x) # max value can be stored 291 | assert cb.bits == (len(bin(x)[2:]) + bits - 1) 292 | 293 | cs = cb.begin_parse() 294 | assert cs.load_var_int(x) == int_by_bits 295 | 296 | cb = CellBuilder() 297 | cb.store_var_int(-1, 16) 298 | cs = cb.begin_parse() 299 | assert cs.load_var_int(16) == -1 300 | 301 | cb = CellBuilder() 302 | cb.store_var_int(-352, 16) 303 | cs = cb.begin_parse() 304 | assert cs.load_var_int(16) == -352 305 | 306 | cb = CellBuilder() 307 | cb.store_var_int(352, 16) 308 | cs = cb.begin_parse() 309 | assert cs.load_var_int(16) == 352 310 | 311 | 312 | def test_store_address(): 313 | cb = CellBuilder() 314 | cb.store_address("EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA") 315 | assert cb.bits == 267 316 | cs = cb.begin_parse() 317 | assert cs.load_address().serialize() == "EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA" 318 | 319 | cb = CellBuilder() 320 | cb.store_address("UQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPTmF") 321 | assert cb.bits == 267 322 | cs = cb.begin_parse() 323 | assert cs.load_address().serialize() == "EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA" 324 | 325 | cb = CellBuilder() 326 | cb.store_address("0:EB2EAF97EA32993470127208218748758A88374AD2BBD739FC75C9AB3A3F233D") 327 | assert cb.bits == 267 328 | cs = cb.begin_parse() 329 | assert cs.load_address().serialize() == "EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA" 330 | 331 | 332 | def test_store_string(): 333 | s_to_store = "Hello world!" 334 | cb = CellBuilder() 335 | cb.store_string(s_to_store) 336 | cs = cb.begin_parse() 337 | my_string = cs.load_string() 338 | assert my_string == s_to_store 339 | 340 | cb = CellBuilder() 341 | large_text = "Test string goes here it will be vey long! " * 2000 342 | cb.store_string(large_text) 343 | cs = cb.begin_parse() 344 | 345 | text_parsed = cs.load_string() 346 | assert text_parsed == large_text 347 | 348 | cb = CellBuilder() 349 | text = "Allow to fetch by size: [will not be loaded]" 350 | cb.store_string(text) 351 | cs = cb.begin_parse() 352 | assert text[:23] == cs.load_string(23 * 8) 353 | 354 | # TODO: test not strict load 355 | 356 | 357 | def test_store_build(): 358 | cb = CellBuilder() 359 | cb.store_bool(True) 360 | cs = cb.begin_parse() 361 | assert cs.load_bool() is True 362 | 363 | cb = CellBuilder() 364 | cb.store_bool(False) 365 | cs = cb.begin_parse() 366 | assert cs.load_bool() is False 367 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_fift.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.fift import Fift 4 | from tonpy.types.stack import StackEntry 5 | 6 | 7 | def test_fift(): 8 | a = Fift() 9 | a.add_lib("TonUtil.fif") 10 | # language=fift 11 | a.run("""""") 12 | 13 | stack = a.get_stack() 14 | assert len(stack) == 1 15 | assert stack[0].get_type() is StackEntry.Type.t_cell 16 | assert stack[0].get().begin_parse().load_uint(64) == 10 17 | 18 | 19 | def test_fift_asm(): 20 | a = Fift() 21 | a.add_lib("Asm.fif") 22 | 23 | # language=fift 24 | a.run("""<{ 25 | IF:<{ 26 | 123456789 PUSHINT 27 | }>ELSE<{ 28 | x{12345} PUSHSLICE 29 | }> 30 | WHILE:<{ ADD }>DO<{ 31 | 10 PUSHINT REPEAT:<{ 32 | CONT:<{ NOP }> 33 | CONT:<{ }> 34 | }> 35 | }> 36 | }>s""") 37 | 38 | assert a.last().get_hash() == '0D755E1797E4C709EA72CEC28DA859C689E28D3E0DE4203779439D9739E9FF15' 39 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_pickle.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | import pickle as pck 4 | from tonpy.types import Cell, CellSlice, CellBuilder 5 | 6 | 7 | def test_pck_cell(): 8 | c = Cell("te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==") 9 | dumped_cell = pck.dumps(c) 10 | loaded_c = pck.loads(dumped_cell) 11 | assert loaded_c.get_hash() == c.get_hash() 12 | assert isinstance(loaded_c, Cell) 13 | 14 | 15 | def test_pck_cell_slice(): 16 | cs = CellSlice("te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==") 17 | dumped_cell = pck.dumps(cs) 18 | loaded_cs = pck.loads(dumped_cell) 19 | assert loaded_cs.get_hash() == cs.get_hash() 20 | assert isinstance(loaded_cs, CellSlice) 21 | assert loaded_cs.load_uint(32) == cs.load_uint(32) 22 | 23 | 24 | def test_pck_cell_builder(): 25 | cb = CellBuilder("te6ccuEBAQEAJABIAEOAHWXV8v1GUyaOAk5BBDDpDrFRBulaV3rnP465NWdH5Gew4RZ/pw==") 26 | dumped_cell = pck.dumps(cb) 27 | loaded_cb = pck.loads(dumped_cell) 28 | assert loaded_cb.get_hash() == cb.get_hash() 29 | assert isinstance(loaded_cb, CellBuilder) 30 | 31 | cb.store_uint(0, 32) 32 | loaded_cb.store_uint(0, 32) 33 | 34 | assert cb.bits == loaded_cb.bits 35 | assert cb.refs == loaded_cb.refs 36 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_python_ton.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | 6 | path_root = Path(__file__).parents[2] 7 | sys.path.append(str(path_root)) 8 | 9 | from tonpy.libs.python_ton import PyCell, PyCellSlice, PyCellBuilder, PyDict, PyEmulator, parse_string_to_cell, \ 10 | load_as_cell_slice 11 | 12 | 13 | def test_load_boc(): 14 | cell = parse_string_to_cell('te6ccuEBAQEACgAUABAAAAAAAAAAe4EqE1s=') 15 | cs = load_as_cell_slice(cell, False) 16 | assert cell.get_hash() == cs.get_hash() 17 | assert cs.load_int(64) == '123' 18 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_raw_emulator.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | 6 | from tonpy.utils.actions import output_actions_count 7 | 8 | path_root = Path(__file__).parents[2] 9 | sys.path.append(str(path_root)) 10 | 11 | from tonpy import Emulator, Cell, VmDict 12 | from tonpy.autogen.block import Account, OutList 13 | from pprint import pprint 14 | 15 | from tonpy.data_for_tests.raw_emulator_data_for_test import cfg_test_0, account_state_test_0, message_test_0, \ 16 | cfg_test_2, account_state_test_2, cfg_test_1, account_state_test_1, in_msg_test_1, account_state_test_3 17 | 18 | 19 | # All txs from mainnet 20 | 21 | def test_emulator_internal(): 22 | em = Emulator(VmDict(32, False, cell_root=cfg_test_0)) 23 | account_state = Cell(account_state_test_0) 24 | success = em.emulate_transaction( 25 | shard_account=account_state, 26 | message=Cell(message_test_0), 27 | unixtime=1687939216, 28 | lt=38846792000007 29 | ) 30 | 31 | assert success is True 32 | vm_account = em.account.to_cell() 33 | cs = vm_account.begin_parse().load_ref(as_cs=True) 34 | account = Account().fetch(cs, rec_unpack=True) 35 | assert account.addr.workchain_id == 0 36 | assert hex(int(account.addr.address, 2))[2:].zfill( 37 | 64).upper() == '5239B71AC50E62C577E626CC12AF146B08B64A4E2E9718FD459C2300E5B205BD' 38 | 39 | assert account.storage.balance.grams.amount.value == 225181653392 40 | assert account.storage.last_trans_lt == 38846792000010 41 | assert account.storage.state.x.code.value.get_hash() == 'C12275085EC7DD21925C33A919680186022C6F2A4CED45DBCE3D3E14D438DC0F' 42 | assert account.storage.state.x.data.value.get_hash() == 'A10C8C4653B1865D4B1ED261EE7D26D6B2B39C719079993E376A173118A01FCF' 43 | assert account.storage_stat.last_paid == 1687939216 44 | assert account.storage_stat.used.bits.value == 6757 45 | assert account.storage_stat.used.cells.value == 20 46 | assert account.storage_stat.used.public_cells.value == 0 47 | assert em.transaction.get_hash() == "D5EA7B2B8027AF84501C9D72AADA58AEFDB258B91C3063645CDBC9EB87075313" 48 | 49 | 50 | def test_emulator_external(): 51 | em = Emulator(VmDict(32, False, cell_root=Cell(cfg_test_1))) 52 | account_state = Cell(account_state_test_1) 53 | success = em.emulate_transaction( 54 | shard_account=account_state, 55 | unixtime=1696334339, 56 | lt=41433522000001, 57 | message=Cell(in_msg_test_1) 58 | ) 59 | assert success is True 60 | assert em.transaction.get_hash() == 'EB828263B0BF90320D44DCEF3240BBE08700C8003FD11B3CBA66BF82B4CAA0A0' 61 | vm_account = em.account.to_cell() 62 | cs = vm_account.begin_parse().load_ref(as_cs=True) 63 | account = Account().fetch(cs, rec_unpack=True) 64 | 65 | assert account.addr.workchain_id == 0 66 | assert hex(int(account.addr.address, 2))[2:].zfill( 67 | 64).upper() == 'D796479201F2C7AFEF411EA75F57E730CAF1E6D4B30C3E38D3952D1914024FFF' 68 | 69 | assert account.storage.balance.grams.amount.value == 993073996 70 | assert account.storage.last_trans_lt == 41433522000003 71 | assert account.storage.state.x.code.value.get_hash() == '84DAFA449F98A6987789BA232358072BC0F76DC4524002A5D0918B9A75D2D599' 72 | assert account.storage.state.x.data.value.get_hash() == 'D91260F27A144AC721A9764EE1EE818DD89D62635F46CF1E65FE5DD1EC6CEE4A' 73 | assert account.storage_stat.last_paid == 1696334339 74 | assert account.storage_stat.used.bits.value == 1315 75 | assert account.storage_stat.used.cells.value == 3 76 | assert account.storage_stat.used.public_cells.value == 0 77 | 78 | actions = OutList(output_actions_count(em.actions)).fetch(em.actions, rec_unpack=True) 79 | 80 | assert actions.action.mode == 3 81 | assert actions.action.out_msg.body.value.get_hash() == '96A296D224F285C67BEE93C30F8A309157F0DAA35DC5B87E410B78630A09CFC7' 82 | assert actions.action.out_msg.info.bounce is True 83 | assert actions.action.out_msg.info.bounced is False 84 | assert actions.action.out_msg.info.created_at == 0 85 | assert actions.action.out_msg.info.created_lt == 0 86 | assert hex(int(actions.action.out_msg.info.dest.address, 2))[2:].zfill( 87 | 64).upper() == 'BE0FAA94D4861E2AC3FD4DB28CCE5CA54363D7C69FE4E1424DB3E3650DFF2ECB' 88 | assert actions.action.out_msg.info.dest.workchain_id == 0 89 | assert actions.action.out_msg.info.value.grams.amount.value == 6000000000 90 | 91 | 92 | def test_emulator_tock(): 93 | em = Emulator(VmDict(32, False, cell_root=Cell(cfg_test_2))) 94 | account_state = Cell(account_state_test_2) 95 | success = em.emulate_tick_tock_transaction( 96 | shard_account=account_state, 97 | unixtime=1696333181, 98 | lt=41433174000003, 99 | is_tock=True 100 | ) 101 | assert success is True 102 | assert em.transaction.get_hash() == '3A9B185EE049B13FDF071A0AF70CB1B9E3C3C5F758EBDC11A828E1EF36D31D66' 103 | vm_account = em.account.to_cell() 104 | cs = vm_account.begin_parse().load_ref(as_cs=True) 105 | account = Account().fetch(cs, rec_unpack=True) 106 | assert account.addr.workchain_id == -1 107 | assert hex(int(account.addr.address, 2))[2:].zfill( 108 | 64).upper() == '5' * 64 109 | 110 | assert account.storage.balance.grams.amount.value == 1637671300957 111 | assert account.storage.last_trans_lt == 41433174000004 112 | assert account.storage.state.x.code.value.get_hash() == '64A43970F2007A1DA6D6FC81773CC095D1CC270E81359E471F3B03469ABEB7B5' 113 | assert account.storage.state.x.data.value.get_hash() == 'EFBEC4D4E8B1FAB1E6A7CE3257350847C8E207F534C6F7FC5B012C0B5BD09F5C' 114 | assert account.storage_stat.last_paid == 0 115 | assert account.storage_stat.used.bits.value == 502074 116 | assert account.storage_stat.used.cells.value == 1927 117 | assert account.storage_stat.used.public_cells.value == 0 118 | 119 | 120 | def test_emulator_tick(): 121 | em = Emulator(VmDict(32, False, cell_root=Cell(cfg_test_2))) 122 | account_state = Cell(account_state_test_3) 123 | success = em.emulate_tick_tock_transaction( 124 | shard_account=account_state, 125 | unixtime=1696334861, 126 | lt=41433681000001, 127 | is_tock=False 128 | ) 129 | assert success is True 130 | assert em.transaction.get_hash() == 'F2D3A3309F8AF3332249F6DB62B8C65B825474F4234D83389585D1162F3140DF' 131 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_stack.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.stack import StackEntry, Stack 4 | from tonpy.types import Cell, CellSlice, CellBuilder 5 | 6 | 7 | def test_stack_none(): 8 | t = StackEntry() 9 | assert t.get_type() is StackEntry.Type.t_null 10 | 11 | 12 | def test_stack_int(): 13 | t = StackEntry(32123121231) 14 | assert t.get_type() is StackEntry.Type.t_int 15 | 16 | tt = t.get() 17 | assert isinstance(tt, int) 18 | assert tt == 32123121231 19 | 20 | 21 | def test_stack_cell(): 22 | t = StackEntry(Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp")) 23 | assert t.get_type() is StackEntry.Type.t_cell 24 | 25 | tt = t.get() 26 | assert isinstance(tt, Cell) 27 | assert tt.get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 28 | 29 | 30 | def test_stack_cell_slice(): 31 | t = StackEntry(CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp")) 32 | assert t.get_type() is StackEntry.Type.t_slice 33 | tt = t.get() 34 | assert isinstance(tt, CellSlice) 35 | assert tt.get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 36 | 37 | 38 | def test_stack_cell_builder(): 39 | cb = CellBuilder().store_uint(10, 64) 40 | t = StackEntry(cb) 41 | assert t.get_type() is StackEntry.Type.t_builder 42 | tt = t.get() 43 | assert isinstance(tt, CellBuilder) 44 | assert tt.end_cell().begin_parse().load_uint(64) == 10 45 | 46 | 47 | def test_stack_stack(): 48 | cb = CellBuilder().store_uint(10, 64) 49 | cs = CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp") 50 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 51 | se = StackEntry(11) 52 | 53 | s = Stack([10, cb, cs, c, se]) 54 | assert len(s) == 5 55 | assert s[0].get() == 10 56 | assert s[1].get().end_cell().begin_parse().load_uint(64) == 10 57 | assert s[2].get().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 58 | assert s[3].get().begin_parse().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 59 | assert s[4].get() == 11 60 | 61 | 62 | def test_serialize(): 63 | cb = StackEntry(CellBuilder().store_uint(10, 64)).serialize() 64 | assert cb.get_hash() == '433877BA3DE8544BBA9DE329EECD324C4E934D5AF46A41C62C44D6F9C7AAEDE6' 65 | 66 | cs = StackEntry(CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp")).serialize() 67 | assert cs.get_hash() == 'DB697A675BC36F7E6A97D9EBC8048F19C9C78B7BB904F196C4A066C7CBEC32E9' 68 | 69 | c = StackEntry(Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp")).serialize() 70 | assert c.get_hash() == '0E418A8BD8268CAB20DD393465EEDF1775F4FE39C96B5B504B94177F9E9E7F0E' 71 | 72 | se = StackEntry(11).serialize() 73 | assert se.get_hash() == '3D45A4D59698AD7BECD4979361CFA7E84384E26099273239546A6B23ABE982C2' 74 | 75 | 76 | def test_serialize_stack(): 77 | cb = CellBuilder().store_uint(10, 64) 78 | cs = CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp") 79 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 80 | se = StackEntry(11) 81 | 82 | s = Stack([10, cb, cs, c, se]).serialize() 83 | assert s.get_hash() == '78241E52B14D56C58495595AB665B207E6ED3CE6F889B1D8C16D88388EE30DDA' 84 | 85 | 86 | def test_deserialize_stack(): 87 | cb = CellBuilder().store_uint(10, 64) 88 | cs = CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp") 89 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 90 | se = StackEntry(11) 91 | 92 | s = Stack([10, cb, cs, c, se]).serialize() 93 | assert s.get_hash() == '78241E52B14D56C58495595AB665B207E6ED3CE6F889B1D8C16D88388EE30DDA' 94 | 95 | ss = Stack.deserialize(s.begin_parse()) 96 | assert ss[0].get() == 10 97 | assert ss[1].get().end_cell().begin_parse().get_hash() == \ 98 | '2703D9A1D01AF9B0B0D7728C8A760C1DD0BC63C22BEB9E71AA3D07061343A54C' 99 | assert isinstance(ss[2].get(), CellSlice) 100 | assert ss[2].get().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 101 | assert ss[3].get().begin_parse().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 102 | assert ss[4].get() == 11 103 | 104 | 105 | def test_deserialize_stack_entry(): 106 | cb = StackEntry.deserialize(StackEntry(CellBuilder().store_uint(10, 64)).serialize().begin_parse()) 107 | cs = StackEntry.deserialize(StackEntry(CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp")).serialize().begin_parse()) 108 | c = StackEntry.deserialize(StackEntry(Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp")).serialize().begin_parse()) 109 | se = StackEntry.deserialize(StackEntry(11).serialize().begin_parse()) 110 | 111 | assert cb.get().end_cell().begin_parse().get_hash() == \ 112 | '2703D9A1D01AF9B0B0D7728C8A760C1DD0BC63C22BEB9E71AA3D07061343A54C' 113 | assert isinstance(cs.get(), CellSlice) 114 | assert cs.get().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 115 | assert c.get().begin_parse().get_hash() == '235CBBDDDA3C8397468C806412A211BD2672C6188D9728C62DD48B3DEED02BA6' 116 | assert se.get() == 11 117 | 118 | 119 | def test_tuples(): 120 | cb = CellBuilder().store_uint(10, 64) 121 | cs = CellSlice("te6ccuEBAQEABQAKAAVBX/xo8FXp") 122 | c = Cell("te6ccuEBAQEABQAKAAVBX/xo8FXp") 123 | se = StackEntry(11) 124 | 125 | tp = StackEntry.create_tuple([10, cb, cs, c, se, [3, 4, 5]]) 126 | assert tp.get_type() == StackEntry.Type.t_tuple 127 | assert tp.serialize().get_hash() == '8AFC0E4F34D520C7B36BFA4AD1704EF67BAD5ABB52C508CB9256816D07724239' 128 | assert len(tp.get()) == 6 129 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_tlb.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.tlb_gen.py import add_tlb 4 | from bitstring import BitArray 5 | from tonpy.types import TLB, CellBuilder 6 | from itertools import product 7 | 8 | 9 | def test_tag_multi_bits(): 10 | # language=tl-b 11 | add_tlb(""" 12 | a$00 = AMultiTagBits; 13 | b$10 = AMultiTagBits; 14 | c$01 = AMultiTagBits; 15 | """, globals()) 16 | 17 | a_instance = CellBuilder().store_bitstring("00").begin_parse() 18 | b_instance = CellBuilder().store_bitstring("10").begin_parse() 19 | c_instance = CellBuilder().store_bitstring("01").begin_parse() 20 | 21 | A_record = AMultiTagBits() # noqa 22 | assert A_record.get_tag(a_instance) == A_record.Tag.a 23 | assert A_record.get_tag(b_instance) == A_record.Tag.b 24 | assert A_record.get_tag(c_instance) == A_record.Tag.c 25 | 26 | 27 | def test_tag_multi_int(): 28 | # language=tl-b 29 | add_tlb(""" 30 | a#32 = AMultiTagInt; 31 | b#1111 = AMultiTagInt; 32 | c#5FE = AMultiTagInt; 33 | """, globals()) 34 | 35 | a_instance = CellBuilder().store_uint(0x32, 8).begin_parse() 36 | b_instance = CellBuilder().store_uint(0x1111, 16).begin_parse() 37 | c_instance = CellBuilder().store_uint(0x5fe, 12).begin_parse() 38 | 39 | A_record = AMultiTagInt() # noqa 40 | assert A_record.get_tag(a_instance) == A_record.Tag.a 41 | assert A_record.get_tag(b_instance) == A_record.Tag.b 42 | assert A_record.get_tag(c_instance) == A_record.Tag.c 43 | 44 | 45 | def test_tag_multi_int_large(): 46 | tlb_text = "" 47 | 48 | for i in range(64): 49 | tag = hex(i)[2:].zfill(2) 50 | tlb_text += f"a{i}#{tag} = ALargeIntTags;\n" 51 | 52 | add_tlb(tlb_text, globals()) 53 | A_record = ALargeIntTags() # noqa 54 | 55 | for i in range(64): 56 | instance = CellBuilder().store_uint(i, 8).begin_parse() 57 | exec(f"assert A_record.get_tag(instance) == A_record.Tag.a{i}", globals(), locals()) 58 | 59 | 60 | def test_tag_multi_bits_large(): 61 | tags = [''.join(tag) for tag in list(product('10', repeat=6))] 62 | 63 | tlb_text = "" 64 | 65 | for tag in tags: 66 | tag_name = int(tag, 2) 67 | tlb_text += f"a{tag_name}${tag} = ALargeBitTags;\n" 68 | 69 | add_tlb(tlb_text, globals()) 70 | A_record = ALargeBitTags() # noqa 71 | 72 | for tag in tags: 73 | instance = CellBuilder().store_bitstring(tag).begin_parse() 74 | exec(f"assert A_record.get_tag(instance) == A_record.Tag.a{int(tag, 2)}", globals(), locals()) 75 | 76 | 77 | def test_tag_multi_bits_large_zfilled(): 78 | tags = [''.join(tag) for tag in list(product('10', repeat=6))] 79 | 80 | tlb_text = "" 81 | 82 | for tag in tags: 83 | tag_name = int(tag, 2) 84 | tlb_text += f"a{tag_name}${tag.zfill(62)} = ALargeBitTagsZfilled;\n" 85 | 86 | add_tlb(tlb_text, globals()) 87 | A_record = ALargeBitTagsZfilled() # noqa 88 | 89 | for tag in tags: 90 | instance = CellBuilder().store_bitstring(tag.zfill(62)).begin_parse() 91 | exec(f"assert A_record.get_tag(instance) == A_record.Tag.a{int(tag, 2)}", globals(), locals()) 92 | 93 | 94 | def test_tag_multi_int_large_zfilled(): 95 | tlb_text = "" 96 | 97 | for i in range(64): 98 | tag = hex(i)[2:].zfill(8) 99 | tlb_text += f"a{i}#{tag} = ALargeIntTagsFilled;\n" 100 | 101 | add_tlb(tlb_text, globals()) 102 | A_record = ALargeIntTagsFilled() # noqa 103 | 104 | for i in range(64): 105 | instance = CellBuilder().store_uint(i, 32).begin_parse() 106 | exec(f"assert A_record.get_tag(instance) == A_record.Tag.a{i}", globals(), locals()) 107 | 108 | 109 | def test_tag_with_aug(): 110 | # language=tl-b 111 | tlb_text = """ 112 | test$001 {x:#} a:(## x) = A x; 113 | test1$000 a:# = A 1; 114 | test2$010 a:# = A 2; 115 | test3$110 a:# = A 3; 116 | test4$111 a:# = A 4; 117 | test5$100 a:# = A 5; 118 | 119 | test$001 {x:#} {y:#} a:(## x) b:(## y) = B x y; 120 | test1$000 {y:#} a:# b:(## y) = B 1 y; 121 | test2$010 {x:#} a:# b:(## x) = B x 2; 122 | test3$110 a:# = B 1 1; 123 | test4$111 a:# = B 2 2; 124 | test5$100 a:# = B 3 3; 125 | 126 | test$001 {x:#} {y:#} {z:#} a:(## x) b:(## y) c:(## z) = C x y z; 127 | test1$000 {y:#} {z:#} a:# b:(## y) c:(## z) = C 1 y z; 128 | test2$010 {x:#} {z:#} a:# b:(## x) c:(## z)= C x 2 z; 129 | test3$110 {z:#} a:# b:# c:(## z) = C 1 1 z; 130 | test4$111 a:# = C 2 2 2; 131 | test5$100 a:# = C 3 3 3; 132 | 133 | 134 | test$001 {x:#} {y:#} {z:#} {h:#} a:(## x) b:(## y) c:(## z) d:(## h) = D x y z h; 135 | test$001 {x:#} {y:#} {z:#} {h:#} {i:#} a:(## x) b:(## y) c:(## z) d:(## h) e:(## i) = E x y z h i; 136 | 137 | 138 | test$001 {x:#} a:(## x) = F ~a x; 139 | test1$011 a:(## 32) = F ~a 0; 140 | """ 141 | add_tlb(tlb_text, globals()) 142 | 143 | A_record = A(0) # noqa 144 | B_record = B(0, 0) # noqa 145 | C_record = C(0, 0, 0) # noqa 146 | D_record = D(0, 0, 0, 0) # noqa 147 | E_record = E(0, 0, 0, 0, 0) # noqa 148 | F_record = F(0) # noqa 149 | 150 | assert A_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == A_record.Tag.test 151 | assert A_record.get_tag(CellBuilder().store_bitstring("000").begin_parse()) == A_record.Tag.test1 152 | assert A_record.get_tag(CellBuilder().store_bitstring("010").begin_parse()) == A_record.Tag.test2 153 | assert A_record.get_tag(CellBuilder().store_bitstring("110").begin_parse()) == A_record.Tag.test3 154 | assert A_record.get_tag(CellBuilder().store_bitstring("111").begin_parse()) == A_record.Tag.test4 155 | assert A_record.get_tag(CellBuilder().store_bitstring("100").begin_parse()) == A_record.Tag.test5 156 | 157 | assert B_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == B_record.Tag.test 158 | assert B_record.get_tag(CellBuilder().store_bitstring("000").begin_parse()) == B_record.Tag.test1 159 | assert B_record.get_tag(CellBuilder().store_bitstring("010").begin_parse()) == B_record.Tag.test2 160 | assert B_record.get_tag(CellBuilder().store_bitstring("110").begin_parse()) == B_record.Tag.test3 161 | assert B_record.get_tag(CellBuilder().store_bitstring("111").begin_parse()) == B_record.Tag.test4 162 | assert B_record.get_tag(CellBuilder().store_bitstring("100").begin_parse()) == B_record.Tag.test5 163 | 164 | assert C_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == C_record.Tag.test 165 | assert C_record.get_tag(CellBuilder().store_bitstring("000").begin_parse()) == C_record.Tag.test1 166 | assert C_record.get_tag(CellBuilder().store_bitstring("010").begin_parse()) == C_record.Tag.test2 167 | assert C_record.get_tag(CellBuilder().store_bitstring("110").begin_parse()) == C_record.Tag.test3 168 | assert C_record.get_tag(CellBuilder().store_bitstring("111").begin_parse()) == C_record.Tag.test4 169 | assert C_record.get_tag(CellBuilder().store_bitstring("100").begin_parse()) == C_record.Tag.test5 170 | 171 | assert D_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == D_record.Tag.test 172 | assert E_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == E_record.Tag.test 173 | 174 | assert F_record.get_tag(CellBuilder().store_bitstring("001").begin_parse()) == F_record.Tag.test 175 | assert F_record.get_tag(CellBuilder().store_bitstring("011").begin_parse()) == F_record.Tag.test1 176 | 177 | 178 | def test_enum(): 179 | # TODO: fix 180 | # language=tl-b 181 | tlb_text = """ 182 | test$_ = A; // simple Enum 183 | test$01 = B; 184 | 185 | a$0 = C; 186 | b$1 = C; 187 | 188 | a$0 = D; 189 | c$101 = D; 190 | b$11 = D; 191 | """ 192 | add_tlb(tlb_text, globals()) 193 | 194 | A_record = A() 195 | B_record = B() 196 | C_record = C() 197 | D_record = D() 198 | 199 | assert A_record.fetch_enum(CellBuilder().begin_parse()) == 0 200 | 201 | cb = CellBuilder() 202 | A_record.store_enum_from(cb) # While enum is $_ it'll do nothing 203 | assert A_record.fetch_enum(cb.begin_parse()) == cb.bits == 0 204 | 205 | assert B_record.fetch_enum(CellBuilder().store_bitstring('01').begin_parse()) == int('01', 2) 206 | 207 | cb = CellBuilder() 208 | B_record.store_enum_from(cb) # While enum is const it'll always save const 209 | assert B_record.fetch_enum(cb.begin_parse()) == int('01', 2) 210 | 211 | assert C_record.fetch_enum(CellBuilder().store_bitstring('0').begin_parse()) == int('0', 2) 212 | 213 | cb = CellBuilder() 214 | C_record.store_enum_from(cb, 0) 215 | assert C_record.fetch_enum(cb.begin_parse()) == int('0', 2) 216 | 217 | assert C_record.fetch_enum(CellBuilder().store_bitstring('1').begin_parse()) == int('1', 2) 218 | 219 | cb = CellBuilder() 220 | C_record.store_enum_from(cb, 1) 221 | assert C_record.fetch_enum(cb.begin_parse()) == int('1', 2) 222 | 223 | assert D_record.fetch_enum(CellBuilder().store_bitstring('0').begin_parse()) == int('0', 2) 224 | 225 | cb = CellBuilder() 226 | D_record.store_enum_from(cb, int('0', 2)) 227 | assert D_record.fetch_enum(cb.begin_parse()) == D_record.cons_tag[0] == int('0', 2) 228 | 229 | assert D_record.fetch_enum(CellBuilder().store_bitstring('11').begin_parse()) == int('11', 2) 230 | 231 | cb = CellBuilder() 232 | D_record.store_enum_from(cb, int('11', 2)) 233 | assert D_record.fetch_enum(cb.begin_parse()) == D_record.cons_tag[2] == int('11', 2) 234 | 235 | assert D_record.fetch_enum(CellBuilder().store_bitstring('101').begin_parse()) == int('101', 2) 236 | 237 | cb = CellBuilder() 238 | D_record.store_enum_from(cb, int('101', 2)) 239 | assert D_record.fetch_enum(cb.begin_parse()) == D_record.cons_tag[1] == int('101', 2) 240 | 241 | 242 | def test_special(): 243 | # language=tl-b 244 | tlb_text = """ 245 | !test$0 = A; 246 | test$0 = B; 247 | """ 248 | 249 | add_tlb(tlb_text, globals()) 250 | 251 | A_record = A() 252 | B_record = B() 253 | 254 | assert A_record.always_special() is True 255 | assert B_record.always_special() is False 256 | 257 | 258 | def test_records(): 259 | # language=tl-b 260 | tlb_text = """ 261 | _ {x:Type} k:# = T x; 262 | a$0 {x:Type} a:# b:(## 32) c:bits512 e:^(T uint32) f:(T x) = A x; 263 | b$1 {x:Type} e:# d:(## 32) c:bits512 b:^(T uint32) a:(T x) = A x; 264 | """ 265 | 266 | add_tlb(tlb_text, globals()) 267 | 268 | A_record = A(TLB()) 269 | T_record = T(TLB()) 270 | test_record = T_record.Record(32) 271 | assert test_record.k == 32 272 | 273 | empty_cell = CellBuilder().store_uint(0, 32).end_cell() 274 | empty_cs = CellBuilder().store_uint(0, 32).begin_parse() 275 | 276 | # Can create Record_a from params 277 | test_record = A_record.Record_a(1, 2, BitArray("0b11"), empty_cell, empty_cs) 278 | assert test_record.a == 1 279 | assert test_record.b == 2 280 | assert test_record.c.bin == '11' 281 | assert test_record.e == empty_cell 282 | assert test_record.f == empty_cs 283 | assert test_record.cell_pack().get_hash() == '47277DEFA53A72F6C47E4A92498C24B69A11A9EACA915F8654A4724227F244AE' 284 | 285 | # Can create Record_b from params 286 | test_record = A_record.Record_b(3, 4, BitArray("0b01"), empty_cell, empty_cs) 287 | assert test_record.e == 3 288 | assert test_record.d == 4 289 | assert test_record.c.bin == '01' 290 | assert test_record.b == empty_cell 291 | assert test_record.a == empty_cs 292 | assert test_record.cell_pack().get_hash() == '9E31F51F54F392AB927D2124E8209C9E1ADF2C28FB0A677DC3C82FFC790150FE' 293 | 294 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_tlb_redefine.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.tlb_gen.py import add_tlb 4 | from tonpy.types import CellBuilder 5 | 6 | 7 | def test_builtin_unpack(): 8 | # language=tl-b 9 | tlb_text = """ 10 | _ a:^(uint256) b:^(int256) c:^(## 32) = C; 11 | _ a:^Cell b:^Any c:^(bits256) d:^C = B; 12 | _ a:^# b:^(#< 5) c:^(#<= 10) d:^B = A; 13 | """ 14 | add_tlb(tlb_text, globals()) 15 | 16 | A_a = CellBuilder().store_uint(1, 32).end_cell() 17 | A_b = CellBuilder().store_uint_less(5, 2).end_cell() 18 | A_c = CellBuilder().store_uint_leq(10, 3).end_cell() 19 | 20 | B_a = CellBuilder().store_bitstring('1001').end_cell() 21 | B_b = CellBuilder().store_bitstring('0110').end_cell() 22 | B_c = CellBuilder().store_bitstring('1' * 256).end_cell() 23 | 24 | C_a = CellBuilder().store_uint(4, 256).end_cell() 25 | C_b = CellBuilder().store_int(5, 256).end_cell() 26 | C_c = CellBuilder().store_uint(6, 32).end_cell() 27 | 28 | final_C = CellBuilder().store_ref(C_a).store_ref(C_b).store_ref(C_c).end_cell() 29 | final_B = CellBuilder().store_ref(B_a).store_ref(B_b).store_ref(B_c).store_ref(final_C).end_cell() 30 | final_A = CellBuilder().store_ref(A_a).store_ref(A_b).store_ref(A_c).store_ref(final_B).end_cell() 31 | assert final_A.get_hash() == '8FC752179C5AD3870CD9A70A205548E8F7389B865316707339DFA33875F0808A' 32 | 33 | rec = A().fetch(final_A, rec_unpack=True) 34 | 35 | assert rec.a == 1 36 | assert rec.b == 2 37 | assert rec.c == 3 38 | assert rec.d.a.begin_parse().to_bitstring() == '1001' 39 | assert rec.d.b.begin_parse().to_bitstring() == '0110' 40 | assert rec.d.c == '1' * 256 41 | assert rec.d.d.a == 4 42 | assert rec.d.d.b == 5 43 | assert rec.d.d.c == 6 44 | 45 | # Pack will create the same cell 46 | assert rec.cell_pack().get_hash() == '8FC752179C5AD3870CD9A70A205548E8F7389B865316707339DFA33875F0808A' 47 | 48 | py_dict = rec.to_dict(rec_unpack=True, convert_cells_to_bocs=True) # it's json dumpable 49 | result = {"a": 1, "b": 2, "c": 3, "d": {"a": "te6ccuEBAQEAAwAGAAGYwZ06Tg==", "b": "te6ccuEBAQEAAwAGAAFotPJUvQ==", 50 | "c": "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", 51 | "d": {"a": 4, "b": 5, "c": 6}}} 52 | 53 | assert py_dict == result 54 | -------------------------------------------------------------------------------- /src/tonpy/tests/test_tvm.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.fift.fift import convert_assembler 4 | from tonpy.tvm.tvm import TVM, method_name_to_id 5 | from tonpy.types import Cell, CellSlice, CellBuilder, Stack, StackEntry, Continuation, VmDict 6 | import faulthandler 7 | 8 | faulthandler.enable() 9 | 10 | 11 | def test_simple_tvm(): 12 | # language=fift 13 | code = convert_assembler("<{ x{0123456789abcdef} PUSHSLICE SHA256U }>c") 14 | t = TVM(code=code) 15 | final_stack = t.run(unpack_stack=False) 16 | 17 | assert t.success is True 18 | assert t.exit_code == -1 19 | assert t.vm_steps == 3 20 | assert t.gas_used == 53 21 | assert t.gas_credit == 0 22 | assert t.vm_final_state_hash == '0000000000000000000000000000000000000000000000000000000000000000' # not implemented 23 | assert t.vm_init_state_hash == '0000000000000000000000000000000000000000000000000000000000000000' 24 | actions = t.c5_updated.begin_parse() 25 | assert actions.bits == 0 26 | assert actions.refs == 0 27 | 28 | assert isinstance(final_stack, Stack) 29 | assert len(final_stack) == 1 30 | assert isinstance(final_stack[0], StackEntry) 31 | assert isinstance(final_stack[0].get(), int) 32 | assert final_stack[0].get() == 38795098326322736171136434460164583034742468093563686343615948953988372535320 33 | 34 | 35 | def test_tvm_c4_c5_stack(): 36 | # language=fift 37 | code = """<{ 38 | ADD 39 | DEPTH 40 | c4 PUSH CTOS SBITREFS 41 | c5 PUSH CTOS SBITREFS 42 | PUSHREF c4 POP 43 | PUSHREF c5 POP 44 | NIL 100 PUSHINT TPUSH 200 PUSHINT TPUSH 45 | 123 PUSHINT 46 | }>c""" 47 | 48 | t = TVM(code=convert_assembler(code), 49 | data=CellBuilder().store_ref(CellBuilder().end_cell()).store_uint(10, 64).end_cell()) 50 | t.set_stack(Stack([20, 2])) 51 | final_stack = t.run(unpack_stack=False) 52 | final_stack = StackEntry.rec_get([i.get() for i in final_stack]) 53 | assert final_stack == [22, 1, 64, 1, 0, 0, [100, 200], 123] 54 | assert t.c4_updated.begin_parse().to_bitstring() == bin(0x99991111)[2:] 55 | assert t.c5_updated.begin_parse().to_bitstring() == bin(0xaaaabbbb)[2:] 56 | 57 | 58 | def test_tvm_c7(): 59 | code = """<{ NOW BLOCKLT LTIME RANDSEED BALANCE }>c""" 60 | t = TVM(code=convert_assembler(code)) 61 | t.set_c7([ 62 | None, 63 | None, 64 | 123, 65 | 321, 66 | 999, 67 | 0x123, 68 | [50000, CellBuilder().end_cell()], 69 | CellBuilder().end_cell().begin_parse()]) 70 | 71 | final_stack = t.run(True) 72 | assert t.success is True 73 | assert final_stack[0] == 321 74 | assert final_stack[1] == 999 75 | assert final_stack[2] == 291 76 | assert final_stack[3][0] == 50000 77 | assert final_stack[3][1].begin_parse().bits == 0 and final_stack[3][1].begin_parse().refs == 0 78 | assert final_stack[4].bits == 0 and final_stack[4].refs == 0 79 | 80 | 81 | def test_tvm_continuation(): 82 | # language=fift 83 | code = """<{ BLESS CONT:<{ 2 INT }> }>c""" 84 | t = TVM(code=convert_assembler(code)) 85 | 86 | # language=fift 87 | t.set_stack([convert_assembler("""<{ 228 PUSHINT }>s""")]) 88 | final_stack = t.run(True) 89 | 90 | assert isinstance(final_stack[0], Continuation) 91 | assert isinstance(final_stack[1], Continuation) 92 | 93 | t = TVM(code=convert_assembler(code)) 94 | # convert continuation to cellslice and run again 95 | t.set_stack([final_stack[1].serialize().begin_parse()]) 96 | 97 | final_stack = t.run(True) 98 | assert isinstance(final_stack[0], Continuation) 99 | assert isinstance(final_stack[1], Continuation) 100 | 101 | 102 | def test_tvm_step_info(): 103 | # language=fift 104 | code = """<{ BLESS 2 INT }>c""" 105 | t = TVM(code=convert_assembler(code)) 106 | # language=fift 107 | t.set_stack([convert_assembler("""<{ 228 PUSHINT }>s""")]) 108 | t.run() 109 | 110 | info = t.vm_steps_detailed 111 | assert len(info) == 3 112 | 113 | step_0 = info[0] 114 | assert step_0.next_op == 'execute BLESS\n' 115 | assert len(step_0.stack) == 1 116 | assert step_0.stack[0].get().get_hash() == '961254B41350A116E5DC3166307071F29DA1F3A286A144350289ACBE1A64C459' 117 | assert step_0.gas_consumed == 0 118 | assert step_0.gas_remaining == 9223372036854775807 119 | 120 | step_1 = info[1] 121 | assert step_1.next_op == 'execute PUSHINT 2\n' 122 | assert len(step_1.stack) == 1 123 | assert isinstance(step_1.stack[0].get(), Continuation) 124 | assert step_1.gas_consumed == 26 125 | assert step_1.gas_remaining == 9223372036854775781 126 | 127 | 128 | def test_tvm_set_libs(): 129 | cell_code = convert_assembler("""<{ 228 PUSHINT }>c""") 130 | code_hash = int(cell_code.get_hash(), 16) 131 | 132 | # language=fift 133 | code = """<{ CTOS BLESS EXECUTE }>c""" 134 | t = TVM(code=convert_assembler(code)) 135 | 136 | lib_cell = CellBuilder() \ 137 | .store_uint(CellSlice.SpecialType.Library.value, 8) \ 138 | .store_uint(code_hash, 256) \ 139 | .end_cell(special=True) 140 | 141 | t.set_stack([lib_cell]) 142 | 143 | libs = VmDict(256) 144 | libs[code_hash] = cell_code 145 | t.set_libs(libs) 146 | 147 | final_stack = t.run(unpack_stack=False) 148 | assert len(t.vm_steps_detailed) == 6 149 | assert len(final_stack) == 1 150 | assert final_stack[0].get() == 228 151 | 152 | 153 | def test_method_name_to_id(): 154 | assert method_name_to_id('get_sale_data') == 72748 155 | 156 | 157 | def test_set_gas_limit(): 158 | cell_code = convert_assembler("""<{ 228 PUSHINT }>c""") 159 | t = TVM(code=cell_code) 160 | t.set_gas_limit(0) 161 | t.run(allow_non_success=True) 162 | assert t.exit_code == 13 # out of gas 163 | 164 | if __name__ == "__main__": 165 | test_set_gas_limit() -------------------------------------------------------------------------------- /src/tonpy/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | import sys 5 | from random import random 6 | from tonpy.utils.address_packer import pack_address 7 | 8 | path_root = Path(__file__).parents[2] 9 | sys.path.append(str(path_root)) 10 | 11 | from tonpy.utils.bit_converter import bitstring_to_utf8 12 | 13 | 14 | def test_bitstring_to_utf8(): 15 | bitstring = "011000010110001001100011" 16 | assert bitstring_to_utf8(bitstring) == 'abc' 17 | 18 | 19 | def test_pack_address(): 20 | cs = pack_address("EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj8jPWRA") 21 | assert cs.get_hash() == '94804DD004BE8628522901CF117E4B03CF69E78FC0A9E92BC578BCCB1B3FD0D2' 22 | 23 | from tonpy.autogen.block import MsgAddress 24 | loaded = MsgAddress().fetch(cs) 25 | address = loaded.to_dict(rec_unpack=True) 26 | assert address['x']['workchain_id'] == 0 27 | assert hex(int(address['x']['address'], 2))[2:].zfill( 28 | 64).upper() == 'EB2EAF97EA32993470127208218748758A88374AD2BBD739FC75C9AB3A3F233D' 29 | -------------------------------------------------------------------------------- /src/tonpy/tlb_gen/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.tlb_gen.py import add_tlb, parse_tlb, process_file 4 | -------------------------------------------------------------------------------- /src/tonpy/tlb_gen/py.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from pathlib import Path 4 | 5 | from tonpy.libs.python_ton import codegen_python_tlb 6 | from enum import Enum 7 | from tonpy.types import * 8 | from typing import Union, Optional 9 | from loguru import logger 10 | 11 | 12 | def parse_tlb(tlb_text: str) -> str: 13 | """ 14 | Parse TLB and generate Python Class objects for tlb 15 | 16 | :param tlb_text: TLB code 17 | :return: Python code that have been generated 18 | """ 19 | 20 | code = codegen_python_tlb(tlb_text) 21 | 22 | return code 23 | 24 | 25 | def add_tlb(tlb_text: str, 26 | imported_globals: globals): 27 | """ 28 | Parse & Run generated python code, so after that you can operate with new loaded class objects 29 | 30 | :param tlb_text: TLB code 31 | :return: 32 | """ 33 | 34 | tlb_text = parse_tlb(tlb_text).split("# definitions of constants") 35 | constants, tlb_text = tlb_text[1], tlb_text[0] 36 | 37 | # Debug 38 | tlb_text_t = tlb_text.split("\n") 39 | for i, j in zip(range(len(tlb_text_t)), tlb_text_t): 40 | print(i, ": ", j) 41 | 42 | exec(tlb_text, globals(), locals()) 43 | 44 | # Write new classes to imported globals 45 | for i in locals()['tlb_classes']: 46 | print("Found class: ", i) 47 | imported_globals[i] = locals()[i] 48 | globals()[i] = locals()[i] 49 | 50 | exec(constants, globals(), locals()) 51 | 52 | 53 | def process_file(filepath: str, out_path: str = None) -> None: 54 | """ 55 | Parse tlb file and convert it to ``.py`` file with codegened code 56 | 57 | :param filepath: Path to ``.tlb`` file to convert to 58 | :return: 59 | """ 60 | 61 | file_path = Path(filepath) 62 | 63 | with open(file_path) as f: 64 | data = f.read() 65 | tlb_code = parse_tlb(data) 66 | 67 | if out_path is None: 68 | new_path = Path(filepath.replace(file_path.name, file_path.name[:-4] + '.py')) 69 | else: 70 | new_path = out_path 71 | 72 | logger.warning(f"New path for TLB gen: {new_path}") 73 | with open(new_path, 'w') as fw: 74 | fw.write(tlb_code) 75 | -------------------------------------------------------------------------------- /src/tonpy/tvm/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.tvm.tvm import TVM, method_name_to_id, C7 4 | from tonpy.tvm.emulator import Emulator 5 | -------------------------------------------------------------------------------- /src/tonpy/tvm/emulator.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import PyEmulator 4 | from tonpy.types import VmDict, Cell, CellSlice 5 | from typing import Union 6 | 7 | 8 | class Emulator: 9 | def __init__(self, config: Union[Cell, VmDict]): 10 | if isinstance(config, VmDict): 11 | config = config.get_cell().cell 12 | else: 13 | config = config.cell 14 | 15 | self.emulator: PyEmulator = PyEmulator(config) 16 | 17 | def emulate_transaction(self, shard_account: Cell, message: Cell, unixtime: int, lt: int) -> bool: 18 | return self.emulator.emulate_transaction(shard_account.cell, message.cell, str(unixtime), str(lt), 19 | 1 if lt >= 3709412000000 else 0) 20 | 21 | def emulate_tick_tock_transaction(self, shard_account: Cell, is_tock: bool, unixtime: int, lt: int) -> bool: 22 | return self.emulator.emulate_tick_tock_transaction(shard_account.cell, is_tock, str(unixtime), str(lt), 23 | 1 if lt >= 3709412000000 else 0) 24 | 25 | def set_rand_seed(self, seed: Union[int, hex]) -> None: 26 | if isinstance(seed, int): 27 | seed = hex(seed)[2:].upper() 28 | 29 | self.emulator.set_rand_seed(seed) 30 | 31 | def set_ignore_chksig(self, flag: bool) -> None: 32 | self.emulator.set_ignore_chksig(flag) 33 | 34 | def set_libs(self, libs: VmDict) -> None: 35 | self.emulator.set_libs(libs.get_cell()) 36 | 37 | def set_debug_enabled(self, flag: bool) -> None: 38 | self.emulator.set_debug_enabled(flag) 39 | 40 | @property 41 | def elapsed_time(self) -> int: 42 | return self.emulator.elapsed_time 43 | 44 | @property 45 | def transaction(self, as_cs=True) -> Union[Cell, CellSlice]: 46 | c = Cell(self.emulator.transaction_cell) 47 | 48 | if as_cs: 49 | return c.begin_parse() 50 | else: 51 | return c 52 | 53 | @property 54 | def account(self, as_cs=True) -> Union[Cell, CellSlice]: 55 | c = Cell(self.emulator.account_cell) 56 | 57 | if as_cs: 58 | return c.begin_parse() 59 | else: 60 | return c 61 | 62 | @property 63 | def actions(self, as_cs=True) -> Union[Cell, CellSlice]: 64 | c = Cell(self.emulator.actions_cell) 65 | 66 | if as_cs: 67 | return c.begin_parse() 68 | else: 69 | return c 70 | -------------------------------------------------------------------------------- /src/tonpy/tvm/tvm.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from typing import Union, List, Optional 4 | 5 | from tonpy.libs.python_ton import PyTVM, method_name_to_id as py_method_name_to_id 6 | from tonpy.types import Cell, Stack, StackEntry, VmDict, begin_cell 7 | from datetime import datetime 8 | 9 | 10 | def method_name_to_id(method_name: str): 11 | """Compute crc for method name, to pass to TVM""" 12 | 13 | return py_method_name_to_id(method_name) 14 | 15 | 16 | class C7: 17 | def __init__(self, magic: int = 0x076ef1ea, actions: int = 0, msgs_sent: int = 0, 18 | time: Union[int, datetime] = None, block_lt: int = 0, trans_lt: int = 0, 19 | rand_seed: Union[int, str] = None, balance_grams: int = 0, balance_extra: Cell = None, 20 | address: Union[dict, Cell] = None, global_config: Cell = None): 21 | self.magic = magic 22 | self.actions = actions 23 | self.msgs_sent = msgs_sent 24 | 25 | if time is None: 26 | time = datetime.now() 27 | 28 | if isinstance(time, datetime): 29 | self.time = int(time.timestamp()) 30 | else: 31 | self.time = time 32 | 33 | self.block_lt = block_lt 34 | self.trans_lt = trans_lt 35 | 36 | if isinstance(rand_seed, str): 37 | self.rand_seed = int(rand_seed, 16) 38 | else: 39 | self.rand_seed = rand_seed 40 | 41 | self.balance_grams = balance_grams 42 | self.balance_extra = balance_extra if balance_extra is not None else begin_cell().end_cell() 43 | 44 | if isinstance(address, Cell): 45 | self.address = address 46 | elif address is None: 47 | self.address = begin_cell().end_cell() 48 | else: 49 | self.address = begin_cell().store_bitstring("100") \ 50 | .store_int(address['workchain'], 8) \ 51 | .store_uint(int(address['address'], 16), 256) 52 | self.global_config = global_config 53 | 54 | def to_data(self): 55 | return [ 56 | self.magic, # [ magic:0x076ef1ea 57 | self.actions, # actions:Integer 58 | self.msgs_sent, # msgs_sent:Integer 59 | self.time, # unixtime:Integer 60 | self.block_lt, # block_lt:Integer 61 | self.trans_lt, # trans_lt:Integer 62 | self.rand_seed, # rand_seed:Integer 63 | [self.balance_grams, self.balance_extra], # balance_remaining:[Integer (Maybe Cell)] 64 | self.address, # myself:MsgAddressInt 65 | self.global_config # global_config:(Maybe Cell) 66 | ] 67 | 68 | 69 | class StepInfo: 70 | stack: Stack = None 71 | gas_consumed: int = None 72 | gas_remaining: int = None 73 | next_op: str = None 74 | 75 | def __init__(self, stack_info): 76 | self.stack = Stack(prev_stack=stack_info.stack) 77 | self.gas_consumed = int(stack_info.gas_consumed) 78 | self.gas_remaining = int(stack_info.gas_remaining) 79 | self.next_op = None 80 | 81 | 82 | class TVM: 83 | def __init__(self, log_level: int = 0, 84 | code: Cell = None, 85 | data: Cell = None, 86 | allow_debug: bool = False, 87 | same_c3: bool = True, 88 | skip_c7: bool = False, 89 | enable_stack_dump=True): 90 | self.tvm = PyTVM(log_level, 91 | code.cell if code is not None else code, 92 | data.cell if data is not None else data, 93 | allow_debug, 94 | same_c3, 95 | skip_c7, 96 | enable_stack_dump) 97 | self.vm_steps_detailed: Optional[List[StepInfo]] = None 98 | self.enable_stack_dump = enable_stack_dump 99 | 100 | def set_stack(self, value: Union[Stack, List]) -> None: 101 | if isinstance(value, list): 102 | self.tvm.set_stack(Stack(list(reversed(value))).stack) 103 | else: 104 | self.tvm.set_stack(value.stack) 105 | 106 | def set_c7(self, value: Union[Union[StackEntry, List], C7]) -> None: 107 | 108 | if isinstance(value, list): 109 | self.tvm.set_c7(StackEntry(value=value).entry) 110 | elif isinstance(value, C7): 111 | self.tvm.set_c7(StackEntry(value=value.to_data()).entry) 112 | else: 113 | assert value.get_type() is StackEntry.Type.t_tuple, "C7 must be tuple" 114 | self.tvm.set_c7(value) 115 | 116 | def set_state_init(self, state_init: Cell) -> bool: 117 | return self.tvm.set_state_init(state_init) 118 | 119 | def set_gas_limit(self, gas_limit=0, gas_max=-1) -> bool: 120 | return self.tvm.set_gasLimit(str(gas_limit), str(gas_max)) 121 | 122 | def clear_stack(self) -> bool: 123 | return self.tvm.clear_stack() 124 | 125 | def fetch_detailed_step_info(self) -> None: 126 | self.vm_steps_detailed = [StepInfo(i) for i in self.tvm.get_stacks()] 127 | ops = self.tvm.get_ops() 128 | assert len(ops) == len(self.vm_steps_detailed) 129 | for i, op in enumerate(ops): 130 | self.vm_steps_detailed[i].next_op = op 131 | 132 | def run(self, unpack_stack=True, allow_non_success=False) -> Union[Stack, List]: 133 | st = Stack(prev_stack=self.tvm.run_vm()) 134 | 135 | if allow_non_success is False: 136 | assert self.exit_code in [-1, 0], f"TVM run failed with exit code: {self.exit_code}" 137 | 138 | if self.enable_stack_dump: 139 | self.fetch_detailed_step_info() 140 | 141 | if not unpack_stack: 142 | return st 143 | return st.unpack_rec() 144 | 145 | def set_libs(self, libs: VmDict) -> None: 146 | self.tvm.set_libs(libs.get_cell().cell) 147 | 148 | @property 149 | def c5_updated(self) -> Cell: 150 | return Cell(self.tvm.actions) 151 | 152 | @property 153 | def c4_updated(self) -> Cell: 154 | return Cell(self.tvm.new_data) 155 | 156 | @property 157 | def vm_final_state_hash(self) -> str: 158 | return self.tvm.vm_final_state_hash 159 | 160 | @property 161 | def vm_init_state_hash(self) -> str: 162 | return self.tvm.vm_init_state_hash 163 | 164 | @property 165 | def success(self) -> bool: 166 | return self.tvm.success 167 | 168 | @property 169 | def gas_credit(self) -> int: 170 | return self.tvm.gas_credit 171 | 172 | @property 173 | def gas_used(self) -> int: 174 | return self.tvm.gas_used 175 | 176 | @property 177 | def exit_code(self) -> int: 178 | return self.tvm.exit_code 179 | 180 | @property 181 | def vm_steps(self) -> int: 182 | return self.tvm.vm_steps 183 | 184 | @property 185 | def code(self) -> Cell: 186 | return Cell(self.tvm.code) 187 | 188 | @code.setter 189 | def code(self, value): 190 | self.tvm.code = value 191 | 192 | @property 193 | def data(self) -> Cell: 194 | return Cell(self.tvm.data) 195 | 196 | @data.setter 197 | def data(self, value): 198 | self.tvm.data = value 199 | -------------------------------------------------------------------------------- /src/tonpy/types/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.cell import Cell 4 | from tonpy.types.cellslice import CellSlice 5 | from tonpy.types.cellbuilder import CellBuilder 6 | from tonpy.types.vmdict import VmDict, TypedVmDict, TypedDataWithExtra, DataWithExtra, AugmentedData, TypedAugmentedData 7 | from tonpy.types.tlb import TLB, RecordBase 8 | from tonpy.types.tlb_types import RefT, NatWidth, TLBComplex, Int, UInt, Bits, NatLeq, NatLess 9 | from tonpy.types.stack import StackEntry, Stack, Continuation 10 | 11 | 12 | def begin_cell(): 13 | return CellBuilder() 14 | -------------------------------------------------------------------------------- /src/tonpy/types/address.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import PySmcAddress, address_from_string, address_from_cell_slice 4 | from typing import Union, TYPE_CHECKING 5 | 6 | from tonpy.types.cellslice import CellSlice 7 | 8 | if TYPE_CHECKING: 9 | from tonpy.types.cellbuilder import CellBuilder 10 | 11 | 12 | class Address: 13 | def __init__(self, value: Union[str, "CellSlice"]): 14 | """Class allow you to interact with TON SmartContract address""" 15 | if isinstance(value, str): 16 | self.my_address: PySmcAddress = address_from_string(value) 17 | else: 18 | self.my_address: PySmcAddress = address_from_cell_slice(value) 19 | 20 | def pack(self) -> "CellSlice": 21 | return CellSlice(self.my_address.pack()) 22 | 23 | @property 24 | def wc(self) -> int: 25 | return self.my_address.wc 26 | 27 | @wc.setter 28 | def wc(self, wc: int): 29 | self.my_address.wc = wc 30 | 31 | @property 32 | def address(self) -> str: 33 | "HEX encoded address" 34 | return self.my_address.address() 35 | 36 | @property 37 | def bounceable(self) -> bool: 38 | return self.my_address.bounceable 39 | 40 | @bounceable.setter 41 | def bounceable(self, flag: bool): 42 | self.my_address.bounceable = flag 43 | 44 | @property 45 | def testnet(self) -> bool: 46 | return self.my_address.testnet 47 | 48 | @testnet.setter 49 | def testnet(self, flag: bool): 50 | self.my_address.testnet = flag 51 | 52 | def serialize(self, base64_url: bool = True): 53 | return self.my_address.rserialize(base64_url) 54 | 55 | def append_to_builder(self, cb: "CellBuilder"): 56 | assert self.my_address.append_to_builder(cb.builder) 57 | 58 | def __eq__(self, other): 59 | # TODO: made this work 60 | raise NotImplementedError 61 | # return self.my_address == other.my_address 62 | 63 | def __getstate__(self): 64 | return self.serialize() 65 | 66 | def __setstate__(self, value): 67 | self.my_address: PySmcAddress = address_from_string(value) 68 | 69 | def __str__(self): 70 | return f'
' 71 | -------------------------------------------------------------------------------- /src/tonpy/types/cell.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import PyCell, parse_string_to_cell, load_as_cell_slice 4 | from typing import Union 5 | 6 | from tonpy.types.cellslice import CellSlice 7 | 8 | 9 | class Cell: 10 | def __init__(self, pc: Union[PyCell, str] = None): 11 | """ 12 | Cell is simple data structure of TON with up to 1023 bits and 4 refs to other cells |br| 13 | 14 | :param pc: PyCell (c++) or BOC str 15 | """ 16 | if pc is None: 17 | self.cell: PyCell = PyCell() 18 | elif isinstance(pc, str): 19 | self.cell: PyCell = parse_string_to_cell(pc) 20 | else: 21 | self.cell: PyCell = pc 22 | 23 | def to_boc(self) -> str: 24 | """Convert cell to BOC string""" 25 | 26 | return self.cell.to_boc() 27 | 28 | def get_hash(self) -> str: 29 | """Get cell hash""" 30 | 31 | return self.cell.get_hash() 32 | 33 | def begin_parse(self, allow_special=True) -> CellSlice: 34 | """Convert cell to CellSlice""" 35 | 36 | return CellSlice(load_as_cell_slice(self.cell, allow_special)) 37 | 38 | def is_null(self) -> bool: 39 | """Some cells are nulls, you can't operate with such ones""" 40 | 41 | return self.cell.is_null() 42 | 43 | def dump(self): 44 | """Recursively dump all cells as hex""" 45 | 46 | return self.cell.dump() 47 | 48 | def dump_as_tlb(self, tlb: str) -> str: 49 | """Dump as C++ PrettyPrint parsed by TLB type""" 50 | 51 | return self.cell.dump_as_tlb(tlb) 52 | 53 | def copy(self) -> PyCell: 54 | """Copy current Cell""" 55 | 56 | return Cell(self.cell.copy()) 57 | 58 | def __getstate__(self): 59 | return self.to_boc() 60 | 61 | def __setstate__(self, boc): 62 | self.cell: PyCell = parse_string_to_cell(boc) 63 | 64 | def __repr__(self): 65 | cs = self.begin_parse() 66 | b = cs.bits 67 | r = cs.refs 68 | del cs 69 | 70 | return f"" 71 | -------------------------------------------------------------------------------- /src/tonpy/types/stack.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import PyStackEntry, PyStack, make_tuple, deserialize_stack_entry, deserialize_stack, \ 4 | PyContinuation 5 | from tonpy.types import Cell, CellSlice, CellBuilder 6 | from typing import Union, Iterable, List 7 | from enum import Enum 8 | 9 | 10 | class Continuation: 11 | def __init__(self, cont: Union[CellSlice, PyContinuation]): 12 | if isinstance(cont, PyContinuation): 13 | self.cont = cont 14 | else: 15 | self.cont = PyContinuation(cont) 16 | 17 | def type(self) -> str: 18 | return self.cont.type() 19 | 20 | def serialize(self) -> Cell: 21 | return Cell(self.cont.serialize()) 22 | 23 | 24 | class StackEntry: 25 | class Type(Enum): 26 | """Possible types of StackEntry""" 27 | t_null = 0 28 | t_int = 1 29 | t_cell = 2 30 | t_builder = 3 31 | t_slice = 4 32 | t_vmcont = 5 33 | t_tuple = 6 34 | t_stack = 7 35 | t_string = 8 36 | t_bytes = 9 37 | t_bitstring = 10 38 | t_box = 11 39 | t_atom = 12 40 | t_object = 13 41 | 42 | def __init__(self, value: "None | Cell | CellSlice | int | CellBuilder | list | Continuation" = None, 43 | entry=None): 44 | if entry: 45 | self.entry = entry 46 | return 47 | 48 | if value is None: 49 | self.entry = PyStackEntry() 50 | elif isinstance(value, int): 51 | self.entry = PyStackEntry(big_int=str(value)) 52 | elif isinstance(value, Cell): 53 | self.entry = PyStackEntry(cell=value.cell) 54 | elif isinstance(value, CellBuilder): 55 | cs = value.end_cell().begin_parse(True) 56 | self.entry = PyStackEntry(cell_builder=cs.cell_slice) 57 | elif isinstance(value, CellSlice): 58 | self.entry = PyStackEntry(cell_slice=value.cell_slice) 59 | elif isinstance(value, list): 60 | self.entry = StackEntry.create_tuple(value).entry 61 | elif isinstance(value, Continuation): 62 | self.entry = PyStackEntry(continuation=value) 63 | else: 64 | raise ValueError(f"Type {type(value)} is not supported") 65 | 66 | def get_type(self): 67 | """Get type of stack entry""" 68 | return StackEntry.Type(self.entry.type()) 69 | 70 | def as_cell(self): 71 | return Cell(self.entry.as_cell()) 72 | 73 | def as_cell_slice(self): 74 | return CellSlice(self.entry.as_cell_slice()) 75 | 76 | def as_int(self): 77 | return int(self.entry.as_int()) 78 | 79 | def as_cont(self): 80 | return Continuation(self.entry.as_cont()) 81 | 82 | def as_str(self): 83 | return str(self.entry.as_string()) 84 | 85 | def as_tuple(self) -> List["StackEntry"]: 86 | return list(map(lambda x: StackEntry(entry=x), self.entry.as_tuple())) 87 | 88 | def as_cell_builder(self): 89 | return CellBuilder(self.entry.as_cell_builder()) 90 | 91 | def serialize(self, short_ints=True, continuations=True) -> Cell: 92 | mode = 0 93 | if short_ints is False: 94 | mode += 1 95 | 96 | if continuations is False: 97 | mode += 2 98 | 99 | # mode: +1 = disable short ints, +2 = disable continuations 100 | return Cell(self.entry.serialize(mode)) 101 | 102 | @staticmethod 103 | def create_tuple(items: Iterable) -> PyStackEntry: 104 | def __convert(value): 105 | if isinstance(value, StackEntry): 106 | return value.entry 107 | else: 108 | return StackEntry(value=value).entry 109 | 110 | return StackEntry(entry=make_tuple(list(map(__convert, items)))) 111 | 112 | def get(self): 113 | """Convert stack entry to type""" 114 | 115 | t = self.get_type() 116 | 117 | if t is StackEntry.Type.t_null: 118 | return None 119 | elif t is StackEntry.Type.t_cell: 120 | return self.as_cell() 121 | elif t is StackEntry.Type.t_slice: 122 | return self.as_cell_slice() 123 | elif t is StackEntry.Type.t_int: 124 | return self.as_int() 125 | elif t is StackEntry.Type.t_builder: 126 | return self.as_cell_builder() 127 | elif t is StackEntry.Type.t_tuple: 128 | return self.as_tuple() 129 | elif t is StackEntry.Type.t_vmcont: 130 | return self.as_cont() 131 | elif t is StackEntry.Type.t_string: 132 | return self.as_str() 133 | else: 134 | raise ValueError(f"Not supported {t}") 135 | 136 | @staticmethod 137 | def rec_get(value): 138 | if isinstance(value, list): 139 | out = [] 140 | 141 | for item in value: 142 | out.append(StackEntry.rec_get(item)) 143 | return out 144 | elif isinstance(value, StackEntry): 145 | return StackEntry.rec_get(value.get()) 146 | else: 147 | return value 148 | 149 | @staticmethod 150 | def deserialize(value: CellSlice) -> "StackEntry": 151 | return StackEntry(entry=deserialize_stack_entry(value.cell_slice)) 152 | 153 | 154 | class Stack: 155 | def __init__(self, values_list: Iterable = None, prev_stack: PyStack = None): 156 | """List of StackEntry""" 157 | if prev_stack is None: 158 | self.stack = PyStack() 159 | else: 160 | self.stack = prev_stack 161 | 162 | if values_list is not None: 163 | for item in reversed(values_list): 164 | self.append(item) 165 | 166 | def __len__(self): 167 | return self.stack.depth() 168 | 169 | def __getitem__(self, item) -> StackEntry: 170 | assert isinstance(item, int) 171 | assert item <= len(self) 172 | return StackEntry(entry=self.stack.at(item)) 173 | 174 | def __iter__(self): 175 | total = len(self) 176 | 177 | while total > 0: 178 | total -= 1 179 | yield self[total] 180 | 181 | def __reversed__(self): 182 | total = len(self) 183 | cur = 0 184 | while cur != total: 185 | yield self[cur] 186 | cur += 1 187 | 188 | def append(self, value: Union[Union[Union[Union[Union[None, Cell], CellSlice], int], StackEntry], CellBuilder]): 189 | if isinstance(value, StackEntry): 190 | self.stack.push(value.entry) 191 | else: 192 | se = StackEntry(value=value) 193 | self.stack.push(se.entry) 194 | 195 | def serialize(self, eoln=False, lisp_stype=False, serialized_bocs=False) -> Cell: 196 | mode = 0 197 | if eoln: 198 | mode += 1 199 | 200 | if lisp_stype: 201 | mode += 2 202 | 203 | if serialized_bocs: 204 | mode += 4 205 | 206 | # mode: +1 = add eoln, +2 = Lisp-style lists, +4 = serialized bocs 207 | return Cell(self.stack.serialize(mode)) 208 | 209 | def pop(self) -> StackEntry: 210 | return StackEntry(entry=self.stack.pop()) 211 | 212 | @staticmethod 213 | def deserialize(value: CellSlice) -> "Stack": 214 | return Stack(prev_stack=deserialize_stack(value.cell_slice)) 215 | 216 | def unpack_rec(self): 217 | return StackEntry.rec_get([i.get() for i in self]) 218 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from collections import defaultdict 4 | from enum import Enum 5 | from typing import Optional, List, Union, Callable 6 | 7 | from tonpy.types import CellSlice, CellBuilder, Cell 8 | 9 | 10 | def rec_dump(item): 11 | output = {} 12 | for name in item.field_names: 13 | value = getattr(item, name) 14 | if issubclass(type(value), RecordBase): 15 | output[name] = rec_dump(value) 16 | else: 17 | output[name] = value 18 | return output 19 | 20 | 21 | class RecordBase: 22 | """ 23 | Each TLB type have ``Record`` subclass means instance of TLB type |br| 24 | If you have multiple constructors name of ``Record`` class will be change to ``Record_{{constructor_name}}`` |br| 25 | Each record have ``__init__`` function witch contains all fields of TLB type |br| 26 | Check out ``test_tlb.py`` ``test_records`` function 27 | """ 28 | 29 | def __init__(self): 30 | self.field_names = [] # names of all fetched fields 31 | self.conditional_fields = [] # names of all fields that are conditional 32 | self.negate_params = [] # params that determinate on serialization process 33 | 34 | def get_tag_enum(self): 35 | """Get current TLB.Record constructor tag in ``Enum`` type of ``TLB.Tag``""" 36 | raise NotImplementedError 37 | 38 | def get_tag(self): 39 | """Get current TLB.Record constructor tag""" 40 | raise NotImplementedError 41 | 42 | def get_tag_len(self): 43 | """Get length of bits for current TLB.Record constructor tag""" 44 | raise NotImplementedError 45 | 46 | def get_type_class(self): 47 | """Get TLB type of current record""" 48 | raise NotImplementedError 49 | 50 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, 51 | strict: bool = True) -> bool: 52 | """ 53 | Unpack current CellSlice as TLB.Record to fields, if success return True |br| 54 | All field values store in class object. If ``rec_unpack`` is True - unpack all types with recursion |br| 55 | Note: simple types that contains only from tags don't need ``rec_unpack``, they will be fetched immediately |br| 56 | 57 | :param cs: CellSlice to be fetched as TLB.Record 58 | :param rec_unpack: Need to unpack all types with recursion 59 | :param strict: If False some failed to parse subtypes can be None 60 | :return: Is unpack was success 61 | """ 62 | raise NotImplementedError 63 | 64 | def cell_unpack(self, cell_ref: Cell, rec_unpack: bool = False, 65 | strict: bool = True) -> bool: 66 | """ 67 | Unpack current Cell as TLB.Record to fields, if success return True |br| 68 | All field values store in class object. If ``rec_unpack`` is True - unpack all types with recursion |br| 69 | Note: simple types that contains only from tags don't need ``rec_unpack``, they will be fetched immediately |br| 70 | If after unpack Cell contains some data - return False |br| 71 | 72 | :param cell_ref: Cell to be fetched as TLB.Record 73 | :param rec_unpack: Need to unpack all types with recursion 74 | :param strict: If False some failed to parse subtypes can be None 75 | :return: Is unpack was success 76 | """ 77 | raise NotImplementedError 78 | 79 | def pack(self, cb: CellBuilder) -> None: 80 | raise NotImplementedError 81 | 82 | def cell_pack(self) -> Cell: 83 | raise NotImplementedError 84 | 85 | def add_r1(self, val: str, y: int, z: int) -> bool: 86 | if y > z: 87 | return False 88 | 89 | x = z - y 90 | setattr(self, val, x) 91 | return x >= 0 92 | 93 | def mul_r1(self, val: str, y: int, z: int) -> bool: 94 | if not y or z % y != 0: 95 | return False 96 | 97 | x = z / y 98 | setattr(self, val, x) 99 | return x >= 0 100 | 101 | def store_from(self, cb, value): 102 | """Recursively pack TLB type to CellBuilder""" 103 | cb.store_slice_or_tlb(value) 104 | 105 | def dump(self): 106 | """Recursively convert TLB to dict""" 107 | return rec_dump(self) 108 | 109 | def to_dict(self, rec_unpack=False, convert_cells_to_bocs=False): 110 | answer = {} 111 | 112 | for field in self.field_names: 113 | value = getattr(self, field) 114 | 115 | if rec_unpack and issubclass(type(value), RecordBase): 116 | answer[field] = value.to_dict(rec_unpack=rec_unpack, convert_cells_to_bocs=convert_cells_to_bocs) 117 | else: 118 | if convert_cells_to_bocs and isinstance(value, (Cell, CellSlice, CellBuilder)): 119 | value = value.to_boc() 120 | 121 | answer[field] = value 122 | return answer 123 | 124 | 125 | class TLB(object): 126 | class Tag(Enum): 127 | """ 128 | Contractor tags enums stored as lexicographic order |br| 129 | 130 | .. code-block:: 131 | 132 | a$0 = A; 133 | b$10 = A; 134 | c$1 = A; 135 | 136 | Means: |br| 137 | 138 | .. code-block:: Python 139 | 140 | class Tag(Enum): 141 | a = 0 142 | b = 1 143 | c = 2 144 | 145 | cons_len = [1, 2, 1] 146 | cons_tag = [0, 2, 1] 147 | """ 148 | 149 | # raise NotImplemented 150 | pass 151 | 152 | class Record(RecordBase): 153 | pass 154 | 155 | cons_len: Union[List[int], int] = None 156 | cons_tag: List[int] = None 157 | tag_to_class = {} 158 | original_cell: Optional[Cell] = None 159 | original_cell_slice: Optional[CellSlice] = None 160 | params_attrs = [] # all params defined for TLB type from __init__ 161 | has_params = False # is type is parametrized 162 | 163 | def get_tag(self, cs: CellSlice) -> Optional["TLB.Tag"]: 164 | """ 165 | Fetch tag from CellSlice ``cs`` and return ``TLB.Tag`` enum |br| 166 | :param cs: CellSlice to fetch tag from 167 | :return: ``TLB.Tag`` enum 168 | """ 169 | raise NotImplemented 170 | 171 | def fetch_enum(self, cs: CellSlice) -> int: 172 | """ 173 | Fetch enum tag value from ``CellSlice`` of type ``TLB`` |br| 174 | 175 | :param cs: CellSlice to fetch enum tag value from 176 | :return: Enum tag value of type ``TLB`` store in ``cs: CellSlice`` 177 | """ 178 | 179 | raise NotImplementedError 180 | 181 | def store_enum_from(self, cb: CellBuilder, value: int = None) -> bool: 182 | """ 183 | Store enum ``value`` from ``self.cons_tag`` to `cb: CellBuilder` |br| 184 | If ``self.const_tag`` is exact (tags are matched positions in lexicographic order) then will store ``value`` |br| 185 | If ``value is None`` and ``TLB.Tag is constant`` will store constant ``TLB.Tag`` else will raise an error |br| 186 | 187 | :param cb: CellBuilder to store enum to 188 | :param value: Value or enum position to store enum from 189 | :return: True 190 | """ 191 | raise NotImplementedError 192 | 193 | def store_from(self, cb, value): 194 | cb.store_slice_or_tlb(value) 195 | 196 | def always_special(self) -> bool: 197 | """Is current type marked as special cell or not""" 198 | return False 199 | 200 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, 201 | strict: bool = True) -> Optional[RecordBase]: 202 | """ 203 | Unpack current TLB and return TLB.Record if success, else return None |br| 204 | 205 | By default, rec_unpack is False because for large TLB structures it can be slow. |br| 206 | More simple way is to skip needed structures one by one and load to python objects only needed ones. |br| 207 | 208 | 209 | :param cs: CellSlice to unpack TLB from 210 | :param rec_unpack: pass to RecordBase ``rec_unpack`` 211 | :param strict: pass to RecordBase ``strict`` 212 | :return: TLB.Record instance or None 213 | """ 214 | 215 | self.original_cell_slice = cs 216 | 217 | try: 218 | t = self.tag_to_class[self.get_tag(cs)]() 219 | 220 | if not t.unpack(cs, rec_unpack, strict=strict): 221 | return None 222 | 223 | return t 224 | except (TabError): 225 | return None 226 | 227 | def cell_unpack(self, cell_ref: Cell, rec_unpack: bool = False, 228 | strict: bool = True) -> Optional[RecordBase]: 229 | """ 230 | Same as ``unpack`` but 231 | 232 | :param cell_ref: 233 | :param rec_unpack: pass to RecordBase ``rec_unpack`` 234 | :param strict: pass to RecordBase ``strict`` 235 | :return: 236 | """ 237 | if cell_ref.is_null(): 238 | return None 239 | 240 | # save original cell 241 | self.original_cell = cell_ref 242 | 243 | cs = cell_ref.begin_parse() 244 | 245 | t = self.unpack(cs, rec_unpack, strict=strict) 246 | 247 | if t is not None and not cs.empty_ext(): 248 | return None 249 | 250 | return t 251 | 252 | def fetch(self, cell_or_slice: Union[Cell, CellSlice], rec_unpack: bool = False, 253 | strict: bool = True, **kwargs) -> "Optional[Union[Union[Union[TLB.Record, Cell], CellSlice], None]]": 254 | """ 255 | :param cell_or_slice: 256 | :param rec_unpack: pass to RecordBase ``rec_unpack`` 257 | :param strict: pass to RecordBase ``strict`` 258 | :return: Will return cell/cellslice if cell is special, else will return record on success, else None 259 | """ 260 | 261 | if self.always_special(): 262 | return cell_or_slice 263 | 264 | if isinstance(cell_or_slice, Cell): 265 | return self.cell_unpack(cell_or_slice, rec_unpack, strict=strict) 266 | elif isinstance(cell_or_slice, CellSlice): 267 | return self.unpack(cell_or_slice, rec_unpack, strict=strict) 268 | else: 269 | raise ValueError(f"Type {type(cell_or_slice)} is not supported") 270 | 271 | def fetch_to(self, to_obj: object, 272 | cell_or_slice: Union[Cell, CellSlice], 273 | unpack_names: List[str], 274 | rec_unpack: bool = False, 275 | strict: bool = True): 276 | """ 277 | Same as fetch, but copy negate params to ``to_obj`` with names from ``unpack_names`` 278 | 279 | :param to_obj: 280 | :param cell_or_slice: 281 | :param unpack_names: 282 | :param rec_unpack: 283 | :param strict: 284 | :return: 285 | """ 286 | 287 | rec = self.fetch(cell_or_slice=cell_or_slice, rec_unpack=rec_unpack, strict=strict) 288 | 289 | if rec is None: 290 | return rec 291 | 292 | if not (len(rec.negate_params) == len(unpack_names)): 293 | return None 294 | 295 | for param, name in zip(rec.negate_params, unpack_names): 296 | setattr(to_obj, name, getattr(rec, param)) 297 | 298 | return rec 299 | 300 | def get_param_record(self, item: str) -> Callable: 301 | """Copy params from TLB to Record""" 302 | TMPClass = type("TMPClass", (getattr(self, item),), {}) 303 | TMPClass.name = item 304 | 305 | if self.has_params: 306 | # copy all params 307 | for i in set(self.params_attrs): 308 | if hasattr(self, i): 309 | setattr(TMPClass, i, getattr(self, i)) 310 | 311 | return TMPClass 312 | 313 | def nat_abs(self, x: int): 314 | return (x > 1) * 2 + (x & 1) 315 | 316 | def store_ref_or_tlb(self, cb: CellBuilder, value): 317 | cb.store_ref_or_tlb(value) 318 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb_types/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.tlb_types.reft import RefT, FakeCell, tAny 4 | from tonpy.types.tlb_types.nat import NatWidth, NatLeq, NatLess 5 | from tonpy.types.tlb_types.complex import TLBComplex 6 | from tonpy.types.tlb_types.builtins import Int, UInt, Bits 7 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb_types/builtins.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.tlb import * 4 | 5 | 6 | class Int(TLB): 7 | def __init__(self, x: int): 8 | super().__init__() 9 | self.size = x 10 | 11 | def args_pack(self, cb: CellBuilder, x: int): 12 | cb.store_int(x, self.size) 13 | 14 | def args_cell_pack(self, x: int): 15 | cb = CellBuilder() 16 | self.args_pack(cb, x) 17 | return cb.end_cell().begin_parse() 18 | 19 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> int: 20 | return cs.load_int(self.size) 21 | 22 | def store_ref_or_tlb(self, cb: CellBuilder, value): 23 | return cb.store_ref(CellBuilder().store_int(value, self.size).end_cell()) 24 | 25 | 26 | class UInt(TLB): 27 | def __init__(self, x: int): 28 | super().__init__() 29 | self.size = x 30 | 31 | def args_pack(self, cb: CellBuilder, x: int): 32 | cb.store_uint(x, self.size) 33 | 34 | def args_cell_pack(self, x: int): 35 | cb = CellBuilder() 36 | self.args_pack(cb, x) 37 | return cb.end_cell().begin_parse() 38 | 39 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> int: 40 | return cs.load_uint(self.size) 41 | 42 | def store_ref_or_tlb(self, cb: CellBuilder, value): 43 | return cb.store_ref(CellBuilder().store_uint(value, self.size).end_cell()) 44 | 45 | 46 | class Bits(TLB): 47 | def __init__(self, x: int): 48 | super().__init__() 49 | self.size = x 50 | 51 | def args_pack(self, cb: CellBuilder, x: str): 52 | cb.store_bitstring(x) 53 | 54 | def args_cell_pack(self, x: str): 55 | cb = CellBuilder() 56 | self.args_pack(cb, x) 57 | return cb.end_cell().begin_parse() 58 | 59 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> str: 60 | return cs.load_bitstring(self.size) 61 | 62 | def store_ref_or_tlb(self, cb: CellBuilder, value): 63 | return cb.store_ref(CellBuilder().store_bitstring(value).end_cell()) 64 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb_types/complex.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.tlb import TLB 4 | from tonpy.types.tlb_types.reft import RefT, FakeCell, tAny 5 | from tonpy.types.tlb_types.nat import NatWidth 6 | 7 | 8 | class TLBComplex(TLB): 9 | constants = {"t_RefCell": RefT(FakeCell()), 10 | "t_Nat": NatWidth(32), 11 | "t_Cell": FakeCell(), 12 | "t_Anything": tAny()} 13 | 14 | def __init__(self): 15 | super().__init__() 16 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb_types/nat.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.tlb import * 4 | 5 | 6 | class NatWidth(TLB): 7 | def __init__(self, x: int): 8 | super().__init__() 9 | self.size = x 10 | 11 | def args_pack(self, cb: CellBuilder, x: int): 12 | cb.store_uint(x, self.size) 13 | 14 | def args_cell_pack(self, x: int): 15 | cb = CellBuilder() 16 | self.args_pack(cb, x) 17 | return cb.end_cell().begin_parse() 18 | 19 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> int: 20 | return cs.load_uint(self.size) 21 | 22 | def store_ref_or_tlb(self, cb: CellBuilder, value): 23 | return cb.store_ref(CellBuilder().store_uint(value, self.size).end_cell()) 24 | 25 | 26 | class NatLess(TLB): 27 | def __init__(self, x): 28 | super().__init__() 29 | self.upper_bound = x 30 | 31 | def args_pack(self, cb: CellBuilder, x: int): 32 | cb.store_uint_less(self.upper_bound, x) 33 | 34 | def args_cell_pack(self, x: int): 35 | cb = CellBuilder() 36 | self.args_pack(cb, x) 37 | return cb.end_cell().begin_parse() 38 | 39 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> int: 40 | return cs.load_uint_less(self.upper_bound) 41 | 42 | def store_ref_or_tlb(self, cb: CellBuilder, value): 43 | return cb.store_ref(CellBuilder().store_uint_less(self.upper_bound, value).end_cell()) 44 | 45 | 46 | class NatLeq(TLB): 47 | def __init__(self, x): 48 | super().__init__() 49 | self.upper_bound = x 50 | 51 | def args_pack(self, cb: CellBuilder, x: int): 52 | cb.store_uint_leq(self.upper_bound, x) 53 | 54 | def args_cell_pack(self, x: int): 55 | cb = CellBuilder() 56 | self.args_pack(cb, x) 57 | return cb.end_cell().begin_parse() 58 | 59 | def unpack(self, cs: CellSlice, rec_unpack: bool = False, strict: bool = False) -> int: 60 | return cs.load_uint_leq(self.upper_bound) 61 | 62 | def store_ref_or_tlb(self, cb: CellBuilder, value): 63 | return cb.store_ref(CellBuilder().store_uint_leq(self.upper_bound, value).end_cell()) 64 | -------------------------------------------------------------------------------- /src/tonpy/types/tlb_types/reft.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types.tlb import * 4 | 5 | 6 | class RefT(TLB): 7 | """TLB Ref type ``^T``""" 8 | 9 | def __init__(self, x: TLB): 10 | super().__init__() 11 | self.type = x 12 | 13 | def fetch(self, cell_ref: Cell, rec_unpack: bool = False, strict: bool = False, load_ref: bool = False, 14 | **kwargs) -> "Optional[TLB.Record]": 15 | """ Load first ref from CellSlice and unpack as type """ 16 | 17 | if load_ref: 18 | assert isinstance(cell_ref, CellSlice) 19 | cell_ref = cell_ref.load_ref() 20 | 21 | if isinstance(cell_ref, Cell): 22 | self.original_cell = cell_ref 23 | else: 24 | self.original_cell_slice = cell_ref 25 | 26 | return self.type.fetch(cell_ref, rec_unpack, strict) 27 | 28 | def store_from(self, cb: CellBuilder, value): 29 | cb1 = CellBuilder() 30 | self.type.store_from(cb, value) 31 | cb.store_ref(cb1.end_cell()) 32 | 33 | def store_ref_or_tlb(self, cb: CellBuilder, value): 34 | self.type.store_ref_or_tlb(cb, value) 35 | 36 | 37 | class FakeCell(TLB): 38 | def fetch(self, cell_ref: Cell, rec_unpack: bool = False, strict: bool = False, **kwargs) -> "Optional[TLB.Record]": 39 | assert isinstance(cell_ref, Cell) 40 | self.original_cell = cell_ref 41 | return cell_ref 42 | 43 | 44 | class tAny(TLB): 45 | def fetch(self, c: Union[Cell, CellSlice], rec_unpack: bool = False, 46 | strict: bool = False, **kwargs) -> "Optional[TLB.Record]": 47 | if isinstance(c, Cell): 48 | self.original_cell = c 49 | return c 50 | else: 51 | self.original_cell_slice = c.load_subslice(c.bits, c.refs) 52 | return self.original_cell_slice 53 | -------------------------------------------------------------------------------- /src/tonpy/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.utils.token import parse_token_data -------------------------------------------------------------------------------- /src/tonpy/utils/actions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy import CellSlice 4 | 5 | 6 | def output_actions_count(actions_list: CellSlice): 7 | i = 0 8 | 9 | while True: 10 | if actions_list.refs > 0: 11 | actions_list = actions_list.preload_ref(as_cs=True) 12 | i += 1 13 | else: 14 | return i 15 | -------------------------------------------------------------------------------- /src/tonpy/utils/address_packer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import pack_address as orig_pack_address 4 | 5 | from tonpy.types.cellslice import CellSlice 6 | 7 | 8 | def pack_address(address: str) -> "CellSlice": 9 | return CellSlice(cs=orig_pack_address(address)) 10 | -------------------------------------------------------------------------------- /src/tonpy/utils/bit_converter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from bitstring import BitArray 4 | 5 | 6 | def bitstring_to_utf8(bitstring_: str, strict: bool = True) -> str: 7 | tmp = len(bitstring_) % 8 8 | 9 | if tmp != 0 and strict: 10 | raise ValueError(f"Final bitstring has {len(bitstring_)} bits, (% 8) check fail") 11 | 12 | if tmp == 0: 13 | bits = BitArray(bin=bitstring_) 14 | else: 15 | bits = BitArray(bin=bitstring_[:-1 * tmp]) 16 | 17 | bytes_data = bits.tobytes() 18 | 19 | # Decode bytes to UTF-8 text 20 | utf8_text = bytes_data.decode("utf-8") 21 | 22 | return utf8_text 23 | 24 | 25 | def convert_str_to_bitsring(string: str) -> str: 26 | encoded_bitstring = string.encode('utf-8') 27 | return ''.join(bin(byte)[2:].zfill(8) for byte in encoded_bitstring) 28 | 29 | 30 | def convert_str_to_int(string: str) -> int: 31 | return int(convert_str_to_bitsring(string), 2) 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/tonpy/utils/bit_int.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | def test_value_len(value: int, value_len: int): 4 | if value < 0: 5 | value *= -1 6 | assert len(bin(value)[2:]) <= value_len, f"Value is bigger than {value_len} bits" 7 | -------------------------------------------------------------------------------- /src/tonpy/utils/shard_account.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.types import begin_cell 4 | 5 | 6 | def get_empty_shard_account(): 7 | return begin_cell() \ 8 | .store_ref(begin_cell().store_uint(0, 1).end_cell()) \ 9 | .store_uint(0, 256) \ 10 | .store_uint(0, 64) \ 11 | .end_cell() 12 | -------------------------------------------------------------------------------- /src/tonpy/utils/token.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Disintar LLP Licensed under the Apache License Version 2.0 2 | 3 | from tonpy.libs.python_ton import parse_token_data as parse_token_data_cell 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | if TYPE_CHECKING: 8 | from tonpy import Cell 9 | 10 | 11 | def parse_token_data(cell: "Cell"): 12 | return parse_token_data_cell(cell.cell) 13 | -------------------------------------------------------------------------------- /win/libcrypto-1_1-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/disintar/tonpy/31367bc3698501defa54c22152ae4543c1a35ecd/win/libcrypto-1_1-x64.dll --------------------------------------------------------------------------------