Beispiel #1
0
    def set(
        self,
        value: Union[
            str,
            bytes,
            Expr,
            Sequence[Byte],
            DynamicArray[Byte],
            ComputedValue[DynamicArray[Byte]],
            "String",
            ComputedValue["String"],
        ],
    ) -> Expr:
        """Set the value of this String to the input value.

        The behavior of this method depends on the input argument type:

            * :code:`str`: set the value to the Python string.
            * :code:`bytes`: set the value to the Python byte string.
            * :code:`Expr`: set the value to the result of a PyTeal expression, which must evaluate to a TealType.bytes.
            * :code:`Sequence[Byte]`: set the bytes of this String to those contained in this Python sequence (e.g. a list or tuple).
            * :code:`DynamicArray[Byte]`: copy the bytes from a DynamicArray.
            * :code:`ComputedValue[DynamicArray[Byte]]`: copy the bytes from a DynamicArray produced by a ComputedValue.
            * :code:`String`: copy the value from another String.
            * :code:`ComputedValue[String]`: copy the value from a String produced by a ComputedValue.

        Args:
            value: The new value this String should take. This must follow the above constraints.

        Returns:
            An expression which stores the given value into this String.
        """

        match value:
            case ComputedValue():
                return self._set_with_computed_type(value)
            case BaseType():
                if value.type_spec() == StringTypeSpec() or (
                    value.type_spec() == DynamicArrayTypeSpec(ByteTypeSpec())
                ):
                    return self.stored_value.store(value.stored_value.load())

                raise TealInputError(
                    f"Got {value} with type spec {value.type_spec()}, expected {StringTypeSpec}"
                )
            case bytes() | bytearray():
                return self.stored_value.store(_encoded_byte_string(value))
            case str():
                return self.stored_value.store(_encoded_byte_string(value.encode()))
            case Expr():
                return _store_encoded_expr_byte_string_into_var(
                    value, self.stored_value
                )
            case CollectionSequence():
                return super().set(cast(Sequence[Byte], value))

        raise TealInputError(
            f"Got {type(value)}, expected DynamicArray, ComputedValue, String, str, bytes, Expr"
        )
Beispiel #2
0
 def __init__(self) -> None:
     super().__init__(ByteTypeSpec())
