Control Flow | Algorand Developer Portal

Control Flow

Control flow in Algorand smart contracts follows common programming paradigms, with support for if statements, while loops, for loops, and switch/match statements. Both Algorand Python and Algorand TypeScript provide familiar syntax for these constructs.

If statements

If statements work as you would expect in any programming language. The conditions must be an expression that evaluates to a boolean.

/**
 * Determines if an account is rich based on its balance
 * @param accountBalance The account balance to check
 * @returns A string describing the account's wealth status
 */
@readonly
public isRich(accountBalance: uint64): string {
  if (accountBalance > 1000) {
    return 'This account is rich!'
  } else if (accountBalance > 100) {
    return 'This account is doing well.'
  } else {
    return 'This account is poor :('
  }
}
@arc4.abimethod

def is_rich(self, account_balance: UInt64) -> String:
    if account_balance > 1000:
        return String("This account is rich!")
    elif account_balance > 100:
        return String("This account is doing well.")
    else:
        return String("This account is poor :(")

Ternary conditions

Ternary conditions allow for compact conditional expressions. The condition must be an expression that evaluates to a boolean.

/**
 * Determines if a number is even or odd
 * @param number The number to check
 * @returns "Even" if the number is even, "Odd" otherwise
 */
@readonly
public isEven(number: uint64): string {
  return number % 2 === 0 ? 'Even' : 'Odd'
}
@arc4.abimethod

def is_even(self, number: UInt64) -> String:
    return String("Even") if number % 2 == 0 else String("Odd")

While loops

While loops iterate as long as the specified condition is true. The condition must be an expression that evaluates to a boolean.

You can use break and continue statements to control loop execution.

/**
 * Demonstrates while loop with continue and break statements
 * @returns The number of iterations performed
 */
@readonly
public loop(): uint64 {
  let num: uint64 = 10
  let loopCount: uint64 = 0
  while (num > 0) {
    if (num > 5) {
      num -= 1
      loopCount += 1
      continue
    }
    num -= 2
    loopCount += 1
    if (num === 1) {
      break
    }
  }
  return loopCount
}
class WhileLoopExample(ARC4Contract):
    @arc4.abimethod
    def loop(self) -> UInt64:
        num = UInt64(10)
        loop_count = UInt64(0)
        while num > 0:
            if num > 5:
                num -= 1
                loop_count += 1
                continue
            num -= 2
            loop_count += 1
            if num == 1:
                break
        return loop_count

For Loops

For loops are used to iterate over sequences, ranges and ARC-4 arrays.

In Algorand Python, utility functions like uenumerate and urange facilitate creating sequences and ranges of UInt64 numbers, and the built-in reversed method works with these. In Algorand TypeScript, standard iteration constructs are available.

Here is an example of how you can use For loops in smart contracts:

/**
 * Demonstrates different types of for loops
 * @returns An array of uint64 values in reversed order
 */
@readonly
public forLoop(): uint64[] {
  let numbers: uint64[] = []
  for (const item of urange(4)) {
    numbers = [...numbers, item]
  }
  let reversed: uint64[] = []
  for (const num of numbers) {
    reversed = [num, ...reversed]
  }
  let sum: uint64 = 0
  for (const num of reversed) {
    sum += num
  }
  assert(sum === 6, 'Sum of reversed array should be 6')
  return reversed
}
FourArray: t.TypeAlias = arc4.StaticArray[arc4.UInt8, t.Literal[4]]
class ForLoopsExample(ARC4Contract):
    @arc4.abimethod
    def for_loop(self) -> FourArray:
        array = FourArray(arc4.UInt8(0), arc4.UInt8(0), arc4.UInt8(0), arc4.UInt8(0))
        for index, item in uenumerate(reversed(urange(4))):  # [3, 2, 1, 0]
            array[index] = arc4.UInt8(item)
        x = UInt64(0)
        for item in urange(1, 5):  # [1, 2, 3, 4]
            x += item
        assert x == 10
        return array

Switch or Match Statements

switch for TypeScript and match for Python provide a clean way to handle multiple conditions. They follow the standard syntax of their respective languages.

/**
 * Returns the day of the week based on a numeric input
 * @param date A number from 0-6 representing a day of the week
 * @returns The name of the day, or "Invalid day" if out of range
 */
@readonly
public getDay(date: uint64): string {
  switch (Uint64(date)) {
    case Uint64(1):
      return 'Monday'
    case Uint64(2):
      return 'Tuesday'
    case Uint64(3):
      return 'Wednesday'
    case Uint64(4):
      return 'Thursday'
    case Uint64(5):
      return 'Friday'
    case Uint64(6):
      return 'Saturday'
    case Uint64(7):
      return 'Sunday'
    default:
      return 'Invalid day'
  }
}
class MatchStatements(ARC4Contract):
    @arc4.abimethod
    def get_day(self, date: UInt64) -> String:
        match date:
            case UInt64(0):
                return String("Monday")
            case UInt64(1):
                return String("Tuesday")
            case UInt64(2):
                return String("Wednesday")
            case UInt64(3):
                return String("Thursday")
            case UInt64(4):
                return String("Friday")
            case UInt64(5):
                return String("Saturday")
            case UInt64(6):
                return String("Sunday")
            case _:
                return String("Invalid day")

Note: Captures and patterns are not supported. Currently, there is only support for basic case/switch functionality; pattern matching and guard clauses are not currently supported.

TEAL Flow Control Opcode

Algorand Python and TypeScript are high-level smart contract languages that allow developers to express control flows in more accessible languages. However, the Algorand Virtual Machine (AVM) executes the Transaction Execution Approval Language (TEAL) flow control opcodes after compilation. TEAL is a low-level assembly language that the AVM understands directly. While developers will write smart contracts in higher-level languages, understanding the underlying TEAL opcodes can be beneficial to comprehend what’s happening line by line. The following chart contains all of the control flow opcodes available in TEAL.

Opcode Description
err Fail immediately.
bnz target branch to TARGET if value A is not zero
bz target branch to TARGET if value A is zero
b target branch unconditionally to TARGET
return use A as success value; end
pop discard A
popn n remove N values from the top of the stack
dup duplicate A
dup2 duplicate A and B
dupn n duplicate A, N times
dig n Nth value from the top of the stack. dig 0 is equivalent to dup
bury n replace the Nth value from the top of the stack with A. bury 0 fails.
cover n remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N.
uncover n remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N.
frame_dig i Nth (signed) value from the frame pointer.
frame_bury i replace the Nth (signed) value from the frame pointer in the stack with A
swap swaps A and B on stack
select selects one of two values based on top-of-stack: B if C != 0, else A
assert immediately fail unless A is a non-zero number
callsub target branch unconditionally to TARGET, saving the next instruction on the call stack
proto a r Prepare top call frame for a retsub that will assume A args and R return values.
retsub pop the top instruction from the call stack and branch to it
switch target … branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.
match target … given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.