Skip to content

AVM Type Encoding

← Back to ABI Encoding

This example demonstrates how to work with AVM-specific types:

  • AVMBytes: Raw byte arrays (no length prefix, unlike ABI string/bytes)
  • AVMString: UTF-8 strings (no length prefix, unlike ABI string)
  • AVMUint64: 64-bit unsigned integers (8 bytes, big-endian) AVM types represent how data is stored natively on the AVM stack, while ABI types follow the ARC-4 encoding specification with length prefixes. The Python SDK provides the AVMType enum in algokit_abi.arc56 with values: AVMType.BYTES, AVMType.STRING, AVMType.UINT64

No LocalNet required - pure ABI encoding/decoding

From the repository’s examples directory:

Terminal window
cd examples
uv run python abi/12_avm_types.py

View source on GitHub

12_avm_types.py
# ruff: noqa: N999, C901, PLR0912, PLR0915
"""
Example: AVM Type Encoding
This example demonstrates how to work with AVM-specific types:
- AVMBytes: Raw byte arrays (no length prefix, unlike ABI string/bytes)
- AVMString: UTF-8 strings (no length prefix, unlike ABI string)
- AVMUint64: 64-bit unsigned integers (8 bytes, big-endian)
AVM types represent how data is stored natively on the AVM stack,
while ABI types follow the ARC-4 encoding specification with length prefixes.
The Python SDK provides the AVMType enum in algokit_abi.arc56
with values: AVMType.BYTES, AVMType.STRING, AVMType.UINT64
No LocalNet required - pure ABI encoding/decoding
"""
from shared import format_hex, print_header, print_info, print_step, print_success
from algokit_abi import abi
from algokit_abi.arc56 import AVMType
def is_avm_type(type_str: str) -> bool:
"""Check if a type string is an AVM type."""
return type_str in (AVMType.BYTES.value, AVMType.STRING.value, AVMType.UINT64.value)
def encode_avm_value_raw(avm_type: AVMType, value: str | bytes | int) -> bytes:
"""
Encode a value to raw AVM representation (as conceptualized in ARC-56).
ARC-56 defines AVM types as having NO length prefixes:
- AVMString: Raw UTF-8 bytes (no 2-byte length prefix)
- AVMBytes: Raw bytes as-is
- AVMUint64: 8-byte big-endian encoding
Args:
avm_type: The AVM type to encode as
value: The value to encode
Returns:
Encoded bytes
"""
if avm_type == AVMType.STRING:
# Raw UTF-8 bytes - no length prefix
if isinstance(value, str):
return value.encode("utf-8")
return bytes(value) if isinstance(value, bytes) else str(value).encode("utf-8")
if avm_type == AVMType.BYTES:
# Raw bytes as-is
if isinstance(value, str):
return value.encode("utf-8")
if isinstance(value, bytes):
return value
return bytes(value)
if avm_type == AVMType.UINT64:
# 8-byte big-endian encoding
int_value = value if isinstance(value, int) else int(value)
return int_value.to_bytes(8, "big")
raise ValueError(f"Unknown AVM type: {avm_type}")
def decode_avm_value(avm_type: AVMType, data: bytes) -> str | bytes | int:
"""
Decode raw AVM bytes to a value.
Args:
avm_type: The AVM type to decode as
data: The bytes to decode
Returns:
Decoded value (string, bytes, or int)
"""
if avm_type == AVMType.STRING:
return data.decode("utf-8")
if avm_type == AVMType.BYTES:
return data
if avm_type == AVMType.UINT64:
return int.from_bytes(data, "big")
raise ValueError(f"Unknown AVM type: {avm_type}")
def main() -> None:
print_header("AVM Type Encoding Example")
# Step 1: Introduction to AVM Types
print_step(1, "Introduction to AVM Types")
print_info("AVM types represent native Algorand Virtual Machine stack values.")
print_info("")
print_info("Three AVM types:")
print_info(" AVMBytes - Raw byte array (no length prefix)")
print_info(" AVMString - UTF-8 string (no length prefix)")
print_info(" AVMUint64 - 64-bit unsigned integer (8 bytes, big-endian)")
print_info("")
print_info("Key difference from ABI types:")
print_info(" ABI string/bytes: 2-byte length prefix + data")
print_info(" AVM string/bytes: Raw data only (no prefix)")
# Step 2: AVMBytes type
print_step(2, "AVMBytes - Raw Byte Arrays")
avm_bytes_type = AVMType.BYTES
print_info(f"Type: {avm_bytes_type.value}")
print_info(f'is_avm_type("AVMBytes"): {is_avm_type("AVMBytes")}')
print_info("")
# Encode raw bytes
raw_bytes = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F]) # "Hello" in ASCII
avm_bytes_encoded = encode_avm_value_raw(AVMType.BYTES, raw_bytes)
print_info('Encoding raw bytes [0x48, 0x65, 0x6c, 0x6c, 0x6f] ("Hello"):')
print_info(f" Input bytes: {format_hex(raw_bytes)}")
print_info(f" Encoded: {format_hex(avm_bytes_encoded)}")
print_info(f" Length: {len(avm_bytes_encoded)} bytes")
print_info("")
# Decode back
avm_bytes_decoded = decode_avm_value(AVMType.BYTES, avm_bytes_encoded)
print_info("Decoding AVMBytes:")
print_info(f" Result type: {type(avm_bytes_decoded).__name__}")
print_info(f" Result: {format_hex(avm_bytes_decoded)}")
# Step 3: AVMString type
print_step(3, "AVMString - UTF-8 Strings (No Length Prefix)")
avm_string_type = AVMType.STRING
print_info(f"Type: {avm_string_type.value}")
print_info(f'is_avm_type("AVMString"): {is_avm_type("AVMString")}')
print_info("")
# Encode various strings
test_strings = ["Hello", "World!", "Algorand"]
for s in test_strings:
encoded = encode_avm_value_raw(AVMType.STRING, s)
print_info(f'"{s}":')
print_info(f" Encoded: {format_hex(encoded)}")
print_info(f" Length: {len(encoded)} bytes")
print_info("")
# Decode back
encoded_hello = encode_avm_value_raw(AVMType.STRING, "Hello")
decoded_hello = decode_avm_value(AVMType.STRING, encoded_hello)
print_info("Decoding AVMString:")
print_info(f" Input: {format_hex(encoded_hello)}")
print_info(f' Result: "{decoded_hello}"')
print_info(f" Result type: {type(decoded_hello).__name__}")
# Step 4: AVMUint64 type
print_step(4, "AVMUint64 - 64-bit Unsigned Integers")
avm_uint64_type = AVMType.UINT64
print_info(f"Type: {avm_uint64_type.value}")
print_info(f'is_avm_type("AVMUint64"): {is_avm_type("AVMUint64")}')
print_info("")
# Encode various uint64 values
test_numbers = [0, 1, 255, 1000, 1000000, 2**32 - 1, 2**64 - 1]
print_info("Encoding uint64 values (8-byte big-endian):")
for num in test_numbers:
encoded = encode_avm_value_raw(AVMType.UINT64, num)
print_info(f" {str(num).rjust(20)}: {format_hex(encoded)}")
print_info("")
# Decode back
encoded_1000 = encode_avm_value_raw(AVMType.UINT64, 1000)
decoded_1000 = decode_avm_value(AVMType.UINT64, encoded_1000)
print_info("Decoding AVMUint64:")
print_info(f" Input: {format_hex(encoded_1000)}")
print_info(f" Result: {decoded_1000}")
print_info(f" Result type: {type(decoded_1000).__name__}")
# Step 5: Compare AVM encoding vs ABI encoding
print_step(5, "AVM Encoding vs ABI Encoding Comparison")
print_info("Comparing how the same values encode differently:")
print_info("")
# String comparison
test_string = "Hello"
avm_string_encoded = encode_avm_value_raw(AVMType.STRING, test_string)
abi_string_type = abi.ABIType.from_string("string")
abi_string_encoded = abi_string_type.encode(test_string)
print_info(f'String: "{test_string}"')
print_info(f" AVM encoding: {format_hex(avm_string_encoded)}")
print_info(f" Length: {len(avm_string_encoded)} bytes (raw UTF-8 only)")
print_info(f" ABI encoding: {format_hex(abi_string_encoded)}")
print_info(f" Length: {len(abi_string_encoded)} bytes (2-byte prefix + UTF-8)")
length_value = (abi_string_encoded[0] << 8) | abi_string_encoded[1]
print_info(f" First 2 bytes: {format_hex(abi_string_encoded[:2])} = {length_value} (length)")
print_info("")
# Bytes comparison
test_bytes_array = bytes([0xDE, 0xAD, 0xBE, 0xEF])
avm_bytes_encoded_cmp = encode_avm_value_raw(AVMType.BYTES, test_bytes_array)
# ABI has no "bytes" type - closest is byte[] or string; we'll use static byte[4]
abi_byte4_type = abi.ABIType.from_string("byte[4]")
abi_byte4_encoded = abi_byte4_type.encode(list(test_bytes_array))
print_info("Bytes: [0xde, 0xad, 0xbe, 0xef]")
print_info(f" AVMBytes encoding: {format_hex(avm_bytes_encoded_cmp)}")
print_info(f" Length: {len(avm_bytes_encoded_cmp)} bytes (raw bytes only)")
print_info(f" ABI byte[4] encoding: {format_hex(abi_byte4_encoded)}")
print_info(f" Length: {len(abi_byte4_encoded)} bytes (static array, no prefix)")
print_info("")
# Uint64 comparison
test_uint64 = 1000
avm_uint64_encoded = encode_avm_value_raw(AVMType.UINT64, test_uint64)
abi_uint64_type = abi.ABIType.from_string("uint64")
abi_uint64_encoded = abi_uint64_type.encode(test_uint64)
print_info(f"Uint64: {test_uint64}")
print_info(f" AVM encoding: {format_hex(avm_uint64_encoded)}")
print_info(f" Length: {len(avm_uint64_encoded)} bytes")
print_info(f" ABI encoding: {format_hex(abi_uint64_encoded)}")
print_info(f" Length: {len(abi_uint64_encoded)} bytes")
print_info(f" Match: {avm_uint64_encoded == abi_uint64_encoded}")
print_info(" (uint64 encoding is identical - both are 8 bytes big-endian)")
# Step 6: is_avm_type helper function
print_step(6, "is_avm_type Helper Function")
print_info("Use is_avm_type() to check if a type string is an AVM type:")
print_info("")
type_checks = [
"AVMBytes",
"AVMString",
"AVMUint64",
"uint64",
"string",
"address",
"byte[]",
"(uint64,bool)",
]
for type_str in type_checks:
is_avm = is_avm_type(type_str)
print_info(f' is_avm_type("{type_str}"): {is_avm}')
# Step 7: AVMType enum values
print_step(7, "AVMType Enum Values")
print_info("The AVMType enum provides type-safe AVM type constants:")
print_info("")
print_info(f" AVMType.BYTES: value = '{AVMType.BYTES.value}'")
print_info(f" AVMType.STRING: value = '{AVMType.STRING.value}'")
print_info(f" AVMType.UINT64: value = '{AVMType.UINT64.value}'")
print_info("")
print_info("Access via enum member or string value:")
avm_bytes_match = AVMType("AVMBytes") == AVMType.BYTES
print_info(f" AVMType.BYTES == AVMType('AVMBytes'): {avm_bytes_match}")
# Step 8: When to use AVM types vs ABI types
print_step(8, "When to Use AVM Types vs ABI Types")
print_info("Use AVM types when:")
print_info(" - Working with raw AVM stack values (global/local state, box storage)")
print_info(" - Reading/writing app state where values have no length prefix")
print_info(" - The ARC-56 spec specifies AVMBytes, AVMString, or AVMUint64")
print_info("")
print_info("Use ABI types when:")
print_info(" - Encoding method arguments for ARC-4 ABI method calls")
print_info(" - Encoding method return values following ARC-4 specification")
print_info(' - The type is an ARC-4 type like "uint64", "string", "address"')
print_info("")
print_info("Example scenarios:")
print_info(" - App global state value stored as uint64 -> AVMUint64")
print_info(" - App global state key stored as string -> AVMString")
print_info(" - Box content stored as raw bytes -> AVMBytes")
print_info(' - ABI method arg of type "string" -> ABIStringType (with length prefix)')
print_info(' - ABI method return of type "uint64" -> ABIUintType (same encoding)')
# Step 9: Practical example - Encoding for app state
print_step(9, "Practical Example - Encoding for App State")
print_info("Simulating app state encoding (as seen in ARC-56 contracts):")
print_info("")
# Simulate a key-value pair in global state
state_key = "counter"
state_value = 42
# Keys are typically AVMString (no length prefix in state key)
encoded_key = encode_avm_value_raw(AVMType.STRING, state_key)
# Values can be AVMUint64 for integer values
encoded_value = encode_avm_value_raw(AVMType.UINT64, state_value)
print_info("Global state entry:")
print_info(f' Key: "{state_key}"')
print_info(f" Key encoded (AVMString): {format_hex(encoded_key)}")
print_info(f" Value: {state_value}")
print_info(f" Value encoded (AVMUint64): {format_hex(encoded_value)}")
print_info("")
# Decode back
decoded_key = decode_avm_value(AVMType.STRING, encoded_key)
decoded_value = decode_avm_value(AVMType.UINT64, encoded_value)
print_info("Decoding back:")
print_info(f' Key: "{decoded_key}"')
print_info(f" Value: {decoded_value}")
# Step 10: Round-trip verification
print_step(10, "Round-Trip Verification")
print_info("Verifying encode/decode round-trips preserve values:")
print_info("")
# AVMString round-trip
original_string = "AlgorandFoundation"
enc_string = encode_avm_value_raw(AVMType.STRING, original_string)
dec_string = decode_avm_value(AVMType.STRING, enc_string)
match_string = original_string == dec_string
print_info(f'AVMString "{original_string}": {"PASS" if match_string else "FAIL"}')
# AVMBytes round-trip
original_bytes = bytes([1, 2, 3, 4, 5, 6, 7, 8])
enc_bytes = encode_avm_value_raw(AVMType.BYTES, original_bytes)
dec_bytes = decode_avm_value(AVMType.BYTES, enc_bytes)
match_bytes = original_bytes == dec_bytes
print_info(f"AVMBytes [1,2,3,4,5,6,7,8]: {'PASS' if match_bytes else 'FAIL'}")
# AVMUint64 round-trip
original_uint64 = 9007199254740991 # Max safe integer
enc_uint64 = encode_avm_value_raw(AVMType.UINT64, original_uint64)
dec_uint64 = decode_avm_value(AVMType.UINT64, enc_uint64)
match_uint64 = original_uint64 == dec_uint64
print_info(f"AVMUint64 {original_uint64}: {'PASS' if match_uint64 else 'FAIL'}")
# Step 11: Summary
print_step(11, "Summary")
print_info("AVM Type Summary:")
print_info("")
print_info("Types:")
print_info(" AVMBytes - Raw bytes, no length prefix")
print_info(" AVMString - UTF-8 string, no length prefix")
print_info(" AVMUint64 - 8-byte big-endian unsigned integer")
print_info("")
print_info("AVMType Enum:")
print_info(" AVMType.BYTES - 'AVMBytes'")
print_info(" AVMType.STRING - 'AVMString'")
print_info(" AVMType.UINT64 - 'AVMUint64'")
print_info("")
print_info("Key Differences from ABI:")
print_info(" - AVMString has no 2-byte length prefix (ABI string does)")
print_info(" - AVMBytes is raw (ABI uses length-prefixed byte arrays)")
print_info(" - AVMUint64 encoding is identical to ABI uint64")
print_info("")
print_info("Use Cases:")
print_info(" - AVM types: App state, box storage, raw stack values")
print_info(" - ABI types: Method calls, ARC-4 encoded arguments/returns")
print_success("AVM Type Encoding example completed successfully!")
if __name__ == "__main__":
main()