PuyaPy compiler
The PuyaPy compiler is a multi-stage, optimising compiler that takes Algorand Python and prepares it for execution on the AVM. PuyaPy ensures the resulting AVM bytecode has execution semantics that match the given Python code. PuyaPy produces output that is directly compatible with AlgoKit typed clients to make deployment and calling easy (among other formats).
The PuyaPy compiler is based on the Puya compiler architecture, which allows for multiple frontend languages to leverage the majority of the compiler logic so adding new frontend languages for execution on Algorand is relatively easy.
Compiler installation
Section titled “Compiler installation”The minimum supported Python version for running the PuyaPy compiler is 3.12.
There are three ways of installing the PuyaPy compiler.
-
You can install AlgoKit CLI and you can then use the
algokit compile pycommand. -
You can install the PuyaPy compiler into your project and thus lock the compiler version for that project:
Terminal window pip install puyapy# ORpoetry add puyapy --group=devNote: if you do this then when you use
algokit compile pywithin that project directory it will invoke the installed compiler rather than a global one. -
You can install the compiler globally using pipx:
Terminal window pipx install puya
Alternatively, it can be installed per project. For example, if you’re using poetry, you can install it as a dev-dependency like so:
poetry add puyapy --group=devIf you just want to play with some examples, you can clone the repo and have a poke around:
git clone https://github.com/algorandfoundation/puya.gitcd puyapoetry installpoetry shell# compile the "Hello World" examplepuyapy examples/hello_worldUsing the compiler
Section titled “Using the compiler”To check that you can run the compiler successfully after installation, you can run the help command:
puyapy -h# ORalgokit compile py -hTo compile a contract or contracts, just supply the path(s) - either to the .py files themselves, or the containing directories. In the case of containing directories, any (non-abstract) contracts discovered therein will be compiled, allowing you to compile multiple contracts at once. You can also supply more than one path at a time to the compiler.
e.g. either puyapy my_project/ or puyapy my_project/contract.py will work to compile a single contract.
Type checking
Section titled “Type checking”The first and second steps of the compiler pipeline are significant to note, because it’s where we perform type checking. We leverage MyPy to do this, so we recommend that you install and use the latest version of MyPy in your development environment to get the best typing information that aligns to what the PuyaPy compiler expects. This should work with standard Python tooling e.g. with Visual Studio Code, PyCharm, et. al.
The easiest way to get a productive development environment with Algorand Python is to instantiate a template with AlgoKit via algokit init -t python. This will give you a full development environment with intellisense, linting, automatic formatting, breakpoint debugging, deployment and CI/CD.
Alternatively, you can construct your own environment by configuring MyPy, Ruff, etc. with the same configuration files used by that template.
The MyPy config that PuyaPy uses is in parse.py
Compiler usage
Section titled “Compiler usage”The options available for the compile can be seen by executing puyapy -h or algokit compile py -h:
Usage: puyapy [ARGS] [OPTIONS]
PuyaPy compiler for compiling Algorand Python to TEAL
╭─ Commands ─────────────────────────────────────────────────────────────────────────────╮│ --help -h Display this message and exit. ││ --version Display application version. │╰────────────────────────────────────────────────────────────────────────────────────────╯╭─ Arguments ────────────────────────────────────────────────────────────────────────────╮│ * PATH Files or directories to compile [required] │╰────────────────────────────────────────────────────────────────────────────────────────╯╭─ Outputs ──────────────────────────────────────────────────────────────────────────────╮│ Options for controlling what is output and where ││ ││ --out-dir Path for outputting artefacts ││ --log-level Minimum level to log to console [choices: ││ notset, debug, info, warning, error, critical] ││ [default: info] ││ --output-teal --no-output-teal Output TEAL code [default: True] ││ --output-source-map Output debug source maps [default: True] ││ --no-output-source-map ││ --output-arc56 --no-output-arc56 Output {contract}.arc56.json ARC-56 app spec ││ file [default: True] ││ --output-arc32 --no-output-arc32 Output {contract}.arc32.json ARC-32 app spec ││ file [default: False] ││ --output-bytecode Output AVM bytecode [default: False] ││ --no-output-bytecode ││ --output-client Output Algorand Python contract client for typed ││ --no-output-client ARC-4 ABI calls [default: False] ││ --debug-level -g Output debug information level, 0 = none, 1 = ││ debug, 2 = reserved for future use [choices: 0, ││ 1, 2] [default: 1] │╰────────────────────────────────────────────────────────────────────────────────────────╯╭─ Compilation ──────────────────────────────────────────────────────────────────────────╮│ Options that affect the compilation process, such as optimisation options etc. ││ ││ --optimization-level -O Set optimization level of output TEAL / AVM bytecode ││ [choices: 0, 1, 2] [default: 1] ││ --target-avm-version Target AVM version [choices: 10, 11, 12, 13] ││ [default: 11] ││ --resource-encoding If "index", then resource types (Application, Asset, ││ Account) in ABI methods should be passed as an index ││ into their appropriate foreign array. The default ││ option "value", as of PuyaPy 5.0, means these values ││ will be passed directly. [choices: index, value] ││ [default: value] ││ --locals-coalescing-strategy Strategy choice for out-of-ssa local variable ││ coalescing. The best choice for your app is best ││ determined through experimentation [choices: ││ root-operand, root-operand-excluding-args, ││ aggressive] [default: root-operand] ││ --validate-abi-args Validates ABI transaction arguments by ensuring they ││ --no-validate-abi-args are encoded correctly [default: True] ││ --validate-abi-return Validates encoding of ABI return values when using ││ --no-validate-abi-return .from_log(), arc4.abi_call, arc4.arc4_create and ││ arc4.arc4_update [default: True] │╰────────────────────────────────────────────────────────────────────────────────────────╯╭─ Templating ───────────────────────────────────────────────────────────────────────────╮│ Options for controlling the generation of TEAL template files ││ ││ --template-var -T Define template vars for use when assembling via ││ --output-bytecode. Should be specified without the prefix ││ (see --template-vars-prefix), e.g. -T SOME_INT=1234 ││ SOME_BYTES=0x1A2B SOME_BOOL=True -T SOME_STR="hello" ││ --template-vars-prefix Define the prefix to use with --template-var [default: ││ TMPL_] │╰────────────────────────────────────────────────────────────────────────────────────────╯╭─ Additional outputs ───────────────────────────────────────────────────────────────────╮│ Controls additional compiler outputs that may be useful to compiler developers. ││ ││ --output-awst Output parsed result of AWST [default: False] ││ --output-awst-json Output parsed result of AWST as JSON [default: ││ False] ││ --output-source-annotations-json Output source annotations result of AWST parse as ││ JSON [default: False] ││ --output-ssa-ir Output IR (in SSA form) before optimizations ││ [default: False] ││ --output-optimization-ir Output IR after each optimization [default: False] ││ --output-destructured-ir Output IR after SSA destructuring and before MIR ││ [default: False] ││ --output-memory-ir Output MIR before lowering to TEAL [default: False] ││ --output-teal-intermediates Output TEAL before peephole optimization and before ││ block optimization [default: False] ││ --output-op-statistics Output statistics about ops used for each program ││ compiled optimization_level: Set optimization level ││ of output TEAL / AVM bytecode [default: False] │╰────────────────────────────────────────────────────────────────────────────────────────╯Defining template values
Section titled “Defining template values”Template Variables, can be replaced with literal values during compilation to bytecode using the --template-var option.
Additionally, Algorand Python functions that create AVM bytecode, such as compile_contract and compile_logicsig, can also provide the specified values.
Examples of Variable Definitions
Section titled “Examples of Variable Definitions”The table below illustrates how different variables and values can be defined:
| Variable Type | Example Algorand Python | Value definition example |
|---|---|---|
| UInt64 | algopy.TemplateVar[UInt64](docs/_build/markdown/"SOME_INT") | SOME_INT=1234 |
| Bytes | algopy.TemplateVar[Bytes](docs/_build/markdown/"SOME_BYTES") | SOME_BYTES=0x1A2B |
| String | algopy.TemplateVar[String](docs/_build/markdown/"SOME_STR") | SOME_STR="hello" |
All template values specified via the command line are prefixed with “TMPL_” by default.
The default prefix can be modified using the --template-vars-prefix option.
Sample pyproject.toml
Section titled “Sample pyproject.toml”A sample pyproject.toml file with known good configuration is:
[tool.poetry]name = "algorand_python_contract"version = "0.1.0"description = "Algorand smart contracts"authors = ["Name <name@domain.tld>"]readme = "README.md"
[tool.poetry.dependencies]python = "^3.12"algokit-utils = "^2.2.0"python-dotenv = "^1.0.0"algorand-python = "^1.0.0"
[tool.poetry.group.dev.dependencies]black = { extras = ["d"], version = "*" }ruff = "^0.1.6"mypy = "*"pytest = "*"pytest-cov = "*"pip-audit = "*"pre-commit = "*"puyapy = "^1.0"
[build-system]requires = ["poetry-core"]build-backend = "poetry.core.masonry.api"
[tool.ruff]line-length = 120select = [ "E", "F", "ANN", "UP", "N", "C4", "B", "A", "YTT", "W", "FBT", "Q", "RUF", "I",]ignore = [ "ANN101", # no type for self "ANN102", # no type for cls]unfixable = ["B", "RUF"]
[tool.ruff.flake8-annotations]allow-star-arg-any = truesuppress-none-returning = true
[tool.pytest.ini_options]pythonpath = ["smart_contracts", "tests"]
[tool.mypy]files = "smart_contracts/"python_version = "3.12"disallow_any_generics = truedisallow_subclassing_any = truedisallow_untyped_calls = truedisallow_untyped_defs = truedisallow_incomplete_defs = truecheck_untyped_defs = truedisallow_untyped_decorators = truewarn_redundant_casts = truewarn_unused_ignores = truewarn_return_any = truestrict_equality = truestrict_concatenate = truedisallow_any_unimported = truedisallow_any_expr = truedisallow_any_decorated = truedisallow_any_explicit = true