AVM Opcodes | Algorand Developer Portal

AVM Opcodes

The coverage file provides a comprehensive list of all opcodes and their respective types, categorized as Mockable, Emulated, or Native within the algorand-python-testing package. This section highlights a subset of opcodes and types that typically require interaction with the test context manager.

Native opcodes are assumed to function as they do in the Algorand Virtual Machine, given their stateless nature. If you encounter issues with any Native opcodes, please raise an issue in the algorand-python-testing repo or contribute a PR following the Contributing guide.

import algopy

from algopy_testing import algopy_testing_context

# Create the context manager for snippets below

ctx_manager = algopy_testing_context()

# Enter the context

context = ctx_manager.__enter__()

Implemented Types

These types are fully implemented in Python and behave identically to their AVM counterparts:

1. Cryptographic Operations

The following opcodes are demonstrated:

from algopy import op

# SHA256 hash

data = algopy.Bytes(b"Hello, World!")

hashed = op.sha256(data)

# Keccak256 hash

keccak_hashed = op.keccak256(data)

# ECDSA verification

message_hash = bytes.fromhex("f809fd0aa0bb0f20b354c6b2f86ea751957a4e262a546bd716f34f69b9516ae1")

sig_r = bytes.fromhex("18d96c7cda4bc14d06277534681ded8a94828eb731d8b842e0da8105408c83cf")

sig_s = bytes.fromhex("7d33c61acf39cbb7a1d51c7126f1718116179adebd31618c4604a1f03b5c274a")

pubkey_x = bytes.fromhex("f8140e3b2b92f7cbdc8196bc6baa9ce86cf15c18e8ad0145d50824e6fa890264")

pubkey_y = bytes.fromhex("bd437b75d6f1db67155a95a0da4b41f2b6b3dc5d42f7db56238449e404a6c0a3")

result = op.ecdsa_verify(op.ECDSA.Secp256r1, message_hash, sig_r, sig_s, pubkey_x, pubkey_y)

assert result

2. Arithmetic and Bitwise Operations

The following opcodes are demonstrated:

from algopy import op

# Addition with carry

result, carry = op.addw(algopy.UInt64(2**63), algopy.UInt64(2**63))

# Bitwise operations

value = algopy.UInt64(42)

bit_length = op.bitlen(value)

is_bit_set = op.getbit(value, 3)

new_value = op.setbit_uint64(value, 2, 1)

For a comprehensive list of all opcodes and types, refer to the coverage page.

Emulated Types Requiring Transaction Context

These types necessitate interaction with the transaction context:

algopy.op.Global

from algopy import op

class MyContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def check_globals(self) -> algopy.UInt64:

return op.Global.min_txn_fee + op.Global.min_balance

... # setup context (below assumes available under 'ctx' variable)

context.ledger.patch_global_fields(

min_txn_fee=algopy.UInt64(1000),

min_balance=algopy.UInt64(100000)
)

contract = MyContract()

result = contract.check_globals()

assert result == algopy.UInt64(101000)

algopy.op.Txn

from algopy import op

class MyContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def check_txn_fields(self) -> algopy.arc4.Address:

return algopy.arc4.Address(op.Txn.sender)

... # setup context (below assumes available under 'ctx' variable)

contract = MyContract()

custom_sender = context.any.account()

with context.txn.create_group(active_txn_overrides={"sender": custom_sender}):

result = contract.check_txn_fields()

assert result == custom_sender

algopy.op.AssetHoldingGet

from algopy import op

class AssetContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def check_asset_holding(self, account: algopy.Account, asset: algopy.Asset) -> algopy.UInt64:

balance, _ = op.AssetHoldingGet.asset_balance(account, asset)

return balance

... # setup context (below assumes available under 'ctx' variable)

asset = context.any.asset(total=algopy.UInt64(1000000))

account = context.any.account(opted_asset_balances={asset.id: algopy.UInt64(5000)})

contract = AssetContract()

result = contract.check_asset_holding(account, asset)

assert result == algopy.UInt64(5000)

algopy.op.AppGlobal

from algopy import op

class StateContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def set_and_get_state(self, key: algopy.Bytes, value: algopy.UInt64) -> algopy.UInt64:

op.AppGlobal.put(key, value)

return op.AppGlobal.get_uint64(key)

... # setup context (below assumes available under 'ctx' variable)

contract = StateContract()

key, value = algopy.Bytes(b"test_key"), algopy.UInt64(42)

result = contract.set_and_get_state(key, value)

assert result == value

stored_value = context.ledger.get_global_state(contract, key)

assert stored_value == 42

algopy.op.Block

from algopy import op

class BlockInfoContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def get_block_seed(self) -> algopy.Bytes:

return op.Block.blk_seed(1000)

... # setup context (below assumes available under 'ctx' variable)

context.ledger.set_block(1000, seed=123456, timestamp=1625097600)

contract = BlockInfoContract()

seed = contract.get_block_seed()

assert seed == algopy.op.itob(123456)

algopy.op.AcctParamsGet

from algopy import op

class AccountParamsContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def get_account_balance(self, account: algopy.Account) -> algopy.UInt64:

balance, exists = op.AcctParamsGet.acct_balance(account)

assert exists

return balance

... # setup context (below assumes available under 'ctx' variable)

account = context.any.account(balance=algopy.UInt64(1000000))

contract = AccountParamsContract()

