Rise In Logo

Build on Algorand

Basic Smart Contract with Python

Basic Smart Contract with Python

Evolution from TEAL to Python

Traditional Algorand smart contracts use TEAL (Transaction Execution Approval Language), a low-level stack-based language.

New Python-based frameworks allow writing smart contracts with familiar syntax:

  • PyTeal: Python library generating TEAL.
  • PuyaPy: A new high-level Pythonic framework compiling Python directly to TEAL.

Key Components

  • ARC-4 Standard: Standardized ABI for method calls.
  • AlgoKit: Toolkit for development, testing, and deployment.
  • PyTeal: Library for TEAL code generation.
  • PuyaPy: Combines Python readability with Algorand-specific features.

What is PuyaPy?

  • Python framework for Algorand smart contracts compiling to TEAL.
  • Enables writing contracts using Python classes and decorators.
  • Supports ARC-4 compliance and simplifies state management.

Core Class Structure

  • All contracts inherit from the ARC4Contract base class.
  • For stateful contracts, inherit from ARC4Contract.
  • __init__ method initializes contract state during deployment.

Example: Voting Contract Skeleton

from algopy import ARC4Contract, UInt64, String
from algopy.arc4 import abimethod

class Voting(ARC4Contract):
  title: String
  description: String
  noOfOptions: UInt64

  def __init__(self) -> None:
    self.title = String("")
    self.description = String("")
    self.noOfOptions = UInt64(0)

Method Types

Example: Voting Method

@abimethod()
def vote(self, option: UInt64) -> None:
  assert Global.latest_timestamp < self.endsAt, "Voting has ended"
  assert Global.latest_timestamp > self.startsAt, "Voting has not started"
  assert option >= 1 and option <= self.noOfOptions, "Invalid option"

  val, exist = self.localState.maybe(Txn.sender)
  assert not exist, "Already voted"
  self.localState[Txn.sender] = option

  match option:
    case 1:
      self.option1Votes += 1
    case 2:
      self.option2Votes += 1
    case 3:
      self.option3Votes += 1
    case 4:
      self.option4Votes += 1
    case _:
      op.exit(0)

State Management System

Global State

  • Stored on-chain, accessible by all users.
  • Declared as class attributes with ARC4 types.

Local State

  • Stored per user account.
  • Use LocalState class for explicit management.

Example: Defining Global and Local State

from algopy import ARC4Contract, UInt64, String, LocalState

class Voting(ARC4Contract):
  title: String
  description: LocalState[String]
  noOfOptions: UInt64

  def __init__(self) -> None:
    self.title = String("")
    self.noOfOptions = UInt64(0)
    self.is_voted = LocalState(UInt64)

ARC4 Data Types

The Algorand Virtual Machine supports uint64 and byte arrays. ARC4 maps Python types to ABI-compatible types:

  • UInt64: 64-bit unsigned int
  • String: UTF-8 string
  • Bytes: Binary data
  • Address: Algorand address
  • Asset: Algorand assets

Example: Storing Data

from algopy import ARC4Contract, UInt64, String
from algopy import arc4

class Store(ARC4Contract):
  stored_value: UInt64
  stored_tag: String

  @arc4.abimethod()
  def store_data(self, store_v: UInt64, s_tag: String) -> None:
    self.stored_value = store_v
    self.stored_tag = s_tag

Function Types & Decorators

ABI Methods (@abimethod())

  • Entry points for external calls.
  • Can modify state (default readonly=False).

Example: Opt-In Method

@abimethod(allow_actions=["OptIn"])
def opt_in(self) -> None:
  pass
  • allow_actions=["OptIn"] enables handling of Opt-In transactions.
  • Opt-In allows users to allocate local state storage.

Additional decorator options:

  • allow_actions: Specify in-built actions.
  • create: Require calling only during deployment (e.g. AppId == 0).
  • name: Name the ABI method selector.
  • readonly: If true, method can be simulated/dry-run without fees.

Subroutines (@subroutine())

  • Reusable helper functions.
  • Can not directly access contract state.
  • Private/internal logic.

Example: Price Calculation

@subroutine()
def price_b(self) -> UInt64:
  return self.shares_a + UInt64(1)  

# Price increases with shares of Outcome A

Advanced Features

Error Handling

  • Use assert for runtime validation.
  • Compiled into TEAL assert opcode.

Example:

from algopy import *
from algopy.arc4 import abimethod

class Demo(ARC4Contract):
  #state variables  
price: UInt64

  @abimethod()
  def set_price(self, cost: UInt64) -> None:
    assert cost > UInt64(0), "Price must be greater than 0"
    self.price = cost

Inner Transactions: Create payments or asset transfers within contract logic.

Example: Delete Application

#Delete the application (only the creator can delete)

@abimethod(allow_actions=["DeleteApplication"])
def delete_application(self) -> None:
  assert Txn.sender == self.creator, "Only the creator can delete the application"
#Transfer remaining funds to the creator 
itxn.Payment(
    receiver=self.creator,
    amount=0,
    close_remainder_to=self.creator,
    fee=1_000,
  ).submit()

Best Practices

  • Minimize state writes (each write costs gas).
  • Validate inputs early using assert.
  • Use readonly=True for methods that do not modify state.
  • Always specify ARC4 types for ABI methods.

Full Contract: Counter

from algopy import *
from algopy.arc4 import abimethod

class Counter(ARC4Contract):
  count: UInt64

  def __init__(self) -> None:
    self.count = UInt64(0)

  @abimethod()
  def increment(self) -> None:
    self.count = self.count + UInt64(1)

  @abimethod()
  def decrement(self) -> None:
    self.count = self.count - UInt64(1)

  @abimethod()
  def custom_increment(self, amount: UInt64) -> None:
    assert amount > UInt64(0), "Amount must be positive"
    self.count = self.count + amount

  @abimethod(readonly=True)
  def read_counter(self) -> UInt64:
    return self.count

Comments

You need to enroll in the course to be able to comment!

Stay in the know

Never miss updates on new programs and opportunities.

Rise In Logo

Rise together in web3!

Disclaimer: The information, programs, and events provided on https://risein.com is strictly for upskilling and networking purposes related to the technical infrastructure of blockchain platforms. We do not provide financial or investment advice, nor do we make any representations regarding the value, profitability, or future price of any blockchain or cryptocurrency. Users are encouraged to conduct their own research and consult with licensed financial professionals before engaging in any investment activities. https://risein.com disclaims any responsibility for financial decisions made by users based on the information provided here.