Beispiel #3
0
def type_spec_from_annotation(annotation: Any) -> TypeSpec:
    """Convert an ABI type annotation into the corresponding TypeSpec.

    For example, calling this function with the input `abi.StaticArray[abi.Bool, Literal[5]]` would
    return `abi.StaticArrayTypeSpec(abi.BoolTypeSpec(), 5)`.

    Args:
        annotation: An annotation representing an ABI type instance.

    Raises:
        TypeError: if the input annotation does not represent a valid ABI type instance or its
            arguments are invalid.

    Returns:
        The TypeSpec that corresponds to the input annotation.
    """
    from pyteal.ast.abi.bool import BoolTypeSpec, Bool
    from pyteal.ast.abi.uint import (
        ByteTypeSpec,
        Byte,
        Uint8TypeSpec,
        Uint8,
        Uint16TypeSpec,
        Uint16,
        Uint32TypeSpec,
        Uint32,
        Uint64TypeSpec,
        Uint64,
    )
    from pyteal.ast.abi.array_dynamic import (
        DynamicArrayTypeSpec,
        DynamicArray,
        DynamicBytesTypeSpec,
        DynamicBytes,
    )
    from pyteal.ast.abi.array_static import (
        StaticArrayTypeSpec,
        StaticArray,
        StaticBytesTypeSpec,
        StaticBytes,
    )
    from pyteal.ast.abi.tuple import (
        TupleTypeSpec,
        Tuple,
        Tuple0,
        Tuple1,
        Tuple2,
        Tuple3,
        Tuple4,
        Tuple5,
        NamedTuple,
        NamedTupleTypeSpec,
    )
    from pyteal.ast.abi.string import StringTypeSpec, String
    from pyteal.ast.abi.address import AddressTypeSpec, Address
    from pyteal.ast.abi.transaction import (
        Transaction,
        TransactionTypeSpec,
        PaymentTransaction,
        PaymentTransactionTypeSpec,
        KeyRegisterTransaction,
        KeyRegisterTransactionTypeSpec,
        AssetConfigTransaction,
        AssetConfigTransactionTypeSpec,
        AssetFreezeTransaction,
        AssetFreezeTransactionTypeSpec,
        AssetTransferTransaction,
        AssetTransferTransactionTypeSpec,
        ApplicationCallTransaction,
        ApplicationCallTransactionTypeSpec,
    )
    from pyteal.ast.abi.reference_type import (
        AccountTypeSpec,
        Account,
        AssetTypeSpec,
        Asset,
        ApplicationTypeSpec,
        Application,
    )

    origin = get_origin(annotation)
    if origin is None:
        origin = annotation

    args = get_args(annotation)

    if origin is Account:
        if len(args) != 0:
            raise TypeError("Account expects 0 arguments. Got: {}".format(args))
        return AccountTypeSpec()

    if origin is Asset:
        if len(args) != 0:
            raise TypeError("Asset expects 0 arguments. Got: {}".format(args))
        return AssetTypeSpec()

    if origin is Application:
        if len(args) != 0:
            raise TypeError("Application expects 0 arguments. Got: {}".format(args))
        return ApplicationTypeSpec()

    if origin is Bool:
        if len(args) != 0:
            raise TypeError("Bool expects 0 type arguments. Got: {}".format(args))
        return BoolTypeSpec()

    if origin is Byte:
        if len(args) != 0:
            raise TypeError("Byte expects 0 type arguments. Got: {}".format(args))
        return ByteTypeSpec()

    if origin is Uint8:
        if len(args) != 0:
            raise TypeError("Uint8 expects 0 type arguments. Got: {}".format(args))
        return Uint8TypeSpec()

    if origin is Uint16:
        if len(args) != 0:
            raise TypeError("Uint16 expects 0 type arguments. Got: {}".format(args))
        return Uint16TypeSpec()

    if origin is Uint32:
        if len(args) != 0:
            raise TypeError("Uint32 expects 0 type arguments. Got: {}".format(args))
        return Uint32TypeSpec()

    if origin is Uint64:
        if len(args) != 0:
            raise TypeError("Uint64 expects 0 type arguments. Got: {}".format(args))
        return Uint64TypeSpec()

    if origin is String:
        if len(args) != 0:
            raise TypeError("String expects 0 arguments. Got: {}".format(args))
        return StringTypeSpec()

    if origin is Address:
        if len(args) != 0:
            raise TypeError("Address expects 0 arguments. Got: {}".format(args))
        return AddressTypeSpec()

    if origin is DynamicBytes:
        if len(args) != 0:
            raise TypeError(f"DynamicBytes expect 0 type argument. Got: {args}")
        return DynamicBytesTypeSpec()

    if origin is DynamicArray:
        if len(args) != 1:
            raise TypeError(
                "DynamicArray expects 1 type argument. Got: {}".format(args)
            )
        value_type_spec = type_spec_from_annotation(args[0])
        return DynamicArrayTypeSpec(value_type_spec)

    if origin is StaticBytes:
        if len(args) != 1:
            raise TypeError(f"StaticBytes expect 1 type argument. Got: {args}")
        array_length = int_literal_from_annotation(args[0])
        return StaticBytesTypeSpec(array_length)

    if origin is StaticArray:
        if len(args) != 2:
            raise TypeError("StaticArray expects 1 type argument. Got: {}".format(args))
        value_type_spec = type_spec_from_annotation(args[0])
        array_length = int_literal_from_annotation(args[1])
        return StaticArrayTypeSpec(value_type_spec, array_length)

    if origin is Tuple:
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if issubclass(origin, NamedTuple):
        return cast(NamedTupleTypeSpec, origin().type_spec())

    if origin is Tuple0:
        if len(args) != 0:
            raise TypeError("Tuple0 expects 0 type arguments. Got: {}".format(args))
        return TupleTypeSpec()

    if origin is Tuple1:
        if len(args) != 1:
            raise TypeError("Tuple1 expects 1 type argument. Got: {}".format(args))
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if origin is Tuple2:
        if len(args) != 2:
            raise TypeError("Tuple2 expects 2 type arguments. Got: {}".format(args))
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if origin is Tuple3:
        if len(args) != 3:
            raise TypeError("Tuple3 expects 3 type arguments. Got: {}".format(args))
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if origin is Tuple4:
        if len(args) != 4:
            raise TypeError("Tuple4 expects 4 type arguments. Got: {}".format(args))
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if origin is Tuple5:
        if len(args) != 5:
            raise TypeError("Tuple5 expects 5 type arguments. Got: {}".format(args))
        return TupleTypeSpec(*(type_spec_from_annotation(arg) for arg in args))

    if origin is Transaction:
        if len(args) != 0:
            raise TypeError("Transaction expects 0 type arguments. Got {}".format(args))
        return TransactionTypeSpec()

    if origin is PaymentTransaction:
        if len(args) != 0:
            raise TypeError(
                "PaymentTransaction expects 0 type arguments. Got {}".format(args)
            )
        return PaymentTransactionTypeSpec()

    if origin is KeyRegisterTransaction:
        if len(args) != 0:
            raise TypeError(
                "KeyRegisterTransaction expects 0 type arguments. Got {}".format(args)
            )
        return KeyRegisterTransactionTypeSpec()

    if origin is AssetConfigTransaction:
        if len(args) != 0:
            raise TypeError(
                "AssetConfigTransaction expects 0 type arguments. Got {}".format(args)
            )
        return AssetConfigTransactionTypeSpec()

    if origin is AssetFreezeTransaction:
        if len(args) != 0:
            raise TypeError(
                "AssetFreezeTransaction expects 0 type arguments. Got {}".format(args)
            )
        return AssetFreezeTransactionTypeSpec()

    if origin is AssetTransferTransaction:
        if len(args) != 0:
            raise TypeError(
                "AssetTransferTransaction expects 0 type arguments. Got {}".format(args)
            )
        return AssetTransferTransactionTypeSpec()

    if origin is ApplicationCallTransaction:
        if len(args) != 0:
            raise TypeError(
                "ApplicationCallTransaction expects 0 type arguments. Got {}".format(
                    args
                )
            )
        return ApplicationCallTransactionTypeSpec()

    raise TypeError("Unknown annotation origin: {}".format(origin))
