From 627b849f33af3e4036ca36246a287ce5b5f087ee Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 10:46:29 -0700 Subject: [PATCH 01/12] Allow 0 length Seq exprs --- pyteal/ast/seq.py | 20 +++++++++----------- pyteal/ast/seq_test.py | 17 ++++++++++++++--- 2 files changed, 23 insertions(+), 14 deletions(-) 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/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))]) From eb74d5aec7210c7d1fc1cb734bf9bc0e06433874 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 10:55:22 -0700 Subject: [PATCH 02/12] Fix failing test --- pyteal/ast/subroutine_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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) From 91b146b61b8f93f44325cfbaccacb1b71c066805 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 11:20:42 -0700 Subject: [PATCH 03/12] Add all TEAL 5 opcodes --- pyteal/ir/ops.py | 255 +++++++++++++++++++++++++---------------------- 1 file changed, 138 insertions(+), 117 deletions(-) diff --git a/pyteal/ir/ops.py b/pyteal/ir/ops.py index 623d3c793..07243ba56 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) + gtxnsa = OpType("gtxnsa", Mode.Signature | Mode.Application, 5) + gtxnsas = OpType("gtxnsas", Mode.Signature | Mode.Application, 5) + args = OpType("args", Mode.Signature, 5) # fmt: on From 9c33c3e270ca86d72a4513b8201ab898d06b42a0 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 11:26:48 -0700 Subject: [PATCH 04/12] Fix gtxnas --- pyteal/ir/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyteal/ir/ops.py b/pyteal/ir/ops.py index 07243ba56..19f3b5457 100644 --- a/pyteal/ir/ops.py +++ b/pyteal/ir/ops.py @@ -166,7 +166,7 @@ def min_version(self) -> int: itxn = OpType("itxn", Mode.Application, 5) itxna = OpType("itxna", Mode.Application, 5) txnas = OpType("txnas", Mode.Signature | Mode.Application, 5) - gtxnsa = OpType("gtxnsa", 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 From d1c4aa5d41ed17f45d4829e618167ee9d5f026fa Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 11:42:11 -0700 Subject: [PATCH 05/12] Add new global fields --- pyteal/ast/global_.py | 20 +++++++++++++++++++- pyteal/ast/global_test.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) 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/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) From a4493c1cb294711974f5fcb8c2b4bd4032556271 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 15:10:59 -0700 Subject: [PATCH 06/12] Add new txn fields and implement InnerTxn --- pyteal/ast/__init__.py | 5 + pyteal/ast/gtxn.py | 10 +- pyteal/ast/gtxn_test.py | 138 ++++++ pyteal/ast/itxn.py | 95 ++++ pyteal/ast/itxn_test.py | 936 ++++++++++++++++++++++++++++++++++++++++ pyteal/ast/txn.py | 329 ++++++++------ pyteal/ast/txn_test.py | 57 +++ 7 files changed, 1443 insertions(+), 127 deletions(-) create mode 100644 pyteal/ast/itxn.py create mode 100644 pyteal/ast/itxn_test.py 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/gtxn.py b/pyteal/ast/gtxn.py index 681569cb4..947738be2 100644 --- a/pyteal/ast/gtxn.py +++ b/pyteal/ast/gtxn.py @@ -16,11 +16,11 @@ 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) @@ -46,11 +46,13 @@ 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) + super().__init__(Op.gtxna, "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) diff --git a/pyteal/ast/gtxn_test.py b/pyteal/ast/gtxn_test.py index 88db8bb55..f20e40353 100644 --- a/pyteal/ast/gtxn_test.py +++ b/pyteal/ast/gtxn_test.py @@ -1306,6 +1306,40 @@ def test_txn_config_asset_clawback_dynamic(): assert actual == expected +def test_txn_created_asset_id(): + for i in GTXN_RANGE: + expr = expr = Gtxn[i].created_asset_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "CreatedAssetID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_created_asset_id_dynamic(): + index = Int(0) + expr = expr = Gtxn[index].created_asset_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock( + [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "CreatedAssetID")] + ) + + actual, _ = expr.__teal__(teal5Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + def test_txn_freeze_asset(): for i in GTXN_RANGE: expr = Gtxn[i].freeze_asset() @@ -1698,3 +1732,107 @@ def test_txn_extra_program_pages_dynamic(): with pytest.raises(TealInputError): expr.__teal__(teal3Options) + + +def test_txn_created_application_id(): + for i in GTXN_RANGE: + expr = expr = Gtxn[i].created_application_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "CreatedApplicationID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_created_application_id_dynamic(): + index = Int(0) + expr = expr = Gtxn[index].created_application_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock( + [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "CreatedApplicationID")] + ) + + actual, _ = expr.__teal__(teal5Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs(): + for i in GTXN_RANGE: + for j in range(32): + expr = Gtxn[i].logs[j] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.gtxna, i, "Logs", j)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs_dynamic(): + index = Int(0) + for j in range(32): + expr = Gtxn[index].logs[j] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock( + [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "Logs", j)] + ) + + actual, _ = expr.__teal__(teal5Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs_length(): + for i in GTXN_RANGE: + expr = Gtxn[i].logs.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumLogs")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs_length_dynamic(): + index = Int(0) + expr = Gtxn[index].logs.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock( + [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumLogs")] + ) + + actual, _ = expr.__teal__(teal5Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py new file mode 100644 index 000000000..5b9479009 --- /dev/null +++ b/pyteal/ast/itxn.py @@ -0,0 +1,95 @@ +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: + @classmethod + def Begin(cls) -> Expr: + return InnerTxnActionExpr(True) + + @classmethod + def Submit(cls) -> Expr: + return InnerTxnActionExpr(False) + + @classmethod + def SetField(cls, field: TxnField, value: Expr) -> Expr: + return InnerTxnFieldExpr(field, value) + + @classmethod + def SetFields(cls, fields: Dict[TxnField, Expr]) -> Expr: + 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, "InnerTxna") +) + +InnerTxn.__module__ = "pyteal" diff --git a/pyteal/ast/itxn_test.py b/pyteal/ast/itxn_test.py new file mode 100644 index 000000000..d4c00b904 --- /dev/null +++ b/pyteal/ast/itxn_test.py @@ -0,0 +1,936 @@ +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) + + +def test_txn_sender(): + expr = InnerTxn.sender() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Sender")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_fee(): + expr = InnerTxn.fee() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Fee")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_first_valid(): + expr = InnerTxn.first_valid() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FirstValid")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_last_valid(): + expr = InnerTxn.last_valid() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LastValid")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_note(): + expr = InnerTxn.note() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Note")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_lease(): + expr = InnerTxn.lease() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Lease")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_receiver(): + expr = InnerTxn.receiver() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Receiver")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_amount(): + expr = InnerTxn.amount() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Amount")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_close_remainder_to(): + expr = InnerTxn.close_remainder_to() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CloseRemainderTo")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_vote_pk(): + expr = InnerTxn.vote_pk() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VotePK")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_selection_pk(): + expr = InnerTxn.selection_pk() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "SelectionPK")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_vote_first(): + expr = InnerTxn.vote_first() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteFirst")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_vote_last(): + expr = InnerTxn.vote_last() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteLast")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_vote_key_dilution(): + expr = InnerTxn.vote_key_dilution() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteKeyDilution")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + +def test_txn_nonparticipation(): + expr = InnerTxn.nonparticipation() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Nonparticipation")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_type(): + expr = InnerTxn.type() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Type")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_type_enum(): + expr = InnerTxn.type_enum() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "TypeEnum")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_xfer_asset(): + expr = InnerTxn.xfer_asset() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "XferAsset")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_asset_amount(): + expr = InnerTxn.asset_amount() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetAmount")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_asset_sender(): + expr = InnerTxn.asset_sender() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetSender")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_asset_receiver(): + expr = InnerTxn.asset_receiver() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetReceiver")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_asset_close_to(): + expr = InnerTxn.asset_close_to() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetCloseTo")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_group_index(): + expr = InnerTxn.group_index() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GroupIndex")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_id(): + expr = InnerTxn.tx_id() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "TxID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_application_id(): + expr = InnerTxn.application_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ApplicationID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_on_completion(): + expr = InnerTxn.on_completion() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "OnCompletion")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_application_args(): + for i in range(32): + expr = InnerTxn.application_args[i] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxna, "ApplicationArgs", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_application_args_length(): + expr = InnerTxn.application_args.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAppArgs")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_accounts(): + for i in range(32): + expr = InnerTxn.accounts[i] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Accounts", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_accounts_length(): + expr = InnerTxn.accounts.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAccounts")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_approval_program(): + expr = InnerTxn.approval_program() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ApprovalProgram")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_clear_state_program(): + expr = InnerTxn.clear_state_program() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ClearStateProgram")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_rekey_to(): + expr = InnerTxn.rekey_to() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "RekeyTo")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset(): + expr = InnerTxn.config_asset() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAsset")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_total(): + expr = InnerTxn.config_asset_total() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetTotal")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_decimals(): + expr = InnerTxn.config_asset_decimals() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetDecimals")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_default_frozen(): + expr = InnerTxn.config_asset_default_frozen() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetDefaultFrozen")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_unit_name(): + expr = InnerTxn.config_asset_unit_name() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetUnitName")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_name(): + expr = InnerTxn.config_asset_name() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetName")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_url(): + expr = InnerTxn.config_asset_url() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetURL")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_metadata_hash(): + expr = InnerTxn.config_asset_metadata_hash() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetMetadataHash")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_manager(): + expr = InnerTxn.config_asset_manager() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetManager")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_reserve(): + expr = InnerTxn.config_asset_reserve() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetReserve")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_freeze(): + expr = InnerTxn.config_asset_freeze() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetFreeze")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_config_asset_clawback(): + expr = InnerTxn.config_asset_clawback() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetClawback")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_created_asset_id(): + expr = InnerTxn.created_asset_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CreatedAssetID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_freeze_asset(): + expr = InnerTxn.freeze_asset() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAsset")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_freeze_asset_account(): + expr = InnerTxn.freeze_asset_account() + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAssetAccount")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_freeze_asset_frozen(): + expr = InnerTxn.freeze_asset_frozen() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAssetFrozen")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_assets(): + for i in range(32): + expr = InnerTxn.assets[i] + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Assets", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_assets_length(): + expr = InnerTxn.assets.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAssets")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_applications(): + for i in range(32): + expr = InnerTxn.applications[i] + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Applications", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_applications_length(): + expr = InnerTxn.applications.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumApplications")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_global_num_uints(): + expr = InnerTxn.global_num_uints() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GlobalNumUint")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_global_num_byte_slices(): + expr = InnerTxn.global_num_byte_slices() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GlobalNumByteSlice")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_local_num_uints(): + expr = InnerTxn.local_num_uints() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LocalNumUint")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_local_num_byte_slices(): + expr = InnerTxn.local_num_byte_slices() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LocalNumByteSlice")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_extra_program_pages(): + expr = InnerTxn.extra_program_pages() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ExtraProgramPages")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_created_application_id(): + expr = InnerTxn.created_application_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CreatedApplicationID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs(): + for i in range(32): + expr = InnerTxn.logs[i] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Logs", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs_length(): + expr = InnerTxn.logs.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumLogs")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index bc7b12044..b57dabe1d 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -2,7 +2,7 @@ from typing import Callable, TYPE_CHECKING from ..types import TealType -from ..errors import TealInputError, verifyFieldVersion +from ..errors import TealInputError, verifyFieldVersion, verifyTealVersion from ..ir import TealOp, Op, TealBlock from .leafexpr import LeafExpr from .int import EnumInt @@ -28,69 +28,74 @@ 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 +108,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,18 +140,27 @@ 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, op: Op, name: str, field: TxnField, index: int) -> None: super().__init__() + if not field.is_array: + raise TealInputError("Unexpected non-array field: {}".format(field)) + self.op = op + 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) + verifyTealVersion( + self.op.min_version, + options.version, + "TEAL version too low to use op {}".format(self.op), + ) - op = TealOp(self, Op.txna, self.field.arg_name, self.index) + op = TealOp(self, self.op, self.field.arg_name, self.index) return TealBlock.FromOp(options, op) def type_of(self): @@ -147,6 +170,30 @@ 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, op: Op, name: str): + self.op = op + self.name = name + + def __call__(self, field: TxnField, index: int) -> TxnaExpr: + return TxnaExpr(self.op, self.name, field, index) + + +TxnaExprBuilder.__module__ = "pyteal" + + class TxnArray(Array): """Represents a transaction array field.""" @@ -158,13 +205,13 @@ 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)) - return self.txnObject.txnaType(self.accessField, index) + return self.txnObject.makeTxnaExpr(self.accessField, index) TxnArray.__module__ = "pyteal" @@ -175,53 +222,53 @@ class TxnObject: def __init__( self, - txnType: Callable[[TxnField], TxnExpr], - txnaType: Callable[[TxnField, int], TxnaExpr], + makeTxnExpr: Callable[[TxnField], TxnExpr], + makeTxnaExpr: Callable[[TxnField, int], 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 +277,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 +286,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 +295,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 +304,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 +313,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 +322,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 +331,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,7 +340,7 @@ 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 . @@ -304,7 +351,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 +360,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 +376,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 +385,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 +394,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 +403,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 +412,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 +421,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 +469,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 +477,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 +486,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 +494,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 +502,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 +510,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 +518,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 +527,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 +535,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 +543,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 +551,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 +559,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 +578,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 +586,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 +594,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 +603,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 +612,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 +621,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 +630,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 +641,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 +690,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, "Txna") +) Txn.__module__ = "pyteal" diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index cfcc28117..4ed8abfbd 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -511,6 +511,20 @@ def test_txn_config_asset_clawback(): assert actual == expected +def test_txn_created_asset_id(): + expr = Txn.created_asset_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.txn, "CreatedAssetID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + def test_txn_freeze_asset(): expr = Txn.freeze_asset() assert expr.type_of() == TealType.uint64 @@ -670,3 +684,46 @@ def test_txn_extra_program_pages(): with pytest.raises(TealInputError): expr.__teal__(teal3Options) + + +def test_txn_created_application_id(): + expr = Txn.created_application_id() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.txn, "CreatedApplicationID")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs(): + for i in range(32): + expr = Txn.logs[i] + assert expr.type_of() == TealType.bytes + + expected = TealSimpleBlock([TealOp(expr, Op.txna, "Logs", i)]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) + + +def test_txn_logs_length(): + expr = Txn.logs.length() + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumLogs")]) + + actual, _ = expr.__teal__(teal5Options) + + assert actual == expected + + with pytest.raises(TealInputError): + expr.__teal__(teal4Options) From bd76b29f13ae98cb9f7506a3c8b033c275b1487e Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 15:13:45 -0700 Subject: [PATCH 07/12] Format --- pyteal/ast/txn.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index b57dabe1d..41ca23ca1 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -64,11 +64,23 @@ class TxnField(Enum): 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_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_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) @@ -91,7 +103,9 @@ class TxnField(Enum): 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: + 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 From 7f6c786058c12cc16b28d87b5e7a67f096784d70 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 15 Sep 2021 17:03:51 -0700 Subject: [PATCH 08/12] Support dynamic array indexing and simplify txn opcode tests --- pyteal/ast/gtxn.py | 55 +- pyteal/ast/gtxn_test.py | 1820 +-------------------------------------- pyteal/ast/itxn.py | 2 +- pyteal/ast/itxn_test.py | 822 +----------------- pyteal/ast/txn.py | 65 +- pyteal/ast/txn_test.py | 947 +++++--------------- 6 files changed, 311 insertions(+), 3400 deletions(-) diff --git a/pyteal/ast/gtxn.py b/pyteal/ast/gtxn.py index 947738be2..9abc1ab29 100644 --- a/pyteal/ast/gtxn.py +++ b/pyteal/ast/gtxn.py @@ -45,8 +45,10 @@ 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__(Op.gtxna, "Gtxna", 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): @@ -57,24 +59,45 @@ def __str__(self): 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, + cast(int, self.txnIndex), + self.field.arg_name, + cast(int, self.index), + ) + return TealBlock.FromOp(options, op) + + op = TealOp(self, opToUse, cast(int, 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, cast(int, 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" @@ -84,7 +107,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/gtxn_test.py b/pyteal/ast/gtxn_test.py index f20e40353..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,1812 +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_created_asset_id(): - for i in GTXN_RANGE: - expr = expr = Gtxn[i].created_asset_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "CreatedAssetID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_created_asset_id_dynamic(): - index = Int(0) - expr = expr = Gtxn[index].created_asset_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock( - [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "CreatedAssetID")] - ) - - actual, _ = expr.__teal__(teal5Options) - actual.addIncoming() - actual = TealBlock.NormalizeBlocks(actual) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -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) - - -def test_txn_created_application_id(): - for i in GTXN_RANGE: - expr = expr = Gtxn[i].created_application_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "CreatedApplicationID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_created_application_id_dynamic(): - index = Int(0) - expr = expr = Gtxn[index].created_application_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock( - [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "CreatedApplicationID")] - ) - - actual, _ = expr.__teal__(teal5Options) - actual.addIncoming() - actual = TealBlock.NormalizeBlocks(actual) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs(): - for i in GTXN_RANGE: - for j in range(32): - expr = Gtxn[i].logs[j] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.gtxna, i, "Logs", j)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs_dynamic(): - index = Int(0) - for j in range(32): - expr = Gtxn[index].logs[j] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock( - [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxnsa, "Logs", j)] - ) - - actual, _ = expr.__teal__(teal5Options) - actual.addIncoming() - actual = TealBlock.NormalizeBlocks(actual) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs_length(): - for i in GTXN_RANGE: - expr = Gtxn[i].logs.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.gtxn, i, "NumLogs")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs_length_dynamic(): - index = Int(0) - expr = Gtxn[index].logs.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock( - [TealOp(index, Op.int, 0), TealOp(expr, Op.gtxns, "NumLogs")] - ) - - actual, _ = expr.__teal__(teal5Options) - actual.addIncoming() - actual = TealBlock.NormalizeBlocks(actual) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) +# txn_test.py performs additional testing diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py index 5b9479009..f3a078279 100644 --- a/pyteal/ast/itxn.py +++ b/pyteal/ast/itxn.py @@ -89,7 +89,7 @@ def SetFields(cls, fields: Dict[TxnField, Expr]) -> Expr: InnerTxnBuilder.__module__ = "pyteal" InnerTxn: TxnObject = TxnObject( - TxnExprBuilder(Op.itxn, "InnerTxn"), TxnaExprBuilder(Op.itxna, "InnerTxna") + TxnExprBuilder(Op.itxn, "InnerTxn"), TxnaExprBuilder(Op.itxna, None, "InnerTxna") ) InnerTxn.__module__ = "pyteal" diff --git a/pyteal/ast/itxn_test.py b/pyteal/ast/itxn_test.py index d4c00b904..454fcef2b 100644 --- a/pyteal/ast/itxn_test.py +++ b/pyteal/ast/itxn_test.py @@ -113,824 +113,4 @@ def test_InnerTxnBuilder_SetFields(): expr.__teal__(teal4Options) -def test_txn_sender(): - expr = InnerTxn.sender() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Sender")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_fee(): - expr = InnerTxn.fee() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Fee")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_first_valid(): - expr = InnerTxn.first_valid() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FirstValid")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_last_valid(): - expr = InnerTxn.last_valid() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LastValid")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_note(): - expr = InnerTxn.note() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Note")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_lease(): - expr = InnerTxn.lease() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Lease")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_receiver(): - expr = InnerTxn.receiver() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Receiver")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_amount(): - expr = InnerTxn.amount() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Amount")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_close_remainder_to(): - expr = InnerTxn.close_remainder_to() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CloseRemainderTo")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_vote_pk(): - expr = InnerTxn.vote_pk() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VotePK")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_selection_pk(): - expr = InnerTxn.selection_pk() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "SelectionPK")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_vote_first(): - expr = InnerTxn.vote_first() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteFirst")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_vote_last(): - expr = InnerTxn.vote_last() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteLast")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_vote_key_dilution(): - expr = InnerTxn.vote_key_dilution() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "VoteKeyDilution")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - -def test_txn_nonparticipation(): - expr = InnerTxn.nonparticipation() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Nonparticipation")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_type(): - expr = InnerTxn.type() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "Type")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_type_enum(): - expr = InnerTxn.type_enum() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "TypeEnum")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_xfer_asset(): - expr = InnerTxn.xfer_asset() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "XferAsset")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_asset_amount(): - expr = InnerTxn.asset_amount() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetAmount")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_asset_sender(): - expr = InnerTxn.asset_sender() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetSender")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_asset_receiver(): - expr = InnerTxn.asset_receiver() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetReceiver")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_asset_close_to(): - expr = InnerTxn.asset_close_to() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "AssetCloseTo")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_group_index(): - expr = InnerTxn.group_index() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GroupIndex")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_id(): - expr = InnerTxn.tx_id() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "TxID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_application_id(): - expr = InnerTxn.application_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ApplicationID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_on_completion(): - expr = InnerTxn.on_completion() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "OnCompletion")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_application_args(): - for i in range(32): - expr = InnerTxn.application_args[i] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxna, "ApplicationArgs", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_application_args_length(): - expr = InnerTxn.application_args.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAppArgs")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_accounts(): - for i in range(32): - expr = InnerTxn.accounts[i] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Accounts", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_accounts_length(): - expr = InnerTxn.accounts.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAccounts")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_approval_program(): - expr = InnerTxn.approval_program() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ApprovalProgram")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_clear_state_program(): - expr = InnerTxn.clear_state_program() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ClearStateProgram")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_rekey_to(): - expr = InnerTxn.rekey_to() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "RekeyTo")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset(): - expr = InnerTxn.config_asset() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAsset")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_total(): - expr = InnerTxn.config_asset_total() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetTotal")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_decimals(): - expr = InnerTxn.config_asset_decimals() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetDecimals")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_default_frozen(): - expr = InnerTxn.config_asset_default_frozen() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetDefaultFrozen")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_unit_name(): - expr = InnerTxn.config_asset_unit_name() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetUnitName")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_name(): - expr = InnerTxn.config_asset_name() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetName")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_url(): - expr = InnerTxn.config_asset_url() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetURL")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_metadata_hash(): - expr = InnerTxn.config_asset_metadata_hash() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetMetadataHash")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_manager(): - expr = InnerTxn.config_asset_manager() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetManager")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_reserve(): - expr = InnerTxn.config_asset_reserve() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetReserve")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_freeze(): - expr = InnerTxn.config_asset_freeze() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetFreeze")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_config_asset_clawback(): - expr = InnerTxn.config_asset_clawback() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ConfigAssetClawback")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_created_asset_id(): - expr = InnerTxn.created_asset_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CreatedAssetID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_freeze_asset(): - expr = InnerTxn.freeze_asset() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAsset")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_freeze_asset_account(): - expr = InnerTxn.freeze_asset_account() - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAssetAccount")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_freeze_asset_frozen(): - expr = InnerTxn.freeze_asset_frozen() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "FreezeAssetFrozen")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_assets(): - for i in range(32): - expr = InnerTxn.assets[i] - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Assets", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_assets_length(): - expr = InnerTxn.assets.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumAssets")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_applications(): - for i in range(32): - expr = InnerTxn.applications[i] - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Applications", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_applications_length(): - expr = InnerTxn.applications.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumApplications")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_global_num_uints(): - expr = InnerTxn.global_num_uints() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GlobalNumUint")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_global_num_byte_slices(): - expr = InnerTxn.global_num_byte_slices() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "GlobalNumByteSlice")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_local_num_uints(): - expr = InnerTxn.local_num_uints() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LocalNumUint")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_local_num_byte_slices(): - expr = InnerTxn.local_num_byte_slices() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "LocalNumByteSlice")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_extra_program_pages(): - expr = InnerTxn.extra_program_pages() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "ExtraProgramPages")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_created_application_id(): - expr = InnerTxn.created_application_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "CreatedApplicationID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs(): - for i in range(32): - expr = InnerTxn.logs[i] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.itxna, "Logs", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs_length(): - expr = InnerTxn.logs.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.itxn, "NumLogs")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) +# txn_test.py performs additional testing diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index 41ca23ca1..4ef61cb50 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, verifyTealVersion +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 @@ -154,11 +160,19 @@ def type_of(self): class TxnaExpr(LeafExpr): """An expression that accesses a transaction array field from the current transaction.""" - def __init__(self, op: Op, name: str, 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.op = op + self.staticOp = staticOp + self.dynamicOp = dynamicOp self.name = name self.field = field self.index = index @@ -168,14 +182,23 @@ def __str__(self): def __teal__(self, options: "CompileOptions"): verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version) + + 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( - self.op.min_version, + opToUse.min_version, options.version, - "TEAL version too low to use op {}".format(self.op), + "TEAL version too low to use op {}".format(opToUse), ) - op = TealOp(self, self.op, self.field.arg_name, self.index) - return TealBlock.FromOp(options, op) + if type(self.index) is int: + op = TealOp(self, opToUse, self.field.arg_name, cast(int, 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() @@ -197,12 +220,13 @@ def __call__(self, field: TxnField) -> TxnExpr: class TxnaExprBuilder: - def __init__(self, op: Op, name: str): - self.op = op + 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: int) -> TxnaExpr: - return TxnaExpr(self.op, self.name, field, index) + def __call__(self, field: TxnField, index: Union[int, Expr]) -> TxnaExpr: + return TxnaExpr(self.staticOp, self.dynamicOp, self.name, field, index) TxnaExprBuilder.__module__ = "pyteal" @@ -221,9 +245,12 @@ def __init__( def length(self) -> TxnExpr: 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.makeTxnaExpr(self.accessField, index) @@ -237,7 +264,7 @@ class TxnObject: def __init__( self, makeTxnExpr: Callable[[TxnField], TxnExpr], - makeTxnaExpr: Callable[[TxnField, int], TxnaExpr], + makeTxnaExpr: Callable[[TxnField, Union[int, Expr]], TxnaExpr], ) -> None: self.makeTxnExpr = makeTxnExpr self.makeTxnaExpr = makeTxnaExpr @@ -720,7 +747,7 @@ def logs(self) -> TxnArray: TxnObject.__module__ = "pyteal" Txn: TxnObject = TxnObject( - TxnExprBuilder(Op.txn, "Txn"), TxnaExprBuilder(Op.txna, "Txna") + TxnExprBuilder(Op.txn, "Txn"), TxnaExprBuilder(Op.txna, Op.txnas, "Txna") ) Txn.__module__ = "pyteal" diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index 4ed8abfbd..c980c1773 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -1,729 +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_created_asset_id(): - expr = Txn.created_asset_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.txn, "CreatedAssetID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -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) - - -def test_txn_created_application_id(): - expr = Txn.created_application_id() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.txn, "CreatedApplicationID")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs(): - for i in range(32): - expr = Txn.logs[i] - assert expr.type_of() == TealType.bytes - - expected = TealSimpleBlock([TealOp(expr, Op.txna, "Logs", i)]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) - - -def test_txn_logs_length(): - expr = Txn.logs.length() - assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([TealOp(expr, Op.txn, "NumLogs")]) - - actual, _ = expr.__teal__(teal5Options) - - assert actual == expected - - with pytest.raises(TealInputError): - expr.__teal__(teal4Options) +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)) From 2b3ed91b6f96e751940796906dc430d289f089ec Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 16 Sep 2021 10:26:13 -0700 Subject: [PATCH 09/12] Document itxn --- docs/accessing_transaction_field.rst | 136 ++++++++++++++++----------- docs/api.rst | 9 +- pyteal/ast/itxn.py | 61 ++++++++++++ pyteal/ast/txn.py | 2 +- 4 files changed, 148 insertions(+), 60 deletions(-) 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() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.fee() ` :code:`TealType.uint64` in microAlgos -:any:`Txn.first_valid() ` :code:`TealType.uint64` round number -:any:`Txn.last_valid() ` :code:`TealType.uint64` round number -:any:`Txn.note() ` :code:`TealType.bytes` transaction note in bytes -:any:`Txn.lease() ` :code:`TealType.bytes` transaction lease in bytes -:any:`Txn.receiver() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.amount() ` :code:`TealType.uint64` in microAlgos -:any:`Txn.close_remainder_to() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.vote_pk() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.selection_pk() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.vote_first() ` :code:`TealType.uint64` -:any:`Txn.vote_last() ` :code:`TealType.uint64` -:any:`Txn.vote_key_dilution() ` :code:`TealType.uint64` -:any:`Txn.type() ` :code:`TealType.bytes` -:any:`Txn.type_enum() ` :code:`TealType.uint64` see table below -:any:`Txn.xfer_asset() ` :code:`TealType.uint64` ID of asset being transferred -:any:`Txn.asset_amount() ` :code:`TealType.uint64` value in Asset's units -:any:`Txn.asset_sender() ` :code:`TealType.bytes` 32 byte address, causes clawback of all value if sender is the clawback -:any:`Txn.asset_receiver() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.asset_close_to() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.group_index() ` :code:`TealType.uint64` position of this transaction within a transaction group, starting at 0 -:any:`Txn.tx_id() ` :code:`TealType.bytes` the computed ID for this transaction, 32 bytes -:any:`Txn.application_id() ` :code:`TealType.uint64` -:any:`Txn.on_completion() ` :code:`TealType.uint64` -:any:`Txn.approval_program() ` :code:`TealType.bytes` -:any:`Txn.clear_state_program() ` :code:`TealType.bytes` -:any:`Txn.rekey_to() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.config_asset() ` :code:`TealType.uint64` ID of asset being configured -:any:`Txn.config_asset_total() ` :code:`TealType.uint64` -:any:`Txn.config_asset_decimals() ` :code:`TealType.uint64` -:any:`Txn.config_asset_default_frozen() ` :code:`TealType.uint64` -:any:`Txn.config_asset_unit_name() ` :code:`TealType.bytes` -:any:`Txn.config_asset_name() ` :code:`TealType.bytes` -:any:`Txn.config_asset_url() ` :code:`TealType.bytes` -:any:`Txn.config_asset_metadata_hash() ` :code:`TealType.bytes` -:any:`Txn.config_asset_manager() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.config_asset_reserve() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.config_asset_freeze() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.config_asset_clawback() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.freeze_asset() ` :code:`TealType.uint64` -:any:`Txn.freeze_asset_account() ` :code:`TealType.bytes` 32 byte address -:any:`Txn.freeze_asset_frozen() ` :code:`TealType.uint64` -:any:`Txn.global_num_uints() ` :code:`TealType.uint64` Maximum global integers in app schema -:any:`Txn.global_num_byte_slices() ` :code:`TealType.uint64` Maximum global byte strings in app schema -:any:`Txn.local_num_uints() ` :code:`TealType.uint64` Maximum local integers in app schema -:any:`Txn.local_num_byte_slices() ` :code:`TealType.uint64` Maximum local byte strings in app schema -:any:`Txn.extra_program_pages() ` :code:`TealType.uint64` Number of extra program pages for app -:any:`Txn.application_args ` :code:`TealType.bytes[]` Array of application arguments -:any:`Txn.accounts ` :code:`TealType.bytes[]` Array of application accounts -:any:`Txn.assets ` :code:`TealType.uint64[]` Array of application assets -:any:`Txn.applications ` :code:`TealType.uint64[]` Array of applications -================================================================================ ========================= ======================================================================= +================================================================================ ========================= ================ ============================================================================ +Operator Type Min TEAL Version Notes +================================================================================ ========================= ================ ============================================================================ +:any:`Txn.sender() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.fee() ` :code:`TealType.uint64` 2 in microAlgos +:any:`Txn.first_valid() ` :code:`TealType.uint64` 2 round number +:any:`Txn.last_valid() ` :code:`TealType.uint64` 2 round number +:any:`Txn.note() ` :code:`TealType.bytes` 2 transaction note in bytes +:any:`Txn.lease() ` :code:`TealType.bytes` 2 transaction lease in bytes +:any:`Txn.receiver() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.amount() ` :code:`TealType.uint64` 2 in microAlgos +:any:`Txn.close_remainder_to() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.vote_pk() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.selection_pk() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.vote_first() ` :code:`TealType.uint64` 2 +:any:`Txn.vote_last() ` :code:`TealType.uint64` 2 +:any:`Txn.vote_key_dilution() ` :code:`TealType.uint64` 2 +:any:`Txn.nonparticipation() ` :code:`TealType.uint64` 5 Marks an account nonparticipating for rewards +:any:`Txn.type() ` :code:`TealType.bytes` 2 +:any:`Txn.type_enum() ` :code:`TealType.uint64` 2 see table below +:any:`Txn.xfer_asset() ` :code:`TealType.uint64` 2 ID of asset being transferred +:any:`Txn.asset_amount() ` :code:`TealType.uint64` 2 value in Asset's units +:any:`Txn.asset_sender() ` :code:`TealType.bytes` 2 32 byte address, causes clawback of all value if sender is the clawback +:any:`Txn.asset_receiver() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.asset_close_to() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.group_index() ` :code:`TealType.uint64` 2 position of this transaction within a transaction group, starting at 0 +:any:`Txn.tx_id() ` :code:`TealType.bytes` 2 the computed ID for this transaction, 32 bytes +:any:`Txn.application_id() ` :code:`TealType.uint64` 2 +:any:`Txn.on_completion() ` :code:`TealType.uint64` 2 +:any:`Txn.approval_program() ` :code:`TealType.bytes` 2 +:any:`Txn.clear_state_program() ` :code:`TealType.bytes` 2 +:any:`Txn.rekey_to() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.config_asset() ` :code:`TealType.uint64` 2 ID of asset being configured +:any:`Txn.config_asset_total() ` :code:`TealType.uint64` 2 +:any:`Txn.config_asset_decimals() ` :code:`TealType.uint64` 2 +:any:`Txn.config_asset_default_frozen() ` :code:`TealType.uint64` 2 +:any:`Txn.config_asset_unit_name() ` :code:`TealType.bytes` 2 +:any:`Txn.config_asset_name() ` :code:`TealType.bytes` 2 +:any:`Txn.config_asset_url() ` :code:`TealType.bytes` 2 +:any:`Txn.config_asset_metadata_hash() ` :code:`TealType.bytes` 2 +:any:`Txn.config_asset_manager() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.config_asset_reserve() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.config_asset_freeze() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.config_asset_clawback() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.freeze_asset() ` :code:`TealType.uint64` 2 +:any:`Txn.freeze_asset_account() ` :code:`TealType.bytes` 2 32 byte address +:any:`Txn.freeze_asset_frozen() ` :code:`TealType.uint64` 2 +:any:`Txn.global_num_uints() ` :code:`TealType.uint64` 3 Maximum global integers in app schema +:any:`Txn.global_num_byte_slices() ` :code:`TealType.uint64` 3 Maximum global byte strings in app schema +:any:`Txn.local_num_uints() ` :code:`TealType.uint64` 3 Maximum local integers in app schema +:any:`Txn.local_num_byte_slices() ` :code:`TealType.uint64` 3 Maximum local byte strings in app schema +:any:`Txn.extra_program_pages() ` :code:`TealType.uint64` 4 Number of extra program pages for app +:any:`Txn.application_args ` :code:`TealType.bytes[]` 2 Array of application arguments +:any:`Txn.accounts ` :code:`TealType.bytes[]` 2 Array of application accounts +:any:`Txn.assets ` :code:`TealType.uint64[]` 3 Array of application assets +:any:`Txn.applications ` :code:`TealType.uint64[]` 3 Array of applications +:any:`InnerTxn.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() ` :code:`TealType.uint64` 5 The ID of the newly created application in this transaction. This is only valid on inner transactions. +:any:`InnerTxn.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() `. 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: = - The current transaction being evaluated. + The current transaction being evaluated. This is an instance of :any:`TxnObject`. .. data:: Gtxn :annotation: = - The current group of transactions being evaluated. + The current group of transactions being evaluated. This is an instance of :any:`TxnGroup`. + + .. data:: InnerTxn + :annotation: = + + The most recently submitted inner transaction. This is an instance of :any:`TxnObject`. diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py index f3a078279..6c8092719 100644 --- a/pyteal/ast/itxn.py +++ b/pyteal/ast/itxn.py @@ -68,20 +68,81 @@ def has_return(self): 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() `. + + 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) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index 4ef61cb50..c87bd4345 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -384,7 +384,7 @@ def vote_key_dilution(self) -> TxnExpr: 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`. From 8e269e020385421ec89a0660ba49b282efc4fdcc Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 16 Sep 2021 10:28:15 -0700 Subject: [PATCH 10/12] Format --- pyteal/ast/itxn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py index 6c8092719..4ba229db8 100644 --- a/pyteal/ast/itxn.py +++ b/pyteal/ast/itxn.py @@ -98,7 +98,7 @@ def Submit(cls) -> Expr: :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. From 7e0a36e0c3aaec846eeac068d1ab367b0f4f3edb Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 16 Sep 2021 10:40:53 -0700 Subject: [PATCH 11/12] README fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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: From a5aa74fd1163366aa4305db5c4fbf6e0b24497ef Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Thu, 16 Sep 2021 14:16:46 -0700 Subject: [PATCH 12/12] Pin mypy version and remove unnecessary casting --- pyteal/ast/binaryexpr.py | 2 +- pyteal/ast/gaid.py | 6 +++--- pyteal/ast/gload.py | 8 +++----- pyteal/ast/gtxn.py | 15 +++++---------- pyteal/ast/txn.py | 2 +- pyteal/compiler/compiler.py | 5 ++++- pyteal/compiler/constants.py | 12 ++++++------ pyteal/compiler/flatten.py | 14 ++++++-------- pyteal/ir/tealconditionalblock.py | 1 - pyteal/ir/tealsimpleblock.py | 1 - 10 files changed, 29 insertions(+), 37 deletions(-) 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/gtxn.py b/pyteal/ast/gtxn.py index 9abc1ab29..24c8bfcc8 100644 --- a/pyteal/ast/gtxn.py +++ b/pyteal/ast/gtxn.py @@ -25,8 +25,8 @@ def __str__(self): 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( @@ -79,19 +79,14 @@ def __teal__(self, options: "CompileOptions"): if type(self.txnIndex) is int: if type(self.index) is int: op = TealOp( - self, - opToUse, - cast(int, self.txnIndex), - self.field.arg_name, - cast(int, self.index), + self, opToUse, self.txnIndex, self.field.arg_name, self.index ) return TealBlock.FromOp(options, op) - - op = TealOp(self, opToUse, cast(int, self.txnIndex), self.field.arg_name) + 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, cast(int, self.index)) + 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) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index c87bd4345..eed322405 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -194,7 +194,7 @@ def __teal__(self, options: "CompileOptions"): ) if type(self.index) is int: - op = TealOp(self, opToUse, self.field.arg_name, cast(int, self.index)) + op = TealOp(self, opToUse, self.field.arg_name, self.index) return TealBlock.FromOp(options, op) op = TealOp(self, opToUse, self.field.arg_name) 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/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