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.
Implemented Types¶
These types are fully implemented in Python and behave identically to their AVM counterparts:
1. Cryptographic Operations¶
The following opcodes are demonstrated:
- op.sha256
- op.keccak256
- op.ecdsa_verify
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:
- op.addw
- op.bitlen
- op.getbit
- op.setbit_uint64
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_decompress():
    mock_result = (algopy.Bytes(b'x_coord'), algopy.Bytes(b'y_coord'))
    with patch('algopy.op.EllipticCurve.decompress', return_value=mock_result) as mock_decompress:
        result = algopy.op.EllipticCurve.decompress(
            algopy.op.EC.BN254g1,
            algopy.Bytes(b'compressed_point')
        )
    assert result == mock_result
    mock_decompress.assert_called_once_with(
        algopy.op.EC.BN254g1,
        algopy.Bytes(b'compressed_point')
    )
test_mock_elliptic_curve_decompress()
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:
- Control complex operations’ behavior not covered by implemented and emulated types. 
- Test edge cases and error conditions. 
- Isolate contract logic from external dependencies.