diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py index af7704198..2f5d2f064 100644 --- a/pyteal/ast/itxn.py +++ b/pyteal/ast/itxn.py @@ -180,6 +180,27 @@ def SetField(cls, field: TxnField, value: Union[Expr, List[Expr]]) -> Expr: ] ) + @classmethod + def Execute(cls, fields: Dict[TxnField, Union[Expr, List[Expr]]]) -> Expr: + """Performs a single transaction given fields passed in. + + A convenience method that accepts fields to submit a single inner transaction, which is equivalent to: + + .. code-block:: python + + InnerTxnBuilder.Begin() + InnerTxnBuilder.SetFields(fields) + InnerTxnBuilder.End() + + 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. + """ + return Seq(cls.Begin(), cls.SetFields(fields), cls.Submit()) + @classmethod def SetFields(cls, fields: Dict[TxnField, Union[Expr, List[Expr]]]) -> Expr: """Set multiple fields of the current inner transaction. diff --git a/pyteal/ast/itxn_test.py b/pyteal/ast/itxn_test.py index af9c3a79c..2e9c01a75 100644 --- a/pyteal/ast/itxn_test.py +++ b/pyteal/ast/itxn_test.py @@ -92,28 +92,29 @@ def test_InnerTxnBuilder_SetField(): expr.__teal__(teal4Options) -def test_InnerTxnBuilder_SetFields(): - cases = ( - ({}, pt.Seq()), - ( - {pt.TxnField.amount: pt.Int(5)}, - pt.InnerTxnBuilder.SetField(pt.TxnField.amount, pt.Int(5)), - ), - ( - { - pt.TxnField.type_enum: pt.TxnType.Payment, - pt.TxnField.close_remainder_to: pt.Txn.sender(), - }, - pt.Seq( - pt.InnerTxnBuilder.SetField(pt.TxnField.type_enum, pt.TxnType.Payment), - pt.InnerTxnBuilder.SetField( - pt.TxnField.close_remainder_to, pt.Txn.sender() - ), +ITXN_FIELDS_CASES = [ + ({}, pt.Seq()), + ( + {pt.TxnField.amount: pt.Int(5)}, + pt.InnerTxnBuilder.SetField(pt.TxnField.amount, pt.Int(5)), + ), + ( + { + pt.TxnField.type_enum: pt.TxnType.Payment, + pt.TxnField.close_remainder_to: pt.Txn.sender(), + }, + pt.Seq( + pt.InnerTxnBuilder.SetField(pt.TxnField.type_enum, pt.TxnType.Payment), + pt.InnerTxnBuilder.SetField( + pt.TxnField.close_remainder_to, pt.Txn.sender() ), ), - ) + ), +] + - for fields, expectedExpr in cases: +def test_InnerTxnBuilder_SetFields(): + for fields, expectedExpr in ITXN_FIELDS_CASES: expr = pt.InnerTxnBuilder.SetFields(fields) assert expr.type_of() == pt.TealType.none assert not expr.has_return() @@ -134,4 +135,27 @@ def test_InnerTxnBuilder_SetFields(): expr.__teal__(teal4Options) +def test_InnerTxnBuilder_Execute(): + for fields, expectedExpr in ITXN_FIELDS_CASES: + expr = pt.InnerTxnBuilder.Execute(fields) + + expected, _ = pt.Seq( + pt.InnerTxnBuilder.Begin(), + expectedExpr, + pt.InnerTxnBuilder.Submit(), + ).__teal__(teal5Options) + expected.addIncoming() + expected = pt.TealBlock.NormalizeBlocks(expected) + + actual, _ = expr.__teal__(teal5Options) + actual.addIncoming() + actual = pt.TealBlock.NormalizeBlocks(actual) + + with pt.TealComponent.Context.ignoreExprEquality(): + assert actual == expected + + with pytest.raises(pt.TealInputError): + expr.__teal__(teal4Options) + + # txn_test.py performs additional testing