├── .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 |
4 |
5 |
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 | 
9 | 
10 | 
11 | [![Telegram Community Chat][telegram-tondev-badge]][telegram-tondev-url]
12 | [](https://opensource.org/licenses/Apache-2.0)
13 | [](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
--------------------------------------------------------------------------------
| |