Transactions | Algorand Developer Portal

Transactions

Algorand Python provides types for accessing fields of other transactions in a group, as well as creating and submitting inner transactions from your smart contract.

The following types are available:

Group Transactions Inner Transaction Field sets Inner Transaction
PaymentTransaction Payment PaymentInnerTransaction
KeyRegistrationTransaction KeyRegistration KeyRegistrationInnerTransaction
AssetConfigTransaction AssetConfig AssetConfigInnerTransaction
AssetTransferTransaction AssetTransfer AssetTransferInnerTransaction
AssetFreezeTransaction AssetFreeze AssetFreezeInnerTransaction
ApplicationCallTransaction ApplicationCall ApplicationCallInnerTransaction
Transaction InnerTransaction InnerTransactionResult

Group Transactions

Group transactions can be used as ARC-4 parameters or instantiated from a group index.

ARC-4 parameter

Group transactions can be used as parameters in ARC-4 method

For example to require a payment transaction in an ARC-4 ABI method:

import algopy

class MyContract(algopy.ARC4Contract):

@algopy.arc4.abimethod()

def process_payment(self, payment: algopy.gtxn.PaymentTransaction) -> None:

...

Group Index

Group transactions can also be created using the group index of the transaction. If instantiating one of the type specific transactions they will be checked to ensure the transaction is of the expected type. Transaction is not checked for a specific type and provides access to all transaction fields

For example, to obtain a reference to a payment transaction:

import algopy

@algopy.subroutine()

def process_payment(group_index: algopy.UInt64) -> None:

pay_txn = algopy.gtxn.PaymentTransaction(group_index)

...

Inner Transactions

Inner transactions are defined using the parameter types, and can then be submitted individually by calling the .submit() method, or as a group by calling submit_txns

Examples

Create and submit an inner transaction

from algopy import Account, UInt64, itxn, subroutine

@subroutine

def example(amount: UInt64, receiver: Account) -> None:

itxn.Payment(

amount=amount,

receiver=receiver,

fee=0,

).submit()

Accessing result of a submitted inner transaction

from algopy import Asset, itxn, subroutine

@subroutine

def example() -> Asset:

asset_txn = itxn.AssetConfig(

asset_name=b"Puya",

unit_name=b"PYA",

total=1000,

decimals=3,

fee=0,

).submit()

return asset_txn.created_asset

Submitting multiple transactions

from algopy import Asset, Bytes, itxn, log, subroutine

@subroutine

def example() -> tuple[Asset, Bytes]:

asset1_params = itxn.AssetConfig(

asset_name=b"Puya",

unit_name=b"PYA",

total=1000,

decimals=3,

fee=0,

)

app_params = itxn.ApplicationCall(

app_id=1234,

app_args=(Bytes(b"arg1"), Bytes(b"arg1"))
    )

asset1_txn, app_txn = itxn.submit_txns(asset1_params, app_params)

# log some details

log(app_txn.logs(0))
    log(asset1_txn.txn_id)
    log(app_txn.txn_id)
    return asset1_txn.created_asset, app_txn.logs(1)

Create an ARC-4 application, and then call it

from algopy import Bytes, arc4, itxn, subroutine

HELLO_WORLD_APPROVAL: bytes = ...

HELLO_WORLD_CLEAR: bytes = ...

@subroutine

def example() -> None:

# create an application

application_txn = itxn.ApplicationCall(

approval_program=HELLO_WORLD_APPROVAL,

clear_state_program=HELLO_WORLD_CLEAR,

fee=0,

).submit()

app = application_txn.created_app

# invoke an ABI method

call_txn = itxn.ApplicationCall(

app_id=app,

app_args=(arc4.arc4_signature("hello(string)string"), arc4.String("World")),

fee=0,

).submit()

# extract result

hello_world_result = arc4.String.from_log(call_txn.last_log)

Create and submit transactions in a loop

from algopy import Account, UInt64, itxn, subroutine

@subroutine

def example(receivers: tuple[Account, Account, Account]) -> None:

for receiver in receivers:

itxn.Payment(

amount=UInt64(1_000_000),

receiver=receiver,

fee=0,

).submit()

Limitations

Inner transactions are powerful, but currently do have some restrictions in how they are used.

Inner transaction objects cannot be passed to or returned from subroutines

from algopy import Application, Bytes, itxn, subroutine

@subroutine

def parameter_not_allowed(txn: itxn.PaymentInnerTransaction) -> None:

# this is a compile error

...

@subroutine

def return_not_allowed() -> itxn.PaymentInnerTransaction:

# this is a compile error

...

@subroutine

def passing_fields_allowed() -> Application:

txn = itxn.ApplicationCall(...).submit()

do_something(txn.txn_id, txn.logs(0))  # this is ok

return txn.created_app  # and this is ok

@subroutine

def do_something(txn_id: Bytes):  # this is just a regular subroutine

...

Inner transaction parameters cannot be reassigned without a .copy()

from algopy import itxn, subroutine

@subroutine

def example() -> None:

payment = itxn.Payment(...)

reassigned_payment = payment  # this is an error

copied_payment = payment.copy()  # this is ok

Inner transactions cannot be reassigned

from algopy import itxn, subroutine

@subroutine

def example() -> None:

payment_txn = itxn.Payment(...).submit()

reassigned_payment_txn = payment_txn  # this is an error

txn_id = payment_txn.txn_id  # this is ok

Inner transactions methods cannot be called if there is a subsequent inner transaction submitted or another subroutine is called

from algopy import itxn, subroutine

@subroutine

def example() -> None:

app_1 = itxn.ApplicationCall(...).submit()

log_from_call1 = app_1.logs(0)  # this is ok

# another inner transaction is submitted

itxn.ApplicationCall(...).submit()

# or another subroutine is called

call_some_other_subroutine()

app1_txn_id = app_1.txn_id  # this is ok, properties are still available

another_log_from_call1 = app_1.logs(1)  # this is not allowed as the array results may no longer be available, instead assign to a variable before submitting another transaction