Group Transaction Comprehension
Video Tutorial
Generate Two Accounts
Create two accounts and add their private keys to your .env
file.
from algosdk.account import generate_account
private_key, address = generate_account()
print(private_key, address)
Generate One Asset
Create one asset and place its ID in your .env
file.
Note: Remember the account that owns the asset, as it will be used for asset transfers later.
from algosdk.transaction import AssetCreateTxn, wait_for_confirmation
from algosdk.v2client.algod import AlgodClient
from algosdk.account import address_from_private_key
import os
from dotenv import load_dotenv
load_dotenv()
algod_token = os.getenv('algod_token')
algod_server = os.getenv('algod_server')
algod_client = AlgodClient(algod_token, algod_server)
private_key = os.getenv('private_key')
address = address_from_private_key(private_key)
params = algod_client.suggested_params()
asset_create_txn = AssetCreateTxn(
sender=address,
sp=params,
total=10,
decimals=0,
default_frozen=False,
manager=address,
reserve=address,
asset_name = 'Test Asset One',
unit_name= 'TEST#1',
)
signed_asset_create_txn = asset_create_txn.sign(private_key)
tx_id = algod_client.send_transaction(signed_asset_create_txn)
print(tx_id)
wait_for_confirmation(algod_client, tx_id)
tx_info = algod_client.pending_transaction_info(tx_id)
print(tx_info)
Launching Contracts
Note: Adjust approval and clear teal filenames, as well as global/local states, as needed.
In this tutorial, we use a second contract, SecondApp
, which has 1 local state uint
.
from algosdk.transaction import ApplicationCreateTxn, StateSchema, OnComplete, wait_for_confirmation, PaymentTxn
from algosdk.account import address_from_private_key
from algosdk.v2client.algod import AlgodClient
from algosdk import logic
from dotenv import load_dotenv
import base64
import os
load_dotenv()
node_token = os.getenv('algod_token')
node_server = os.getenv('algod_server')
algod_client = AlgodClient(node_token, node_server)
private_key = os.getenv('private_key')
address = address_from_private_key(private_key)
params = algod_client.suggested_params()
approval_teal_file_name = 'TransactionComp.approval.teal'
clear_teal_file_name = 'TransactionComp.clear.teal'
with open(f'./{approval_teal_file_name}', 'r') as f:
approval_teal_source = f.read()
with open(f'{clear_teal_file_name}', 'r') as f:
clear_teal_source = f.read()
approval_result = algod_client.compile(approval_teal_source)
approval_program = base64.b64decode(approval_result['result'])
clear_result = algod_client.compile(clear_teal_source)
clear_program = base64.b64decode(clear_result['result'])
global_schema = StateSchema(num_uints=0, num_byte_slices=0)
local_schema = StateSchema(num_uints=0, num_byte_slices=0)
txn = ApplicationCreateTxn(
sender = address,
sp = params,
on_complete=OnComplete.NoOpOC,
approval_program=approval_program,
clear_program=clear_program,
global_schema=global_schema,
local_schema=local_schema,
)
signed_txn = txn.sign(private_key)
try:
txid = algod_client.send_transaction(signed_txn)
except Exception as e:
print(e)
print(f'Tx ID: {txid}')
wait_for_confirmation(algod_client, txid)
tx_info = algod_client.pending_transaction_info(txid)
print(f'App ID: {tx_info['application-index']}')
app_address = logic.get_application_address(tx_info['application-index'])
print(f'Application Address: {app_address}')
activate_contract = PaymentTxn(
sender = address,
sp = params,
receiver = app_address,
amt = 100_000
)
signed_activation = activate_contract.sign(private_key)
activation_tx = algod_client.send_transaction(signed_activation)
print(f'MBR For Contract to be Active Account Funded: {activation_tx}')
Group Transaction Comprehension (GTXN)
In this section, we put it all together and use relative indexing in our group transaction method to enhance scalability.
from algopy import ARC4Contract, Txn, Bytes, UInt64, String, itxn, Global, OnCompleteAction, gtxn, TransactionType
from algopy.arc4 import abimethod, Address, arc4_signature, abi_call
from algopy.arc4 import UInt64 as arc4UInt64
class TransactionComp(ARC4Contract):
def __init__(self) -> None:
pass
@abimethod
def txn_comprehension(self) -> tuple[Address, Address, Bytes, arc4UInt64, arc4UInt64, String, String, arc4UInt64, Address, arc4UInt64, String, Address]:
sender = Address(Txn.sender)
foreign_accounts = Address(Txn.accounts(1))
app_arg_1 = Txn.application_args(0)
txn_foreign_applications = Txn.applications(0)
txn_foreign_application_id = arc4UInt64(txn_foreign_applications.id)
foreign_asset_1 = Txn.assets(0)
foreign_asset_id = arc4UInt64(foreign_asset_1.id)
foreign_asset_name = String.from_bytes(foreign_asset_1.name)
foreign_asset_unit_name = String.from_bytes(foreign_asset_1.unit_name)
sender_foreign_asset_balance = arc4UInt64(foreign_asset_1.balance(Txn.sender))
foreign_asset_creator_address = Address(foreign_asset_1.creator)
fee_for_this_transaction = arc4UInt64(Txn.fee)
transaction_type = String.from_bytes(Txn.type)
transaction_id = Address(Txn.tx_id)
return sender, foreign_accounts, app_arg_1, txn_foreign_application_id, foreign_asset_id, foreign_asset_name, foreign_asset_unit_name, sender_foreign_asset_balance, foreign_asset_creator_address, fee_for_this_transaction, transaction_type, transaction_id
@abimethod
def inner_txn_comprehension(self) -> tuple[UInt64, UInt64, UInt64]:
asset_config_txn = itxn.AssetConfig(
total=1,
unit_name="TEST#1",
asset_name="TEST ASSET ONE",
decimals=0,
default_frozen=False,
manager=Global.current_application_address,
reserve=Global.current_application_address,
fee=Global.min_txn_fee,
).submit()
asset_config_txn_2 = itxn.AssetConfig(
total=1,
unit_name="TEST#2",
asset_name="TEST ASSET TWO",
decimals=0,
default_frozen=False,
manager=Global.current_application_address,
reserve=Global.current_application_address,
fee=Global.min_txn_fee,
)
asset_config_txn_3 = itxn.AssetConfig(
total=1,
unit_name="TEST#3",
asset_name="TEST ASSET THREE",
decimals=0,
default_frozen=False,
manager=Global.current_application_address,
reserve=Global.current_application_address,
fee=Global.min_txn_fee,
)
submit_tx_1, submit_tx_2 = itxn.submit_txns(asset_config_txn_2, asset_config_txn_3)
return asset_config_txn.created_asset.id, submit_tx_1.created_asset.id, submit_tx_2.created_asset.id
@abimethod
def inner_txn_two(self) -> UInt64:
method_signature = arc4_signature('return_nothing()void')
itxn.ApplicationCall(
app_id=727594507,
app_args=(method_signature,),
on_completion=OnCompleteAction.NoOp,
fee=Global.min_txn_fee
).submit()
result, txn = abi_call[UInt64](
'local_state_return_something',
app_id=727594507,
on_completion=OnCompleteAction.OptIn,
fee=Global.min_txn_fee
)
txn = abi_call(
'return_nothing',
app_id=727594507,
fee=Global.min_txn_fee
)
return result
@abimethod
def group_txn_comprehension(
self,
first_transaction: gtxn.PaymentTransaction
) -> tuple[UInt64, UInt64, UInt64]:
assert first_transaction.amount == 1000
second_transaction = gtxn.Transaction(Txn.group_index + 1)
if second_transaction.type == TransactionType.Payment:
second_transaction_amount = second_transaction.amount
assert second_transaction_amount == 2000
elif second_transaction.type == TransactionType.AssetTransfer:
second_transaction_amount = second_transaction.asset_amount
assert second_transaction_amount == 1
third_transaction = gtxn.Transaction(Txn.group_index + 2)
if third_transaction.type == TransactionType.Payment:
third_transaction_amount = third_transaction.amount
assert third_transaction_amount == 2000
elif third_transaction.type == TransactionType.AssetTransfer:
third_transaction_amount = third_transaction.asset_amount
assert third_transaction_amount == 1
return first_transaction.amount, second_transaction_amount, third_transaction_amount