Beispiel #4
0
def type_spec_from_algosdk(t: Union[algosdk.abi.ABIType, str]) -> TypeSpec:

    from pyteal.ast.abi.reference_type import ReferenceTypeSpecs
    from pyteal.ast.abi.transaction import TransactionTypeSpecs

    from pyteal.ast.abi.array_dynamic import DynamicArrayTypeSpec
    from pyteal.ast.abi.array_static import StaticArrayTypeSpec
    from pyteal.ast.abi.tuple import TupleTypeSpec
    from pyteal.ast.abi.bool import BoolTypeSpec
    from pyteal.ast.abi.string import StringTypeSpec
    from pyteal.ast.abi.address import AddressTypeSpec
    from pyteal.ast.abi.uint import (
        ByteTypeSpec,
        Uint8TypeSpec,
        Uint16TypeSpec,
        Uint32TypeSpec,
        Uint64TypeSpec,
    )

    match t:
        # Currently reference and transaction types are only strings
        case str():
            if algosdk.abi.is_abi_reference_type(t):
                ref_dict: dict[str, TypeSpec] = {
                    str(rts): rts for rts in ReferenceTypeSpecs
                }
                if t in ref_dict:
                    return ref_dict[t]
                else:
                    raise TealInputError(f"Invalid reference type: {t}")

            elif algosdk.abi.is_abi_transaction_type(t):
                txn_dict: dict[str, TypeSpec] = {
                    str(tts): tts for tts in TransactionTypeSpecs
                }
                if t in txn_dict:
                    return txn_dict[t]
                else:
                    raise TealInputError(f"Invalid transaction type: {t}")
            else:
                raise TealInputError(f"Invalid ABI type: {t}")

        case algosdk.abi.ABIType():
            match t:
                case algosdk.abi.ArrayDynamicType():
                    return DynamicArrayTypeSpec(type_spec_from_algosdk(t.child_type))
                case algosdk.abi.ArrayStaticType():
                    return StaticArrayTypeSpec(
                        type_spec_from_algosdk(t.child_type), t.static_length
                    )
                case algosdk.abi.TupleType():
                    return TupleTypeSpec(
                        *[type_spec_from_algosdk(ct) for ct in t.child_types]
                    )
                case algosdk.abi.UintType():
                    match t.bit_size:
                        case 8:
                            return Uint8TypeSpec()
                        case 16:
                            return Uint16TypeSpec()
                        case 32:
                            return Uint32TypeSpec()
                        case 64:
                            return Uint64TypeSpec()
                case algosdk.abi.ByteType():
                    return ByteTypeSpec()
                case algosdk.abi.BoolType():
                    return BoolTypeSpec()
                case algosdk.abi.StringType():
                    return StringTypeSpec()
                case algosdk.abi.AddressType():
                    return AddressTypeSpec()
                case algosdk.abi.UfixedType():
                    raise TealInputError("Ufixed not supported")

    raise TealInputError(f"Invalid Type: {t}")
