Arweave AO Lua Programming Guide

Arweave AO (Actor Oriented) uses the Lua programming language to describe processes. This article explains the basic syntax for writing Lua in AO and AO-specific patterns. Variables and Data Types Basic Variable Declaration -- Global variables MyVariable = "Hello World" Counter = 42 IsActive = true -- Local variables local localVar = "Local variable" local number = 123 Data Types Lua is a dynamically typed language that supports the following data types: -- Numeric types local integer = 42 local float = 3.14 local scientific = 1.23e-4 -- String types local str1 = "Double quotes" local str2 = 'Single quotes' local multiline = [[ Multi-line strings are possible ]] -- Boolean values local isTrue = true local isFalse = false -- nil (undefined value) local nothing = nil Tables (Arrays and Dictionaries) Lua tables are flexible data structures that function as both arrays and dictionaries. Arrays -- Arrays (indices start from 1) local fruits = {"apple", "banana", "orange"} print(fruits[1]) -- "apple" print(#fruits) -- 3 (array length) Dictionaries (Associative Arrays) -- Dictionary local person = { name = "John", age = 25, active = true } -- Access methods print(person.name) -- "John" print(person["age"]) -- 25 Composite Tables -- Mixed arrays and dictionaries local mixed = { "first", -- [1] "second", -- [2] name = "Mixed", -- ["name"] count = 100 -- ["count"] } -- Nested tables local config = { database = { host = "localhost", port = 5432, credentials = { username = "admin", password = "secret" } } } Functions Basic Function Definition -- Regular function function greet(name) return "Hello, " .. name end -- Local function local function add(a, b) return a + b end -- Anonymous function local multiply = function(x, y) return x * y end Advanced Function Features -- Multiple return values function getCoordinates() return 10, 20 end local x, y = getCoordinates() -- Variable arguments function sum(...) local args = {...} local total = 0 for i = 1, #args do total = total + args[i] end return total end -- Usage example local result = sum(1, 2, 3, 4, 5) -- 15 Control Structures Conditional Statements local score = 85 if score >= 90 then print("Excellent") elseif score >= 70 then print("Good") else print("Needs improvement") end Loop Processing -- Numeric loop for i = 1, 10 do print("Count: " .. i) end -- Reverse loop for i = 10, 1, -1 do print(i) end -- Array iteration local items = {"a", "b", "c"} for i = 1, #items do print(i, items[i]) end -- Key-value pair iteration (all elements) local data = {name = "John", age = 30, city = "Tokyo"} for key, value in pairs(data) do print(key, value) end -- Array-specific iteration (consecutive indices only) for index, value in ipairs(items) do print(index, value) end -- while statement local count = 0 while count = amount end -- Balance inquiry handler Handlers.add( "balance", Handlers.utils.hasMatchingTag("Action", "Balance"), function(msg) local account = msg.Tags.Account or msg.From local balance = Balances[account] or 0 ao.send({ Target = msg.From, Tags = { Action = "Balance-Response", Account = account, Balance = tostring(balance) } }) end ) -- Transfer handler Handlers.add( "transfer", Handlers.utils.hasMatchingTag("Action", "Transfer"), function(msg) local from = msg.From local to = msg.Tags.To local amount = tonumber(msg.Tags.Amount) -- Validation if not to or not amount or amount

Jun 12, 2025 - 06:00
 0
Arweave AO Lua Programming Guide

Arweave AO (Actor Oriented) uses the Lua programming language to describe processes. This article explains the basic syntax for writing Lua in AO and AO-specific patterns.

Variables and Data Types

Basic Variable Declaration

-- Global variables
MyVariable = "Hello World"
Counter = 42
IsActive = true

-- Local variables
local localVar = "Local variable"
local number = 123

Data Types

Lua is a dynamically typed language that supports the following data types:

-- Numeric types
local integer = 42
local float = 3.14
local scientific = 1.23e-4

-- String types
local str1 = "Double quotes"
local str2 = 'Single quotes'
local multiline = [[
Multi-line
strings are possible
]]

-- Boolean values
local isTrue = true
local isFalse = false

-- nil (undefined value)
local nothing = nil

Tables (Arrays and Dictionaries)

Lua tables are flexible data structures that function as both arrays and dictionaries.

Arrays

-- Arrays (indices start from 1)
local fruits = {"apple", "banana", "orange"}
print(fruits[1])  -- "apple"
print(#fruits)    -- 3 (array length)

Dictionaries (Associative Arrays)

-- Dictionary
local person = {
    name = "John",
    age = 25,
    active = true
}

-- Access methods
print(person.name)     -- "John"
print(person["age"])   -- 25

Composite Tables

-- Mixed arrays and dictionaries
local mixed = {
    "first",           -- [1]
    "second",          -- [2]
    name = "Mixed",    -- ["name"]
    count = 100        -- ["count"]
}

-- Nested tables
local config = {
    database = {
        host = "localhost",
        port = 5432,
        credentials = {
            username = "admin",
            password = "secret"
        }
    }
}

Functions

Basic Function Definition

-- Regular function
function greet(name)
    return "Hello, " .. name
end

-- Local function
local function add(a, b)
    return a + b
end

-- Anonymous function
local multiply = function(x, y)
    return x * y
end

Advanced Function Features

-- Multiple return values
function getCoordinates()
    return 10, 20
end
local x, y = getCoordinates()

-- Variable arguments
function sum(...)
    local args = {...}
    local total = 0
    for i = 1, #args do
        total = total + args[i]
    end
    return total
end

-- Usage example
local result = sum(1, 2, 3, 4, 5)  -- 15

Control Structures

Conditional Statements

local score = 85

if score >= 90 then
    print("Excellent")
elseif score >= 70 then
    print("Good")
else
    print("Needs improvement")
end

Loop Processing

-- Numeric loop
for i = 1, 10 do
    print("Count: " .. i)
end

-- Reverse loop
for i = 10, 1, -1 do
    print(i)
end

-- Array iteration
local items = {"a", "b", "c"}
for i = 1, #items do
    print(i, items[i])
end

-- Key-value pair iteration (all elements)
local data = {name = "John", age = 30, city = "Tokyo"}
for key, value in pairs(data) do
    print(key, value)
end

-- Array-specific iteration (consecutive indices only)
for index, value in ipairs(items) do
    print(index, value)
end

-- while statement
local count = 0
while count < 5 do
    print("Count: " .. count)
    count = count + 1
end

AO-Specific Syntax

AO has several special rules in addition to regular Lua.

Persisted Variables

-- Global variables starting with capital letters are automatically persisted
PersistentData = PersistentData or {}
TotalSupply = 1000000
Balances = Balances or {}

-- Conditional initialization pattern (for process restart compatibility)
if not Owner then
    Owner = ao.env.Process.Owner
end

AO Built-in Functions

-- Message sending
ao.send({
    Target = "ProcessID",
    Tags = {
        Action = "Transfer",
        Amount = "100"
    },
    Data = "Additional data"
})

-- Environment variable access
local processId = ao.id
local owner = ao.env.Process.Owner

Message Handlers

AO's characteristic message processing system.

Basic Handler

Handlers.add(
    "balance_check",  -- Handler name
    Handlers.utils.hasMatchingTag("Action", "Balance"),  -- Condition
    function(msg)     -- Processing function
        local account = msg.Tags.Account or msg.From
        local balance = Balances[account] or 0

        ao.send({
            Target = msg.From,
            Tags = {
                Action = "Balance-Response",
                Balance = tostring(balance)
            }
        })
    end
)

Handlers with Complex Conditions

-- Custom condition function
local function isValidTransfer(msg)
    return msg.Tags.Action == "Transfer" and
           msg.Tags.To and
           msg.Tags.Amount and
           tonumber(msg.Tags.Amount) > 0
end

Handlers.add(
    "transfer",
    isValidTransfer,
    function(msg)
        local from = msg.From
        local to = msg.Tags.To
        local amount = tonumber(msg.Tags.Amount)

        -- Transfer processing
        if Balances[from] and Balances[from] >= amount then
            Balances[from] = Balances[from] - amount
            Balances[to] = (Balances[to] or 0) + amount

            ao.send({
                Target = msg.From,
                Tags = { Action = "Transfer-Success" }
            })
        else
            ao.send({
                Target = msg.From,
                Tags = { Action = "Transfer-Error", Error = "Insufficient balance" }
            })
        end
    end
)

Common Patterns

Safe Type Conversion

-- Safe numeric conversion
local function safeToNumber(value, default)
    local num = tonumber(value)
    return num and num or (default or 0)
end

-- Safe string conversion
local function safeToString(value)
    return tostring(value or "")
end

Validation Functions

local function validateTransfer(from, to, amount)
    -- Sender/recipient check
    if not from or not to or from == to then
        return false, "Invalid sender or recipient"
    end

    -- Amount check
    local numAmount = tonumber(amount)
    if not numAmount or numAmount <= 0 then
        return false, "Invalid amount"
    end

    -- Balance check
    if not Balances[from] or Balances[from] < numAmount then
        return false, "Insufficient balance"
    end

    return true, numAmount
end

-- Usage example
local isValid, amountOrError = validateTransfer(msg.From, msg.Tags.To, msg.Tags.Amount)
if not isValid then
    ao.send({
        Target = msg.From,
        Tags = { Action = "Error", Message = amountOrError }
    })
    return
end

Utility Functions

-- Table copying
local function deepCopy(original)
    local copy = {}
    for key, value in pairs(original) do
        if type(value) == "table" then
            copy[key] = deepCopy(value)
        else
            copy[key] = value
        end
    end
    return copy
end

-- Check if table is empty
local function isEmpty(tbl)
    return next(tbl) == nil
end

-- Search for element in array
local function findInArray(array, value)
    for i, v in ipairs(array) do
        if v == value then
            return i
        end
    end
    return nil
end

Error Handling

pcall (Protected Call)

-- Safe function execution
local success, result = pcall(function()
    return someRiskyFunction()
end)

if success then
    print("Success: " .. tostring(result))
else
    print("Error: " .. tostring(result))
end

assert

-- Condition check
local function divide(a, b)
    assert(type(a) == "number", "First argument must be a number")
    assert(type(b) == "number", "Second argument must be a number")
    assert(b ~= 0, "Cannot divide by zero")
    return a / b
end

Custom Error Handling

local function safeExecute(func, errorHandler)
    local success, result = pcall(func)
    if success then
        return result
    else
        if errorHandler then
            return errorHandler(result)
        else
            print("An error occurred: " .. tostring(result))
            return nil
        end
    end
end

-- Usage example
local result = safeExecute(
    function() return calculateSomething() end,
    function(error) return "Calculation failed: " .. error end
)

Practical Example: Simple Token Contract

Here's a practical example combining the knowledge covered so far:

-- Token basic information
TokenName = "MyToken"
TokenSymbol = "MTK"
TotalSupply = 1000000
Balances = Balances or {}

-- Initialization
if not Owner then
    Owner = ao.env.Process.Owner
    Balances[Owner] = TotalSupply
end

-- Utility functions
local function hasBalance(account, amount)
    return Balances[account] and Balances[account] >= amount
end

-- Balance inquiry handler
Handlers.add(
    "balance",
    Handlers.utils.hasMatchingTag("Action", "Balance"),
    function(msg)
        local account = msg.Tags.Account or msg.From
        local balance = Balances[account] or 0

        ao.send({
            Target = msg.From,
            Tags = {
                Action = "Balance-Response",
                Account = account,
                Balance = tostring(balance)
            }
        })
    end
)

-- Transfer handler
Handlers.add(
    "transfer",
    Handlers.utils.hasMatchingTag("Action", "Transfer"),
    function(msg)
        local from = msg.From
        local to = msg.Tags.To
        local amount = tonumber(msg.Tags.Amount)

        -- Validation
        if not to or not amount or amount <= 0 then
            ao.send({
                Target = from,
                Tags = { Action = "Transfer-Error", Error = "Invalid parameters" }
            })
            return
        end

        if not hasBalance(from, amount) then
            ao.send({
                Target = from,
                Tags = { Action = "Transfer-Error", Error = "Insufficient balance" }
            })
            return
        end

        -- Execute transfer
        Balances[from] = Balances[from] - amount
        Balances[to] = (Balances[to] or 0) + amount

        ao.send({
            Target = from,
            Tags = {
                Action = "Transfer-Success",
                From = from,
                To = to,
                Amount = tostring(amount)
            }
        })
    end
)

Summary

Lua programming in AO requires understanding the following key points in addition to regular Lua knowledge:

  1. Persistence concept: Automatic persistence of global variables starting with capital letters
  2. Message-driven architecture: Asynchronous processing using Handlers
  3. State management: Conditional initialization for process restart compatibility
  4. Error handling: Robust error handling in distributed environments

Understanding these concepts enables you to build efficient and secure processes on the AO platform. While AO's distributed computing environment requires asynchronous, message-based thinking that differs from traditional synchronous programming, Lua's simple syntax helps reduce that complexity.