balance = contract.get_account_balance(account)

assert balance == algopy.UInt64(1000000)

algopy.op.AppParamsGet

class AppParamsContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def get_app_creator(self, app_id: algopy.Application) -> algopy.arc4.Address:

creator, exists = algopy.op.AppParamsGet.app_creator(app_id)

assert exists

return algopy.arc4.Address(creator)

... # setup context (below assumes available under 'ctx' variable)

contract = AppParamsContract()

app = context.any.application()

creator = contract.get_app_creator(app)

assert creator == context.default_sender

algopy.op.AssetParamsGet

from algopy import op

class AssetParamsContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def get_asset_total(self, asset_id: algopy.UInt64) -> algopy.UInt64:

total, exists = op.AssetParamsGet.asset_total(asset_id)

assert exists

return total

... # setup context (below assumes available under 'ctx' variable)

asset = context.any.asset(total=algopy.UInt64(1000000), decimals=algopy.UInt64(6))

contract = AssetParamsContract()

total = contract.get_asset_total(asset.id)

assert total == algopy.UInt64(1000000)

algopy.op.Box

from algopy import op

class BoxStorageContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def store_and_retrieve(self, key: algopy.Bytes, value: algopy.Bytes) -> algopy.Bytes:

op.Box.put(key, value)

retrieved_value, exists = op.Box.get(key)

assert exists

return retrieved_value

... # setup context (below assumes available under 'ctx' variable)

contract = BoxStorageContract()

key, value = algopy.Bytes(b"test_key"), algopy.Bytes(b"test_value")

result = contract.store_and_retrieve(key, value)

assert result == value

stored_value = context.ledger.get_box(contract, key)

assert stored_value == value.value

Mockable Opcodes

These opcodes are mockable in algorand-python-testing, allowing for controlled testing of complex operations:

algopy.compile_contract

from unittest.mock import patch, MagicMock

import algopy

mocked_response = MagicMock()

mocked_response.local_bytes = algopy.UInt64(4)

class MockContract(algopy.Contract):

...

class ContractFactory(algopy.ARC4Contract):

...

@algopy.arc4.abimethod

def compile_and_get_bytes(self) -> algopy.UInt64:

contract_response = algopy.compile_contract(MockContract)

return contract_response.local_bytes

... # setup context (below assumes available under 'ctx' variable)

contract = ContractFactory()

with patch('algopy.compile_contract', return_value=mocked_response):

assert contract.compile_and_get_bytes() == 4

algopy.arc4.abi_call

import unittest

from unittest.mock import patch, MagicMock

import algopy

import typing

class MockAbiCall:

def __call__(self, *args: typing.Any, **_kwargs: typing.Any) -> tuple[typing.Any, typing.Any]:

return (

algopy.arc4.UInt64(11),

MagicMock(),

)

def __getitem__(self, _item: object) -> typing.Self:

return self

class MyContract(algopy.ARC4Contract):

@algopy.arc4.abimethod

def my_method(self, arg1: algopy.UInt64, arg2: algopy.UInt64) -> algopy.UInt64:

return algopy.arc4.abi_call[algopy.arc4.UInt64]("my_other_method", arg1, arg2)[0].native

... # setup context (below assumes available under 'ctx' variable)

contract = MyContract()

with patch('algopy.arc4.abi_call', MockAbiCall()):

result = contract.my_method(algopy.UInt64(10), algopy.UInt64(1))

assert result == 11

algopy.op.vrf_verify

from unittest.mock import patch, MagicMock

import algopy

def test_mock_vrf_verify():

mock_result = (algopy.Bytes(b'mock_output'), True)

with patch('algopy.op.vrf_verify', return_value=mock_result) as mock_vrf_verify:

result = algopy.op.vrf_verify(

algopy.op.VrfVerify.VrfAlgorand,

algopy.Bytes(b'proof'),

algopy.Bytes(b'message'),

algopy.Bytes(b'public_key')
        )

assert result == mock_result

mock_vrf_verify.assert_called_once_with(

algopy.op.VrfVerify.VrfAlgorand,

algopy.Bytes(b'proof'),

algopy.Bytes(b'message'),

algopy.Bytes(b'public_key')
    )

test_mock_vrf_verify()

algopy.op.EllipticCurve

from unittest.mock import patch, MagicMock

import algopy

def test_mock_elliptic_curve_add():

mock_result = algopy.Bytes(b'result')

with patch('algopy.op.EllipticCurve.add', return_value=mock_result) as mock_add:

result = algopy.op.EllipticCurve.add(

algopy.op.EC.BN254g1,

algopy.Bytes(b'a'),

algopy.Bytes(b'b')
        )

assert result == mock_result

mock_add.assert_called_once_with(

algopy.op.EC.BN254g1,

algopy.Bytes(b'a'),

algopy.Bytes(b'b'),
    )

test_mock_elliptic_curve_add()

These examples demonstrate how to mock key mockable opcodes in algorand-python-testing. Use similar techniques (in your preferred testing framework) for other mockable opcodes like algopy.compile_logicsig, algopy.arc4.arc4_create, and algopy.arc4.arc4_update.

Mocking these opcodes allows you to:

  1. Control complex operations’ behavior not covered by implemented and emulated types.
  2. Test edge cases and error conditions.
  3. Isolate contract logic from external dependencies.
ctx_manager.__exit__(None, None, None)