Alpha preview. Not for production use.
Working anonymously. to save your work permanently.
Reference

Algorand Python (Puya)

Write Algorand smart contracts using Python syntax with type hints.

Overview

Puya allows you to write Algorand smart contracts using Python syntax. It compiles Python code with type hints to efficient TEAL bytecode.

Note
Puya uses a subset of Python. Not all Python features are available - the compiler will tell you if you use unsupported features.

Basic Contract Structure

counter.py Python
1from algopy import ARC4Contract, GlobalState, UInt64
2
3class Counter(ARC4Contract):
4    def __init__(self) -> None:
5        self.count = GlobalState(UInt64(0))
6
7    @arc4.abimethod
8    def increment(self) -> None:
9        self.count.value += 1
10
11    @arc4.abimethod
12    def get_count(self) -> UInt64:
13        return self.count.value

State Management

Global State

Python
1from algopy import ARC4Contract, GlobalState, UInt64, Bytes, Account
2
3class MyContract(ARC4Contract):
4    def __init__(self) -> None:
5        # Typed global state
6        self.counter = GlobalState(UInt64(0))
7        self.owner = GlobalState(Account())
8        self.data = GlobalState(Bytes())

Local State

Python
1from algopy import ARC4Contract, LocalState, UInt64, Txn
2
3class UserContract(ARC4Contract):
4    def __init__(self) -> None:
5        self.balance = LocalState(UInt64)
6
7    @arc4.abimethod
8    def deposit(self) -> None:
9        self.balance[Txn.sender] += Txn.amount

Box Storage

Python
1from algopy import ARC4Contract, Box, BoxMap, Bytes, arc4
2
3class StorageContract(ARC4Contract):
4    def __init__(self) -> None:
5        self.config = Box(Bytes, key=b"config")
6        self.users = BoxMap(arc4.Address, UserData, key_prefix=b"u")

Types

Primitive Types

Python
1from algopy import UInt64, Bytes, String, Bool
2
3# 64-bit unsigned integer
4count: UInt64 = UInt64(100)
5
6# Byte array
7data: Bytes = Bytes(b"hello")
8
9# String (UTF-8 bytes)
10name: String = String("Alice")
11
12# Boolean
13active: Bool = Bool(True)

ARC4 Types

Python
1from algopy import arc4
2
3# Fixed-size unsigned integers
4small: arc4.UInt8 = arc4.UInt8(255)
5medium: arc4.UInt32 = arc4.UInt32(1000000)
6large: arc4.UInt256 = arc4.UInt256(10**50)
7
8# Address
9addr: arc4.Address = arc4.Address(Txn.sender)
10
11# Dynamic array
12numbers: arc4.DynamicArray[arc4.UInt64] = arc4.DynamicArray[arc4.UInt64]()
13
14# Static array
15fixed: arc4.StaticArray[arc4.UInt8, typing.Literal[4]] = ...

Methods

ABI Methods

Python
1from algopy import ARC4Contract, arc4, UInt64
2
3class Calculator(ARC4Contract):
4    @arc4.abimethod
5    def add(self, a: UInt64, b: UInt64) -> UInt64:
6        return a + b
7
8    @arc4.abimethod
9    def multiply(self, a: UInt64, b: UInt64) -> UInt64:
10        return a * b
11
12    @arc4.abimethod(create="require")
13    def create(self) -> None:
14        """Called only on contract creation"""
15        self.owner.value = Txn.sender

Bare Methods

Python
1from algopy import ARC4Contract, arc4
2
3class MyContract(ARC4Contract):
4    @arc4.baremethod(create="require")
5    def create(self) -> None:
6        pass
7
8    @arc4.baremethod(opt_in="require")
9    def opt_in(self) -> None:
10        pass
11
12    @arc4.baremethod(delete_application="require")
13    def delete(self) -> None:
14        assert Txn.sender == self.owner.value

Transaction Context

Python
1from algopy import Txn, Global, itxn
2
3# Access current transaction
4sender = Txn.sender
5amount = Txn.amount
6app_id = Txn.application_id
7
8# Access global state
9timestamp = Global.latest_timestamp
10current_round = Global.round
11
12# Inner transactions
13itxn.Payment(
14    receiver=recipient,
15    amount=UInt64(1_000_000)  # 1 ALGO
16).submit()
17
18itxn.AssetTransfer(
19    xfer_asset=asset_id,
20    asset_receiver=recipient,
21    asset_amount=amount
22).submit()

Assertions

Python
1from algopy import ARC4Contract, arc4, Txn, assert_
2
3class Guarded(ARC4Contract):
4    @arc4.abimethod
5    def admin_only(self) -> None:
6        assert Txn.sender == self.owner.value, "Not authorized"
7
8    @arc4.abimethod
9    def withdraw(self, amount: UInt64) -> None:
10        assert_(amount > 0, "Amount must be positive")
11        assert_(self.balance.value >= amount, "Insufficient balance")

Structs

Python
1from algopy import arc4, ARC4Contract
2
3class UserProfile(arc4.Struct):
4    name: arc4.String
5    balance: arc4.UInt64
6    active: arc4.Bool
7
8class Users(ARC4Contract):
9    def __init__(self) -> None:
10        self.profiles = BoxMap(arc4.Address, UserProfile, key_prefix=b"p")
11
12    @arc4.abimethod
13    def create_profile(self, name: arc4.String) -> None:
14        profile = UserProfile(
15            name=name,
16            balance=arc4.UInt64(0),
17            active=arc4.Bool(True)
18        )
19        self.profiles[arc4.Address(Txn.sender)] = profile