From 8ac26ea348e8db9bf73964aaca1c7197737f1671 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 6 Jun 2022 11:14:57 -0400 Subject: [PATCH 1/7] define reference type spec --- pyteal/ast/abi/reference_type.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyteal/ast/abi/reference_type.py b/pyteal/ast/abi/reference_type.py index f55812dc0..eea146995 100644 --- a/pyteal/ast/abi/reference_type.py +++ b/pyteal/ast/abi/reference_type.py @@ -3,6 +3,12 @@ from pyteal.ast.txn import Txn +class ReferenceTypeSpec(): + pass + +class ReferenceType(): + pass + class AccountTypeSpec(UintTypeSpec): def __init__(self): super().__init__(8) From 531fe7db32f36acc47421a0a7432c13d95a91807 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 6 Jun 2022 11:47:36 -0400 Subject: [PATCH 2/7] fmt, lint --- pyteal/ast/abi/reference_type.py | 125 +++++++++++++++++++++++----- pyteal/ast/router_test.py | 136 +++++++++++++++---------------- 2 files changed, 174 insertions(+), 87 deletions(-) diff --git a/pyteal/ast/abi/reference_type.py b/pyteal/ast/abi/reference_type.py index eea146995..791a37989 100644 --- a/pyteal/ast/abi/reference_type.py +++ b/pyteal/ast/abi/reference_type.py @@ -1,18 +1,111 @@ -from pyteal.ast.abi.uint import Uint, UintTypeSpec +from typing import Final, TypeVar, Union, cast +from abc import abstractmethod +from pyteal.ast.abi.type import BaseType, ComputedValue, TypeSpec + +from pyteal.ast.binaryexpr import GetByte +from pyteal.ast.bytes import Bytes +from pyteal.ast.int import Int from pyteal.ast.expr import Expr +from pyteal.ast.ternaryexpr import SetByte from pyteal.ast.txn import Txn +from pyteal.errors import TealInputError +from pyteal.types import TealType + + +T = TypeVar("T", bound="ReferenceType") + + +class ReferenceTypeSpec(TypeSpec): + def __init__(self) -> None: + super().__init__() + self.size: Final = 8 + + @abstractmethod + def new_instance(self) -> "ReferenceType": + pass + @abstractmethod + def annotation_type(self) -> "type[ReferenceType]": + pass -class ReferenceTypeSpec(): - pass + def bit_size(self) -> int: + """Get the bit size of this uint type""" + return self.size -class ReferenceType(): - pass + def is_dynamic(self) -> bool: + return False -class AccountTypeSpec(UintTypeSpec): - def __init__(self): - super().__init__(8) + def byte_length_static(self) -> int: + return 1 + def storage_type(self) -> TealType: + return TealType.uint64 + + def __eq__(self, other: object) -> bool: + return type(self) is type(other) + + @abstractmethod + def __str__(self) -> str: + pass + + +class ReferenceType(BaseType): + @abstractmethod + def __init__(self, spec: ReferenceTypeSpec) -> None: + super().__init__(spec) + + def type_spec(self) -> ReferenceTypeSpec: + return cast(ReferenceTypeSpec, super().type_spec()) + + def get(self) -> Expr: + return self.stored_value.load() + + def set( + self: T, value: Union[int, Expr, "ReferenceType", ComputedValue[T]] + ) -> Expr: + if isinstance(value, ComputedValue): + return self._set_with_computed_type(value) + + if isinstance(value, BaseType) and not ( + isinstance(value.type_spec(), ReferenceTypeSpec) + ): + raise TealInputError( + "Type {} is not assignable to type {}".format( + value.type_spec(), self.type_spec() + ) + ) + + match value: + case int(): + return self.stored_value.store(Int(value)) + case Expr(): + return self.stored_value.store(value) + case ReferenceType(): + return self.stored_value.store(value.get()) + case _: + raise TealInputError( + "Expected int, Expr, ReferenceType or ComputedValue, got {}".format( + type(value) + ) + ) + + def decode( + self, + encoded: Expr, + *, + startIndex: Expr = None, + endIndex: Expr = None, + length: Expr = None, + ) -> Expr: + if startIndex is None: + startIndex = Int(0) + return self.stored_value.store(GetByte(encoded, startIndex)) + + def encode(self) -> Expr: + return SetByte(Bytes(b"\x00"), Int(0), self.stored_value.load()) + + +class AccountTypeSpec(ReferenceTypeSpec): def new_instance(self) -> "Account": return Account() @@ -29,7 +122,7 @@ def __eq__(self, other: object) -> bool: AccountTypeSpec.__module__ = "pyteal" -class Account(Uint): +class Account(ReferenceType): def __init__(self) -> None: super().__init__(AccountTypeSpec()) @@ -40,10 +133,7 @@ def deref(self) -> Expr: Account.__module__ = "pyteal" -class AssetTypeSpec(UintTypeSpec): - def __init__(self): - super().__init__(8) - +class AssetTypeSpec(ReferenceTypeSpec): def new_instance(self) -> "Asset": return Asset() @@ -60,7 +150,7 @@ def __eq__(self, other: object) -> bool: AssetTypeSpec.__module__ = "pyteal" -class Asset(Uint): +class Asset(ReferenceType): def __init__(self) -> None: super().__init__(AssetTypeSpec()) @@ -71,10 +161,7 @@ def deref(self) -> Expr: Asset.__module__ = "pyteal" -class ApplicationTypeSpec(UintTypeSpec): - def __init__(self): - super().__init__(8) - +class ApplicationTypeSpec(ReferenceTypeSpec): def new_instance(self) -> "Application": return Application() @@ -91,7 +178,7 @@ def __eq__(self, other: object) -> bool: ApplicationTypeSpec.__module__ = "pyteal" -class Application(Uint): +class Application(ReferenceType): def __init__(self) -> None: super().__init__(ApplicationTypeSpec()) diff --git a/pyteal/ast/router_test.py b/pyteal/ast/router_test.py index bba67c88b..655dbcfb0 100644 --- a/pyteal/ast/router_test.py +++ b/pyteal/ast/router_test.py @@ -264,74 +264,74 @@ def test_call_config(): raise pt.TealInternalError(f"unexpected cond_on_cc {cond_on_cc}") -def test_method_config(): - never_mc = pt.MethodConfig(no_op=pt.CallConfig.NEVER) - assert never_mc.is_never() - assert never_mc.approval_cond() == 0 - assert never_mc.clear_state_cond() == 0 - - on_complete_pow_set = power_set(ON_COMPLETE_CASES) - approval_check_names_n_ocs = [ - (camel_to_snake(oc.name), oc) - for oc in ON_COMPLETE_CASES - if str(oc) != str(pt.OnComplete.ClearState) - ] - for on_complete_set in on_complete_pow_set: - oc_names = [camel_to_snake(oc.name) for oc in on_complete_set] - ordered_call_configs = full_ordered_combination_gen( - list(pt.CallConfig), len(on_complete_set) - ) - for call_configs in ordered_call_configs: - mc = pt.MethodConfig(**dict(zip(oc_names, call_configs))) - match mc.clear_state: - case pt.CallConfig.NEVER: - assert mc.clear_state_cond() == 0 - case pt.CallConfig.ALL: - assert mc.clear_state_cond() == 1 - case pt.CallConfig.CALL: - with pt.TealComponent.Context.ignoreExprEquality(): - assert assemble_helper( - mc.clear_state_cond() - ) == assemble_helper(pt.Txn.application_id() != pt.Int(0)) - case pt.CallConfig.CREATE: - with pt.TealComponent.Context.ignoreExprEquality(): - assert assemble_helper( - mc.clear_state_cond() - ) == assemble_helper(pt.Txn.application_id() == pt.Int(0)) - if mc.is_never() or all( - getattr(mc, i) == pt.CallConfig.NEVER - for i, _ in approval_check_names_n_ocs - ): - assert mc.approval_cond() == 0 - continue - elif all( - getattr(mc, i) == pt.CallConfig.ALL - for i, _ in approval_check_names_n_ocs - ): - assert mc.approval_cond() == 1 - continue - list_of_cc = [ - ( - typing.cast(pt.CallConfig, getattr(mc, i)).condition_under_config(), - oc, - ) - for i, oc in approval_check_names_n_ocs - ] - list_of_expressions = [] - for expr_or_int, oc in list_of_cc: - match expr_or_int: - case pt.Expr(): - list_of_expressions.append( - pt.And(pt.Txn.on_completion() == oc, expr_or_int) - ) - case 0: - continue - case 1: - list_of_expressions.append(pt.Txn.on_completion() == oc) - with pt.TealComponent.Context.ignoreExprEquality(): - assert assemble_helper(mc.approval_cond()) == assemble_helper( - pt.Or(*list_of_expressions) - ) +# def test_method_config(): +# never_mc = pt.MethodConfig(no_op=pt.CallConfig.NEVER) +# assert never_mc.is_never() +# assert never_mc.approval_cond() == 0 +# assert never_mc.clear_state_cond() == 0 +# +# on_complete_pow_set = power_set(ON_COMPLETE_CASES) +# approval_check_names_n_ocs = [ +# (camel_to_snake(oc.name), oc) +# for oc in ON_COMPLETE_CASES +# if str(oc) != str(pt.OnComplete.ClearState) +# ] +# for on_complete_set in on_complete_pow_set: +# oc_names = [camel_to_snake(oc.name) for oc in on_complete_set] +# ordered_call_configs = full_ordered_combination_gen( +# list(pt.CallConfig), len(on_complete_set) +# ) +# for call_configs in ordered_call_configs: +# mc = pt.MethodConfig(**dict(zip(oc_names, call_configs))) +# match mc.clear_state: +# case pt.CallConfig.NEVER: +# assert mc.clear_state_cond() == 0 +# case pt.CallConfig.ALL: +# assert mc.clear_state_cond() == 1 +# case pt.CallConfig.CALL: +# with pt.TealComponent.Context.ignoreExprEquality(): +# assert assemble_helper( +# mc.clear_state_cond() +# ) == assemble_helper(pt.Txn.application_id() != pt.Int(0)) +# case pt.CallConfig.CREATE: +# with pt.TealComponent.Context.ignoreExprEquality(): +# assert assemble_helper( +# mc.clear_state_cond() +# ) == assemble_helper(pt.Txn.application_id() == pt.Int(0)) +# if mc.is_never() or all( +# getattr(mc, i) == pt.CallConfig.NEVER +# for i, _ in approval_check_names_n_ocs +# ): +# assert mc.approval_cond() == 0 +# continue +# elif all( +# getattr(mc, i) == pt.CallConfig.ALL +# for i, _ in approval_check_names_n_ocs +# ): +# assert mc.approval_cond() == 1 +# continue +# list_of_cc = [ +# ( +# typing.cast(pt.CallConfig, getattr(mc, i)).condition_under_config(), +# oc, +# ) +# for i, oc in approval_check_names_n_ocs +# ] +# list_of_expressions = [] +# for expr_or_int, oc in list_of_cc: +# match expr_or_int: +# case pt.Expr(): +# list_of_expressions.append( +# pt.And(pt.Txn.on_completion() == oc, expr_or_int) +# ) +# case 0: +# continue +# case 1: +# list_of_expressions.append(pt.Txn.on_completion() == oc) +# with pt.TealComponent.Context.ignoreExprEquality(): +# assert assemble_helper(mc.approval_cond()) == assemble_helper( +# pt.Or(*list_of_expressions) +# ) def test_on_complete_action(): From cb2f2369638fa3560b2d3beff2a4e89465b3980a Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 8 Jun 2022 03:16:31 -0700 Subject: [PATCH 3/7] Create params and holding objects for references (#385) * Create reference params and holding objects * Add tests * Remove set method and unify tests --- pyteal/__init__.pyi | 4 + pyteal/ast/__init__.py | 15 +- pyteal/ast/abi/reference_type.py | 134 +++++++----- pyteal/ast/abi/reference_type_test.py | 304 ++++++++++++-------------- pyteal/ast/acct.py | 32 +++ pyteal/ast/acct_test.py | 21 ++ pyteal/ast/app.py | 55 ++++- pyteal/ast/app_test.py | 38 ++++ pyteal/ast/asset.py | 102 +++++++++ pyteal/ast/asset_test.py | 60 +++++ pyteal/ast/maybe_test.py | 20 ++ pyteal/ast/subroutine_test.py | 2 +- 12 files changed, 561 insertions(+), 226 deletions(-) diff --git a/pyteal/__init__.pyi b/pyteal/__init__.pyi index 5e49e918e..7a3c85ba9 100644 --- a/pyteal/__init__.pyi +++ b/pyteal/__init__.pyi @@ -30,18 +30,22 @@ from pyteal.config import ( __all__ = [ "ABIReturnSubroutine", "AccountParam", + "AccountParamObject", "Add", "Addr", "And", "App", "AppField", "AppParam", + "AppParamObject", "Approve", "Arg", "Array", "Assert", "AssetHolding", + "AssetHoldingObject", "AssetParam", + "AssetParamObject", "Balance", "BareCallActions", "BinaryExpr", diff --git a/pyteal/ast/__init__.py b/pyteal/ast/__init__.py index 68c6fd149..693ee4a8d 100644 --- a/pyteal/ast/__init__.py +++ b/pyteal/ast/__init__.py @@ -24,9 +24,14 @@ from pyteal.ast.gitxn import Gitxn, GitxnExpr, GitxnaExpr, InnerTxnGroup from pyteal.ast.gload import ImportScratchValue from pyteal.ast.global_ import Global, GlobalField -from pyteal.ast.app import App, AppField, OnComplete, AppParam -from pyteal.ast.asset import AssetHolding, AssetParam -from pyteal.ast.acct import AccountParam +from pyteal.ast.app import App, AppField, OnComplete, AppParam, AppParamObject +from pyteal.ast.asset import ( + AssetHolding, + AssetHoldingObject, + AssetParam, + AssetParamObject, +) +from pyteal.ast.acct import AccountParam, AccountParamObject # inner txns from pyteal.ast.itxn import InnerTxnBuilder, InnerTxn, InnerTxnAction @@ -177,9 +182,13 @@ "AppField", "OnComplete", "AppParam", + "AppParamObject", "AssetHolding", + "AssetHoldingObject", "AssetParam", + "AssetParamObject", "AccountParam", + "AccountParamObject", "InnerTxnBuilder", "InnerTxn", "InnerTxnAction", diff --git a/pyteal/ast/abi/reference_type.py b/pyteal/ast/abi/reference_type.py index 3c6e4e363..269fe57dc 100644 --- a/pyteal/ast/abi/reference_type.py +++ b/pyteal/ast/abi/reference_type.py @@ -1,13 +1,13 @@ -from typing import List, Final, TypeVar, Union, cast +from typing import List, Final, TypeVar, cast from abc import abstractmethod -from pyteal.ast.abi.type import BaseType, ComputedValue, TypeSpec +from pyteal.ast.abi.type import BaseType, TypeSpec +from pyteal.ast.abi.uint import NUM_BITS_IN_BYTE, uint_decode -from pyteal.ast.binaryexpr import GetByte -from pyteal.ast.bytes import Bytes -from pyteal.ast.int import Int from pyteal.ast.expr import Expr -from pyteal.ast.ternaryexpr import SetByte from pyteal.ast.txn import Txn +from pyteal.ast.acct import AccountParamObject +from pyteal.ast.asset import AssetHoldingObject, AssetParamObject +from pyteal.ast.app import AppParamObject from pyteal.errors import TealInputError from pyteal.types import TealType @@ -16,10 +16,6 @@ class ReferenceTypeSpec(TypeSpec): - def __init__(self) -> None: - super().__init__() - self.size: Final = 8 - @abstractmethod def new_instance(self) -> "ReferenceType": pass @@ -29,8 +25,8 @@ def annotation_type(self) -> "type[ReferenceType]": pass def bit_size(self) -> int: - """Get the bit size of this uint type""" - return self.size + """Get the bit size of the index this reference type holds""" + return NUM_BITS_IN_BYTE def is_dynamic(self) -> bool: return False @@ -41,13 +37,6 @@ def byte_length_static(self) -> int: def storage_type(self) -> TealType: return TealType.uint64 - def __eq__(self, other: object) -> bool: - return type(self) is type(other) - - @abstractmethod - def __str__(self) -> str: - pass - class ReferenceType(BaseType): @abstractmethod @@ -57,37 +46,16 @@ def __init__(self, spec: ReferenceTypeSpec) -> None: def type_spec(self) -> ReferenceTypeSpec: return cast(ReferenceTypeSpec, super().type_spec()) - def get(self) -> Expr: - return self.stored_value.load() + def referenced_index(self) -> Expr: + """Get the reference index for this value. - def set( - self: T, value: Union[int, Expr, "ReferenceType", ComputedValue[T]] - ) -> Expr: - if isinstance(value, ComputedValue): - return self._set_with_computed_type(value) - - if isinstance(value, BaseType) and not ( - isinstance(value.type_spec(), ReferenceTypeSpec) - ): - raise TealInputError( - "Type {} is not assignable to type {}".format( - value.type_spec(), self.type_spec() - ) - ) - - match value: - case int(): - return self.stored_value.store(Int(value)) - case Expr(): - return self.stored_value.store(value) - case ReferenceType(): - return self.stored_value.store(value.get()) - case _: - raise TealInputError( - "Expected int, Expr, ReferenceType or ComputedValue, got {}".format( - type(value) - ) - ) + The three reference types (account, application, asset) contain indexes into a foreign array + of the transaction. This method returns that index. + + If this reference type is an application or asset, note that this DOES NOT return the + application or asset ID. See :code:`application_id()` or :code:`asset_id()` for that. + """ + return self.stored_value.load() def decode( self, @@ -97,12 +65,17 @@ def decode( endIndex: Expr = None, length: Expr = None, ) -> Expr: - if startIndex is None: - startIndex = Int(0) - return self.stored_value.store(GetByte(encoded, startIndex)) + return uint_decode( + self.type_spec().bit_size(), + self.stored_value, + encoded, + startIndex, + endIndex, + length, + ) def encode(self) -> Expr: - return SetByte(Bytes(b"\x00"), Int(0), self.stored_value.load()) + raise TealInputError("A ReferenceType cannot be encoded") class AccountTypeSpec(ReferenceTypeSpec): @@ -126,9 +99,30 @@ class Account(ReferenceType): def __init__(self) -> None: super().__init__(AccountTypeSpec()) - def deref(self) -> Expr: + def address(self) -> Expr: + """Get the address of the account.""" return Txn.accounts[self.stored_value.load()] + def params(self) -> AccountParamObject: + """Get information about the account.""" + return AccountParamObject(self.referenced_index()) + + def asset_holding(self, asset: "Expr | Asset") -> AssetHoldingObject: + """Get information about an asset held by this account. + + Args: + asset: An identifier for the asset. It must be one of the following: an abi.Asset + reference object, an expression holding an index into Txn.ForeignAssets that + corresponds to the asset (in which case it must evaluate to uint64), or since v4, an + expression holding an asset ID that appears in Txn.ForeignAssets (in which case it + must evaluate to uint64). + """ + if isinstance(asset, Asset): + asset_ref = asset.referenced_index() + else: + asset_ref = asset + return AssetHoldingObject(asset_ref, self.referenced_index()) + Account.__module__ = "pyteal" @@ -154,8 +148,29 @@ class Asset(ReferenceType): def __init__(self) -> None: super().__init__(AssetTypeSpec()) - def deref(self) -> Expr: - return Txn.assets[self.stored_value.load()] + def asset_id(self) -> Expr: + """Get the ID of the asset.""" + return Txn.assets[self.referenced_index()] + + def holding(self, account: Expr | Account) -> AssetHoldingObject: + """Get information about this asset held by an account. + + Args: + account: An identifier for the account. It must be one of the following: an abi.Account + reference object, an expression holding an index into Txn.Accounts that corresponds + to the account (in which case it must evaluate to uint64), or since v4, an + expression holding an account address that appears in Txn.Accounts or is Txn.Sender + (in which case it must evaluate to bytes). + """ + if isinstance(account, Account): + account_ref = account.referenced_index() + else: + account_ref = account + return AssetHoldingObject(self.referenced_index(), account_ref) + + def params(self) -> AssetParamObject: + """Get information about the asset's parameters.""" + return AssetParamObject(self.referenced_index()) Asset.__module__ = "pyteal" @@ -182,9 +197,14 @@ class Application(ReferenceType): def __init__(self) -> None: super().__init__(ApplicationTypeSpec()) - def deref(self) -> Expr: + def application_id(self) -> Expr: + """Get the ID of the application.""" return Txn.applications[self.stored_value.load()] + def params(self) -> AppParamObject: + """Get information about the application's parameters.""" + return AppParamObject(self.referenced_index()) + Application.__module__ = "pyteal" diff --git a/pyteal/ast/abi/reference_type_test.py b/pyteal/ast/abi/reference_type_test.py index f799d4e08..0bf0df9a3 100644 --- a/pyteal/ast/abi/reference_type_test.py +++ b/pyteal/ast/abi/reference_type_test.py @@ -1,9 +1,75 @@ +import pytest + import pyteal as pt from pyteal import abi options = pt.CompileOptions(version=5) +def test_ReferenceTypeSpecs_list(): + assert abi.ReferenceTypeSpecs == [ + abi.AccountTypeSpec(), + abi.AssetTypeSpec(), + abi.ApplicationTypeSpec(), + ] + + +def test_ReferenceType_referenced_index(): + for value in (abi.Account(), abi.Asset(), abi.Application()): + expr = value.referenced_index() + assert expr.type_of() == pt.TealType.uint64 + assert expr.has_return() is False + + expected = pt.TealSimpleBlock( + [ + pt.TealOp(expr, pt.Op.load, value.stored_value.slot), + ] + ) + actual, _ = expr.__teal__(options) + actual.addIncoming() + actual = pt.TealBlock.NormalizeBlocks(actual) + + with pt.TealComponent.Context.ignoreExprEquality(): + assert actual == expected + + +def test_ReferenceType_encode(): + for value in (abi.Account(), abi.Asset(), abi.Application()): + with pytest.raises( + pt.TealInputError, match=r"A ReferenceType cannot be encoded$" + ): + value.encode() + + +def test_ReferenceType_decode(): + encoded = pt.Bytes("encoded") + for value in (abi.Account(), abi.Asset(), abi.Application()): + for startIndex in (None, pt.Int(1)): + for endIndex in (None, pt.Int(2)): + for length in (None, pt.Int(3)): + expr = value.decode( + encoded, startIndex=startIndex, endIndex=endIndex, length=length + ) + assert expr.type_of() == pt.TealType.none + assert expr.has_return() is False + + expected_decoding = value.stored_value.store( + pt.GetByte( + encoded, startIndex if startIndex is not None else pt.Int(0) + ) + ) + expected, _ = expected_decoding.__teal__(options) + expected.addIncoming() + expected = pt.TealBlock.NormalizeBlocks(expected) + + actual, _ = expr.__teal__(options) + actual.addIncoming() + actual = pt.TealBlock.NormalizeBlocks(actual) + + with pt.TealComponent.Context.ignoreExprEquality(): + assert actual == expected + + def test_Account_str(): assert str(abi.AccountTypeSpec()) == "account" @@ -31,34 +97,16 @@ def test_Account_typespec(): assert abi.Account().type_spec() == abi.AccountTypeSpec() -def test_Account_encode(): +def test_Account_address(): value = abi.Account() - expr = value.encode() + expr = value.address() assert expr.type_of() == pt.TealType.bytes assert expr.has_return() is False - expectEncoding = pt.SetByte(pt.Bytes(b"\x00"), pt.Int(0), value.stored_value.load()) - - expected, _ = expectEncoding.__teal__(options) - expected.addIncoming() - expected = pt.TealBlock.NormalizeBlocks(expected) - - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected - - -def test_Account_get(): - value = abi.Account() - expr = value.get() - assert expr.type_of() == pt.TealType.uint64 - assert expr.has_return() is False - expected = pt.TealSimpleBlock( [ - pt.TealOp(expr, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.txnas, "Accounts"), ] ) actual, _ = expr.__teal__(options) @@ -69,47 +117,40 @@ def test_Account_get(): assert actual == expected -def test_Account_set(): - val_to_set = 2 +def test_Account_params(): value = abi.Account() - expr = value.set(val_to_set) - assert expr.type_of() == pt.TealType.none - assert expr.has_return() is False - expected = pt.TealSimpleBlock( - [ - pt.TealOp(expr, pt.Op.int, val_to_set), - pt.TealOp(None, pt.Op.store, value.stored_value.slot), - ] - ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) + params = value.params() + + assert type(params) is pt.AccountParamObject + + expected = value.referenced_index() + actual = params._account with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + assert actual.__teal__(options) == expected.__teal__(options) -def test_Account_deref(): - val_to_set = 2 +def test_Account_asset_holding(): value = abi.Account() - value.set(val_to_set) - expr = value.deref() - assert expr.type_of() == pt.TealType.bytes - assert expr.has_return() is False - expected = pt.TealSimpleBlock( - [ - pt.TealOp(None, pt.Op.load, value.stored_value.slot), - pt.TealOp(None, pt.Op.txnas, "Accounts"), - ] - ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) + assets = ((pt.Int(6), pt.Int(6)), (a := abi.Asset(), a.referenced_index())) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + for asset, expected_asset in assets: + holding = value.asset_holding(asset) + + assert type(holding) is pt.AssetHoldingObject + + expected_account = value.referenced_index() + actual_account = holding._account + + actual_asset = holding._asset + + with pt.TealComponent.Context.ignoreExprEquality(): + assert actual_account.__teal__(options) == expected_account.__teal__( + options + ) + assert actual_asset.__teal__(options) == expected_asset.__teal__(options) def test_Asset_str(): @@ -139,34 +180,16 @@ def test_Asset_typespec(): assert abi.Asset().type_spec() == abi.AssetTypeSpec() -def test_Asset_encode(): - value = abi.Asset() - expr = value.encode() - assert expr.type_of() == pt.TealType.bytes - assert expr.has_return() is False - - expectEncoding = pt.SetByte(pt.Bytes(b"\x00"), pt.Int(0), value.stored_value.load()) - - expected, _ = expectEncoding.__teal__(options) - expected.addIncoming() - expected = pt.TealBlock.NormalizeBlocks(expected) - - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected - - -def test_Asset_get(): +def test_Asset_asset_id(): value = abi.Asset() - expr = value.get() + expr = value.asset_id() assert expr.type_of() == pt.TealType.uint64 assert expr.has_return() is False expected = pt.TealSimpleBlock( [ - pt.TealOp(expr, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.txnas, "Assets"), ] ) actual, _ = expr.__teal__(options) @@ -177,47 +200,47 @@ def test_Asset_get(): assert actual == expected -def test_Asset_set(): - val_to_set = 2 +def test_Asset_holding(): value = abi.Asset() - expr = value.set(val_to_set) - assert expr.type_of() == pt.TealType.none - assert expr.has_return() is False - expected = pt.TealSimpleBlock( - [ - pt.TealOp(expr, pt.Op.int, val_to_set), - pt.TealOp(None, pt.Op.store, value.stored_value.slot), - ] + accounts = ( + (pt.Int(6), pt.Int(6)), + ( + pt.Addr("QSA6K5MNJPEGO5SDSWXBM3K4UEI3Q2NCPS2OUXVJI5QPCHMVI27MFRSHKI"), + pt.Addr("QSA6K5MNJPEGO5SDSWXBM3K4UEI3Q2NCPS2OUXVJI5QPCHMVI27MFRSHKI"), + ), + (a := abi.Account(), a.referenced_index()), ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + for account, expected_account in accounts: + holding = value.holding(account) + + assert type(holding) is pt.AssetHoldingObject + + expected_asset = value.referenced_index() + actual_asset = holding._asset + actual_account = holding._account -def test_Asset_deref(): - val_to_set = 2 + with pt.TealComponent.Context.ignoreExprEquality(): + assert actual_asset.__teal__(options) == expected_asset.__teal__(options) + assert actual_account.__teal__(options) == expected_account.__teal__( + options + ) + + +def test_Asset_params(): value = abi.Asset() - value.set(val_to_set) - expr = value.deref() - assert expr.type_of() == pt.TealType.uint64 - assert expr.has_return() is False - expected = pt.TealSimpleBlock( - [ - pt.TealOp(None, pt.Op.load, value.stored_value.slot), - pt.TealOp(None, pt.Op.txnas, "Assets"), - ] - ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) + params = value.params() + + assert type(params) is pt.AssetParamObject + + expected = value.referenced_index() + actual = params._asset with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + assert actual.__teal__(options) == expected.__teal__(options) def test_Application_str(): @@ -247,34 +270,16 @@ def test_Application_typespec(): assert abi.Application().type_spec() == abi.ApplicationTypeSpec() -def test_Application_encode(): - value = abi.Application() - expr = value.encode() - assert expr.type_of() == pt.TealType.bytes - assert expr.has_return() is False - - expectEncoding = pt.SetByte(pt.Bytes(b"\x00"), pt.Int(0), value.stored_value.load()) - - expected, _ = expectEncoding.__teal__(options) - expected.addIncoming() - expected = pt.TealBlock.NormalizeBlocks(expected) - - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected - - -def test_Application_get(): +def test_Application_application_id(): value = abi.Application() - expr = value.get() + expr = value.application_id() assert expr.type_of() == pt.TealType.uint64 assert expr.has_return() is False expected = pt.TealSimpleBlock( [ - pt.TealOp(expr, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.load, value.stored_value.slot), + pt.TealOp(None, pt.Op.txnas, "Applications"), ] ) actual, _ = expr.__teal__(options) @@ -285,44 +290,15 @@ def test_Application_get(): assert actual == expected -def test_Application_set(): - val_to_set = 2 +def test_Application_params(): value = abi.Application() - expr = value.set(val_to_set) - assert expr.type_of() == pt.TealType.none - assert expr.has_return() is False - - expected = pt.TealSimpleBlock( - [ - pt.TealOp(expr, pt.Op.int, val_to_set), - pt.TealOp(None, pt.Op.store, value.stored_value.slot), - ] - ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) - with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + params = value.params() + assert type(params) is pt.AppParamObject -def test_Application_deref(): - val_to_set = 2 - value = abi.Application() - value.set(val_to_set) - expr = value.deref() - assert expr.type_of() == pt.TealType.uint64 - assert expr.has_return() is False - - expected = pt.TealSimpleBlock( - [ - pt.TealOp(None, pt.Op.load, value.stored_value.slot), - pt.TealOp(None, pt.Op.txnas, "Applications"), - ] - ) - actual, _ = expr.__teal__(options) - actual.addIncoming() - actual = pt.TealBlock.NormalizeBlocks(actual) + expected = value.referenced_index() + actual = params._app with pt.TealComponent.Context.ignoreExprEquality(): - assert actual == expected + assert actual.__teal__(options) == expected.__teal__(options) diff --git a/pyteal/ast/acct.py b/pyteal/ast/acct.py index c08f2760b..97eee199b 100644 --- a/pyteal/ast/acct.py +++ b/pyteal/ast/acct.py @@ -1,3 +1,5 @@ +from typing import Final + from pyteal.types import TealType, require_type from pyteal.ir import Op from pyteal.ast.expr import Expr @@ -55,3 +57,33 @@ def authAddr(cls, acct: Expr) -> MaybeValue: AccountParam.__module__ = "pyteal" + + +class AccountParamObject: + """Represents information about an account""" + + def __init__(self, account: Expr) -> None: + """Create a new AccountParamObject for the given account. + + Args: + app: An index into Txn.accounts that corresponds to the application to check or an + address available at runtime. May evaluate to uint64 or bytes, respectively. + """ + self._account: Final = account + + def balance(self) -> MaybeValue: + """Get the current balance in microAlgos for the account""" + return AccountParam.balance(self._account) + + def min_balance(self) -> MaybeValue: + """Get the minimum balance in microAlgos for the account.""" + return AccountParam.minBalance(self._account) + + def auth_address(self) -> MaybeValue: + """Get the authorizing address for the account. + + If the account is not rekeyed, the empty address is returned.""" + return AccountParam.authAddr(self._account) + + +AccountParamObject.__module__ = "pyteal" diff --git a/pyteal/ast/acct_test.py b/pyteal/ast/acct_test.py index 52fc89132..66958b6ae 100644 --- a/pyteal/ast/acct_test.py +++ b/pyteal/ast/acct_test.py @@ -1,4 +1,5 @@ import pyteal as pt +from pyteal.ast.maybe_test import assert_MaybeValue_equality options = pt.CompileOptions() teal4Options = pt.CompileOptions(version=4) @@ -73,3 +74,23 @@ def test_acct_param_auth_addr_valid(): with pt.TealComponent.Context.ignoreExprEquality(): assert actual == expected + + +def test_AccountParamObject(): + for account in ( + pt.Int(7), + pt.Addr("QSA6K5MNJPEGO5SDSWXBM3K4UEI3Q2NCPS2OUXVJI5QPCHMVI27MFRSHKI"), + ): + obj = pt.AccountParamObject(account) + + assert obj._account is account + + assert_MaybeValue_equality( + obj.balance(), pt.AccountParam.balance(account), teal6Options + ) + assert_MaybeValue_equality( + obj.min_balance(), pt.AccountParam.minBalance(account), teal6Options + ) + assert_MaybeValue_equality( + obj.auth_address(), pt.AccountParam.authAddr(account), teal6Options + ) diff --git a/pyteal/ast/app.py b/pyteal/ast/app.py index adf18f6a7..6a36b31e1 100644 --- a/pyteal/ast/app.py +++ b/pyteal/ast/app.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Final from enum import Enum from pyteal.types import TealType, require_type @@ -356,3 +356,56 @@ def address(cls, app: Expr) -> MaybeValue: AppParam.__module__ = "pyteal" + + +class AppParamObject: + """Represents information about an application's parameters""" + + def __init__(self, app: Expr) -> None: + """Create a new AppParamObject for the given application. + + Args: + app: An identifier for the app. It must be an index into Txn.ForeignApps that + corresponds to the app to check, or since v4, an application ID that appears in + Txn.ForeignApps or is the CurrentApplicationID. In either case, it must evaluate to + uint64. + """ + self._app: Final = app + + def approval_program(self) -> MaybeValue: + """Get the bytecode of Approval Program for the application.""" + return AppParam.approvalProgram(self._app) + + def clear_state_program(self) -> MaybeValue: + return AppParam.clearStateProgram(self._app) + + def global_num_unit(self) -> MaybeValue: + """Get the number of uint64 values allowed in Global State for the application.""" + return AppParam.globalNumUnit(self._app) + + def global_num_byte_slice(self) -> MaybeValue: + """Get the number of byte array values allowed in Global State for the application.""" + return AppParam.globalNumByteSlice(self._app) + + def local_num_unit(self) -> MaybeValue: + """Get the number of uint64 values allowed in Local State for the application.""" + return AppParam.localNumUnit(self._app) + + def local_num_byte_slice(self) -> MaybeValue: + """Get the number of byte array values allowed in Local State for the application.""" + return AppParam.localNumByteSlice(self._app) + + def extra_program_pages(self) -> MaybeValue: + """Get the number of Extra Program Pages of code space for the application.""" + return AppParam.extraProgramPages(self._app) + + def creator_address(self) -> MaybeValue: + """Get the creator address for the application.""" + return AppParam.creator(self._app) + + def address(self) -> MaybeValue: + """Get the escrow address for the application.""" + return AppParam.address(self._app) + + +AppParamObject.__module__ = "pyteal" diff --git a/pyteal/ast/app_test.py b/pyteal/ast/app_test.py index 5d16ab876..52241ae20 100644 --- a/pyteal/ast/app_test.py +++ b/pyteal/ast/app_test.py @@ -1,6 +1,7 @@ import pytest import pyteal as pt +from pyteal.ast.maybe_test import assert_MaybeValue_equality options = pt.CompileOptions() teal4Options = pt.CompileOptions(version=4) @@ -674,3 +675,40 @@ def test_app_param_address_valid(): def test_app_param_address_invalid(): with pytest.raises(pt.TealTypeError): pt.AppParam.address(pt.Txn.sender()) + + +def test_AppParamObject(): + for app in (pt.Int(1), pt.Int(100)): + obj = pt.AppParamObject(app) + + assert obj._app is app + + assert_MaybeValue_equality( + obj.approval_program(), pt.AppParam.approvalProgram(app), teal5Options + ) + assert_MaybeValue_equality( + obj.clear_state_program(), pt.AppParam.clearStateProgram(app), teal5Options + ) + assert_MaybeValue_equality( + obj.global_num_unit(), pt.AppParam.globalNumUnit(app), teal5Options + ) + assert_MaybeValue_equality( + obj.global_num_byte_slice(), + pt.AppParam.globalNumByteSlice(app), + teal5Options, + ) + assert_MaybeValue_equality( + obj.local_num_unit(), pt.AppParam.localNumUnit(app), teal5Options + ) + assert_MaybeValue_equality( + obj.local_num_byte_slice(), pt.AppParam.localNumByteSlice(app), teal5Options + ) + assert_MaybeValue_equality( + obj.extra_program_pages(), pt.AppParam.extraProgramPages(app), teal5Options + ) + assert_MaybeValue_equality( + obj.creator_address(), pt.AppParam.creator(app), teal5Options + ) + assert_MaybeValue_equality( + obj.address(), pt.AppParam.address(app), teal5Options + ) diff --git a/pyteal/ast/asset.py b/pyteal/ast/asset.py index a39879be2..41c440615 100644 --- a/pyteal/ast/asset.py +++ b/pyteal/ast/asset.py @@ -1,3 +1,5 @@ +from typing import Final + from pyteal.types import TealType, require_type from pyteal.ir import Op from pyteal.ast.expr import Expr @@ -51,6 +53,39 @@ def frozen(cls, account: Expr, asset: Expr) -> MaybeValue: AssetHolding.__module__ = "pyteal" +class AssetHoldingObject: + """Represents information about an account's holding of an asset""" + + def __init__(self, asset: Expr, account: Expr) -> None: + """Create a new AssetParamObject for the given asset. + + Args: + asset: An identifier for the asset. It must be an index into Txn.ForeignAssets that + corresponds to the asset to check, or since v4, an asset ID that appears in + Txn.ForeignAssets. In either case, it must evaluate to uint64. + account: An identifier for the account. It must be an index into Txn.Accounts that + corresponds to the account to check (in which case it must evaluate to uint64), or + since v4, an account address that appears in Txn.Accounts or is Txn.Sender (in which + case it must evaluate to bytes). + """ + self._asset: Final = asset + self._account: Final = account + + def balance(self) -> MaybeValue: + """Get the amount of the asset held by the account.""" + return AssetHolding.balance(self._account, self._asset) + + def frozen(self) -> MaybeValue: + """Check if the asset is frozen for the account. + + A value of 1 indicates frozen and 0 indicates not frozen. + """ + return AssetHolding.frozen(self._account, self._asset) + + +AssetHoldingObject.__module__ = "pyteal" + + class AssetParam: @classmethod def total(cls, asset: Expr) -> MaybeValue: @@ -259,3 +294,70 @@ def creator(cls, asset: Expr) -> MaybeValue: AssetParam.__module__ = "pyteal" + + +class AssetParamObject: + """Represents information about an asset's parameters""" + + def __init__(self, asset: Expr) -> None: + """Create a new AssetParamObject for the given asset. + + Args: + asset: An identifier for the asset. It must be an index into Txn.ForeignAssets that + corresponds to the asset to check, or since v4, an asset ID that appears in + Txn.ForeignAssets. In either case, it must evaluate to uint64. + """ + self._asset: Final = asset + + def total(self) -> MaybeValue: + """Get the total number of units of the asset.""" + return AssetParam.total(self._asset) + + def decimals(self) -> MaybeValue: + """Get the number of decimals for the asset.""" + return AssetParam.decimals(self._asset) + + def default_frozen(self) -> MaybeValue: + """Check if the asset is frozen by default.""" + return AssetParam.defaultFrozen(self._asset) + + def unit_name(self) -> MaybeValue: + """Get the unit name of the asset.""" + return AssetParam.unitName(self._asset) + + def name(self) -> MaybeValue: + """Get the name of the asset.""" + return AssetParam.name(self._asset) + + def url(self) -> MaybeValue: + """Get the URL of the asset.""" + return AssetParam.url(self._asset) + + def metadata_hash(self) -> MaybeValue: + """Get the arbitrary commitment for the asset. + + If set, this will be 32 bytes long.""" + return AssetParam.metadataHash(self._asset) + + def manager_address(self) -> MaybeValue: + """Get the manager address for the asset.""" + return AssetParam.manager(self._asset) + + def reserve_address(self) -> MaybeValue: + """Get the reserve address for the asset.""" + return AssetParam.reserve(self._asset) + + def freeze_address(self) -> MaybeValue: + """Get the freeze address for the asset.""" + return AssetParam.freeze(self._asset) + + def clawback_address(self) -> MaybeValue: + """Get the clawback address for the asset.""" + return AssetParam.clawback(self._asset) + + def creator_address(self) -> MaybeValue: + """Get the creator address for the asset.""" + return AssetParam.creator(self._asset) + + +AssetParamObject.__module__ = "pyteal" diff --git a/pyteal/ast/asset_test.py b/pyteal/ast/asset_test.py index 6ac9cf0e1..642afd076 100644 --- a/pyteal/ast/asset_test.py +++ b/pyteal/ast/asset_test.py @@ -1,6 +1,7 @@ import pytest import pyteal as pt +from pyteal.ast.maybe_test import assert_MaybeValue_equality teal2Options = pt.CompileOptions() teal4Options = pt.CompileOptions(version=4) @@ -706,3 +707,62 @@ def test_asset_param_creator_valid(): def test_asset_param_creator_invalid(): with pytest.raises(pt.TealTypeError): pt.AssetParam.creator(pt.Txn.sender()) + + +def test_AssetHoldingObject(): + for asset in (pt.Int(1), pt.Int(100)): + for account in ( + pt.Int(7), + pt.Addr("QSA6K5MNJPEGO5SDSWXBM3K4UEI3Q2NCPS2OUXVJI5QPCHMVI27MFRSHKI"), + ): + obj = pt.AssetHoldingObject(asset, account) + + assert obj._asset is asset + assert obj._account is account + + assert_MaybeValue_equality( + obj.balance(), pt.AssetHolding.balance(account, asset), teal5Options + ) + assert_MaybeValue_equality( + obj.frozen(), pt.AssetHolding.frozen(account, asset), teal5Options + ) + + +def test_AssetParamObject(): + for asset in (pt.Int(1), pt.Int(100)): + obj = pt.AssetParamObject(asset) + + assert obj._asset is asset + + assert_MaybeValue_equality( + obj.total(), pt.AssetParam.total(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.decimals(), pt.AssetParam.decimals(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.default_frozen(), pt.AssetParam.defaultFrozen(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.unit_name(), pt.AssetParam.unitName(asset), teal5Options + ) + assert_MaybeValue_equality(obj.name(), pt.AssetParam.name(asset), teal5Options) + assert_MaybeValue_equality(obj.url(), pt.AssetParam.url(asset), teal5Options) + assert_MaybeValue_equality( + obj.metadata_hash(), pt.AssetParam.metadataHash(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.manager_address(), pt.AssetParam.manager(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.reserve_address(), pt.AssetParam.reserve(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.freeze_address(), pt.AssetParam.freeze(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.clawback_address(), pt.AssetParam.clawback(asset), teal5Options + ) + assert_MaybeValue_equality( + obj.creator_address(), pt.AssetParam.creator(asset), teal5Options + ) diff --git a/pyteal/ast/maybe_test.py b/pyteal/ast/maybe_test.py index fa57ba5ca..0b4da7471 100644 --- a/pyteal/ast/maybe_test.py +++ b/pyteal/ast/maybe_test.py @@ -3,6 +3,26 @@ options = pt.CompileOptions() +def assert_MaybeValue_equality( + actual: pt.MaybeValue, expected: pt.MaybeValue, options: pt.CompileOptions +): + actual_block, _ = actual.__teal__(options) + actual_block.addIncoming() + actual_block = pt.TealBlock.NormalizeBlocks(actual_block) + + expected_block, _ = expected.__teal__(options) + expected_block.addIncoming() + expected_block = pt.TealBlock.NormalizeBlocks(expected_block) + + with pt.TealComponent.Context.ignoreExprEquality(), pt.TealComponent.Context.ignoreScratchSlotEquality(): + assert actual_block == expected_block + + assert pt.TealBlock.MatchScratchSlotReferences( + pt.TealBlock.GetReferencedScratchSlots(actual_block), + pt.TealBlock.GetReferencedScratchSlots(expected_block), + ) + + def test_maybe_value(): ops = ( pt.Op.app_global_get_ex, diff --git a/pyteal/ast/subroutine_test.py b/pyteal/ast/subroutine_test.py index a58df675f..2e9df0ac2 100644 --- a/pyteal/ast/subroutine_test.py +++ b/pyteal/ast/subroutine_test.py @@ -240,7 +240,7 @@ def fn_w_tuple1arg( def test_subroutine_return_reference(): @ABIReturnSubroutine def invalid_ret_type(*, output: pt.abi.Account): - return output.set(0) + return output.decode(pt.Bytes(b"\x00")) with pytest.raises(pt.TealInputError): invalid_ret_type.method_signature() From 23cb4580943d88c2f57e328e329dab00e0513a02 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Wed, 8 Jun 2022 08:06:41 -0400 Subject: [PATCH 4/7] fmt --- pyteal/ast/router_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyteal/ast/router_test.py b/pyteal/ast/router_test.py index b16f6c422..157e9c41c 100644 --- a/pyteal/ast/router_test.py +++ b/pyteal/ast/router_test.py @@ -372,6 +372,7 @@ def test_method_config(): pt.Or(*list_of_expressions) ) + def test_on_complete_action(): with pytest.raises(pt.TealInputError) as contradict_err: pt.OnCompleteAction(action=pt.Seq(), call_config=pt.CallConfig.NEVER) From 9732cd0599e9e044e7bbe10794eb6abad40da631 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Wed, 8 Jun 2022 14:08:00 -0400 Subject: [PATCH 5/7] Update pyteal/ast/acct.py Co-authored-by: Zeph Grunschlag --- pyteal/ast/acct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyteal/ast/acct.py b/pyteal/ast/acct.py index 97eee199b..7f61e10ee 100644 --- a/pyteal/ast/acct.py +++ b/pyteal/ast/acct.py @@ -66,7 +66,7 @@ def __init__(self, account: Expr) -> None: """Create a new AccountParamObject for the given account. Args: - app: An index into Txn.accounts that corresponds to the application to check or an + account: An index into Txn.accounts that corresponds to the application to check or an address available at runtime. May evaluate to uint64 or bytes, respectively. """ self._account: Final = account From 059c7ed0cd984ba9a5dd41c693d7343f68fab9a2 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Wed, 8 Jun 2022 15:23:40 -0400 Subject: [PATCH 6/7] Update pyteal/ast/app.py Co-authored-by: Zeph Grunschlag --- pyteal/ast/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyteal/ast/app.py b/pyteal/ast/app.py index 6a36b31e1..fabfc8b83 100644 --- a/pyteal/ast/app.py +++ b/pyteal/ast/app.py @@ -379,7 +379,7 @@ def approval_program(self) -> MaybeValue: def clear_state_program(self) -> MaybeValue: return AppParam.clearStateProgram(self._app) - def global_num_unit(self) -> MaybeValue: + def global_num_uint(self) -> MaybeValue: """Get the number of uint64 values allowed in Global State for the application.""" return AppParam.globalNumUnit(self._app) @@ -387,7 +387,7 @@ def global_num_byte_slice(self) -> MaybeValue: """Get the number of byte array values allowed in Global State for the application.""" return AppParam.globalNumByteSlice(self._app) - def local_num_unit(self) -> MaybeValue: + def local_num_uint(self) -> MaybeValue: """Get the number of uint64 values allowed in Local State for the application.""" return AppParam.localNumUnit(self._app) From 6c0b3470b1e39979d4b9a46b4d4033f9ee39c5ca Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Wed, 8 Jun 2022 15:31:36 -0400 Subject: [PATCH 7/7] check types, fix tests --- pyteal/ast/app.py | 1 + pyteal/ast/app_test.py | 4 ++-- pyteal/ast/asset.py | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyteal/ast/app.py b/pyteal/ast/app.py index fabfc8b83..c4d0c597b 100644 --- a/pyteal/ast/app.py +++ b/pyteal/ast/app.py @@ -370,6 +370,7 @@ def __init__(self, app: Expr) -> None: Txn.ForeignApps or is the CurrentApplicationID. In either case, it must evaluate to uint64. """ + require_type(app, TealType.uint64) self._app: Final = app def approval_program(self) -> MaybeValue: diff --git a/pyteal/ast/app_test.py b/pyteal/ast/app_test.py index 52241ae20..0258ce782 100644 --- a/pyteal/ast/app_test.py +++ b/pyteal/ast/app_test.py @@ -690,7 +690,7 @@ def test_AppParamObject(): obj.clear_state_program(), pt.AppParam.clearStateProgram(app), teal5Options ) assert_MaybeValue_equality( - obj.global_num_unit(), pt.AppParam.globalNumUnit(app), teal5Options + obj.global_num_uint(), pt.AppParam.globalNumUnit(app), teal5Options ) assert_MaybeValue_equality( obj.global_num_byte_slice(), @@ -698,7 +698,7 @@ def test_AppParamObject(): teal5Options, ) assert_MaybeValue_equality( - obj.local_num_unit(), pt.AppParam.localNumUnit(app), teal5Options + obj.local_num_uint(), pt.AppParam.localNumUnit(app), teal5Options ) assert_MaybeValue_equality( obj.local_num_byte_slice(), pt.AppParam.localNumByteSlice(app), teal5Options diff --git a/pyteal/ast/asset.py b/pyteal/ast/asset.py index 41c440615..70b530cb6 100644 --- a/pyteal/ast/asset.py +++ b/pyteal/ast/asset.py @@ -68,7 +68,9 @@ def __init__(self, asset: Expr, account: Expr) -> None: since v4, an account address that appears in Txn.Accounts or is Txn.Sender (in which case it must evaluate to bytes). """ + require_type(asset, TealType.uint64) self._asset: Final = asset + require_type(account, TealType.anytype) self._account: Final = account def balance(self) -> MaybeValue: @@ -307,6 +309,7 @@ def __init__(self, asset: Expr) -> None: corresponds to the asset to check, or since v4, an asset ID that appears in Txn.ForeignAssets. In either case, it must evaluate to uint64. """ + require_type(asset, TealType.uint64) self._asset: Final = asset def total(self) -> MaybeValue: