Repository: algorand/pyteal (language: python)

Issue:
Support for creating inner transactions
## Summary
algorand/go-algorand#2661 introduced application actions, aka inner transactions which application can create. This issue is to expose that functionality in PyTeal as well.

## Scope

Due to the simplicity of the `tx_begin`, `tx_field`, and `tx_submit` opcodes, the design is also pretty simple.

Four new functions should be created:

1. `InnerTxnBuilder.Begin()`: This will begin an inner transaction. It will evaluate to the `tx_begin` opcode.

2. `InnerTxnBuilder.SetField(field: TxnField, value: Expr)`: This will set a single field on an inner transaction. This must be called after `InnerTxnBuilder.Begin()` to be a valid operation. The `field` argument must be from the  existing [`TxnField` enum](https://github.com/algorand/pyteal/blob/7186838c8f44899b71e8ac61acc0ad231c758266/pyteal/ast/txn.py#L30). The `value` argument must be a PyTeal expression which evaluates to a type that is compatible with the indicated `field` type. The compiler will throw an error if the types do not match, for instance if a byteslice is set to the `TxnField.amount` field.

3. `InnerTxnBuilder.SetFields(values: Dict[TxnField, Expr])`: This function is similar to `InnerTxnBuilder.SetField`, except it can accept multiple fields and values for convenience. The `values` argument must be a dictionary from `TxnField` to PyTeal expressions. Note that starting in Python 3.6 (which is coincidentally the lowest version PyTeal supports), dictionaries have a defined order, so this will not introduce any nondeterminism.

4. `InnerTxnBuilder.Submit()`: This will submit an inner transaction. It will evaluate to the `tx_submit` opcode.

PyTeal will not do any validation of inner transaction fields beyond the simple typechecking described above. This means that when the AVM adds support for more inner transaction features (e.g. the `acfg` txn type), PyTeal does not need to be updated.

### Example

A PyTeal-ized version of https://github.com/algorand/go-algorand/blob/e32b2c2ef9423526bbec7d749d912d3a6153410b/test/scripts/e2e_subs/tealprogs/app-escrow.teal#L116-L142

```python
withdraw_amount = Btoi(Txn.application_args[1])
handle_withdraw = Seq(
    App.localPut(Int(0), balance_key, App.localGet(Int(0), balance_key) - withdraw_amount),
    InnerTxnBuilder.Begin(),
    # set one field
    InnerTxnBuilder.SetField(TxnField.type_enum, TxnType.Payment),
    # set multiple fields at once
    InnerTxnBuilder.SetFields({
        TxnField.amount: withdraw_amount,
        TxnField.receiver: Txn.sender()
    }),
    InnerTxnBuilder.Submit(),
    Approve(),
)
```

## Priority
Needed for TEAL 5 support.
Dynamic Array Reference
## Problem

I'd like to be able to reference the elements of the Txn.application_args array dynamically using the looping construct with ScratchVars.  Currently this is not supported but I see support is available in the Gtxn array.

example of what I'd like to do:
```python
def set_properties(acct, args, start):
    idx = ScratchVar()

    init = idx.store(Int(start))
    cond = idx.load()<args.length()
    step = idx.store(idx.load() + Int(2))

    return Seq([
        For(init, cond, step).Do(
            App.localPut(acct, args[idx.load()], args[idx.load() + Int(1)])
        ),
        Int(1)
    ])

```
This may be supported in some way but I can't seem to find it.

## Solution

Accept the Union[int,Expr] in the Txn array items `__getitem__` call 




Discussion / hints:

https://github.com/algorand/go-algorand/pull/2847 this may be a prerequisite?
This will be added for TEAL 5. We will also support dynamically getting arguments with [`Arg`](https://pyteal.readthedocs.io/en/stable/api.html#pyteal.Arg) for LogicSigs.

Fix patch:
diff --git a/README.md b/README.md
index e613d355e..f2c7d1bc1 100644
--- a/README.md
+++ b/README.md
@@ -45,10 +45,10 @@ Active venv:
 Pip install PyTeal in editable state
  * `pip install -e .`
 
-Install dependencies :
+Install dependencies:
 * `pip3 install -r requirements.txt`
  
-Type checking using mypy
+Type checking using mypy:
 * `mypy pyteal`
 
 Run tests:
diff --git a/docs/accessing_transaction_field.rst b/docs/accessing_transaction_field.rst
index a5a3c877c..a19410073 100644
--- a/docs/accessing_transaction_field.rst
+++ b/docs/accessing_transaction_field.rst
@@ -12,62 +12,66 @@ Transaction Fields
 Information about the current transaction being evaluated can be obtained using the :any:`Txn`
 object. Below are the PyTeal expressions that refer to transaction fields:
 
-================================================================================ ========================= =======================================================================
-Operator                                  Type                      Notes
-================================================================================ ========================= =======================================================================
-:any:`Txn.sender() <TxnObject.sender>`                                           :code:`TealType.bytes`    32 byte address
-:any:`Txn.fee() <TxnObject.fee>`                                                 :code:`TealType.uint64`   in microAlgos
-:any:`Txn.first_valid() <TxnObject.first_valid>`                                 :code:`TealType.uint64`   round number 
-:any:`Txn.last_valid() <TxnObject.last_valid>`                                   :code:`TealType.uint64`   round number
-:any:`Txn.note() <TxnObject.note>`                                               :code:`TealType.bytes`    transaction note in bytes
-:any:`Txn.lease() <TxnObject.lease>`                                             :code:`TealType.bytes`    transaction lease in bytes
-:any:`Txn.receiver() <TxnObject.receiver>`                                       :code:`TealType.bytes`    32 byte address
-:any:`Txn.amount() <TxnObject.amount>`                                           :code:`TealType.uint64`   in microAlgos
-:any:`Txn.close_remainder_to() <TxnObject.close_remainder_to>`                   :code:`TealType.bytes`    32 byte address
-:any:`Txn.vote_pk() <TxnObject.vote_pk>`                                         :code:`TealType.bytes`    32 byte address
-:any:`Txn.selection_pk() <TxnObject.selection_pk>`                               :code:`TealType.bytes`    32 byte address
-:any:`Txn.vote_first() <TxnObject.vote_first>`                                   :code:`TealType.uint64`
-:any:`Txn.vote_last() <TxnObject.vote_last>`                                     :code:`TealType.uint64`
-:any:`Txn.vote_key_dilution() <TxnObject.vote_key_dilution>`                     :code:`TealType.uint64`
-:any:`Txn.type() <TxnObject.type>`                                               :code:`TealType.bytes`
-:any:`Txn.type_enum() <TxnObject.type_enum>`                                     :code:`TealType.uint64`   see table below
-:any:`Txn.xfer_asset() <TxnObject.xfer_asset>`                                   :code:`TealType.uint64`   ID of asset being transferred
-:any:`Txn.asset_amount() <TxnObject.asset_amount>`                               :code:`TealType.uint64`   value in Asset's units
-:any:`Txn.asset_sender() <TxnObject.asset_sender>`                               :code:`TealType.bytes`    32 byte address, causes clawback of all value if sender is the clawback
-:any:`Txn.asset_receiver() <TxnObject.asset_receiver>`                           :code:`TealType.bytes`    32 byte address
-:any:`Txn.asset_close_to() <TxnObject.asset_close_to>`                           :code:`TealType.bytes`    32 byte address
-:any:`Txn.group_index() <TxnObject.group_index>`                                 :code:`TealType.uint64`   position of this transaction within a transaction group, starting at 0
-:any:`Txn.tx_id() <TxnObject.tx_id>`                                             :code:`TealType.bytes`    the computed ID for this transaction, 32 bytes
-:any:`Txn.application_id() <TxnObject.application_id>`                           :code:`TealType.uint64`
-:any:`Txn.on_completion() <TxnObject.on_completion>`                             :code:`TealType.uint64`
-:any:`Txn.approval_program() <TxnObject.approval_program>`                       :code:`TealType.bytes`
-:any:`Txn.clear_state_program() <TxnObject.clear_state_program>`                 :code:`TealType.bytes`
-:any:`Txn.rekey_to() <TxnObject.rekey_to>`                                       :code:`TealType.bytes`    32 byte address
-:any:`Txn.config_asset() <TxnObject.config_asset>`                               :code:`TealType.uint64`   ID of asset being configured
-:any:`Txn.config_asset_total() <TxnObject.config_asset_total>`                   :code:`TealType.uint64`
-:any:`Txn.config_asset_decimals() <TxnObject.config_asset_decimals>`             :code:`TealType.uint64`
-:any:`Txn.config_asset_default_frozen() <TxnObject.config_asset_default_frozen>` :code:`TealType.uint64`
-:any:`Txn.config_asset_unit_name() <TxnObject.config_asset_unit_name>`           :code:`TealType.bytes`
-:any:`Txn.config_asset_name() <TxnObject.config_asset_name>`                     :code:`TealType.bytes`
-:any:`Txn.config_asset_url() <TxnObject.config_asset_url>`                       :code:`TealType.bytes`
-:any:`Txn.config_asset_metadata_hash() <TxnObject.config_asset_metadata_hash>`   :code:`TealType.bytes`
-:any:`Txn.config_asset_manager() <TxnObject.config_asset_manager>`               :code:`TealType.bytes`    32 byte address
-:any:`Txn.config_asset_reserve() <TxnObject.config_asset_reserve>`               :code:`TealType.bytes`    32 byte address
-:any:`Txn.config_asset_freeze() <TxnObject.config_asset_freeze>`                 :code:`TealType.bytes`    32 byte address
-:any:`Txn.config_asset_clawback() <TxnObject.config_asset_clawback>`             :code:`TealType.bytes`    32 byte address
-:any:`Txn.freeze_asset() <TxnObject.freeze_asset>`                               :code:`TealType.uint64`
-:any:`Txn.freeze_asset_account() <TxnObject.freeze_asset_account>`               :code:`TealType.bytes`    32 byte address
-:any:`Txn.freeze_asset_frozen() <TxnObject.freeze_asset_frozen>`                 :code:`TealType.uint64`
-:any:`Txn.global_num_uints() <TxnObject.global_num_uints>`                       :code:`TealType.uint64`   Maximum global integers in app schema
-:any:`Txn.global_num_byte_slices() <TxnObject.global_num_byte_slices>`           :code:`TealType.uint64`   Maximum global byte strings in app schema
-:any:`Txn.local_num_uints() <TxnObject.local_num_uints>`                         :code:`TealType.uint64`   Maximum local integers in app schema
-:any:`Txn.local_num_byte_slices() <TxnObject.local_num_byte_slices>`             :code:`TealType.uint64`   Maximum local byte strings in app schema
-:any:`Txn.extra_program_pages() <TxnObject.extra_program_pages>`                 :code:`TealType.uint64`   Number of extra program pages for app
-:any:`Txn.application_args <TxnObject.application_args>`                         :code:`TealType.bytes[]`  Array of application arguments
-:any:`Txn.accounts <TxnObject.accounts>`                                         :code:`TealType.bytes[]`  Array of application accounts
-:any:`Txn.assets <TxnObject.assets>`                                             :code:`TealType.uint64[]` Array of application assets
-:any:`Txn.applications <TxnObject.applications>`                                 :code:`TealType.uint64[]` Array of applications
-================================================================================ ========================= =======================================================================
+================================================================================ ========================= ================ ============================================================================
+Operator                                                                         Type                      Min TEAL Version Notes
+================================================================================ ========================= ================ ============================================================================
+:any:`Txn.sender() <TxnObject.sender>`                                           :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.fee() <TxnObject.fee>`                                                 :code:`TealType.uint64`   2                in microAlgos
+:any:`Txn.first_valid() <TxnObject.first_valid>`                                 :code:`TealType.uint64`   2                round number 
+:any:`Txn.last_valid() <TxnObject.last_valid>`                                   :code:`TealType.uint64`   2                round number
+:any:`Txn.note() <TxnObject.note>`                                               :code:`TealType.bytes`    2                transaction note in bytes
+:any:`Txn.lease() <TxnObject.lease>`                                             :code:`TealType.bytes`    2                transaction lease in bytes
+:any:`Txn.receiver() <TxnObject.receiver>`                                       :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.amount() <TxnObject.amount>`                                           :code:`TealType.uint64`   2                in microAlgos
+:any:`Txn.close_remainder_to() <TxnObject.close_remainder_to>`                   :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.vote_pk() <TxnObject.vote_pk>`                                         :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.selection_pk() <TxnObject.selection_pk>`                               :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.vote_first() <TxnObject.vote_first>`                                   :code:`TealType.uint64`   2
+:any:`Txn.vote_last() <TxnObject.vote_last>`                                     :code:`TealType.uint64`   2
+:any:`Txn.vote_key_dilution() <TxnObject.vote_key_dilution>`                     :code:`TealType.uint64`   2
+:any:`Txn.nonparticipation() <TxnObject.nonparticipation>`                       :code:`TealType.uint64`   5                Marks an account nonparticipating for rewards
+:any:`Txn.type() <TxnObject.type>`                                               :code:`TealType.bytes`    2
+:any:`Txn.type_enum() <TxnObject.type_enum>`                                     :code:`TealType.uint64`   2                see table below
+:any:`Txn.xfer_asset() <TxnObject.xfer_asset>`                                   :code:`TealType.uint64`   2                ID of asset being transferred
+:any:`Txn.asset_amount() <TxnObject.asset_amount>`                               :code:`TealType.uint64`   2                value in Asset's units
+:any:`Txn.asset_sender() <TxnObject.asset_sender>`                               :code:`TealType.bytes`    2                32 byte address, causes clawback of all value if sender is the clawback
+:any:`Txn.asset_receiver() <TxnObject.asset_receiver>`                           :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.asset_close_to() <TxnObject.asset_close_to>`                           :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.group_index() <TxnObject.group_index>`                                 :code:`TealType.uint64`   2                position of this transaction within a transaction group, starting at 0
+:any:`Txn.tx_id() <TxnObject.tx_id>`                                             :code:`TealType.bytes`    2                the computed ID for this transaction, 32 bytes
+:any:`Txn.application_id() <TxnObject.application_id>`                           :code:`TealType.uint64`   2
+:any:`Txn.on_completion() <TxnObject.on_completion>`                             :code:`TealType.uint64`   2
+:any:`Txn.approval_program() <TxnObject.approval_program>`                       :code:`TealType.bytes`    2
+:any:`Txn.clear_state_program() <TxnObject.clear_state_program>`                 :code:`TealType.bytes`    2
+:any:`Txn.rekey_to() <TxnObject.rekey_to>`                                       :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.config_asset() <TxnObject.config_asset>`                               :code:`TealType.uint64`   2                ID of asset being configured
+:any:`Txn.config_asset_total() <TxnObject.config_asset_total>`                   :code:`TealType.uint64`   2
+:any:`Txn.config_asset_decimals() <TxnObject.config_asset_decimals>`             :code:`TealType.uint64`   2
+:any:`Txn.config_asset_default_frozen() <TxnObject.config_asset_default_frozen>` :code:`TealType.uint64`   2
+:any:`Txn.config_asset_unit_name() <TxnObject.config_asset_unit_name>`           :code:`TealType.bytes`    2
+:any:`Txn.config_asset_name() <TxnObject.config_asset_name>`                     :code:`TealType.bytes`    2
+:any:`Txn.config_asset_url() <TxnObject.config_asset_url>`                       :code:`TealType.bytes`    2
+:any:`Txn.config_asset_metadata_hash() <TxnObject.config_asset_metadata_hash>`   :code:`TealType.bytes`    2
+:any:`Txn.config_asset_manager() <TxnObject.config_asset_manager>`               :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.config_asset_reserve() <TxnObject.config_asset_reserve>`               :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.config_asset_freeze() <TxnObject.config_asset_freeze>`                 :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.config_asset_clawback() <TxnObject.config_asset_clawback>`             :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.freeze_asset() <TxnObject.freeze_asset>`                               :code:`TealType.uint64`   2
+:any:`Txn.freeze_asset_account() <TxnObject.freeze_asset_account>`               :code:`TealType.bytes`    2                32 byte address
+:any:`Txn.freeze_asset_frozen() <TxnObject.freeze_asset_frozen>`                 :code:`TealType.uint64`   2
+:any:`Txn.global_num_uints() <TxnObject.global_num_uints>`                       :code:`TealType.uint64`   3                Maximum global integers in app schema
+:any:`Txn.global_num_byte_slices() <TxnObject.global_num_byte_slices>`           :code:`TealType.uint64`   3                Maximum global byte strings in app schema
+:any:`Txn.local_num_uints() <TxnObject.local_num_uints>`                         :code:`TealType.uint64`   3                Maximum local integers in app schema
+:any:`Txn.local_num_byte_slices() <TxnObject.local_num_byte_slices>`             :code:`TealType.uint64`   3                Maximum local byte strings in app schema
+:any:`Txn.extra_program_pages() <TxnObject.extra_program_pages>`                 :code:`TealType.uint64`   4                Number of extra program pages for app
+:any:`Txn.application_args <TxnObject.application_args>`                         :code:`TealType.bytes[]`  2                Array of application arguments
+:any:`Txn.accounts <TxnObject.accounts>`                                         :code:`TealType.bytes[]`  2                Array of application accounts
+:any:`Txn.assets <TxnObject.assets>`                                             :code:`TealType.uint64[]` 3                Array of application assets
+:any:`Txn.applications <TxnObject.applications>`                                 :code:`TealType.uint64[]` 3                Array of applications
+:any:`InnerTxn.created_asset_id() <TxnObject.created_asset_id>`                  :code:`TealType.uint64`   5                The ID of the newly created asset in this transaction. This is only valid on inner transactions.
+:any:`InnerTxn.created_application_id() <TxnObject.created_application_id>`      :code:`TealType.uint64`   5                The ID of the newly created application in this transaction. This is only valid on inner transactions.
+:any:`InnerTxn.logs <TxnObject.logs>`                                            :code:`TealType.bytes[]`  5                Array of application logged items. This is only valid on inner transactions.
+================================================================================ ========================= ================ ============================================================================
 
 Transaction Type
 ~~~~~~~~~~~~~~~~
@@ -90,7 +94,8 @@ Transaction Array Fields
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
 Some of the exposed transaction fields are arrays with the type :code:`TealType.uint64[]` or :code:`TealType.bytes[]`.
-These fields are :code:`Txn.application_args`, :code:`Txn.assets`, :code:`Txn.accounts`, and :code:`Txn.applications`.
+These fields are :code:`Txn.application_args`, :code:`Txn.assets`, :code:`Txn.accounts`, :code:`Txn.applications`,
+and :code:`InnerTxn.logs`.
 
 The length of these array fields can be found using the :code:`.length()` method, and individual
 items can be accesses using bracket notation. For example:
@@ -101,6 +106,9 @@ items can be accesses using bracket notation. For example:
   Txn.application_args[0] # get the first application argument
   Txn.application_args[1] # get the second application argument
 
+  # as of TEAL v5, PyTeal expressions can be used to dynamically index into array properties as well
+  Txn.application_args[Txn.application_args.length() - Int(1)] # get the last application argument
+
 .. _txn_special_case_arrays:
 
 Special case: :code:`Txn.accounts` and :code:`Txn.applications`
@@ -133,6 +141,9 @@ available on the elements of :code:`Gtxn`. For example:
   Gtxn[0].sender() # get the sender of the first transaction in the atomic transfer group
   Gtxn[1].receiver() # get the receiver of the second transaction in the atomic transfer group
 
+  # as of TEAL v3, PyTeal expressions can be used to dynamically index into Gtxn as well
+  Gtxn[Txn.group_index() - Int(1)].sender() # get the sender of the previous transaction in the atomic transfer group
+
 :code:`Gtxn` is zero-indexed and the maximum size of an atomic transfer group is 16. The size of the
 current transaction group is available as :any:`Global.group_size()`. A standalone transaction will
 have a group size of :code:`1`.
@@ -140,6 +151,17 @@ have a group size of :code:`1`.
 To find the current transaction's index in the transfer group, use :any:`Txn.group_index() <TxnObject.group_index>`. If the
 current transaction is standalone, it's group index will be :code:`0`.
 
+Inner Transactions
+------------------
+
+.. note::
+    Inner transactions are only available in TEAL version 5 or higher.
+
+Inner transactions can be created and submitted with expressions from the :any:`InnerTxnBuilder` class.
+The properties of the most recently submitted inner transaction can be accessed using the :any:`InnerTxn`
+object. This object is an instance of :any:`TxnObject`, meaning all of the above transaction fields
+on :code:`Txn` are available on :code:`InnerTxn` as well.
+
 Global Parameters
 -----------------
 
diff --git a/docs/api.rst b/docs/api.rst
index 02f4d80ae..b427806b5 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -13,9 +13,14 @@ PyTeal Package
    .. data:: Txn
       :annotation: = <pyteal.TxnObject object>
 
-      The current transaction being evaluated.
+      The current transaction being evaluated. This is an instance of :any:`TxnObject`.
    
    .. data:: Gtxn
       :annotation: = <pyteal.TxnGroup object>
 
-      The current group of transactions being evaluated.
+      The current group of transactions being evaluated. This is an instance of :any:`TxnGroup`.
+
+   .. data:: InnerTxn
+      :annotation: = <pyteal.TxnObject object>
+
+      The most recently submitted inner transaction. This is an instance of :any:`TxnObject`.
diff --git a/pyteal/ast/__init__.py b/pyteal/ast/__init__.py
index 8ce8b01b0..400228463 100644
--- a/pyteal/ast/__init__.py
+++ b/pyteal/ast/__init__.py
@@ -17,6 +17,9 @@
 from .app import App, AppField, OnComplete, AppParam
 from .asset import AssetHolding, AssetParam
 
+# inner txns
+from .itxn import InnerTxnBuilder, InnerTxn
+
 # meta
 from .array import Array
 from .tmpl import Tmpl
@@ -139,6 +142,8 @@
     "AppParam",
     "AssetHolding",
     "AssetParam",
+    "InnerTxnBuilder",
+    "InnerTxn",
     "Array",
     "Tmpl",
     "Nonce",
diff --git a/pyteal/ast/binaryexpr.py b/pyteal/ast/binaryexpr.py
index 57ce21cfb..c3ecbf442 100644
--- a/pyteal/ast/binaryexpr.py
+++ b/pyteal/ast/binaryexpr.py
@@ -21,7 +21,7 @@ def __init__(
     ) -> None:
         super().__init__()
         if type(inputType) is tuple:
-            leftType, rightType = cast(Tuple[TealType, TealType], inputType)
+            leftType, rightType = inputType
         else:
             leftType = cast(TealType, inputType)
             rightType = leftType
diff --git a/pyteal/ast/gaid.py b/pyteal/ast/gaid.py
index 364f49ebb..f8716302a 100644
--- a/pyteal/ast/gaid.py
+++ b/pyteal/ast/gaid.py
@@ -26,7 +26,7 @@ def __init__(self, txnIndex: Union[int, Expr]) -> None:
             must be less than the index of the current transaction.
         """
         super().__init__()
-        if type(txnIndex) == int:
+        if type(txnIndex) is int:
             if txnIndex < 0 or txnIndex >= MAX_GROUP_SIZE:
                 raise TealInputError(
                     "Invalid transaction index {}, shoud be in [0, {})".format(
@@ -47,8 +47,8 @@ def __teal__(self, options: "CompileOptions"):
             "TEAL version too low to use Gaid expression",
         )
 
-        if type(self.txnIndex) == int:
-            op = TealOp(self, Op.gaid, cast(int, self.txnIndex))
+        if type(self.txnIndex) is int:
+            op = TealOp(self, Op.gaid, self.txnIndex)
             return TealBlock.FromOp(options, op)
 
         op = TealOp(self, Op.gaids)
diff --git a/pyteal/ast/gload.py b/pyteal/ast/gload.py
index 3f6db639c..39322e842 100644
--- a/pyteal/ast/gload.py
+++ b/pyteal/ast/gload.py
@@ -29,7 +29,7 @@ def __init__(self, txnIndex: Union[int, Expr], slotId: int) -> None:
                 in the range [0-256).
         """
         super().__init__()
-        if type(txnIndex) == int:
+        if type(txnIndex) is int:
             if txnIndex < 0 or txnIndex >= MAX_GROUP_SIZE:
                 raise TealInputError(
                     "Invalid transaction index {}, shoud be in [0, {})".format(
@@ -56,10 +56,8 @@ def __teal__(self, options: "CompileOptions"):
             "TEAL version too low to use Gload expression",
         )
 
-        if type(self.txnIndex) == int:
-            op = TealOp(
-                self, Op.gload, cast(int, self.txnIndex), cast(int, self.slotId)
-            )
+        if type(self.txnIndex) is int:
+            op = TealOp(self, Op.gload, self.txnIndex, self.slotId)
             return TealBlock.FromOp(options, op)
 
         op = TealOp(self, Op.gloads, self.slotId)
diff --git a/pyteal/ast/global_.py b/pyteal/ast/global_.py
index eba803f28..89117a51a 100644
--- a/pyteal/ast/global_.py
+++ b/pyteal/ast/global_.py
@@ -21,6 +21,8 @@ class GlobalField(Enum):
     latest_timestamp = (7, "LatestTimestamp", TealType.uint64, 2)
     current_app_id = (8, "CurrentApplicationID", TealType.uint64, 2)
     creator_address = (9, "CreatorAddress", TealType.bytes, 3)
+    current_app_address = (10, "CurrentApplicationAddress", TealType.bytes, 5)
+    group_id = (11, "GroupID", TealType.bytes, 5)
 
     def __init__(self, id: int, name: str, type: TealType, min_version: int) -> None:
         self.id = id
@@ -110,8 +112,24 @@ def current_application_id(cls) -> "Global":
     def creator_address(cls) -> "Global":
         """Address of the creator of the current application.
 
-        Fails if no such application is executing. Requires TEAL version 3 or higher."""
+        Fails during Signature mode. Requires TEAL version 3 or higher."""
         return cls(GlobalField.creator_address)
 
+    @classmethod
+    def current_application_address(cls) -> "Global":
+        """Get the address of that the current application controls.
+
+        Fails during Signature mode. Requires TEAL version 5 or higher."""
+        return cls(GlobalField.current_app_address)
+
+    @classmethod
+    def group_id(cls) -> "Global":
+        """Get the ID of the current transaction group.
+
+        If the current transaction is not part of a group, this will return 32 zero bytes.
+
+        Requires TEAL version 5 or higher."""
+        return cls(GlobalField.group_id)
+
 
 Global.__module__ = "pyteal"
diff --git a/pyteal/ast/gtxn.py b/pyteal/ast/gtxn.py
index 681569cb4..24c8bfcc8 100644
--- a/pyteal/ast/gtxn.py
+++ b/pyteal/ast/gtxn.py
@@ -16,17 +16,17 @@ class GtxnExpr(TxnExpr):
     """An expression that accesses a transaction field from a transaction in the current group."""
 
     def __init__(self, txnIndex: Union[int, Expr], field: TxnField) -> None:
-        super().__init__(field)
+        super().__init__(Op.gtxn, "Gtxn", field)
         self.txnIndex = txnIndex
 
     def __str__(self):
-        return "(Gtxn {} {})".format(self.txnIndex, self.field.arg_name)
+        return "({} {} {})".format(self.name, self.txnIndex, self.field.arg_name)
 
     def __teal__(self, options: "CompileOptions"):
         verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
 
-        if type(self.txnIndex) == int:
-            op = TealOp(self, Op.gtxn, cast(int, self.txnIndex), self.field.arg_name)
+        if type(self.txnIndex) is int:
+            op = TealOp(self, Op.gtxn, self.txnIndex, self.field.arg_name)
             return TealBlock.FromOp(options, op)
 
         verifyTealVersion(
@@ -45,34 +45,54 @@ def __teal__(self, options: "CompileOptions"):
 class GtxnaExpr(TxnaExpr):
     """An expression that accesses a transaction array field from a transaction in the current group."""
 
-    def __init__(self, txnIndex: Union[int, Expr], field: TxnField, index: int) -> None:
-        super().__init__(field, index)
+    def __init__(
+        self, txnIndex: Union[int, Expr], field: TxnField, index: Union[int, Expr]
+    ) -> None:
+        super().__init__(Op.gtxna, Op.gtxnas, "Gtxna", field, index)
         self.txnIndex = txnIndex
 
     def __str__(self):
-        return "(Gtxna {} {} {})".format(self.txnIndex, self.field.arg_name, self.index)
+        return "({} {} {} {})".format(
+            self.name, self.txnIndex, self.field.arg_name, self.index
+        )
 
     def __teal__(self, options: "CompileOptions"):
         verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
 
-        if type(self.txnIndex) == int:
-            op = TealOp(
-                self,
-                Op.gtxna,
-                cast(int, self.txnIndex),
-                self.field.arg_name,
-                self.index,
-            )
-            return TealBlock.FromOp(options, op)
+        if type(self.txnIndex) is int:
+            if type(self.index) is int:
+                opToUse = Op.gtxna
+            else:
+                opToUse = Op.gtxnas
+        else:
+            if type(self.index) is int:
+                opToUse = Op.gtxnsa
+            else:
+                opToUse = Op.gtxnsas
 
         verifyTealVersion(
-            Op.gtxnsa.min_version,
+            opToUse.min_version,
             options.version,
-            "TEAL version too low to index Gtxn with dynamic values",
+            "TEAL version too low to use op {}".format(opToUse),
         )
 
-        op = TealOp(self, Op.gtxnsa, self.field.arg_name, self.index)
-        return TealBlock.FromOp(options, op, cast(Expr, self.txnIndex))
+        if type(self.txnIndex) is int:
+            if type(self.index) is int:
+                op = TealOp(
+                    self, opToUse, self.txnIndex, self.field.arg_name, self.index
+                )
+                return TealBlock.FromOp(options, op)
+            op = TealOp(self, opToUse, self.txnIndex, self.field.arg_name)
+            return TealBlock.FromOp(options, op, cast(Expr, self.index))
+
+        if type(self.index) is int:
+            op = TealOp(self, opToUse, self.field.arg_name, self.index)
+            return TealBlock.FromOp(options, op, cast(Expr, self.txnIndex))
+
+        op = TealOp(self, opToUse, self.field.arg_name)
+        return TealBlock.FromOp(
+            options, op, cast(Expr, self.txnIndex), cast(Expr, self.index)
+        )
 
 
 GtxnaExpr.__module__ = "pyteal"
@@ -82,7 +102,7 @@ class TxnGroup:
     """Represents a group of transactions."""
 
     def __getitem__(self, txnIndex: Union[int, Expr]) -> TxnObject:
-        if type(txnIndex) == int:
+        if type(txnIndex) is int:
             if txnIndex < 0 or txnIndex >= MAX_GROUP_SIZE:
                 raise TealInputError(
                     "Invalid Gtxn index {}, shoud be in [0, {})".format(
diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py
new file mode 100644
index 000000000..4ba229db8
--- /dev/null
+++ b/pyteal/ast/itxn.py
@@ -0,0 +1,156 @@
+from typing import Dict, Tuple, TYPE_CHECKING
+
+from ..types import TealType, require_type
+from ..errors import TealInputError, verifyTealVersion
+from ..ir import TealOp, Op, TealBlock
+from .expr import Expr
+from .txn import TxnField, TxnExprBuilder, TxnaExprBuilder, TxnObject
+from .seq import Seq
+
+if TYPE_CHECKING:
+    from ..compiler import CompileOptions
+
+
+class InnerTxnActionExpr(Expr):
+    def __init__(self, begin: bool) -> None:
+        super().__init__()
+        self.begin = begin
+
+    def __str__(self):
+        return "(InnerTxn{})".format("Begin" if self.begin else "Submit")
+
+    def __teal__(self, options: "CompileOptions"):
+        op = Op.itxn_begin if self.begin else Op.itxn_submit
+
+        verifyTealVersion(
+            op.min_version,
+            options.version,
+            "TEAL version too low to create inner transactions",
+        )
+
+        return TealBlock.FromOp(options, TealOp(self, op))
+
+    def type_of(self):
+        return TealType.none
+
+    def has_return(self):
+        return False
+
+
+class InnerTxnFieldExpr(Expr):
+    def __init__(self, field: TxnField, value: Expr) -> None:
+        super().__init__()
+        if field.is_array:
+            raise TealInputError("Unexpected array field: {}".format(field))
+        require_type(value.type_of(), field.type_of())
+        self.field = field
+        self.value = value
+
+    def __str__(self):
+        return "(InnerTxnSetField {} {})".format(self.field.arg_name, self.value)
+
+    def __teal__(self, options: "CompileOptions"):
+        verifyTealVersion(
+            Op.itxn_field.min_version,
+            options.version,
+            "TEAL version too low to create inner transactions",
+        )
+
+        return TealBlock.FromOp(
+            options, TealOp(self, Op.itxn_field, self.field.arg_name), self.value
+        )
+
+    def type_of(self):
+        return TealType.none
+
+    def has_return(self):
+        return False
+
+
+class InnerTxnBuilder:
+    """This class represents expressions used to create, modify, and submit inner transactions.
+
+    Inner transactions are transactions which applications can dynamically create. Each inner
+    transaction will appear as a transaction inside of the current transaction being executed.
+
+    As of TEAL version 5, only the transaction types :any:`TxnType.Payment`, :any:`TxnType.AssetTransfer`,
+    :any:`TxnType.AssetConfig`, and :any:`TxnType.AssetFreeze` are allowed. Additionally, not all
+    fields are allowed to be set. For example, it is not currently allowed to set the rekeyTo field
+    of an inner transaction.
+    """
+
+    @classmethod
+    def Begin(cls) -> Expr:
+        """Begin preparation of a new inner transaction.
+
+        This new inner transaction is initialized with its sender to the application address (:any:`Global.current_application_address`);
+        fee to the minimum allowable, taking into account :code:`MinTxnFee` and credit from
+        overpaying in earlier transactions; :code:`FirstValid`/:code:`LastValid` to the values in
+        the top-level transaction, and all other fields to zero values.
+
+        Requires TEAL version 5 or higher. This operation is only permitted in application mode.
+        """
+        return InnerTxnActionExpr(True)
+
+    @classmethod
+    def Submit(cls) -> Expr:
+        """Execute the current inner transaction.
+
+        :any:`InnerTxnBuilder.Begin` and :any:`InnerTxnBuilder.SetField` must be called before
+        submitting an inner transaction.
+
+        This will fail fail if 16 inner transactions have already been executed, or if the
+        inner transaction itself fails. Upon failure, the current program will immediately exit and
+        fail as well.
+
+        If the inner transaction is successful, then its effects can be immediately observed by this
+        program with stateful expressions such as :any:`Balance`. Additionally, the fields of the
+        most recently submitted inner transaction can be examined using the :any:`InnerTxn` object.
+        If the inner transaction creates an asset, the new asset ID can be found by looking at
+        :any:`InnerTxn.created_asset_id() <TxnObject.created_asset_id>`.
+
+        Requires TEAL version 5 or higher. This operation is only permitted in application mode.
+        """
+        return InnerTxnActionExpr(False)
+
+    @classmethod
+    def SetField(cls, field: TxnField, value: Expr) -> Expr:
+        """Set a field of the current inner transaction.
+
+        :any:`InnerTxnBuilder.Begin` must be called before setting any fields on an inner
+        transaction.
+
+        Requires TEAL version 5 or higher. This operation is only permitted in application mode.
+
+        Args:
+            field: The field to set on the inner transaction.
+            value: The value to that the field should take. This must evaluate to a type that is
+                compatible with the field being set.
+        """
+        return InnerTxnFieldExpr(field, value)
+
+    @classmethod
+    def SetFields(cls, fields: Dict[TxnField, Expr]) -> Expr:
+        """Set multiple fields of the current inner transaction.
+
+        :any:`InnerTxnBuilder.Begin` must be called before setting any fields on an inner
+        transaction.
+
+        Requires TEAL version 5 or higher. This operation is only permitted in application mode.
+
+        Args:
+            fields: A dictionary whose keys are fields to set and whose values are the value each
+                field should take. Each value must evaluate to a type that is compatible with the
+                field being set.
+        """
+        fieldsToSet = [cls.SetField(field, value) for field, value in fields.items()]
+        return Seq(fieldsToSet)
+
+
+InnerTxnBuilder.__module__ = "pyteal"
+
+InnerTxn: TxnObject = TxnObject(
+    TxnExprBuilder(Op.itxn, "InnerTxn"), TxnaExprBuilder(Op.itxna, None, "InnerTxna")
+)
+
+InnerTxn.__module__ = "pyteal"
diff --git a/pyteal/ast/seq.py b/pyteal/ast/seq.py
index e58c61255..8768e4a7c 100644
--- a/pyteal/ast/seq.py
+++ b/pyteal/ast/seq.py
@@ -2,10 +2,10 @@
 
 from ..types import TealType, require_type
 from ..errors import TealInputError
+from ..ir import TealSimpleBlock
 from .expr import Expr
 
 if TYPE_CHECKING:
-    from ..ir import TealSimpleBlock
     from ..compiler import CompileOptions
 
 
@@ -43,8 +43,6 @@ def __init__(self, *exprs):
         if len(exprs) == 1 and isinstance(exprs[0], list):
             exprs = exprs[0]
 
-        if len(exprs) == 0:
-            raise TealInputError("Seq requires children.")
         for i, expr in enumerate(exprs):
             if not isinstance(expr, Expr):
                 raise TealInputError("{} is not a pyteal expression.".format(expr))
@@ -54,16 +52,12 @@ def __init__(self, *exprs):
         self.args = exprs
 
     def __teal__(self, options: "CompileOptions"):
-        start = None
-        end = None
-        for i, arg in enumerate(self.args):
+        start = TealSimpleBlock([])
+        end = start
+        for arg in self.args:
             argStart, argEnd = arg.__teal__(options)
-            if i == 0:
-                start = argStart
-            else:
-                cast("TealSimpleBlock", end).setNextBlock(argStart)
+            end.setNextBlock(argStart)
             end = argEnd
-
         return start, end
 
     def __str__(self):
@@ -74,6 +68,8 @@ def __str__(self):
         return ret_str
 
     def type_of(self):
+        if len(self.args) == 0:
+            return TealType.none
         return self.args[-1].type_of()
 
     def has_return(self):
@@ -81,6 +77,8 @@ def has_return(self):
         # TODO: technically if ANY expression, not just the final one, returns true for has_return,
         # this could return true as well. But in that case all expressions after the one that
         # returns true for has_return is dead code, so it could be optimized away
+        if len(self.args) == 0:
+            return False
         return self.args[-1].has_return()
 
 
diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py
index bc7b12044..eed322405 100644
--- a/pyteal/ast/txn.py
+++ b/pyteal/ast/txn.py
@@ -1,10 +1,16 @@
 from enum import Enum
-from typing import Callable, TYPE_CHECKING
-
-from ..types import TealType
-from ..errors import TealInputError, verifyFieldVersion
+from typing import Callable, Optional, Union, cast, TYPE_CHECKING
+
+from ..types import TealType, require_type
+from ..errors import (
+    TealInputError,
+    TealCompileError,
+    verifyFieldVersion,
+    verifyTealVersion,
+)
 from ..ir import TealOp, Op, TealBlock
 from .leafexpr import LeafExpr
+from .expr import Expr
 from .int import EnumInt
 from .array import Array
 
@@ -28,69 +34,88 @@ class TxnType:
 
 
 class TxnField(Enum):
-    sender = (0, "Sender", TealType.bytes, 2)
-    fee = (1, "Fee", TealType.uint64, 2)
-    first_valid = (2, "FirstValid", TealType.uint64, 2)
-    first_valid_time = (3, "FirstValidTime", TealType.uint64, 2)
-    last_valid = (4, "LastValid", TealType.uint64, 2)
-    note = (5, "Note", TealType.bytes, 2)
-    lease = (6, "Lease", TealType.bytes, 2)
-    receiver = (7, "Receiver", TealType.bytes, 2)
-    amount = (8, "Amount", TealType.uint64, 2)
-    close_remainder_to = (9, "CloseRemainderTo", TealType.bytes, 2)
-    vote_pk = (10, "VotePK", TealType.bytes, 2)
-    selection_pk = (11, "SelectionPK", TealType.bytes, 2)
-    vote_first = (12, "VoteFirst", TealType.uint64, 2)
-    vote_last = (13, "VoteLast", TealType.uint64, 2)
-    vote_key_dilution = (14, "VoteKeyDilution", TealType.uint64, 2)
-    type = (15, "Type", TealType.bytes, 2)
-    type_enum = (16, "TypeEnum", TealType.uint64, 2)
-    xfer_asset = (17, "XferAsset", TealType.uint64, 2)
-    asset_amount = (18, "AssetAmount", TealType.uint64, 2)
-    asset_sender = (19, "AssetSender", TealType.bytes, 2)
-    asset_receiver = (20, "AssetReceiver", TealType.bytes, 2)
-    asset_close_to = (21, "AssetCloseTo", TealType.bytes, 2)
-    group_index = (22, "GroupIndex", TealType.uint64, 2)
-    tx_id = (23, "TxID", TealType.bytes, 2)
-    application_id = (24, "ApplicationID", TealType.uint64, 2)
-    on_completion = (25, "OnCompletion", TealType.uint64, 2)
-    application_args = (26, "ApplicationArgs", TealType.bytes, 2)
-    num_app_args = (27, "NumAppArgs", TealType.uint64, 2)
-    accounts = (28, "Accounts", TealType.bytes, 2)
-    num_accounts = (2, "NumAccounts", TealType.uint64, 2)
-    approval_program = (30, "ApprovalProgram", TealType.bytes, 2)
-    clear_state_program = (31, "ClearStateProgram", TealType.bytes, 2)
-    rekey_to = (32, "RekeyTo", TealType.bytes, 2)
-    config_asset = (33, "ConfigAsset", TealType.uint64, 2)
-    config_asset_total = (34, "ConfigAssetTotal", TealType.uint64, 2)
-    config_asset_decimals = (35, "ConfigAssetDecimals", TealType.uint64, 2)
-    config_asset_default_frozen = (36, "ConfigAssetDefaultFrozen", TealType.uint64, 2)
-    config_asset_unit_name = (37, "ConfigAssetUnitName", TealType.bytes, 2)
-    config_asset_name = (38, "ConfigAssetName", TealType.bytes, 2)
-    config_asset_url = (39, "ConfigAssetURL", TealType.bytes, 2)
-    config_asset_metadata_hash = (40, "ConfigAssetMetadataHash", TealType.bytes, 2)
-    config_asset_manager = (41, "ConfigAssetManager", TealType.bytes, 2)
-    config_asset_reserve = (42, "ConfigAssetReserve", TealType.bytes, 2)
-    config_asset_freeze = (43, "ConfigAssetFreeze", TealType.bytes, 2)
-    config_asset_clawback = (44, "ConfigAssetClawback", TealType.bytes, 2)
-    freeze_asset = (45, "FreezeAsset", TealType.uint64, 2)
-    freeze_asset_account = (46, "FreezeAssetAccount", TealType.bytes, 2)
-    freeze_asset_frozen = (47, "FreezeAssetFrozen", TealType.uint64, 2)
-    assets = (48, "Assets", TealType.uint64, 3)
-    num_assets = (49, "NumAssets", TealType.uint64, 3)
-    applications = (50, "Applications", TealType.uint64, 3)
-    num_applications = (51, "NumApplications", TealType.uint64, 3)
-    global_num_uints = (52, "GlobalNumUint", TealType.uint64, 3)
-    global_num_byte_slices = (53, "GlobalNumByteSlice", TealType.uint64, 3)
-    local_num_uints = (54, "LocalNumUint", TealType.uint64, 3)
-    local_num_byte_slices = (55, "LocalNumByteSlice", TealType.uint64, 3)
-    extra_program_pages = (56, "ExtraProgramPages", TealType.uint64, 4)
-    nonparticipation = (57, "Nonparticipation", TealType.uint64, 5)
-
-    def __init__(self, id: int, name: str, type: TealType, min_version: int) -> None:
+    sender = (0, "Sender", TealType.bytes, False, 2)
+    fee = (1, "Fee", TealType.uint64, False, 2)
+    first_valid = (2, "FirstValid", TealType.uint64, False, 2)
+    first_valid_time = (3, "FirstValidTime", TealType.uint64, False, 2)
+    last_valid = (4, "LastValid", TealType.uint64, False, 2)
+    note = (5, "Note", TealType.bytes, False, 2)
+    lease = (6, "Lease", TealType.bytes, False, 2)
+    receiver = (7, "Receiver", TealType.bytes, False, 2)
+    amount = (8, "Amount", TealType.uint64, False, 2)
+    close_remainder_to = (9, "CloseRemainderTo", TealType.bytes, False, 2)
+    vote_pk = (10, "VotePK", TealType.bytes, False, 2)
+    selection_pk = (11, "SelectionPK", TealType.bytes, False, 2)
+    vote_first = (12, "VoteFirst", TealType.uint64, False, 2)
+    vote_last = (13, "VoteLast", TealType.uint64, False, 2)
+    vote_key_dilution = (14, "VoteKeyDilution", TealType.uint64, False, 2)
+    type = (15, "Type", TealType.bytes, False, 2)
+    type_enum = (16, "TypeEnum", TealType.uint64, False, 2)
+    xfer_asset = (17, "XferAsset", TealType.uint64, False, 2)
+    asset_amount = (18, "AssetAmount", TealType.uint64, False, 2)
+    asset_sender = (19, "AssetSender", TealType.bytes, False, 2)
+    asset_receiver = (20, "AssetReceiver", TealType.bytes, False, 2)
+    asset_close_to = (21, "AssetCloseTo", TealType.bytes, False, 2)
+    group_index = (22, "GroupIndex", TealType.uint64, False, 2)
+    tx_id = (23, "TxID", TealType.bytes, False, 2)
+    application_id = (24, "ApplicationID", TealType.uint64, False, 2)
+    on_completion = (25, "OnCompletion", TealType.uint64, False, 2)
+    application_args = (26, "ApplicationArgs", TealType.bytes, True, 2)
+    num_app_args = (27, "NumAppArgs", TealType.uint64, False, 2)
+    accounts = (28, "Accounts", TealType.bytes, True, 2)
+    num_accounts = (2, "NumAccounts", TealType.uint64, False, 2)
+    approval_program = (30, "ApprovalProgram", TealType.bytes, False, 2)
+    clear_state_program = (31, "ClearStateProgram", TealType.bytes, False, 2)
+    rekey_to = (32, "RekeyTo", TealType.bytes, False, 2)
+    config_asset = (33, "ConfigAsset", TealType.uint64, False, 2)
+    config_asset_total = (34, "ConfigAssetTotal", TealType.uint64, False, 2)
+    config_asset_decimals = (35, "ConfigAssetDecimals", TealType.uint64, False, 2)
+    config_asset_default_frozen = (
+        36,
+        "ConfigAssetDefaultFrozen",
+        TealType.uint64,
+        False,
+        2,
+    )
+    config_asset_unit_name = (37, "ConfigAssetUnitName", TealType.bytes, False, 2)
+    config_asset_name = (38, "ConfigAssetName", TealType.bytes, False, 2)
+    config_asset_url = (39, "ConfigAssetURL", TealType.bytes, False, 2)
+    config_asset_metadata_hash = (
+        40,
+        "ConfigAssetMetadataHash",
+        TealType.bytes,
+        False,
+        2,
+    )
+    config_asset_manager = (41, "ConfigAssetManager", TealType.bytes, False, 2)
+    config_asset_reserve = (42, "ConfigAssetReserve", TealType.bytes, False, 2)
+    config_asset_freeze = (43, "ConfigAssetFreeze", TealType.bytes, False, 2)
+    config_asset_clawback = (44, "ConfigAssetClawback", TealType.bytes, False, 2)
+    freeze_asset = (45, "FreezeAsset", TealType.uint64, False, 2)
+    freeze_asset_account = (46, "FreezeAssetAccount", TealType.bytes, False, 2)
+    freeze_asset_frozen = (47, "FreezeAssetFrozen", TealType.uint64, False, 2)
+    assets = (48, "Assets", TealType.uint64, True, 3)
+    num_assets = (49, "NumAssets", TealType.uint64, False, 3)
+    applications = (50, "Applications", TealType.uint64, True, 3)
+    num_applications = (51, "NumApplications", TealType.uint64, False, 3)
+    global_num_uints = (52, "GlobalNumUint", TealType.uint64, False, 3)
+    global_num_byte_slices = (53, "GlobalNumByteSlice", TealType.uint64, False, 3)
+    local_num_uints = (54, "LocalNumUint", TealType.uint64, False, 3)
+    local_num_byte_slices = (55, "LocalNumByteSlice", TealType.uint64, False, 3)
+    extra_program_pages = (56, "ExtraProgramPages", TealType.uint64, False, 4)
+    nonparticipation = (57, "Nonparticipation", TealType.uint64, False, 5)
+    logs = (58, "Logs", TealType.bytes, True, 5)
+    num_logs = (59, "NumLogs", TealType.uint64, False, 5)
+    created_asset_id = (60, "CreatedAssetID", TealType.uint64, False, 5)
+    created_application_id = (61, "CreatedApplicationID", TealType.uint64, False, 5)
+
+    def __init__(
+        self, id: int, name: str, type: TealType, is_array: bool, min_version: int
+    ) -> None:
         self.id = id
         self.arg_name = name
         self.ret_type = type
+        self.is_array = is_array
         self.min_version = min_version
 
     def type_of(self) -> TealType:
@@ -103,17 +128,26 @@ def type_of(self) -> TealType:
 class TxnExpr(LeafExpr):
     """An expression that accesses a transaction field from the current transaction."""
 
-    def __init__(self, field: TxnField) -> None:
+    def __init__(self, op: Op, name: str, field: TxnField) -> None:
         super().__init__()
+        if field.is_array:
+            raise TealInputError("Unexpected array field: {}".format(field))
+        self.op = op
+        self.name = name
         self.field = field
 
     def __str__(self):
-        return "(Txn {})".format(self.field.arg_name)
+        return "({} {})".format(self.name, self.field.arg_name)
 
     def __teal__(self, options: "CompileOptions"):
         verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
+        verifyTealVersion(
+            self.op.min_version,
+            options.version,
+            "TEAL version too low to use op {}".format(self.op),
+        )
 
-        op = TealOp(self, Op.txn, self.field.arg_name)
+        op = TealOp(self, self.op, self.field.arg_name)
         return TealBlock.FromOp(options, op)
 
     def type_of(self):
@@ -126,19 +160,45 @@ def type_of(self):
 class TxnaExpr(LeafExpr):
     """An expression that accesses a transaction array field from the current transaction."""
 
-    def __init__(self, field: TxnField, index: int) -> None:
+    def __init__(
+        self,
+        staticOp: Op,
+        dynamicOp: Optional[Op],
+        name: str,
+        field: TxnField,
+        index: Union[int, Expr],
+    ) -> None:
         super().__init__()
+        if not field.is_array:
+            raise TealInputError("Unexpected non-array field: {}".format(field))
+        self.staticOp = staticOp
+        self.dynamicOp = dynamicOp
+        self.name = name
         self.field = field
         self.index = index
 
     def __str__(self):
-        return "(Txna {} {})".format(self.field.arg_name, self.index)
+        return "({} {} {})".format(self.name, self.field.arg_name, self.index)
 
     def __teal__(self, options: "CompileOptions"):
         verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
 
-        op = TealOp(self, Op.txna, self.field.arg_name, self.index)
-        return TealBlock.FromOp(options, op)
+        opToUse = self.staticOp if type(self.index) is int else self.dynamicOp
+        if opToUse is None:
+            raise TealCompileError("Dynamic array indexing not supported", self)
+
+        verifyTealVersion(
+            opToUse.min_version,
+            options.version,
+            "TEAL version too low to use op {}".format(opToUse),
+        )
+
+        if type(self.index) is int:
+            op = TealOp(self, opToUse, self.field.arg_name, self.index)
+            return TealBlock.FromOp(options, op)
+
+        op = TealOp(self, opToUse, self.field.arg_name)
+        return TealBlock.FromOp(options, op, cast(Expr, self.index))
 
     def type_of(self):
         return self.field.type_of()
@@ -147,6 +207,31 @@ def type_of(self):
 TxnaExpr.__module__ = "pyteal"
 
 
+class TxnExprBuilder:
+    def __init__(self, op: Op, name: str):
+        self.op = op
+        self.name = name
+
+    def __call__(self, field: TxnField) -> TxnExpr:
+        return TxnExpr(self.op, self.name, field)
+
+
+TxnExprBuilder.__module__ = "pyteal"
+
+
+class TxnaExprBuilder:
+    def __init__(self, staticOp: Op, dynamicOp: Optional[Op], name: str):
+        self.staticOp = staticOp
+        self.dynamicOp = dynamicOp
+        self.name = name
+
+    def __call__(self, field: TxnField, index: Union[int, Expr]) -> TxnaExpr:
+        return TxnaExpr(self.staticOp, self.dynamicOp, self.name, field, index)
+
+
+TxnaExprBuilder.__module__ = "pyteal"
+
+
 class TxnArray(Array):
     """Represents a transaction array field."""
 
@@ -158,13 +243,16 @@ def __init__(
         self.lengthField = lengthField
 
     def length(self) -> TxnExpr:
-        return self.txnObject.txnType(self.lengthField)
+        return self.txnObject.makeTxnExpr(self.lengthField)
 
-    def __getitem__(self, index: int) -> TxnaExpr:
-        if not isinstance(index, int) or index < 0:
-            raise TealInputError("Invalid array index: {}".format(index))
+    def __getitem__(self, index: Union[int, Expr]) -> TxnaExpr:
+        if type(index) is int:
+            if index < 0:
+                raise TealInputError("Invalid array index: {}".format(index))
+        else:
+            require_type(cast(Expr, index).type_of(), TealType.uint64)
 
-        return self.txnObject.txnaType(self.accessField, index)
+        return self.txnObject.makeTxnaExpr(self.accessField, index)
 
 
 TxnArray.__module__ = "pyteal"
@@ -175,53 +263,53 @@ class TxnObject:
 
     def __init__(
         self,
-        txnType: Callable[[TxnField], TxnExpr],
-        txnaType: Callable[[TxnField, int], TxnaExpr],
+        makeTxnExpr: Callable[[TxnField], TxnExpr],
+        makeTxnaExpr: Callable[[TxnField, Union[int, Expr]], TxnaExpr],
     ) -> None:
-        self.txnType = txnType
-        self.txnaType = txnaType
+        self.makeTxnExpr = makeTxnExpr
+        self.makeTxnaExpr = makeTxnaExpr
 
     def sender(self) -> TxnExpr:
         """Get the 32 byte address of the sender.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#sender
         """
-        return self.txnType(TxnField.sender)
+        return self.makeTxnExpr(TxnField.sender)
 
     def fee(self) -> TxnExpr:
         """Get the transaction fee in micro Algos.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#fee
         """
-        return self.txnType(TxnField.fee)
+        return self.makeTxnExpr(TxnField.fee)
 
     def first_valid(self) -> TxnExpr:
         """Get the first valid round number.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#firstvalid
         """
-        return self.txnType(TxnField.first_valid)
+        return self.makeTxnExpr(TxnField.first_valid)
 
     def last_valid(self) -> TxnExpr:
         """Get the last valid round number.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#lastvalid
         """
-        return self.txnType(TxnField.last_valid)
+        return self.makeTxnExpr(TxnField.last_valid)
 
     def note(self) -> TxnExpr:
         """Get the transaction note.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#note
         """
-        return self.txnType(TxnField.note)
+        return self.makeTxnExpr(TxnField.note)
 
     def lease(self) -> TxnExpr:
         """Get the transaction lease.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#lease
         """
-        return self.txnType(TxnField.lease)
+        return self.makeTxnExpr(TxnField.lease)
 
     def receiver(self) -> TxnExpr:
         """Get the 32 byte address of the receiver.
@@ -230,7 +318,7 @@ def receiver(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#receiver
         """
-        return self.txnType(TxnField.receiver)
+        return self.makeTxnExpr(TxnField.receiver)
 
     def amount(self) -> TxnExpr:
         """Get the amount of the transaction in micro Algos.
@@ -239,7 +327,7 @@ def amount(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#amount
         """
-        return self.txnType(TxnField.amount)
+        return self.makeTxnExpr(TxnField.amount)
 
     def close_remainder_to(self) -> TxnExpr:
         """Get the 32 byte address of the CloseRemainderTo field.
@@ -248,7 +336,7 @@ def close_remainder_to(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#closeremainderto
         """
-        return self.txnType(TxnField.close_remainder_to)
+        return self.makeTxnExpr(TxnField.close_remainder_to)
 
     def vote_pk(self) -> TxnExpr:
         """Get the root participation public key.
@@ -257,7 +345,7 @@ def vote_pk(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#votepk
         """
-        return self.txnType(TxnField.vote_pk)
+        return self.makeTxnExpr(TxnField.vote_pk)
 
     def selection_pk(self) -> TxnExpr:
         """Get the VRF public key.
@@ -266,7 +354,7 @@ def selection_pk(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#selectionpk
         """
-        return self.txnType(TxnField.selection_pk)
+        return self.makeTxnExpr(TxnField.selection_pk)
 
     def vote_first(self) -> TxnExpr:
         """Get the first round that the participation key is valid.
@@ -275,7 +363,7 @@ def vote_first(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#votefirst
         """
-        return self.txnType(TxnField.vote_first)
+        return self.makeTxnExpr(TxnField.vote_first)
 
     def vote_last(self) -> TxnExpr:
         """Get the last round that the participation key is valid.
@@ -284,7 +372,7 @@ def vote_last(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#votelast
         """
-        return self.txnType(TxnField.vote_last)
+        return self.makeTxnExpr(TxnField.vote_last)
 
     def vote_key_dilution(self) -> TxnExpr:
         """Get the dilution for the 2-level participation key.
@@ -293,10 +381,10 @@ def vote_key_dilution(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#votekeydilution
         """
-        return self.txnType(TxnField.vote_key_dilution)
+        return self.makeTxnExpr(TxnField.vote_key_dilution)
 
     def nonparticipation(self) -> TxnExpr:
-        """Get flag for participation key .
+        """Marks an account nonparticipating for rewards.
 
         Only set when :any:`type_enum()` is :any:`TxnType.KeyRegistration`.
 
@@ -304,7 +392,7 @@ def nonparticipation(self) -> TxnExpr:
 
         Requires TEAL version 5 or higher.
         """
-        return self.txnType(TxnField.nonparticipation)
+        return self.makeTxnExpr(TxnField.nonparticipation)
 
     def type(self) -> TxnExpr:
         """Get the type of this transaction as a byte string.
@@ -313,14 +401,14 @@ def type(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#type
         """
-        return self.txnType(TxnField.type)
+        return self.makeTxnExpr(TxnField.type)
 
     def type_enum(self) -> TxnExpr:
         """Get the type of this transaction.
 
         See :any:`TxnType` for possible values.
         """
-        return self.txnType(TxnField.type_enum)
+        return self.makeTxnExpr(TxnField.type_enum)
 
     def xfer_asset(self) -> TxnExpr:
         """Get the ID of the asset being transferred.
@@ -329,7 +417,7 @@ def xfer_asset(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#xferasset
         """
-        return self.txnType(TxnField.xfer_asset)
+        return self.makeTxnExpr(TxnField.xfer_asset)
 
     def asset_amount(self) -> TxnExpr:
         """Get the amount of the asset being transferred, measured in the asset's units.
@@ -338,7 +426,7 @@ def asset_amount(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetamount
         """
-        return self.txnType(TxnField.asset_amount)
+        return self.makeTxnExpr(TxnField.asset_amount)
 
     def asset_sender(self) -> TxnExpr:
         """Get the 32 byte address of the subject of clawback.
@@ -347,7 +435,7 @@ def asset_sender(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetsender
         """
-        return self.txnType(TxnField.asset_sender)
+        return self.makeTxnExpr(TxnField.asset_sender)
 
     def asset_receiver(self) -> TxnExpr:
         """Get the recipient of the asset transfer.
@@ -356,7 +444,7 @@ def asset_receiver(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetreceiver
         """
-        return self.txnType(TxnField.asset_receiver)
+        return self.makeTxnExpr(TxnField.asset_receiver)
 
     def asset_close_to(self) -> TxnExpr:
         """Get the closeout address of the asset transfer.
@@ -365,7 +453,7 @@ def asset_close_to(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetcloseto
         """
-        return self.txnType(TxnField.asset_close_to)
+        return self.makeTxnExpr(TxnField.asset_close_to)
 
     def group_index(self) -> TxnExpr:
         """Get the position of the transaction within the atomic transaction group.
@@ -374,46 +462,46 @@ def group_index(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#group
         """
-        return self.txnType(TxnField.group_index)
+        return self.makeTxnExpr(TxnField.group_index)
 
     def tx_id(self) -> TxnExpr:
         """Get the 32 byte computed ID for the transaction."""
-        return self.txnType(TxnField.tx_id)
+        return self.makeTxnExpr(TxnField.tx_id)
 
     def application_id(self) -> TxnExpr:
         """Get the application ID from the ApplicationCall portion of the current transaction.
 
         Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall`.
         """
-        return self.txnType(TxnField.application_id)
+        return self.makeTxnExpr(TxnField.application_id)
 
     def on_completion(self) -> TxnExpr:
         """Get the on completion action from the ApplicationCall portion of the transaction.
 
         Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall`.
         """
-        return self.txnType(TxnField.on_completion)
+        return self.makeTxnExpr(TxnField.on_completion)
 
     def approval_program(self) -> TxnExpr:
         """Get the approval program.
 
         Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall`.
         """
-        return self.txnType(TxnField.approval_program)
+        return self.makeTxnExpr(TxnField.approval_program)
 
     def clear_state_program(self) -> TxnExpr:
         """Get the clear state program.
 
         Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall`.
         """
-        return self.txnType(TxnField.clear_state_program)
+        return self.makeTxnExpr(TxnField.clear_state_program)
 
     def rekey_to(self) -> TxnExpr:
         """Get the sender's new 32 byte AuthAddr.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#rekeyto
         """
-        return self.txnType(TxnField.rekey_to)
+        return self.makeTxnExpr(TxnField.rekey_to)
 
     def config_asset(self) -> TxnExpr:
         """Get the asset ID in asset config transaction.
@@ -422,7 +510,7 @@ def config_asset(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#configasset
         """
-        return self.txnType(TxnField.config_asset)
+        return self.makeTxnExpr(TxnField.config_asset)
 
     def config_asset_total(self) -> TxnExpr:
         """Get the total number of units of this asset created.
@@ -430,7 +518,7 @@ def config_asset_total(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#total"""
-        return self.txnType(TxnField.config_asset_total)
+        return self.makeTxnExpr(TxnField.config_asset_total)
 
     def config_asset_decimals(self) -> TxnExpr:
         """Get the number of digits to display after the decimal place when displaying the asset.
@@ -439,7 +527,7 @@ def config_asset_decimals(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#decimals
         """
-        return self.txnType(TxnField.config_asset_decimals)
+        return self.makeTxnExpr(TxnField.config_asset_decimals)
 
     def config_asset_default_frozen(self) -> TxnExpr:
         """Check if the asset's slots are frozen by default or not.
@@ -447,7 +535,7 @@ def config_asset_default_frozen(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#defaultfrozen"""
-        return self.txnType(TxnField.config_asset_default_frozen)
+        return self.makeTxnExpr(TxnField.config_asset_default_frozen)
 
     def config_asset_unit_name(self) -> TxnExpr:
         """Get the unit name of the asset.
@@ -455,7 +543,7 @@ def config_asset_unit_name(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#unitname"""
-        return self.txnType(TxnField.config_asset_unit_name)
+        return self.makeTxnExpr(TxnField.config_asset_unit_name)
 
     def config_asset_name(self) -> TxnExpr:
         """Get the asset name.
@@ -463,7 +551,7 @@ def config_asset_name(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetname"""
-        return self.txnType(TxnField.config_asset_name)
+        return self.makeTxnExpr(TxnField.config_asset_name)
 
     def config_asset_url(self) -> TxnExpr:
         """Get the asset URL.
@@ -471,7 +559,7 @@ def config_asset_url(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#url"""
-        return self.txnType(TxnField.config_asset_url)
+        return self.makeTxnExpr(TxnField.config_asset_url)
 
     def config_asset_metadata_hash(self) -> TxnExpr:
         """Get the 32 byte commitment to some unspecified asset metdata.
@@ -480,7 +568,7 @@ def config_asset_metadata_hash(self) -> TxnExpr:
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#metadatahash
         """
-        return self.txnType(TxnField.config_asset_metadata_hash)
+        return self.makeTxnExpr(TxnField.config_asset_metadata_hash)
 
     def config_asset_manager(self) -> TxnExpr:
         """Get the 32 byte asset manager address.
@@ -488,7 +576,7 @@ def config_asset_manager(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#manageraddr"""
-        return self.txnType(TxnField.config_asset_manager)
+        return self.makeTxnExpr(TxnField.config_asset_manager)
 
     def config_asset_reserve(self) -> TxnExpr:
         """Get the 32 byte asset reserve address.
@@ -496,7 +584,7 @@ def config_asset_reserve(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#reserveaddr"""
-        return self.txnType(TxnField.config_asset_reserve)
+        return self.makeTxnExpr(TxnField.config_asset_reserve)
 
     def config_asset_freeze(self) -> TxnExpr:
         """Get the 32 byte asset freeze address.
@@ -504,7 +592,7 @@ def config_asset_freeze(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#freezeaddr"""
-        return self.txnType(TxnField.config_asset_freeze)
+        return self.makeTxnExpr(TxnField.config_asset_freeze)
 
     def config_asset_clawback(self) -> TxnExpr:
         """Get the 32 byte asset clawback address.
@@ -512,7 +600,18 @@ def config_asset_clawback(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#clawbackaddr"""
-        return self.txnType(TxnField.config_asset_clawback)
+        return self.makeTxnExpr(TxnField.config_asset_clawback)
+
+    def created_asset_id(self) -> TxnExpr:
+        """Get the asset ID allocated by the creation of an ASA.
+
+        Currently this only works on inner transactions.
+
+        Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig` and this is an asset creation transaction.
+
+        Requires TEAL version 5 or higher.
+        """
+        return self.makeTxnExpr(TxnField.created_asset_id)
 
     def freeze_asset(self) -> TxnExpr:
         """Get the asset ID being frozen or un-frozen.
@@ -520,7 +619,7 @@ def freeze_asset(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetFreeze`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#freezeasset"""
-        return self.txnType(TxnField.freeze_asset)
+        return self.makeTxnExpr(TxnField.freeze_asset)
 
     def freeze_asset_account(self) -> TxnExpr:
         """Get the 32 byte address of the account whose asset slot is being frozen or un-frozen.
@@ -528,7 +627,7 @@ def freeze_asset_account(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetFreeze`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#freezeaccount"""
-        return self.txnType(TxnField.freeze_asset_account)
+        return self.makeTxnExpr(TxnField.freeze_asset_account)
 
     def freeze_asset_frozen(self) -> TxnExpr:
         """Get the new frozen value for the asset.
@@ -536,7 +635,7 @@ def freeze_asset_frozen(self) -> TxnExpr:
         Only set when :any:`type_enum()` is :any:`TxnType.AssetFreeze`.
 
         For more information, see https://developer.algorand.org/docs/reference/transactions/#assetfrozen"""
-        return self.txnType(TxnField.freeze_asset_frozen)
+        return self.makeTxnExpr(TxnField.freeze_asset_frozen)
 
     def global_num_uints(self) -> TxnExpr:
         """Get the schema count of global state integers in an application creation call.
@@ -545,7 +644,7 @@ def global_num_uints(self) -> TxnExpr:
 
         Requires TEAL version 3 or higher.
         """
-        return self.txnType(TxnField.global_num_uints)
+        return self.makeTxnExpr(TxnField.global_num_uints)
 
     def global_num_byte_slices(self) -> TxnExpr:
         """Get the schema count of global state byte slices in an application creation call.
@@ -554,7 +653,7 @@ def global_num_byte_slices(self) -> TxnExpr:
 
         Requires TEAL version 3 or higher.
         """
-        return self.txnType(TxnField.global_num_byte_slices)
+        return self.makeTxnExpr(TxnField.global_num_byte_slices)
 
     def local_num_uints(self) -> TxnExpr:
         """Get the schema count of local state integers in an application creation call.
@@ -563,7 +662,7 @@ def local_num_uints(self) -> TxnExpr:
 
         Requires TEAL version 3 or higher.
         """
-        return self.txnType(TxnField.local_num_uints)
+        return self.makeTxnExpr(TxnField.local_num_uints)
 
     def local_num_byte_slices(self) -> TxnExpr:
         """Get the schema count of local state byte slices in an application creation call.
@@ -572,7 +671,7 @@ def local_num_byte_slices(self) -> TxnExpr:
 
         Requires TEAL version 3 or higher.
         """
-        return self.txnType(TxnField.local_num_byte_slices)
+        return self.makeTxnExpr(TxnField.local_num_byte_slices)
 
     def extra_program_pages(self) -> TxnExpr:
         """Get the number of additional pages for each of the application's approval and clear state programs.
@@ -583,7 +682,18 @@ def extra_program_pages(self) -> TxnExpr:
 
         Requires TEAL version 4 or higher.
         """
-        return self.txnType(TxnField.extra_program_pages)
+        return self.makeTxnExpr(TxnField.extra_program_pages)
+
+    def created_application_id(self) -> TxnExpr:
+        """Get the application ID allocated by the creation of an application.
+
+        Currently this only works on inner transactions.
+
+        Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall` and this is an app creation call.
+
+        Requires TEAL version 5 or higher.
+        """
+        return self.makeTxnExpr(TxnField.created_application_id)
 
     @property
     def application_args(self) -> TxnArray:
@@ -621,9 +731,23 @@ def applications(self) -> TxnArray:
         """
         return TxnArray(self, TxnField.applications, TxnField.num_applications)
 
+    @property
+    def logs(self) -> TxnArray:
+        """The log messages emitted by an application call.
+
+        Currently this only works on inner transactions.
+
+        :type: TxnArray
+
+        Requires TEAL version 5 or higher.
+        """
+        return TxnArray(self, TxnField.logs, TxnField.num_logs)
+
 
 TxnObject.__module__ = "pyteal"
 
-Txn: TxnObject = TxnObject(TxnExpr, TxnaExpr)
+Txn: TxnObject = TxnObject(
+    TxnExprBuilder(Op.txn, "Txn"), TxnaExprBuilder(Op.txna, Op.txnas, "Txna")
+)
 
 Txn.__module__ = "pyteal"
diff --git a/pyteal/compiler/compiler.py b/pyteal/compiler/compiler.py
index 770aa6155..804f61871 100644
--- a/pyteal/compiler/compiler.py
+++ b/pyteal/compiler/compiler.py
@@ -191,7 +191,10 @@ def compileTeal(
         TealInputError: if an operation in ast is not supported by the supplied mode and version.
         TealInternalError: if an internal error is encounter during compilation.
     """
-    if not (MIN_TEAL_VERSION <= version <= MAX_TEAL_VERSION) or type(version) != int:
+    if (
+        not (MIN_TEAL_VERSION <= version <= MAX_TEAL_VERSION)
+        or type(version) is not int
+    ):
         raise TealInputError(
             "Unsupported TEAL version: {}. Excepted an integer in the range [{}, {}]".format(
                 version, MIN_TEAL_VERSION, MAX_TEAL_VERSION
diff --git a/pyteal/compiler/constants.py b/pyteal/compiler/constants.py
index 7105a6413..5152122a4 100644
--- a/pyteal/compiler/constants.py
+++ b/pyteal/compiler/constants.py
@@ -46,7 +46,7 @@ def extractIntValue(op: TealOp) -> Union[str, int]:
         raise TealInternalError("Unexpected args in int opcode: {}".format(op.args))
 
     value = cast(Union[str, int], op.args[0])
-    if type(value) == int or cast(str, value).startswith("TMPL_"):
+    if type(value) is int or cast(str, value).startswith("TMPL_"):
         return value
     if value not in intEnumValues:
         raise TealInternalError("Int constant not recognized: {}".format(value))
@@ -60,10 +60,10 @@ def extractBytesValue(op: TealOp) -> Union[str, bytes]:
         If the op is loading a template variable, returns the name of the variable as a string.
         Otherwise, returns the byte string that the op is loading.
     """
-    if len(op.args) != 1 or type(op.args[0]) != str:
+    if len(op.args) != 1 or type(op.args[0]) is not str:
         raise TealInternalError("Unexpected args in byte opcode: {}".format(op.args))
 
-    value = cast(str, op.args[0])
+    value = op.args[0]
     if value.startswith("TMPL_"):
         return value
     if value.startswith('"') and value.endswith('"'):
@@ -134,7 +134,7 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]:
 
     intBlock = [i for i in sortedInts if intFreqs[i] > 1]
     byteBlock = [
-        ("0x" + cast(bytes, b).hex()) if type(b) == bytes else cast(str, b)
+        ("0x" + b.hex()) if type(b) is bytes else cast(str, b)
         for b in sortedBytes
         if byteFreqs[b] > 1
     ]
@@ -178,8 +178,8 @@ def createConstantBlocks(ops: List[TealComponent]) -> List[TealComponent]:
                 )
                 if byteFreqs[byteValue] == 1:
                     encodedValue = (
-                        ("0x" + cast(bytes, byteValue).hex())
-                        if type(byteValue) == bytes
+                        ("0x" + byteValue.hex())
+                        if type(byteValue) is bytes
                         else cast(str, byteValue)
                     )
                     assembled.append(
diff --git a/pyteal/compiler/flatten.py b/pyteal/compiler/flatten.py
index 3d0815369..95454f1b2 100644
--- a/pyteal/compiler/flatten.py
+++ b/pyteal/compiler/flatten.py
@@ -44,22 +44,20 @@ def blockIndexByReference(block: TealBlock) -> int:
             continue
 
         if type(block) is TealSimpleBlock:
-            simpleBlock = cast(TealSimpleBlock, block)
-            assert simpleBlock.nextBlock is not None
+            assert block.nextBlock is not None
 
-            nextIndex = blockIndexByReference(simpleBlock.nextBlock)
+            nextIndex = blockIndexByReference(block.nextBlock)
 
             if nextIndex != i + 1:
                 references[nextIndex] += 1
                 code.append(TealOp(None, Op.b, indexToLabel(nextIndex)))
 
         elif type(block) is TealConditionalBlock:
-            conditionalBlock = cast(TealConditionalBlock, block)
-            assert conditionalBlock.trueBlock is not None
-            assert conditionalBlock.falseBlock is not None
+            assert block.trueBlock is not None
+            assert block.falseBlock is not None
 
-            trueIndex = blockIndexByReference(conditionalBlock.trueBlock)
-            falseIndex = blockIndexByReference(conditionalBlock.falseBlock)
+            trueIndex = blockIndexByReference(block.trueBlock)
+            falseIndex = blockIndexByReference(block.falseBlock)
 
             if falseIndex == i + 1:
                 references[trueIndex] += 1
diff --git a/pyteal/ir/ops.py b/pyteal/ir/ops.py
index 623d3c793..19f3b5457 100644
--- a/pyteal/ir/ops.py
+++ b/pyteal/ir/ops.py
@@ -31,123 +31,144 @@ def min_version(self) -> int:
         return self.value.min_version
 
     # fmt: off
-    err               = OpType("err",               Mode.Signature | Mode.Application, 2)
-    sha256            = OpType("sha256",            Mode.Signature | Mode.Application, 2)
-    keccak256         = OpType("keccak256",         Mode.Signature | Mode.Application, 2)
-    sha512_256        = OpType("sha512_256",        Mode.Signature | Mode.Application, 2)
-    ed25519verify     = OpType("ed25519verify",     Mode.Signature,                    2)
-    add               = OpType("+",                 Mode.Signature | Mode.Application, 2)
-    minus             = OpType("-",                 Mode.Signature | Mode.Application, 2)
-    div               = OpType("/",                 Mode.Signature | Mode.Application, 2)
-    mul               = OpType("*",                 Mode.Signature | Mode.Application, 2)
-    lt                = OpType("<",                 Mode.Signature | Mode.Application, 2)
-    gt                = OpType(">",                 Mode.Signature | Mode.Application, 2)
-    le                = OpType("<=",                Mode.Signature | Mode.Application, 2)
-    ge                = OpType(">=",                Mode.Signature | Mode.Application, 2)
-    logic_and         = OpType("&&",                Mode.Signature | Mode.Application, 2)
-    logic_or          = OpType("||",                Mode.Signature | Mode.Application, 2)
-    eq                = OpType("==",                Mode.Signature | Mode.Application, 2)
-    neq               = OpType("!=",                Mode.Signature | Mode.Application, 2)
-    logic_not         = OpType("!",                 Mode.Signature | Mode.Application, 2)
-    len               = OpType("len",               Mode.Signature | Mode.Application, 2)
-    itob              = OpType("itob",              Mode.Signature | Mode.Application, 2)
-    btoi              = OpType("btoi",              Mode.Signature | Mode.Application, 2)
-    mod               = OpType("%",                 Mode.Signature | Mode.Application, 2)
-    bitwise_or        = OpType("|",                 Mode.Signature | Mode.Application, 2)
-    bitwise_and       = OpType("&",                 Mode.Signature | Mode.Application, 2)
-    bitwise_xor       = OpType("^",                 Mode.Signature | Mode.Application, 2)
-    bitwise_not       = OpType("~",                 Mode.Signature | Mode.Application, 2)
-    mulw              = OpType("mulw",              Mode.Signature | Mode.Application, 2)
-    addw              = OpType("addw",              Mode.Signature | Mode.Application, 2)
-    intcblock         = OpType("intcblock",         Mode.Signature | Mode.Application, 2)
-    intc              = OpType("intc",              Mode.Signature | Mode.Application, 2)
-    intc_0            = OpType("intc_0",            Mode.Signature | Mode.Application, 2)
-    intc_1            = OpType("intc_1",            Mode.Signature | Mode.Application, 2)
-    intc_2            = OpType("intc_2",            Mode.Signature | Mode.Application, 2)
-    intc_3            = OpType("intc_3",            Mode.Signature | Mode.Application, 2)
-    int               = OpType("int",               Mode.Signature | Mode.Application, 2)
-    bytecblock        = OpType("bytecblock",        Mode.Signature | Mode.Application, 2)
-    bytec             = OpType("bytec",             Mode.Signature | Mode.Application, 2)
-    bytec_0           = OpType("bytec_0",           Mode.Signature | Mode.Application, 2)
-    bytec_1           = OpType("bytec_1",           Mode.Signature | Mode.Application, 2)
-    bytec_2           = OpType("bytec_2",           Mode.Signature | Mode.Application, 2)
-    bytec_3           = OpType("bytec_3",           Mode.Signature | Mode.Application, 2)
-    byte              = OpType("byte",              Mode.Signature | Mode.Application, 2)
-    addr              = OpType("addr",              Mode.Signature | Mode.Application, 2)
-    arg               = OpType("arg",               Mode.Signature,                    2)
-    txn               = OpType("txn",               Mode.Signature | Mode.Application, 2)
-    global_           = OpType("global",            Mode.Signature | Mode.Application, 2)
-    gtxn              = OpType("gtxn",              Mode.Signature | Mode.Application, 2)
-    load              = OpType("load",              Mode.Signature | Mode.Application, 2)
-    store             = OpType("store",             Mode.Signature | Mode.Application, 2)
-    txna              = OpType("txna",              Mode.Signature | Mode.Application, 2)
-    gtxna             = OpType("gtxna",             Mode.Signature | Mode.Application, 2)
-    bnz               = OpType("bnz",               Mode.Signature | Mode.Application, 2)
-    bz                = OpType("bz",                Mode.Signature | Mode.Application, 2)
-    b                 = OpType("b",                 Mode.Signature | Mode.Application, 2)
-    return_           = OpType("return",            Mode.Signature | Mode.Application, 2)
-    pop               = OpType("pop",               Mode.Signature | Mode.Application, 2)
-    dup               = OpType("dup",               Mode.Signature | Mode.Application, 2)
-    dup2              = OpType("dup2",              Mode.Signature | Mode.Application, 2)
-    concat            = OpType("concat",            Mode.Signature | Mode.Application, 2)
-    substring         = OpType("substring",         Mode.Signature | Mode.Application, 2)
-    substring3        = OpType("substring3",        Mode.Signature | Mode.Application, 2)
-    balance           = OpType("balance",           Mode.Application,                  2)
-    app_opted_in      = OpType("app_opted_in",      Mode.Application,                  2)
-    app_local_get     = OpType("app_local_get",     Mode.Application,                  2)
-    app_local_get_ex  = OpType("app_local_get_ex",  Mode.Application,                  2)
-    app_global_get    = OpType("app_global_get",    Mode.Application,                  2)
-    app_global_get_ex = OpType("app_global_get_ex", Mode.Application,                  2)
-    app_local_put     = OpType("app_local_put",     Mode.Application,                  2)
-    app_global_put    = OpType("app_global_put",    Mode.Application,                  2)
-    app_local_del     = OpType("app_local_del",     Mode.Application,                  2)
-    app_global_del    = OpType("app_global_del",    Mode.Application,                  2)
-    asset_holding_get = OpType("asset_holding_get", Mode.Application,                  2)
-    asset_params_get  = OpType("asset_params_get",  Mode.Application,                  2)
-    gtxns             = OpType("gtxns",             Mode.Signature | Mode.Application, 3)
-    gtxnsa            = OpType("gtxnsa",            Mode.Signature | Mode.Application, 3)
-    assert_           = OpType("assert",            Mode.Signature | Mode.Application, 3)
-    dig               = OpType("dig",               Mode.Signature | Mode.Application, 3)
-    swap              = OpType("swap",              Mode.Signature | Mode.Application, 3)
-    select            = OpType("select",            Mode.Signature | Mode.Application, 3)
-    getbit            = OpType("getbit",            Mode.Signature | Mode.Application, 3)
-    setbit            = OpType("setbit",            Mode.Signature | Mode.Application, 3)
-    getbyte           = OpType("getbyte",           Mode.Signature | Mode.Application, 3)
-    setbyte           = OpType("setbyte",           Mode.Signature | Mode.Application, 3)
-    min_balance       = OpType("min_balance",       Mode.Application,                  3)
-    pushbytes         = OpType("pushbytes",         Mode.Signature | Mode.Application, 3)
-    pushint           = OpType("pushint",           Mode.Signature | Mode.Application, 3)
-    shl               = OpType("shl",               Mode.Signature | Mode.Application, 4)
-    shr               = OpType("shr",               Mode.Signature | Mode.Application, 4)
-    sqrt              = OpType("sqrt",              Mode.Signature | Mode.Application, 4)
-    bitlen            = OpType("bitlen",            Mode.Signature | Mode.Application, 4)
-    exp               = OpType("exp",               Mode.Signature | Mode.Application, 4)
-    divmodw           = OpType("divmodw",           Mode.Signature | Mode.Application, 4)
-    expw              = OpType("expw",              Mode.Signature | Mode.Application, 4)
-    b_add             = OpType("b+",                Mode.Signature | Mode.Application, 4)
-    b_minus           = OpType("b-",                Mode.Signature | Mode.Application, 4)
-    b_div             = OpType("b/",                Mode.Signature | Mode.Application, 4)
-    b_mul             = OpType("b*",                Mode.Signature | Mode.Application, 4)
-    b_lt              = OpType("b<",                Mode.Signature | Mode.Application, 4)
-    b_gt              = OpType("b>",                Mode.Signature | Mode.Application, 4)
-    b_le              = OpType("b<=",               Mode.Signature | Mode.Application, 4)
-    b_ge              = OpType("b>=",               Mode.Signature | Mode.Application, 4)
-    b_eq              = OpType("b==",               Mode.Signature | Mode.Application, 4)
-    b_neq             = OpType("b!=",               Mode.Signature | Mode.Application, 4)
-    b_mod             = OpType("b%",                Mode.Signature | Mode.Application, 4)
-    b_or              = OpType("b|",                Mode.Signature | Mode.Application, 4)
-    b_and             = OpType("b&",                Mode.Signature | Mode.Application, 4)
-    b_xor             = OpType("b^",                Mode.Signature | Mode.Application, 4)
-    b_not             = OpType("b~",                Mode.Signature | Mode.Application, 4)
-    bzero             = OpType("bzero",             Mode.Signature | Mode.Application, 4)
-    gload             = OpType("gload",             Mode.Application,                  4)
-    gloads            = OpType("gloads",            Mode.Application,                  4)
-    gaid              = OpType("gaid",              Mode.Application,                  4)
-    gaids             = OpType("gaids",             Mode.Application,                  4)
-    callsub           = OpType("callsub",           Mode.Signature | Mode.Application, 4)
-    retsub            = OpType("retsub",            Mode.Signature | Mode.Application, 4)
-    app_params_get    = OpType("app_params_get",    Mode.Application,                  5)
-
+    err                 = OpType("err",                 Mode.Signature | Mode.Application, 2)
+    sha256              = OpType("sha256",              Mode.Signature | Mode.Application, 2)
+    keccak256           = OpType("keccak256",           Mode.Signature | Mode.Application, 2)
+    sha512_256          = OpType("sha512_256",          Mode.Signature | Mode.Application, 2)
+    ed25519verify       = OpType("ed25519verify",       Mode.Signature | Mode.Application, 2)
+    add                 = OpType("+",                   Mode.Signature | Mode.Application, 2)
+    minus               = OpType("-",                   Mode.Signature | Mode.Application, 2)
+    div                 = OpType("/",                   Mode.Signature | Mode.Application, 2)
+    mul                 = OpType("*",                   Mode.Signature | Mode.Application, 2)
+    lt                  = OpType("<",                   Mode.Signature | Mode.Application, 2)
+    gt                  = OpType(">",                   Mode.Signature | Mode.Application, 2)
+    le                  = OpType("<=",                  Mode.Signature | Mode.Application, 2)
+    ge                  = OpType(">=",                  Mode.Signature | Mode.Application, 2)
+    logic_and           = OpType("&&",                  Mode.Signature | Mode.Application, 2)
+    logic_or            = OpType("||",                  Mode.Signature | Mode.Application, 2)
+    eq                  = OpType("==",                  Mode.Signature | Mode.Application, 2)
+    neq                 = OpType("!=",                  Mode.Signature | Mode.Application, 2)
+    logic_not           = OpType("!",                   Mode.Signature | Mode.Application, 2)
+    len                 = OpType("len",                 Mode.Signature | Mode.Application, 2)
+    itob                = OpType("itob",                Mode.Signature | Mode.Application, 2)
+    btoi                = OpType("btoi",                Mode.Signature | Mode.Application, 2)
+    mod                 = OpType("%",                   Mode.Signature | Mode.Application, 2)
+    bitwise_or          = OpType("|",                   Mode.Signature | Mode.Application, 2)
+    bitwise_and         = OpType("&",                   Mode.Signature | Mode.Application, 2)
+    bitwise_xor         = OpType("^",                   Mode.Signature | Mode.Application, 2)
+    bitwise_not         = OpType("~",                   Mode.Signature | Mode.Application, 2)
+    mulw                = OpType("mulw",                Mode.Signature | Mode.Application, 2)
+    addw                = OpType("addw",                Mode.Signature | Mode.Application, 2)
+    intcblock           = OpType("intcblock",           Mode.Signature | Mode.Application, 2)
+    intc                = OpType("intc",                Mode.Signature | Mode.Application, 2)
+    intc_0              = OpType("intc_0",              Mode.Signature | Mode.Application, 2)
+    intc_1              = OpType("intc_1",              Mode.Signature | Mode.Application, 2)
+    intc_2              = OpType("intc_2",              Mode.Signature | Mode.Application, 2)
+    intc_3              = OpType("intc_3",              Mode.Signature | Mode.Application, 2)
+    int                 = OpType("int",                 Mode.Signature | Mode.Application, 2)
+    bytecblock          = OpType("bytecblock",          Mode.Signature | Mode.Application, 2)
+    bytec               = OpType("bytec",               Mode.Signature | Mode.Application, 2)
+    bytec_0             = OpType("bytec_0",             Mode.Signature | Mode.Application, 2)
+    bytec_1             = OpType("bytec_1",             Mode.Signature | Mode.Application, 2)
+    bytec_2             = OpType("bytec_2",             Mode.Signature | Mode.Application, 2)
+    bytec_3             = OpType("bytec_3",             Mode.Signature | Mode.Application, 2)
+    byte                = OpType("byte",                Mode.Signature | Mode.Application, 2)
+    addr                = OpType("addr",                Mode.Signature | Mode.Application, 2)
+    arg                 = OpType("arg",                 Mode.Signature,                    2)
+    txn                 = OpType("txn",                 Mode.Signature | Mode.Application, 2)
+    global_             = OpType("global",              Mode.Signature | Mode.Application, 2)
+    gtxn                = OpType("gtxn",                Mode.Signature | Mode.Application, 2)
+    load                = OpType("load",                Mode.Signature | Mode.Application, 2)
+    store               = OpType("store",               Mode.Signature | Mode.Application, 2)
+    txna                = OpType("txna",                Mode.Signature | Mode.Application, 2)
+    gtxna               = OpType("gtxna",               Mode.Signature | Mode.Application, 2)
+    bnz                 = OpType("bnz",                 Mode.Signature | Mode.Application, 2)
+    bz                  = OpType("bz",                  Mode.Signature | Mode.Application, 2)
+    b                   = OpType("b",                   Mode.Signature | Mode.Application, 2)
+    return_             = OpType("return",              Mode.Signature | Mode.Application, 2)
+    pop                 = OpType("pop",                 Mode.Signature | Mode.Application, 2)
+    dup                 = OpType("dup",                 Mode.Signature | Mode.Application, 2)
+    dup2                = OpType("dup2",                Mode.Signature | Mode.Application, 2)
+    concat              = OpType("concat",              Mode.Signature | Mode.Application, 2)
+    substring           = OpType("substring",           Mode.Signature | Mode.Application, 2)
+    substring3          = OpType("substring3",          Mode.Signature | Mode.Application, 2)
+    balance             = OpType("balance",             Mode.Application,                  2)
+    app_opted_in        = OpType("app_opted_in",        Mode.Application,                  2)
+    app_local_get       = OpType("app_local_get",       Mode.Application,                  2)
+    app_local_get_ex    = OpType("app_local_get_ex",    Mode.Application,                  2)
+    app_global_get      = OpType("app_global_get",      Mode.Application,                  2)
+    app_global_get_ex   = OpType("app_global_get_ex",   Mode.Application,                  2)
+    app_local_put       = OpType("app_local_put",       Mode.Application,                  2)
+    app_global_put      = OpType("app_global_put",      Mode.Application,                  2)
+    app_local_del       = OpType("app_local_del",       Mode.Application,                  2)
+    app_global_del      = OpType("app_global_del",      Mode.Application,                  2)
+    asset_holding_get   = OpType("asset_holding_get",   Mode.Application,                  2)
+    asset_params_get    = OpType("asset_params_get",    Mode.Application,                  2)
+    gtxns               = OpType("gtxns",               Mode.Signature | Mode.Application, 3)
+    gtxnsa              = OpType("gtxnsa",              Mode.Signature | Mode.Application, 3)
+    assert_             = OpType("assert",              Mode.Signature | Mode.Application, 3)
+    dig                 = OpType("dig",                 Mode.Signature | Mode.Application, 3)
+    swap                = OpType("swap",                Mode.Signature | Mode.Application, 3)
+    select              = OpType("select",              Mode.Signature | Mode.Application, 3)
+    getbit              = OpType("getbit",              Mode.Signature | Mode.Application, 3)
+    setbit              = OpType("setbit",              Mode.Signature | Mode.Application, 3)
+    getbyte             = OpType("getbyte",             Mode.Signature | Mode.Application, 3)
+    setbyte             = OpType("setbyte",             Mode.Signature | Mode.Application, 3)
+    min_balance         = OpType("min_balance",         Mode.Application,                  3)
+    pushbytes           = OpType("pushbytes",           Mode.Signature | Mode.Application, 3)
+    pushint             = OpType("pushint",             Mode.Signature | Mode.Application, 3)
+    shl                 = OpType("shl",                 Mode.Signature | Mode.Application, 4)
+    shr                 = OpType("shr",                 Mode.Signature | Mode.Application, 4)
+    sqrt                = OpType("sqrt",                Mode.Signature | Mode.Application, 4)
+    bitlen              = OpType("bitlen",              Mode.Signature | Mode.Application, 4)
+    exp                 = OpType("exp",                 Mode.Signature | Mode.Application, 4)
+    divmodw             = OpType("divmodw",             Mode.Signature | Mode.Application, 4)
+    expw                = OpType("expw",                Mode.Signature | Mode.Application, 4)
+    b_add               = OpType("b+",                  Mode.Signature | Mode.Application, 4)
+    b_minus             = OpType("b-",                  Mode.Signature | Mode.Application, 4)
+    b_div               = OpType("b/",                  Mode.Signature | Mode.Application, 4)
+    b_mul               = OpType("b*",                  Mode.Signature | Mode.Application, 4)
+    b_lt                = OpType("b<",                  Mode.Signature | Mode.Application, 4)
+    b_gt                = OpType("b>",                  Mode.Signature | Mode.Application, 4)
+    b_le                = OpType("b<=",                 Mode.Signature | Mode.Application, 4)
+    b_ge                = OpType("b>=",                 Mode.Signature | Mode.Application, 4)
+    b_eq                = OpType("b==",                 Mode.Signature | Mode.Application, 4)
+    b_neq               = OpType("b!=",                 Mode.Signature | Mode.Application, 4)
+    b_mod               = OpType("b%",                  Mode.Signature | Mode.Application, 4)
+    b_or                = OpType("b|",                  Mode.Signature | Mode.Application, 4)
+    b_and               = OpType("b&",                  Mode.Signature | Mode.Application, 4)
+    b_xor               = OpType("b^",                  Mode.Signature | Mode.Application, 4)
+    b_not               = OpType("b~",                  Mode.Signature | Mode.Application, 4)
+    bzero               = OpType("bzero",               Mode.Signature | Mode.Application, 4)
+    gload               = OpType("gload",               Mode.Application,                  4)
+    gloads              = OpType("gloads",              Mode.Application,                  4)
+    gaid                = OpType("gaid",                Mode.Application,                  4)
+    gaids               = OpType("gaids",               Mode.Application,                  4)
+    callsub             = OpType("callsub",             Mode.Signature | Mode.Application, 4)
+    retsub              = OpType("retsub",              Mode.Signature | Mode.Application, 4)
+    ecdsa_verify        = OpType("ecdsa_verify",        Mode.Signature | Mode.Application, 5)
+    ecdsa_pk_decompress = OpType("ecdsa_pk_decompress", Mode.Signature | Mode.Application, 5)
+    ecdsa_pk_recover    = OpType("ecdsa_pk_recover",    Mode.Signature | Mode.Application, 5)
+    loads               = OpType("loads",               Mode.Signature | Mode.Application, 5)
+    stores              = OpType("stores",              Mode.Signature | Mode.Application, 5)
+    cover               = OpType("cover",               Mode.Signature | Mode.Application, 5)
+    uncover             = OpType("uncover",             Mode.Signature | Mode.Application, 5)
+    extract             = OpType("extract",             Mode.Signature | Mode.Application, 5)
+    extract3            = OpType("extract3",            Mode.Signature | Mode.Application, 5)
+    extract_uint16      = OpType("extract_uint16",      Mode.Signature | Mode.Application, 5)
+    extract_uint32      = OpType("extract_uint32",      Mode.Signature | Mode.Application, 5)
+    extract_uint64      = OpType("extract_uint64",      Mode.Signature | Mode.Application, 5)
+    app_params_get      = OpType("app_params_get",      Mode.Application,                  5)
+    log                 = OpType("log",                 Mode.Application,                  5)
+    itxn_begin          = OpType("itxn_begin",          Mode.Application,                  5)
+    itxn_field          = OpType("itxn_field",          Mode.Application,                  5)
+    itxn_submit         = OpType("itxn_submit",         Mode.Application,                  5)
+    itxn                = OpType("itxn",                Mode.Application,                  5)
+    itxna               = OpType("itxna",               Mode.Application,                  5)
+    txnas               = OpType("txnas",               Mode.Signature | Mode.Application, 5)
+    gtxnas              = OpType("gtxnas",              Mode.Signature | Mode.Application, 5)
+    gtxnsas             = OpType("gtxnsas",             Mode.Signature | Mode.Application, 5)
+    args                = OpType("args",                Mode.Signature,                    5)
     # fmt: on
 
 
diff --git a/pyteal/ir/tealconditionalblock.py b/pyteal/ir/tealconditionalblock.py
index f807bbfe4..d86d8b035 100644
--- a/pyteal/ir/tealconditionalblock.py
+++ b/pyteal/ir/tealconditionalblock.py
@@ -44,7 +44,6 @@ def __repr__(self) -> str:
     def __eq__(self, other: object) -> bool:
         if type(other) is not TealConditionalBlock:
             return False
-        other = cast(TealConditionalBlock, other)
         return (
             self.ops == other.ops
             and self.trueBlock == other.trueBlock
diff --git a/pyteal/ir/tealsimpleblock.py b/pyteal/ir/tealsimpleblock.py
index 05436b661..485e4c6e6 100644
--- a/pyteal/ir/tealsimpleblock.py
+++ b/pyteal/ir/tealsimpleblock.py
@@ -49,7 +49,6 @@ def __eq__(self, other: object) -> bool:
         if type(other) is not TealSimpleBlock:
             return False
         self.visited = True
-        other = cast(TealSimpleBlock, other)
         equal = self.ops == other.ops and self.nextBlock == other.nextBlock
         self.visited = False
         return equal


Test patch:
diff --git a/pyteal/ast/global_test.py b/pyteal/ast/global_test.py
index 4e8237248..0654ec6bd 100644
--- a/pyteal/ast/global_test.py
+++ b/pyteal/ast/global_test.py
@@ -7,6 +7,7 @@
 
 teal2Options = CompileOptions(version=2)
 teal3Options = CompileOptions(version=3)
+teal5Options = CompileOptions(version=5)
 
 
 def test_global_min_txn_fee():
@@ -120,3 +121,31 @@ def test_global_creator_address():
 
     with pytest.raises(TealInputError):
         expr.__teal__(teal2Options)
+
+
+def test_global_current_application_address():
+    expr = Global.current_application_address()
+    assert expr.type_of() == TealType.bytes
+
+    expected = TealSimpleBlock([TealOp(expr, Op.global_, "CurrentApplicationAddress")])
+
+    actual, _ = expr.__teal__(teal5Options)
+
+    assert actual == expected
+
+    with pytest.raises(TealInputError):
+        expr.__teal__(teal3Options)
+
+
+def test_global_group_id():
+    expr = Global.group_id()
+    assert expr.type_of() == TealType.bytes
+
+    expected = TealSimpleBlock([TealOp(expr, Op.global_, "GroupID")])
+
+    actual, _ = expr.__teal__(teal5Options)
+
+    assert actual == expected
+
+    with pytest.raises(TealInputError):
+        expr.__teal__(teal3Options)
diff --git a/pyteal/ast/gtxn_test.py b/pyteal/ast/gtxn_test.py
index 88db8bb55..168090918 100644
--- a/pyteal/ast/gtxn_test.py
+++ b/pyteal/ast/gtxn_test.py
@@ -2,16 +2,6 @@
 
 from .. import *
 
-# this is not necessary but mypy complains if it's not included
-from .. import MAX_GROUP_SIZE, CompileOptions
-
-GTXN_RANGE = range(MAX_GROUP_SIZE)
-
-teal2Options = CompileOptions(version=2)
-teal3Options = CompileOptions(version=3)
-teal4Options = CompileOptions(version=4)
-teal5Options = CompileOptions(version=5)
-
 
 def test_gtxn_invalid():
     with pytest.raises(TealInputError):
@@ -27,1674 +17,4 @@ def test_gtxn_invalid():
         Gtxn[Bytes("index")].sender()
 
 
-def test_gtxn_dynamic_teal_2():
-    with pytest.raises(TealInputError):
-        Gtxn[Int(0)].sender().__teal__(teal2Options)
-
-
-def test_gtxn_sender():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].sender()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Sender")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_sender_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].sender()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Sender")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_fee():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].fee()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Fee")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_fee_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].fee()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Fee")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_first_valid():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].first_valid()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "FirstValid")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_first_valid_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].first_valid()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "FirstValid")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_last_valid():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].last_valid()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "LastValid")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_last_valid_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].last_valid()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "LastValid")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_note():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].note()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Note")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_note_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].note()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Note")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_lease():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].lease()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Lease")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_lease_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].lease()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Lease")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_receiver():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].receiver()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Receiver")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_receiver_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].receiver()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Receiver")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_amount():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].amount()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Amount")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_amount_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].amount()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Amount")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_close_remainder_to():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].close_remainder_to()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "CloseRemainderTo")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_close_remainder_to_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].close_remainder_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "CloseRemainderTo")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_vote_pk():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].vote_pk()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "VotePK")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_vote_pk_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].vote_pk()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "VotePK")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_selection_pk():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].selection_pk()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "SelectionPK")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_selection_pk_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].selection_pk()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "SelectionPK")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_vote_first():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].vote_first()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "VoteFirst")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_vote_first_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].vote_first()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "VoteFirst")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_vote_last():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].vote_last()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "VoteLast")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_vote_last_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].vote_last()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "VoteLast")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_vote_key_dilution():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].vote_key_dilution()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "VoteKeyDilution")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_vote_key_dilution_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].vote_key_dilution()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "VoteKeyDilution")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_type():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].type()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Type")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_type_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].type()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "Type")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_type_enum():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].type_enum()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "TypeEnum")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_type_enum_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].type_enum()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "TypeEnum")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_xfer_asset():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].xfer_asset()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "XferAsset")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_xfer_asset_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].xfer_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "XferAsset")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_asset_amount():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].asset_amount()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "AssetAmount")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_asset_amount_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].asset_amount()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "AssetAmount")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_asset_sender():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].asset_sender()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "AssetSender")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_asset_sender_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].asset_sender()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "AssetSender")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_asset_receiver():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].asset_receiver()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "AssetReceiver")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_asset_receiver_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].asset_receiver()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "AssetReceiver")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_asset_close_to():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].asset_close_to()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "AssetCloseTo")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_asset_close_to_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].asset_close_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "AssetCloseTo")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_group_index():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].group_index()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "GroupIndex")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_group_index_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].group_index()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "GroupIndex")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_id():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].tx_id()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "TxID")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_gtxn_id_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].tx_id()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "TxID")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_gtxn_nonparticipation():
-
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].nonparticipation()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "Nonparticipation")])
-
-        actual, _ = expr.__teal__(teal5Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal4Options)
-
-
-def test_gtxn_nonparticipation_dynamic():
-
-    i = Int(0)
-    expr = Gtxn[i].nonparticipation()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(i, Op.int, 0), TealOp(expr, Op.gtxns, "Nonparticipation")]
-    )
-
-    actual, _ = expr.__teal__(teal5Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-    assert actual == expected
-
-
-def test_txn_application_id():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].application_id()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ApplicationID")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_application_id_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].application_id()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ApplicationID")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_on_completion():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].on_completion()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "OnCompletion")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_on_completion_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].on_completion()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "OnCompletion")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_application_args():
-    for i in GTXN_RANGE:
-        for j in range(32):
-            expr = Gtxn[i].application_args[j]
-            assert expr.type_of() == TealType.bytes
-
-            expected = TealSimpleBlock(
-                [TealOp(expr, Op.gtxna, i, "ApplicationArgs", j)]
-            )
-
-            actual, _ = expr.__teal__(teal2Options)
-
-            assert actual == expected
-
-
-def test_txn_application_args_dynamic():
-    index = Int(0)
-    for j in range(32):
-        expr = Gtxn[index].application_args[j]
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock(
-            [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "ApplicationArgs", j)]
-        )
-
-        actual, _ = expr.__teal__(teal3Options)
-        actual.addIncoming()
-        actual = TealBlock.NormalizeBlocks(actual)
-
-        assert actual == expected
-
-
-def test_txn_application_args_length():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].application_args.length()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumAppArgs")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_application_args_length_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].application_args.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumAppArgs")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_accounts():
-    for i in GTXN_RANGE:
-        for j in range(32):
-            expr = Gtxn[i].accounts[j]
-            assert expr.type_of() == TealType.bytes
-
-            expected = TealSimpleBlock([TealOp(expr, Op.gtxna, i, "Accounts", j)])
-
-            actual, _ = expr.__teal__(teal2Options)
-
-            assert actual == expected
-
-
-def test_txn_accounts_dynamic():
-    index = Int(0)
-    for j in range(32):
-        expr = Gtxn[index].accounts[j]
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock(
-            [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "Accounts", j)]
-        )
-
-        actual, _ = expr.__teal__(teal3Options)
-        actual.addIncoming()
-        actual = TealBlock.NormalizeBlocks(actual)
-
-        assert actual == expected
-
-
-def test_txn_accounts_length():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].accounts.length()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumAccounts")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_accounts_length_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].accounts.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumAccounts")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_approval_program():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].approval_program()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ApprovalProgram")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_approval_program_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].approval_program()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ApprovalProgram")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_clear_state_program():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].clear_state_program()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ClearStateProgram")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_clear_state_program_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].clear_state_program()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ClearStateProgram")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_rekey_to():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].rekey_to()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "RekeyTo")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_rekey_to_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].rekey_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "RekeyTo")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAsset")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAsset")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_total():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_total()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetTotal")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_total_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_total()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetTotal")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_decimals():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_decimals()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetDecimals")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_decimals_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_decimals()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetDecimals")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_default_frozen():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_default_frozen()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock(
-            [TealOp(expr, Op.gtxn, i, "ConfigAssetDefaultFrozen")]
-        )
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_default_frozen_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_default_frozen()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetDefaultFrozen")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_unit_name():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_unit_name()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetUnitName")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_unit_name_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_unit_name()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetUnitName")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_name():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_name()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetName")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_name_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_name()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetName")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_url():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_url()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetURL")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_url_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_url()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetURL")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_metadata_hash():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_metadata_hash()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock(
-            [TealOp(expr, Op.gtxn, i, "ConfigAssetMetadataHash")]
-        )
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_metadata_hash_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_metadata_hash()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetMetadataHash")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_manager():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_manager()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetManager")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_manager_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_manager()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetManager")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_reserve():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_reserve()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetReserve")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_reserve_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_reserve()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetReserve")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_freeze():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_freeze()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetFreeze")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_freeze_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_freeze()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetFreeze")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_clawback():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].config_asset_clawback()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ConfigAssetClawback")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_config_asset_clawback_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].config_asset_clawback()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ConfigAssetClawback")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].freeze_asset()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "FreezeAsset")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_freeze_asset_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].freeze_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "FreezeAsset")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset_account():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].freeze_asset_account()
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "FreezeAssetAccount")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_freeze_asset_account_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].freeze_asset_account()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "FreezeAssetAccount")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset_frozen():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].freeze_asset_frozen()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "FreezeAssetFrozen")])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_freeze_asset_frozen_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].freeze_asset_frozen()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "FreezeAssetFrozen")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-
-def test_txn_assets():
-    for i in GTXN_RANGE:
-        for j in range(32):
-            expr = Gtxn[i].assets[j]
-            assert expr.type_of() == TealType.uint64
-
-            expected = TealSimpleBlock([TealOp(expr, Op.gtxna, i, "Assets", j)])
-
-            actual, _ = expr.__teal__(teal3Options)
-
-            assert actual == expected
-
-            with pytest.raises(TealInputError):
-                expr.__teal__(teal2Options)
-
-
-def test_txn_assets_dynamic():
-    index = Int(0)
-    for j in range(32):
-        expr = Gtxn[index].assets[j]
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock(
-            [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "Assets", j)]
-        )
-
-        actual, _ = expr.__teal__(teal3Options)
-        actual.addIncoming()
-        actual = TealBlock.NormalizeBlocks(actual)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_assets_length():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].assets.length()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumAssets")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_assets_length_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].assets.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumAssets")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_applications():
-    for i in GTXN_RANGE:
-        for j in range(32):
-            expr = Gtxn[i].applications[j]
-            assert expr.type_of() == TealType.uint64
-
-            expected = TealSimpleBlock([TealOp(expr, Op.gtxna, i, "Applications", j)])
-
-            actual, _ = expr.__teal__(teal3Options)
-
-            assert actual == expected
-
-            with pytest.raises(TealInputError):
-                expr.__teal__(teal2Options)
-
-
-def test_txn_applications_dynamic():
-    index = Int(0)
-    for j in range(32):
-        expr = Gtxn[index].applications[j]
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock(
-            [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "Applications", j)]
-        )
-
-        actual, _ = expr.__teal__(teal3Options)
-        actual.addIncoming()
-        actual = TealBlock.NormalizeBlocks(actual)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_applications_length():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].applications.length()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumApplications")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_applications_length_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].applications.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumApplications")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_uints():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].global_num_uints()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "GlobalNumUint")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_uints_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].global_num_uints()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "GlobalNumUint")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_byte_slices():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].global_num_byte_slices()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "GlobalNumByteSlice")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_byte_slices_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].global_num_byte_slices()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "GlobalNumByteSlice")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_uints():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].local_num_uints()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "LocalNumUint")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_uints_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].local_num_uints()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "LocalNumUint")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_byte_slices():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].local_num_byte_slices()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "LocalNumByteSlice")])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_byte_slices_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].local_num_byte_slices()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "LocalNumByteSlice")]
-    )
-
-    actual, _ = expr.__teal__(teal3Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_extra_program_pages():
-    for i in GTXN_RANGE:
-        expr = Gtxn[i].extra_program_pages()
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "ExtraProgramPages")])
-
-        actual, _ = expr.__teal__(teal4Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal3Options)
-
-
-def test_txn_extra_program_pages_dynamic():
-    index = Int(0)
-    expr = Gtxn[index].extra_program_pages()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock(
-        [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "ExtraProgramPages")]
-    )
-
-    actual, _ = expr.__teal__(teal4Options)
-    actual.addIncoming()
-    actual = TealBlock.NormalizeBlocks(actual)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal3Options)
+# txn_test.py performs additional testing
diff --git a/pyteal/ast/itxn_test.py b/pyteal/ast/itxn_test.py
new file mode 100644
index 000000000..454fcef2b
--- /dev/null
+++ b/pyteal/ast/itxn_test.py
@@ -0,0 +1,116 @@
+import pytest
+
+from .. import *
+from ..types import types_match
+
+# this is not necessary but mypy complains if it's not included
+from .. import MAX_GROUP_SIZE, CompileOptions
+
+teal4Options = CompileOptions(version=4)
+teal5Options = CompileOptions(version=5)
+
+
+def test_InnerTxnBuilder_Begin():
+    expr = InnerTxnBuilder.Begin()
+    assert expr.type_of() == TealType.none
+    assert not expr.has_return()
+
+    expected = TealSimpleBlock([TealOp(expr, Op.itxn_begin)])
+
+    actual, _ = expr.__teal__(teal5Options)
+
+    assert actual == expected
+
+    with pytest.raises(TealInputError):
+        expr.__teal__(teal4Options)
+
+
+def test_InnerTxnBuilder_Submit():
+    expr = InnerTxnBuilder.Submit()
+    assert expr.type_of() == TealType.none
+    assert not expr.has_return()
+
+    expected = TealSimpleBlock([TealOp(expr, Op.itxn_submit)])
+
+    actual, _ = expr.__teal__(teal5Options)
+
+    assert actual == expected
+
+    with pytest.raises(TealInputError):
+        expr.__teal__(teal4Options)
+
+
+def test_InnerTxnBuilder_SetField():
+    for field in TxnField:
+        if field.is_array:
+            with pytest.raises(TealInputError):
+                InnerTxnBuilder.SetField(field, Int(0))
+            continue
+
+        for value, opArgs in (
+            (Int(0), (Op.int, 0)),
+            (Bytes("value"), (Op.byte, '"value"')),
+        ):
+            assert field.type_of() in (TealType.uint64, TealType.bytes)
+
+            if not types_match(field.type_of(), value.type_of()):
+                with pytest.raises(TealTypeError):
+                    InnerTxnBuilder.SetField(field, value)
+                continue
+
+            expr = InnerTxnBuilder.SetField(field, value)
+            assert expr.type_of() == TealType.none
+            assert not expr.has_return()
+
+            expected = TealSimpleBlock(
+                [TealOp(value, *opArgs), TealOp(expr, Op.itxn_field, field.arg_name)]
+            )
+
+            actual, _ = expr.__teal__(teal5Options)
+            actual.addIncoming()
+            actual = TealBlock.NormalizeBlocks(actual)
+
+            assert actual == expected
+
+            with pytest.raises(TealInputError):
+                expr.__teal__(teal4Options)
+
+
+def test_InnerTxnBuilder_SetFields():
+    cases = (
+        ({}, Seq()),
+        ({TxnField.amount: Int(5)}, InnerTxnBuilder.SetField(TxnField.amount, Int(5))),
+        (
+            {
+                TxnField.type_enum: TxnType.Payment,
+                TxnField.close_remainder_to: Txn.sender(),
+            },
+            Seq(
+                InnerTxnBuilder.SetField(TxnField.type_enum, TxnType.Payment),
+                InnerTxnBuilder.SetField(TxnField.close_remainder_to, Txn.sender()),
+            ),
+        ),
+    )
+
+    for fields, expectedExpr in cases:
+        expr = InnerTxnBuilder.SetFields(fields)
+        assert expr.type_of() == TealType.none
+        assert not expr.has_return()
+
+        expected, _ = expectedExpr.__teal__(teal5Options)
+        expected.addIncoming()
+        expected = TealBlock.NormalizeBlocks(expected)
+
+        actual, _ = expr.__teal__(teal5Options)
+        actual.addIncoming()
+        actual = TealBlock.NormalizeBlocks(actual)
+
+        with TealComponent.Context.ignoreExprEquality():
+            assert actual == expected
+
+        if len(fields) != 0:
+            with pytest.raises(TealInputError):
+                expr.__teal__(teal4Options)
+
+
+# txn_test.py performs additional testing
diff --git a/pyteal/ast/seq_test.py b/pyteal/ast/seq_test.py
index a3ebdf563..0487a558f 100644
--- a/pyteal/ast/seq_test.py
+++ b/pyteal/ast/seq_test.py
@@ -8,6 +8,18 @@
 options = CompileOptions()
 
 
+def test_seq_zero():
+    for expr in (Seq(), Seq([])):
+        assert expr.type_of() == TealType.none
+        assert not expr.has_return()
+
+        expected = TealSimpleBlock([])
+
+        actual, _ = expr.__teal__(options)
+
+        assert actual == expected
+
+
 def test_seq_one():
     items = [Int(0)]
     expr = Seq(items)
@@ -16,6 +28,8 @@ def test_seq_one():
     expected, _ = items[0].__teal__(options)
 
     actual, _ = expr.__teal__(options)
+    actual.addIncoming()
+    actual = TealBlock.NormalizeBlocks(actual)
 
     assert actual == expected
 
@@ -71,9 +85,6 @@ def test_seq_has_return():
 
 
 def test_seq_invalid():
-    with pytest.raises(TealInputError):
-        Seq([])
-
     with pytest.raises(TealTypeError):
         Seq([Int(1), Pop(Int(2))])
 
diff --git a/pyteal/ast/subroutine_test.py b/pyteal/ast/subroutine_test.py
index de79e800d..b7157ab16 100644
--- a/pyteal/ast/subroutine_test.py
+++ b/pyteal/ast/subroutine_test.py
@@ -192,8 +192,8 @@ def mySubroutine():
         assert declaration.type_of() == returnValue.type_of()
         assert declaration.has_return() == returnValue.has_return()
 
-        options.currentSubroutine = definition
-        expected, _ = returnValue.__teal__(options)
+        options.setSubroutine(definition)
+        expected, _ = Seq([returnValue]).__teal__(options)
 
         actual, _ = declaration.__teal__(options)
         options.setSubroutine(None)
@@ -233,7 +233,7 @@ def mySubroutine(a1):
 
         assert declaration.body.args[0].slot is argSlots[-1]
 
-        options.currentSubroutine = definition
+        options.setSubroutine(definition)
         expected, _ = Seq([declaration.body.args[0], returnValue]).__teal__(options)
 
         actual, _ = declaration.__teal__(options)
@@ -278,7 +278,7 @@ def mySubroutine(a1, a2):
         assert declaration.body.args[0].slot is argSlots[-1]
         assert declaration.body.args[1].slot is argSlots[-2]
 
-        options.currentSubroutine = definition
+        options.setSubroutine(definition)
         expected, _ = Seq(
             [declaration.body.args[0], declaration.body.args[1], returnValue]
         ).__teal__(options)
@@ -324,7 +324,7 @@ def mySubroutine(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10):
         for i in range(10):
             assert declaration.body.args[i].slot is argSlots[-i - 1]
 
-        options.currentSubroutine = definition
+        options.setSubroutine(definition)
         expected, _ = Seq(declaration.body.args[:10] + [returnValue]).__teal__(options)
 
         actual, _ = declaration.__teal__(options)
diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py
index cfcc28117..c980c1773 100644
--- a/pyteal/ast/txn_test.py
+++ b/pyteal/ast/txn_test.py
@@ -1,672 +1,228 @@
+from typing import Dict, Callable
+
 import pytest
 
 from .. import *
 
 # this is not necessary but mypy complains if it's not included
-from .. import CompileOptions
-
-teal2Options = CompileOptions(version=2)
-teal3Options = CompileOptions(version=3)
-teal4Options = CompileOptions(version=4)
-teal5Options = CompileOptions(version=5)
-
-
-def test_txn_sender():
-    expr = Txn.sender()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Sender")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_fee():
-    expr = Txn.fee()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Fee")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_first_valid():
-    expr = Txn.first_valid()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "FirstValid")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_last_valid():
-    expr = Txn.last_valid()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "LastValid")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_note():
-    expr = Txn.note()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Note")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_lease():
-    expr = Txn.lease()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Lease")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_receiver():
-    expr = Txn.receiver()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Receiver")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_amount():
-    expr = Txn.amount()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Amount")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_close_remainder_to():
-    expr = Txn.close_remainder_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "CloseRemainderTo")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_vote_pk():
-    expr = Txn.vote_pk()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "VotePK")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_selection_pk():
-    expr = Txn.selection_pk()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "SelectionPK")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_vote_first():
-    expr = Txn.vote_first()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "VoteFirst")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_vote_last():
-    expr = Txn.vote_last()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "VoteLast")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_vote_key_dilution():
-    expr = Txn.vote_key_dilution()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "VoteKeyDilution")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_nonparticipation():
-    expr = Txn.nonparticipation()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Nonparticipation")])
-
-    actual, _ = expr.__teal__(teal5Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal4Options)
-
-
-def test_txn_type():
-    expr = Txn.type()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "Type")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_type_enum():
-    expr = Txn.type_enum()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "TypeEnum")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_xfer_asset():
-    expr = Txn.xfer_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "XferAsset")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_asset_amount():
-    expr = Txn.asset_amount()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "AssetAmount")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_asset_sender():
-    expr = Txn.asset_sender()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "AssetSender")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_asset_receiver():
-    expr = Txn.asset_receiver()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "AssetReceiver")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_asset_close_to():
-    expr = Txn.asset_close_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "AssetCloseTo")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_group_index():
-    expr = Txn.group_index()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "GroupIndex")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_id():
-    expr = Txn.tx_id()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "TxID")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_application_id():
-    expr = Txn.application_id()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ApplicationID")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_on_completion():
-    expr = Txn.on_completion()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "OnCompletion")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_application_args():
-    for i in range(32):
-        expr = Txn.application_args[i]
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.txna, "ApplicationArgs", i)])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_application_args_length():
-    expr = Txn.application_args.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumAppArgs")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_accounts():
-    for i in range(32):
-        expr = Txn.accounts[i]
-        assert expr.type_of() == TealType.bytes
-
-        expected = TealSimpleBlock([TealOp(expr, Op.txna, "Accounts", i)])
-
-        actual, _ = expr.__teal__(teal2Options)
-
-        assert actual == expected
-
-
-def test_txn_accounts_length():
-    expr = Txn.accounts.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumAccounts")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_approval_program():
-    expr = Txn.approval_program()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ApprovalProgram")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_clear_state_program():
-    expr = Txn.clear_state_program()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ClearStateProgram")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_rekey_to():
-    expr = Txn.rekey_to()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "RekeyTo")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset():
-    expr = Txn.config_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAsset")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_total():
-    expr = Txn.config_asset_total()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetTotal")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_decimals():
-    expr = Txn.config_asset_decimals()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetDecimals")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_default_frozen():
-    expr = Txn.config_asset_default_frozen()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetDefaultFrozen")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_unit_name():
-    expr = Txn.config_asset_unit_name()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetUnitName")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_name():
-    expr = Txn.config_asset_name()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetName")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_url():
-    expr = Txn.config_asset_url()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetURL")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_metadata_hash():
-    expr = Txn.config_asset_metadata_hash()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetMetadataHash")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_manager():
-    expr = Txn.config_asset_manager()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetManager")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_reserve():
-    expr = Txn.config_asset_reserve()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetReserve")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_freeze():
-    expr = Txn.config_asset_freeze()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetFreeze")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_config_asset_clawback():
-    expr = Txn.config_asset_clawback()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ConfigAssetClawback")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset():
-    expr = Txn.freeze_asset()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "FreezeAsset")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset_account():
-    expr = Txn.freeze_asset_account()
-    assert expr.type_of() == TealType.bytes
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "FreezeAssetAccount")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_freeze_asset_frozen():
-    expr = Txn.freeze_asset_frozen()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "FreezeAssetFrozen")])
-
-    actual, _ = expr.__teal__(teal2Options)
-
-    assert actual == expected
-
-
-def test_txn_assets():
-    for i in range(32):
-        expr = Txn.assets[i]
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.txna, "Assets", i)])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_assets_length():
-    expr = Txn.assets.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumAssets")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_applications():
-    for i in range(32):
-        expr = Txn.applications[i]
-        assert expr.type_of() == TealType.uint64
-
-        expected = TealSimpleBlock([TealOp(expr, Op.txna, "Applications", i)])
-
-        actual, _ = expr.__teal__(teal3Options)
-
-        assert actual == expected
-
-        with pytest.raises(TealInputError):
-            expr.__teal__(teal2Options)
-
-
-def test_txn_applications_length():
-    expr = Txn.applications.length()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumApplications")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_uints():
-    expr = Txn.global_num_uints()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "GlobalNumUint")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_global_num_byte_slices():
-    expr = Txn.global_num_byte_slices()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "GlobalNumByteSlice")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_uints():
-    expr = Txn.local_num_uints()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "LocalNumUint")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_local_num_byte_slices():
-    expr = Txn.local_num_byte_slices()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "LocalNumByteSlice")])
-
-    actual, _ = expr.__teal__(teal3Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal2Options)
-
-
-def test_txn_extra_program_pages():
-    expr = Txn.extra_program_pages()
-    assert expr.type_of() == TealType.uint64
-
-    expected = TealSimpleBlock([TealOp(expr, Op.txn, "ExtraProgramPages")])
-
-    actual, _ = expr.__teal__(teal4Options)
-
-    assert actual == expected
-
-    with pytest.raises(TealInputError):
-        expr.__teal__(teal3Options)
+from .. import Expr, TxnField, TxnObject, TxnArray, CompileOptions
+
+fieldToMethod: Dict[TxnField, Callable[[TxnObject], Expr]] = {
+    TxnField.sender: lambda txn: txn.sender(),
+    TxnField.fee: lambda txn: txn.fee(),
+    TxnField.first_valid: lambda txn: txn.first_valid(),
+    TxnField.last_valid: lambda txn: txn.last_valid(),
+    TxnField.note: lambda txn: txn.note(),
+    TxnField.lease: lambda txn: txn.lease(),
+    TxnField.receiver: lambda txn: txn.receiver(),
+    TxnField.amount: lambda txn: txn.amount(),
+    TxnField.close_remainder_to: lambda txn: txn.close_remainder_to(),
+    TxnField.vote_pk: lambda txn: txn.vote_pk(),
+    TxnField.selection_pk: lambda txn: txn.selection_pk(),
+    TxnField.vote_first: lambda txn: txn.vote_first(),
+    TxnField.vote_last: lambda txn: txn.vote_last(),
+    TxnField.vote_key_dilution: lambda txn: txn.vote_key_dilution(),
+    TxnField.type: lambda txn: txn.type(),
+    TxnField.type_enum: lambda txn: txn.type_enum(),
+    TxnField.xfer_asset: lambda txn: txn.xfer_asset(),
+    TxnField.asset_amount: lambda txn: txn.asset_amount(),
+    TxnField.asset_sender: lambda txn: txn.asset_sender(),
+    TxnField.asset_receiver: lambda txn: txn.asset_receiver(),
+    TxnField.asset_close_to: lambda txn: txn.asset_close_to(),
+    TxnField.group_index: lambda txn: txn.group_index(),
+    TxnField.tx_id: lambda txn: txn.tx_id(),
+    TxnField.application_id: lambda txn: txn.application_id(),
+    TxnField.on_completion: lambda txn: txn.on_completion(),
+    TxnField.approval_program: lambda txn: txn.approval_program(),
+    TxnField.clear_state_program: lambda txn: txn.clear_state_program(),
+    TxnField.rekey_to: lambda txn: txn.rekey_to(),
+    TxnField.config_asset: lambda txn: txn.config_asset(),
+    TxnField.config_asset_total: lambda txn: txn.config_asset_total(),
+    TxnField.config_asset_decimals: lambda txn: txn.config_asset_decimals(),
+    TxnField.config_asset_default_frozen: lambda txn: txn.config_asset_default_frozen(),
+    TxnField.config_asset_unit_name: lambda txn: txn.config_asset_unit_name(),
+    TxnField.config_asset_name: lambda txn: txn.config_asset_name(),
+    TxnField.config_asset_url: lambda txn: txn.config_asset_url(),
+    TxnField.config_asset_metadata_hash: lambda txn: txn.config_asset_metadata_hash(),
+    TxnField.config_asset_manager: lambda txn: txn.config_asset_manager(),
+    TxnField.config_asset_reserve: lambda txn: txn.config_asset_reserve(),
+    TxnField.config_asset_freeze: lambda txn: txn.config_asset_freeze(),
+    TxnField.config_asset_clawback: lambda txn: txn.config_asset_clawback(),
+    TxnField.freeze_asset: lambda txn: txn.freeze_asset(),
+    TxnField.freeze_asset_account: lambda txn: txn.freeze_asset_account(),
+    TxnField.freeze_asset_frozen: lambda txn: txn.freeze_asset_frozen(),
+    TxnField.global_num_uints: lambda txn: txn.global_num_uints(),
+    TxnField.global_num_byte_slices: lambda txn: txn.global_num_byte_slices(),
+    TxnField.local_num_uints: lambda txn: txn.local_num_uints(),
+    TxnField.local_num_byte_slices: lambda txn: txn.local_num_byte_slices(),
+    TxnField.extra_program_pages: lambda txn: txn.extra_program_pages(),
+    TxnField.nonparticipation: lambda txn: txn.nonparticipation(),
+    TxnField.created_asset_id: lambda txn: txn.created_asset_id(),
+    TxnField.created_application_id: lambda txn: txn.created_application_id(),
+}
+
+arrayFieldToProperty: Dict[TxnField, Callable[[TxnObject], TxnArray]] = {
+    TxnField.application_args: lambda txn: txn.application_args,
+    TxnField.accounts: lambda txn: txn.accounts,
+    TxnField.assets: lambda txn: txn.assets,
+    TxnField.applications: lambda txn: txn.applications,
+    TxnField.logs: lambda txn: txn.logs,
+}
+
+arrayFieldToLengthField: Dict[TxnField, TxnField] = {
+    TxnField.application_args: TxnField.num_app_args,
+    TxnField.accounts: TxnField.num_accounts,
+    TxnField.assets: TxnField.num_assets,
+    TxnField.applications: TxnField.num_applications,
+    TxnField.logs: TxnField.num_logs,
+}
+
+
+def test_txn_fields():
+    dynamicGtxnArg = Int(0)
+
+    txnObjects = [
+        (Txn, Op.txn, Op.txna, Op.txnas, [], []),
+        *[
+            (Gtxn[i], Op.gtxn, Op.gtxna, Op.gtxnas, [i], [])
+            for i in range(MAX_GROUP_SIZE)
+        ],
+        (
+            Gtxn[dynamicGtxnArg],
+            Op.gtxns,
+            Op.gtxnsa,
+            Op.gtxnsas,
+            [],
+            [TealOp(dynamicGtxnArg, Op.int, 0)],
+        ),
+        (InnerTxn, Op.itxn, Op.itxna, None, [], []),
+    ]
+
+    for (
+        txnObject,
+        op,
+        staticArrayOp,
+        dynamicArrayOp,
+        immediateArgsPrefix,
+        irPrefix,
+    ) in txnObjects:
+        for field in TxnField:
+            if field.is_array:
+                array = arrayFieldToProperty[field](txnObject)
+                lengthExpr = array.length()
+
+                lengthFieldName = arrayFieldToLengthField[field].arg_name
+                immediateArgs = immediateArgsPrefix + [lengthFieldName]
+                expected = TealSimpleBlock(
+                    irPrefix + [TealOp(lengthExpr, op, *immediateArgs)]
+                )
+                expected.addIncoming()
+                expected = TealBlock.NormalizeBlocks(expected)
+
+                version = max(op.min_version, field.min_version)
+
+                actual, _ = lengthExpr.__teal__(CompileOptions(version=version))
+                actual.addIncoming()
+                actual = TealBlock.NormalizeBlocks(actual)
+
+                assert (
+                    actual == expected
+                ), "{}: array length for field {} does not match expected".format(
+                    op, field
+                )
+
+                if version > 2:
+                    with pytest.raises(TealInputError):
+                        lengthExpr.__teal__(CompileOptions(version=version - 1))
+
+                for i in range(32):  # just an arbitrary large int
+                    elementExpr = array[i]
+
+                    immediateArgs = immediateArgsPrefix + [field.arg_name, i]
+                    expected = TealSimpleBlock(
+                        irPrefix + [TealOp(elementExpr, staticArrayOp, *immediateArgs)]
+                    )
+                    expected.addIncoming()
+                    expected = TealBlock.NormalizeBlocks(expected)
+
+                    version = max(staticArrayOp.min_version, field.min_version)
+
+                    actual, _ = elementExpr.__teal__(CompileOptions(version=version))
+                    actual.addIncoming()
+                    actual = TealBlock.NormalizeBlocks(actual)
+
+                    assert (
+                        actual == expected
+                    ), "{}: static array field {} does not match expected".format(
+                        staticArrayOp, field
+                    )
+
+                    if version > 2:
+                        with pytest.raises(TealInputError):
+                            elementExpr.__teal__(CompileOptions(version=version - 1))
+
+                if dynamicArrayOp is not None:
+                    dynamicIndex = Int(2)
+                    dynamicElementExpr = array[dynamicIndex]
+
+                    immediateArgs = immediateArgsPrefix + [field.arg_name]
+                    expected = TealSimpleBlock(
+                        irPrefix
+                        + [
+                            TealOp(dynamicIndex, Op.int, 2),
+                            TealOp(dynamicElementExpr, dynamicArrayOp, *immediateArgs),
+                        ]
+                    )
+                    expected.addIncoming()
+                    expected = TealBlock.NormalizeBlocks(expected)
+
+                    version = max(dynamicArrayOp.min_version, field.min_version)
+
+                    actual, _ = dynamicElementExpr.__teal__(
+                        CompileOptions(version=version)
+                    )
+                    actual.addIncoming()
+                    actual = TealBlock.NormalizeBlocks(actual)
+
+                    assert (
+                        actual == expected
+                    ), "{}: dynamic array field {} does not match expected".format(
+                        dynamicArrayOp, field
+                    )
+
+                    if version > 2:
+                        with pytest.raises(TealInputError):
+                            dynamicElementExpr.__teal__(
+                                CompileOptions(version=version - 1)
+                            )
+
+                continue
+
+            if field in arrayFieldToLengthField.values():
+                # ignore length fields since they are checked with their arrays
+                continue
+
+            if field == TxnField.first_valid_time:
+                # ignore first_valid_time since it is not exposed on TxnObject yet
+                continue
+
+            expr = fieldToMethod[field](txnObject)
+
+            immediateArgs = immediateArgsPrefix + [field.arg_name]
+            expected = TealSimpleBlock(irPrefix + [TealOp(expr, op, *immediateArgs)])
+            expected.addIncoming()
+            expected = TealBlock.NormalizeBlocks(expected)
+
+            version = max(op.min_version, field.min_version)
+
+            actual, _ = expr.__teal__(CompileOptions(version=version))
+            actual.addIncoming()
+            actual = TealBlock.NormalizeBlocks(actual)
+
+            assert actual == expected, "{}: field {} does not match expected".format(
+                op, field
+            )
+
+            if version > 2:
+                with pytest.raises(TealInputError):
+                    expr.__teal__(CompileOptions(version=version - 1))