Beispiel #5
0
 def __init__(self, array_length: int) -> None:
     super().__init__(ByteTypeSpec(), array_length)
Beispiel #6
0
    def set(
        self,
        value: Union[
            str,
            bytes,
            Expr,
            Sequence[Byte],
            StaticArray[Byte, Literal[AddressLength.Bytes]],
            ComputedValue[StaticArray[Byte, Literal[AddressLength.Bytes]]],
            "Address",
            ComputedValue["Address"],
        ],
    ):
        """Set the value of this Address to the input value.

        The behavior of this method depends on the input argument type:

            * :code:`str`: set the value to the address from the encoded address string. This string must be a valid 58-character base-32 Algorand address with checksum.
            * :code:`bytes`: set the value to the raw address bytes. This byte string must have length 32.
            * :code:`Expr`: set the value to the result of a PyTeal expression, which must evaluate to a TealType.bytes. The program will fail if the evaluated byte string length is not 32.
            * :code:`Sequence[Byte]`: set the bytes of this Address to those contained in this Python sequence (e.g. a list or tuple). A compiler error will occur if the sequence length is not 32.
            * :code:`StaticArray[Byte, 32]`: copy the bytes from a StaticArray of 32 bytes.
            * :code:`ComputedValue[StaticArray[Byte, 32]]`: copy the bytes from a StaticArray of 32 bytes produced by a ComputedValue.
            * :code:`Address`: copy the value from another Address.
            * :code:`ComputedValue[Address]`: copy the value from an Address produced by a ComputedValue.

        Args:
            value: The new value this Address should take. This must follow the above constraints.

        Returns:
            An expression which stores the given value into this Address.
        """

        match value:
            case ComputedValue():
                pts = value.produced_type_spec()
                if pts == AddressTypeSpec() or pts == StaticArrayTypeSpec(
                    ByteTypeSpec(), AddressLength.Bytes
                ):
                    return value.store_into(self)

                raise TealInputError(
                    f"Got ComputedValue with type spec {pts}, expected AddressTypeSpec or StaticArray[Byte, Literal[AddressLength.Bytes]]"
                )
            case BaseType():
                if (
                    value.type_spec() == AddressTypeSpec()
                    or value.type_spec()
                    == StaticArrayTypeSpec(ByteTypeSpec(), AddressLength.Bytes)
                ):
                    return self.stored_value.store(value.stored_value.load())

                raise TealInputError(
                    f"Got {value} with type spec {value.type_spec()}, expected AddressTypeSpec"
                )
            case str():
                # Addr throws if value is invalid address
                return self.stored_value.store(Addr(value))
            case bytes():
                if len(value) == AddressLength.Bytes:
                    return self.stored_value.store(Bytes(value))
                raise TealInputError(
                    f"Got bytes with length {len(value)}, expected {AddressLength.Bytes}"
                )
            case Expr():
                return Seq(
                    self.stored_value.store(value),
                    Assert(
                        Len(self.stored_value.load()) == Int(AddressLength.Bytes.value)
                    ),
                )
            case CollectionSequence():
                if len(value) != AddressLength.Bytes:
                    raise TealInputError(
                        f"Got bytes with length {len(value)}, expected {AddressLength.Bytes}"
                    )
                return super().set(cast(Sequence[Byte], value))

        raise TealInputError(
            f"Got {type(value)}, expected Sequence, StaticArray, ComputedValue, Address, str, bytes, Expr"
        )
Beispiel #7
0
 def __init__(self) -> None:
     super().__init__(ByteTypeSpec(), AddressLength.Bytes)