Skip to content

Types represent the type of an expression (ExpressionAbc), type name (TypeNameAbc) and VariableDeclaration.

wake.ir.types module #

TypeAbc class #

Bases: ABC

Abstract base class for all types.

Source code in wake/ir/types.py
class TypeAbc(ABC):
    """
    Abstract base class for all types.
    """

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> typ.Optional[TypeAbc]:
        if type_identifier.startswith("t_address"):
            return Address.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_bool"):
            return Bool.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_int"):
            return Int.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_uint"):
            return UInt.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_stringliteral_"):
            return StringLiteral.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_string_"):
            return String.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_bytes_"):
            return Bytes.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        # must go after t_bytes_ !!
        elif type_identifier.startswith("t_bytes"):
            return FixedBytes.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_function"):
            return Function.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_tuple"):
            return Tuple.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_type"):
            return Type.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_rational"):
            return Rational.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_modifier"):
            return Modifier.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_array"):
            return Array.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_mapping"):
            return Mapping.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_contract") or type_identifier.startswith(
            "t_super"
        ):
            return Contract.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_struct"):
            return Struct.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_enum"):
            return Enum.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_magic"):
            return Magic.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_userDefinedValueType"):
            return UserDefinedValueType.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_module"):
            return Module.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_fixed"):
            return Fixed.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        elif type_identifier.startswith("t_ufixed"):
            return UFixed.from_type_identifier(
                type_identifier, reference_resolver, cu_hash
            )
        else:
            return None

    @property
    @abstractmethod
    def abi_type(self) -> str:
        """
        Raises:
            NotImplementedError: If the type cannot be represented in contract ABI.

        Returns:
            ABI type string.
        """
        ...

abi_type: str abstractmethod property #

Raises:

Type Description
NotImplementedError

If the type cannot be represented in contract ABI.

Returns:

Type Description
str

ABI type string.

Address class #

Bases: TypeAbc

Address type.

Source code in wake/ir/types.py
class Address(TypeAbc):
    """
    Address type.
    """

    _is_payable: bool

    def __init__(self, is_payable: bool):
        self._is_payable = is_payable

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Address):
            return False
        return self._is_payable == other._is_payable

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Address:
        type_identifier.read("t_address")

        if type_identifier.startswith("_payable"):
            type_identifier.read("_payable")
            return Address(True)
        else:
            return Address(False)

    @property
    def abi_type(self) -> str:
        return "address"

    @property
    def is_payable(self) -> bool:
        """
        Returns:
            `True` if the address is payable, `False` otherwise.
        """
        return self._is_payable

is_payable: bool property #

Returns:

Type Description
bool

True if the address is payable, False otherwise.

Bool class #

Bases: TypeAbc

Boolean type.

Source code in wake/ir/types.py
class Bool(TypeAbc):
    """
    Boolean type.
    """

    def __eq__(self, other: object) -> bool:
        return isinstance(other, Bool)

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Bool:
        type_identifier.read("t_bool")
        return Bool()

    @property
    def abi_type(self) -> str:
        return "bool"

IntAbc class #

Bases: TypeAbc

Base class for Int and UInt types.

Source code in wake/ir/types.py
class IntAbc(TypeAbc):
    """
    Base class for [Int][wake.ir.types.Int] and [UInt][wake.ir.types.UInt] types.
    """

    _bits_count: int

    @property
    def bits_count(self) -> int:
        """
        Can only be a multiple of 8, with a minimum of 8 and a maximum of 256.

        Returns:
            Number of bits used to represent this integer.
        """
        return self._bits_count

bits_count: int property #

Can only be a multiple of 8, with a minimum of 8 and a maximum of 256.

Returns:

Type Description
int

Number of bits used to represent this integer.

Int class #

Bases: IntAbc

Signed integer type.

Source code in wake/ir/types.py
class Int(IntAbc):
    """
    Signed integer type.
    """

    def __init__(self, bits_count: int):
        self._bits_count = bits_count

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Int):
            return False
        return self._bits_count == other._bits_count

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Int:
        type_identifier.read("t_int")
        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        number = match.group("number")
        type_identifier.read(number)
        return Int(int(number))

    @property
    def abi_type(self) -> str:
        return f"int{self._bits_count}"

UInt class #

Bases: IntAbc

Unsigned integer type.

Source code in wake/ir/types.py
class UInt(IntAbc):
    """
    Unsigned integer type.
    """

    def __init__(self, bits_count: int):
        self._bits_count = bits_count

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UInt):
            return False
        return self._bits_count == other._bits_count

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> UInt:
        type_identifier.read("t_uint")
        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        number = match.group("number")
        type_identifier.read(number)
        return UInt(int(number))

    @property
    def abi_type(self) -> str:
        return f"uint{self._bits_count}"

FixedAbc class #

Bases: TypeAbc

Base class for Fixed and UFixed types.

Info

Currently not fully implemented in Solidity.

Source code in wake/ir/types.py
class FixedAbc(TypeAbc):
    """
    Base class for [Fixed][wake.ir.types.Fixed] and [UFixed][wake.ir.types.UFixed] types.
    !!! info
        Currently not fully implemented in Solidity.
    """

    _total_bits: int
    _fractional_digits: int

    @property
    def total_bits(self) -> int:
        """
        Returns:
            Total number of bits used to represent this fixed point number.
        """
        return self._total_bits

    @property
    def fractional_digits(self) -> int:
        """
        Returns:
            Number of decimal places available.
        """
        return self._fractional_digits

total_bits: int property #

Returns:

Type Description
int

Total number of bits used to represent this fixed point number.

fractional_digits: int property #

Returns:

Type Description
int

Number of decimal places available.

Fixed class #

Bases: FixedAbc

Signed fixed-point number type as specified by the Solidity docs.

Info

Currently not fully implemented in Solidity.

Source code in wake/ir/types.py
class Fixed(FixedAbc):
    """
    Signed fixed-point number type as specified by the [Solidity docs](https://docs.soliditylang.org/en/latest/types.html?highlight=fixed#fixed-point-numbers).
    !!! info
        Currently not fully implemented in Solidity.
    """

    def __init__(self, total_bits: int, fractional_digits: int):
        self._total_bits = total_bits
        self._fractional_digits = fractional_digits

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Fixed):
            return False
        return (
            self._total_bits == other._total_bits
            and self._fractional_digits == other._fractional_digits
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Fixed:
        type_identifier.read("t_fixed")
        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        total_bits = match.group("number")
        type_identifier.read(total_bits)

        type_identifier.read("x")

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        fractional_digits = match.group("number")
        type_identifier.read(fractional_digits)
        return Fixed(int(total_bits), int(fractional_digits))

    @property
    def abi_type(self) -> str:
        return f"fixed{self._total_bits}x{self._fractional_digits}"

UFixed class #

Bases: FixedAbc

Unsigned fixed point number type as specified by the Solidity docs.

Info

Currently not fully implemented in Solidity.

Source code in wake/ir/types.py
class UFixed(FixedAbc):
    """
    Unsigned fixed point number type as specified by the [Solidity docs](https://docs.soliditylang.org/en/latest/types.html?highlight=ufixed#fixed-point-numbers).
    !!! info
        Currently not fully implemented in Solidity.
    """

    def __init__(self, total_bits: int, fractional_digits: int):
        self._total_bits = total_bits
        self._fractional_digits = fractional_digits

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UFixed):
            return False
        return (
            self._total_bits == other._total_bits
            and self._fractional_digits == other._fractional_digits
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> UFixed:
        type_identifier.read("t_ufixed")
        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        total_bits = match.group("number")
        type_identifier.read(total_bits)

        type_identifier.read("x")

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        fractional_digits = match.group("number")
        type_identifier.read(fractional_digits)
        return UFixed(int(total_bits), int(fractional_digits))

    @property
    def abi_type(self) -> str:
        return f"ufixed{self._total_bits}x{self._fractional_digits}"

StringLiteral class #

Bases: TypeAbc

String literal type.

Warning

This expression is of the StringLiteral type:

"Hello, world!"

However, this expression is of the String type and contains a child expression of the StringLiteral type:

string("Hello, world!")

Source code in wake/ir/types.py
class StringLiteral(TypeAbc):
    """
    String literal type.
    !!! warning
        This expression is of the [StringLiteral][wake.ir.types.StringLiteral] type:
        ```solidity
        "Hello, world!"
        ```

        However, this expression is of the [String][wake.ir.types.String] type and contains a child expression of the [StringLiteral][wake.ir.types.StringLiteral] type:
        ```solidity
        string("Hello, world!")
        ```
    """

    _keccak256_hash: bytes

    def __init__(self, keccak256_hash: bytes):
        self._keccak256_hash = keccak256_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, StringLiteral):
            return False
        return self._keccak256_hash == other._keccak256_hash

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> StringLiteral:
        type_identifier.read("t_stringliteral_")
        match = HEX_RE.match(type_identifier.data)
        assert match is not None
        hex = match.group("hex")
        type_identifier.read(hex)
        return StringLiteral(bytes.fromhex(hex))

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def keccak256_hash(self) -> bytes:
        """
        Returns:
            Keccak256 hash of the string literal.
        """
        return self._keccak256_hash

keccak256_hash: bytes property #

Returns:

Type Description
bytes

Keccak256 hash of the string literal.

String class #

Bases: TypeAbc

String type.

Source code in wake/ir/types.py
class String(TypeAbc):
    """
    String type.
    """

    _data_location: DataLocation
    _is_pointer: bool
    _is_slice: bool

    def __init__(self, data_location: DataLocation, is_pointer: bool, is_slice: bool):
        self._data_location = data_location
        self._is_pointer = is_pointer
        self._is_slice = is_slice

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, String):
            return False
        return (
            self._data_location == other._data_location
            and self._is_pointer == other._is_pointer
            and self._is_slice == other._is_slice
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> String:
        type_identifier.read("t_string")
        if type_identifier.startswith("_storage"):
            data_location = DataLocation.STORAGE
            type_identifier.read("_storage")
        elif type_identifier.startswith("_memory"):
            data_location = DataLocation.MEMORY
            type_identifier.read("_memory")
        elif type_identifier.startswith("_calldata"):
            data_location = DataLocation.CALLDATA
            type_identifier.read("_calldata")
        else:
            assert False, f"Unexpected string type data location {type_identifier}"

        if type_identifier.startswith("_ptr"):
            is_pointer = True
            type_identifier.read("_ptr")
        else:
            is_pointer = False

        if type_identifier.startswith("_slice"):
            is_slice = True
            type_identifier.read("_slice")
        else:
            is_slice = False

        return String(data_location, is_pointer, is_slice)

    @property
    def abi_type(self) -> str:
        return "string"

    @property
    def data_location(self) -> DataLocation:
        """
        Can be either [CALLDATA][wake.ir.enums.DataLocation.CALLDATA], [MEMORY][wake.ir.enums.DataLocation.MEMORY] or [STORAGE][wake.ir.enums.DataLocation.STORAGE]

        Returns:
            Data location of the string expression.
        """
        return self._data_location

    @property
    def is_pointer(self) -> bool:
        """
        Storage references can be pointers or bound references. In general, local variables are of
        pointer type, state variables are bound references. Assignments to pointers or deleting
        them will not modify storage (that will only change the pointer). Assignment from
        non-storage objects to a variable of storage pointer type is not possible.

        For anything other than [STORAGE][wake.ir.enums.DataLocation.STORAGE], this always returns `True` because assignments
        never change the contents of the original value.

        Returns:
            Whether the string expression is a pointer to storage.
        """
        return self._is_pointer

    @property
    def is_slice(self) -> bool:
        """
        !!! example
            ```solidity
            function foo(string calldata s) public pure {
                s[0:5]; // s[0:5] is a string slice
            }
            ```

        Returns:
            Whether this is a slice of a string expression.
        """
        return self._is_slice

data_location: DataLocation property #

Can be either CALLDATA, MEMORY or STORAGE

Returns:

Type Description
DataLocation

Data location of the string expression.

is_pointer: bool property #

Storage references can be pointers or bound references. In general, local variables are of pointer type, state variables are bound references. Assignments to pointers or deleting them will not modify storage (that will only change the pointer). Assignment from non-storage objects to a variable of storage pointer type is not possible.

For anything other than STORAGE, this always returns True because assignments never change the contents of the original value.

Returns:

Type Description
bool

Whether the string expression is a pointer to storage.

is_slice: bool property #

Example

function foo(string calldata s) public pure {
    s[0:5]; // s[0:5] is a string slice
}

Returns:

Type Description
bool

Whether this is a slice of a string expression.

Bytes class #

Bases: TypeAbc

Bytes type.

Source code in wake/ir/types.py
class Bytes(TypeAbc):
    """
    Bytes type.
    """

    _data_location: DataLocation
    _is_pointer: bool
    _is_slice: bool

    def __init__(self, data_location: DataLocation, is_pointer: bool, is_slice: bool):
        self._data_location = data_location
        self._is_pointer = is_pointer
        self._is_slice = is_slice

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Bytes):
            return False
        return (
            self._data_location == other._data_location
            and self._is_pointer == other._is_pointer
            and self._is_slice == other._is_slice
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Bytes:
        type_identifier.read("t_bytes")
        if type_identifier.startswith("_storage"):
            data_location = DataLocation.STORAGE
            type_identifier.read("_storage")
        elif type_identifier.startswith("_memory"):
            data_location = DataLocation.MEMORY
            type_identifier.read("_memory")
        elif type_identifier.startswith("_calldata"):
            data_location = DataLocation.CALLDATA
            type_identifier.read("_calldata")
        else:
            assert False, f"Unexpected string type data location {type_identifier}"

        if type_identifier.startswith("_ptr"):
            is_pointer = True
            type_identifier.read("_ptr")
        else:
            is_pointer = False

        if type_identifier.startswith("_slice"):
            is_slice = True
            type_identifier.read("_slice")
        else:
            is_slice = False

        return Bytes(data_location, is_pointer, is_slice)

    @property
    def abi_type(self) -> str:
        return "bytes"

    @property
    def data_location(self) -> DataLocation:
        """
        Can be either [CALLDATA][wake.ir.enums.DataLocation.CALLDATA], [MEMORY][wake.ir.enums.DataLocation.MEMORY] or [STORAGE][wake.ir.enums.DataLocation.STORAGE]

        Returns:
            Data location of the bytes expression.
        """
        return self._data_location

    @property
    def is_pointer(self) -> bool:
        """
        Storage references can be pointers or bound references. In general, local variables are of
        pointer type, state variables are bound references. Assignments to pointers or deleting
        them will not modify storage (that will only change the pointer). Assignment from
        non-storage objects to a variable of storage pointer type is not possible.

        For anything other than [STORAGE][wake.ir.enums.DataLocation.STORAGE], this always returns `True` because assignments
        never change the contents of the original value.

        Returns:
            Whether the bytes expression is a pointer to storage.
        """
        return self._is_pointer

    @property
    def is_slice(self) -> bool:
        """
        !!! example
            ```solidity
            function foo(bytes calldata b) public pure {
                b[0:5]; // s[0:5] is a bytes slice
            }
            ```

        Returns:
            Whether this is a slice of a bytes expression.
        """
        return self._is_slice

data_location: DataLocation property #

Can be either CALLDATA, MEMORY or STORAGE

Returns:

Type Description
DataLocation

Data location of the bytes expression.

is_pointer: bool property #

Storage references can be pointers or bound references. In general, local variables are of pointer type, state variables are bound references. Assignments to pointers or deleting them will not modify storage (that will only change the pointer). Assignment from non-storage objects to a variable of storage pointer type is not possible.

For anything other than STORAGE, this always returns True because assignments never change the contents of the original value.

Returns:

Type Description
bool

Whether the bytes expression is a pointer to storage.

is_slice: bool property #

Example

function foo(bytes calldata b) public pure {
    b[0:5]; // s[0:5] is a bytes slice
}

Returns:

Type Description
bool

Whether this is a slice of a bytes expression.

FixedBytes class #

Bases: TypeAbc

Fixed-size byte array type.

Source code in wake/ir/types.py
class FixedBytes(TypeAbc):
    """
    Fixed-size byte array type.
    """

    _bytes_count: int

    def __init__(self, bytes_count: int):
        self._bytes_count = bytes_count

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, FixedBytes):
            return False
        return self._bytes_count == other._bytes_count

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> FixedBytes:
        type_identifier.read("t_bytes")
        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None
        number = match.group("number")
        type_identifier.read(number)
        return FixedBytes(int(number))

    @property
    def abi_type(self) -> str:
        return f"bytes{self._bytes_count}"

    @property
    def bytes_count(self) -> int:
        """
        Is at least 1 and at most 32.

        Returns:
            Number of bytes used to represent this fixed-size byte array.
        """
        return self._bytes_count

bytes_count: int property #

Is at least 1 and at most 32.

Returns:

Type Description
int

Number of bytes used to represent this fixed-size byte array.

Function class #

Bases: TypeAbc

Function type.

Warning

Given the following function:

function foo(uint a, uint b) public pure returns(uint, uint) {
    return (a + b, a - b);
}
and the following call:
foo(1, 2);
the type of foo is Function, but the type of foo(1, 2) is Tuple.

Source code in wake/ir/types.py
class Function(TypeAbc):
    """
    Function type.

    !!! warning
        Given the following function:
        ```solidity
        function foo(uint a, uint b) public pure returns(uint, uint) {
            return (a + b, a - b);
        }
        ```
        and the following call:
        ```solidity
        foo(1, 2);
        ```
        the type of `foo` is [Function][wake.ir.types.Function], but the type of `:::solidity foo(1, 2)` is [Tuple][wake.ir.types.Tuple].
    """

    _kind: FunctionTypeKind
    _state_mutability: StateMutability
    _parameters: typ.Tuple[TypeAbc, ...]
    _return_parameters: typ.Tuple[TypeAbc, ...]
    _gas_set: bool
    _value_set: bool
    _salt_set: bool
    _attached_to: typ.Optional[typ.Tuple[TypeAbc, ...]]

    def __init__(
        self,
        kind: FunctionTypeKind,
        state_mutability: StateMutability,
        parameters: typ.Iterable[TypeAbc],
        return_parameters: typ.Iterable[TypeAbc],
        gas_set: bool,
        value_set: bool,
        salt_set: bool,
        attached_to: typ.Optional[typ.Iterable[TypeAbc]],
    ):
        self._kind = kind
        self._state_mutability = state_mutability
        self._parameters = tuple(parameters)
        self._return_parameters = tuple(return_parameters)
        self._gas_set = gas_set
        self._value_set = value_set
        self._salt_set = salt_set
        if attached_to is not None:
            self._attached_to = tuple(attached_to)
        else:
            self._attached_to = None

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Function):
            return False
        return (
            self._kind == other._kind
            and self._state_mutability == other._state_mutability
            and self._parameters == other._parameters
            and self._return_parameters == other._return_parameters
            and self._gas_set == other._gas_set
            and self._value_set == other._value_set
            and self._salt_set == other._salt_set
            and self._attached_to == other._attached_to
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Function:
        type_identifier.read("t_function_")

        matched = []
        for kind in FunctionTypeKind:
            if type_identifier.startswith(kind):
                matched.append(kind)
        assert len(matched) >= 1, f"Unexpected function kind {type_identifier}"
        kind = FunctionTypeKind(max(matched, key=len))
        type_identifier.read(kind)

        if type_identifier.startswith("_payable"):
            state_mutability = StateMutability.PAYABLE
            type_identifier.read("_payable")
        elif type_identifier.startswith("_pure"):
            state_mutability = StateMutability.PURE
            type_identifier.read("_pure")
        elif type_identifier.startswith("_nonpayable"):
            state_mutability = StateMutability.NONPAYABLE
            type_identifier.read("_nonpayable")
        elif type_identifier.startswith("_view"):
            state_mutability = StateMutability.VIEW
            type_identifier.read("_view")
        else:
            assert False, f"Unexpected function state mutability {type_identifier}"

        parameters = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert not any(param is None for param in parameters)

        type_identifier.read("returns")
        return_parameters = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert not any(param is None for param in return_parameters)

        if type_identifier.startswith("gas"):
            gas_set = True
            type_identifier.read("gas")
        else:
            gas_set = False

        if type_identifier.startswith("value"):
            value_set = True
            type_identifier.read("value")
        else:
            value_set = False

        if type_identifier.startswith("salt"):
            salt_set = True
            type_identifier.read("salt")
        else:
            salt_set = False

        if type_identifier.startswith("bound_to"):
            type_identifier.read("bound_to")
            bound_to = _parse_list(type_identifier, reference_resolver, cu_hash)
            assert not any(param is None for param in bound_to)
            attached_to = bound_to
        elif type_identifier.startswith(
            "attached_to"
        ):  # bound_to was renamed to attached_to in 0.8.18
            type_identifier.read("attached_to")
            attached_to = _parse_list(type_identifier, reference_resolver, cu_hash)
            assert not any(param is None for param in attached_to)
        else:
            attached_to = None

        return Function(
            kind,
            state_mutability,
            parameters,  # pyright: ignore reportArgumentType
            return_parameters,  # pyright: ignore reportArgumentType
            gas_set,
            value_set,
            salt_set,
            attached_to,  # pyright: ignore reportArgumentType
        )

    @property
    def abi_type(self) -> str:
        return "function"

    @property
    def kind(self) -> FunctionTypeKind:
        """
        Returns:
            Kind of the function type.
        """
        return self._kind

    @property
    def state_mutability(self) -> StateMutability:
        """
        Returns:
            State mutability of the function type.
        """
        return self._state_mutability

    @property
    def parameters(self) -> typ.Tuple[TypeAbc, ...]:
        """
        Returns:
            Expression types of the parameters of the function type.
        """
        return self._parameters

    @property
    def return_parameters(self) -> typ.Tuple[TypeAbc, ...]:
        """
        Returns:
            Expression types of the return parameters of the function type.
        """
        return self._return_parameters

    @property
    def gas_set(self) -> bool:
        """
        !!! example
            In the case of the old syntax (deprecated), the `gas` [MemberAccess][wake.ir.expressions.member_access.MemberAccess] expression is of the [Function][wake.ir.types.Function] type which returns a [Function][wake.ir.types.Function] with `gas_set` set to `True`.
            ```solidity
            foo.gas(10)(1, 2);
            ```

            In the case of the new syntax, the `{gas: 10}` [FunctionCallOptions][wake.ir.expressions.function_call_options.FunctionCallOptions] expression is of the [Function][wake.ir.types.Function] type with `gas_set` set to `True`.
            ```solidity
            foo{gas: 10}(1, 2);
            ```

        Returns:
            `True` if the gas is set in the function type.
        """
        return self._gas_set

    @property
    def value_set(self) -> bool:
        """
        !!! example
            In the case of the old syntax (deprecated), the `value` [MemberAccess][wake.ir.expressions.member_access.MemberAccess] expression is of the [Function][wake.ir.types.Function] type which returns a [Function][wake.ir.types.Function] with `value_set` set to `True`.
            ```solidity
            foo.value(1)(1, 2);
            ```

            In the case of the new syntax, the `{value: 1}` [FunctionCallOptions][wake.ir.expressions.function_call_options.FunctionCallOptions] expression is of the [Function][wake.ir.types.Function] type with `value_set` set to `True`.
            ```solidity
            foo{value: 1}(1, 2);
            ```

        Returns:
            `True` if the value is set in the function type.
        """
        return self._value_set

    @property
    def salt_set(self) -> bool:
        """
        !!! example
            In the following example, the `{salt: salt}` [FunctionCallOptions][wake.ir.expressions.function_call_options.FunctionCallOptions] expression is of the [Function][wake.ir.types.Function] type with `salt_set` set to `True`.
            ```solidity
            new Foo{salt: salt}();
            ```

        Returns:
            `True` if the salt is set in the function type.
        """
        return self._salt_set

    @property
    def attached_to(self) -> typ.Optional[typ.Tuple[TypeAbc, ...]]:
        """
        A function type can be attached to a type using the [UsingForDirective][wake.ir.meta.using_for_directive.UsingForDirective] or internally in the case of a Solidity global symbol.
        !!! example
            In the following example, the `add` [MemberAccess][wake.ir.expressions.member_access.MemberAccess] expression on line 9 is of the [Function][wake.ir.types.Function] type and is attached to the [UInt][wake.ir.types.UInt] type.
            ```solidity linenums="1"
            function add(uint a, uint b) pure returns (uint) {
                return a + b;
            }

            using {add} for uint;

            contract Foo {
                function bar(uint x) public pure returns(uint) {
                    return x.add(1);
                }
            }
            ```

            In this example, the `push` [MemberAccess][wake.ir.expressions.member_access.MemberAccess] expression on line 9 is of the [Function][wake.ir.types.Function] type and is attached to the [Array][wake.ir.types.Array] type.
            ```solidity
            arr.push(1);
            ```

        Returns:
            Type to which the function is attached to.
        """
        return self._attached_to

kind: FunctionTypeKind property #

Returns:

Type Description
FunctionTypeKind

Kind of the function type.

state_mutability: StateMutability property #

Returns:

Type Description
StateMutability

State mutability of the function type.

parameters: typ.Tuple[TypeAbc, ...] property #

Returns:

Type Description
Tuple[TypeAbc, ...]

Expression types of the parameters of the function type.

return_parameters: typ.Tuple[TypeAbc, ...] property #

Returns:

Type Description
Tuple[TypeAbc, ...]

Expression types of the return parameters of the function type.

gas_set: bool property #

Example

In the case of the old syntax (deprecated), the gas MemberAccess expression is of the Function type which returns a Function with gas_set set to True.

foo.gas(10)(1, 2);

In the case of the new syntax, the {gas: 10} FunctionCallOptions expression is of the Function type with gas_set set to True.

foo{gas: 10}(1, 2);

Returns:

Type Description
bool

True if the gas is set in the function type.

value_set: bool property #

Example

In the case of the old syntax (deprecated), the value MemberAccess expression is of the Function type which returns a Function with value_set set to True.

foo.value(1)(1, 2);

In the case of the new syntax, the {value: 1} FunctionCallOptions expression is of the Function type with value_set set to True.

foo{value: 1}(1, 2);

Returns:

Type Description
bool

True if the value is set in the function type.

salt_set: bool property #

Example

In the following example, the {salt: salt} FunctionCallOptions expression is of the Function type with salt_set set to True.

new Foo{salt: salt}();

Returns:

Type Description
bool

True if the salt is set in the function type.

attached_to: typ.Optional[typ.Tuple[TypeAbc, ...]] property #

A function type can be attached to a type using the UsingForDirective or internally in the case of a Solidity global symbol.

Example

In the following example, the add MemberAccess expression on line 9 is of the Function type and is attached to the UInt type.

function add(uint a, uint b) pure returns (uint) {
    return a + b;
}

using {add} for uint;

contract Foo {
    function bar(uint x) public pure returns(uint) {
        return x.add(1);
    }
}

In this example, the push MemberAccess expression on line 9 is of the Function type and is attached to the Array type.

arr.push(1);

Returns:

Type Description
Optional[Tuple[TypeAbc, ...]]

Type to which the function is attached to.

Tuple class #

Bases: TypeAbc

Tuple type.

Source code in wake/ir/types.py
class Tuple(TypeAbc):
    """
    Tuple type.
    """

    _components: typ.Tuple[typ.Optional[TypeAbc], ...]

    def __init__(self, components: typ.Iterable[typ.Optional[TypeAbc]]):
        self._components = tuple(components)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Tuple):
            return False
        return self._components == other._components

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Tuple:
        type_identifier.read("t_tuple")
        components = _parse_list(type_identifier, reference_resolver, cu_hash)
        return Tuple(components)

    @property
    def abi_type(self) -> str:
        if any(component is None for component in self._components):
            raise NotImplementedError
        return (
            "("
            + ",".join(
                component.abi_type  # pyright: ignore reportOptionalMemberAccess
                for component in self._components
            )
            + ")"
        )

    @property
    def components(self) -> typ.Tuple[typ.Optional[TypeAbc], ...]:
        """
        A component type can be `None` in the case of a tuple with a missing component.
        !!! example
            In the following example, the `(success, )` expression is of the [Tuple][wake.ir.types.Tuple] type with the components of the type [Bool][wake.ir.types.Bool] and `None`.
            ```solidity
            bool success;
            (success, ) = address(addr).call{value: 1}("");
            ```

        Returns:
            Expression types of the components of the tuple type.
        """
        return self._components

components: typ.Tuple[typ.Optional[TypeAbc], ...] property #

A component type can be None in the case of a tuple with a missing component.

Example

In the following example, the (success, ) expression is of the Tuple type with the components of the type Bool and None.

bool success;
(success, ) = address(addr).call{value: 1}("");

Returns:

Type Description
Tuple[Optional[TypeAbc], ...]

Expression types of the components of the tuple type.

Type class #

Bases: TypeAbc

Type type. As opposed to other types, this type describes the type of a type, not the type of an instance of a type.

Source code in wake/ir/types.py
class Type(TypeAbc):
    """
    Type type. As opposed to other types, this type describes the type of a type, not the type of an instance of a type.
    """

    _actual_type: TypeAbc

    def __init__(self, actual_type: TypeAbc):
        self._actual_type = actual_type

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Type):
            return False
        return self._actual_type == other._actual_type

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Type:
        type_identifier.read("t_type")
        actual_type = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert len(actual_type) == 1 and actual_type[0] is not None
        return Type(actual_type[0])

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def actual_type(self) -> TypeAbc:
        """
        !!! example
            `payable` in the following example is of the [Type][wake.ir.types.Type] type with the [Address][wake.ir.types.Address] actual type.
            ```solidity
            payable(addr);
            ```

            `super` in the following example is of the [Type][wake.ir.types.Type] type with the [Contract][wake.ir.types.Contract] actual type.
            ```solidity
            super.foo();
            ```

            `string` in the following example is of the [Type][wake.ir.types.Type] type with the [String][wake.ir.types.String] actual type.
            ```solidity
            string.concat("foo", "bar");
            ```

            `Foo` in the following example on line 4 is of the [Type][wake.ir.types.Type] type with the [Enum][wake.ir.types.Enum] actual type.
            ```solidity linenums="1"
            enum Foo { A, B }

            function bar() pure returns (Foo) {
                return Foo.A;
            }
            ```

        Returns:
            Actual type of the type type.
        """
        return self._actual_type

actual_type: TypeAbc property #

Example

payable in the following example is of the Type type with the Address actual type.

payable(addr);

super in the following example is of the Type type with the Contract actual type.

super.foo();

string in the following example is of the Type type with the String actual type.

string.concat("foo", "bar");

Foo in the following example on line 4 is of the Type type with the Enum actual type.

1
2
3
4
5
enum Foo { A, B }

function bar() pure returns (Foo) {
    return Foo.A;
}

Returns:

Type Description
TypeAbc

Actual type of the type type.

Rational class #

Bases: TypeAbc

Rational type. Represents the type of constants or expressions with constants.

Example

1, 0x1234, 1e18, 1 * 2 / 3 are all of the Rational type.

Source code in wake/ir/types.py
class Rational(TypeAbc):
    """
    Rational type. Represents the type of constants or expressions with constants.

    !!! example
        `:::solidity 1`, `:::solidity 0x1234`, `:::solidity 1e18`, `:::solidity 1 * 2 / 3` are all of the [Rational][wake.ir.types.Rational] type.
    """

    _numerator: int
    _denominator: int

    def __init__(self, numerator: int, denominator: int):
        self._numerator = numerator
        self._denominator = denominator

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Rational):
            return False
        return (
            self._numerator == other._numerator
            and self._denominator == other._denominator
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Rational:
        type_identifier.read("t_rational_")

        if type_identifier.startswith("minus_"):
            type_identifier.read("minus_")
            numerator = -1
        else:
            numerator = 1

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid rational"
        number = match.group("number")
        type_identifier.read(number)
        numerator *= int(number)

        type_identifier.read("_by_")

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid rational"
        number = match.group("number")
        type_identifier.read(number)
        denominator = int(number)
        return Rational(numerator, denominator)

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def numerator(self) -> int:
        """
        If the rational is negative, the numerator will be negative.

        Returns:
            Numerator of the rational number.
        """
        return self._numerator

    @property
    def denominator(self) -> int:
        """
        Returns:
            Denominator of the rational number.
        """
        return self._denominator

numerator: int property #

If the rational is negative, the numerator will be negative.

Returns:

Type Description
int

Numerator of the rational number.

denominator: int property #

Returns:

Type Description
int

Denominator of the rational number.

Modifier class #

Bases: TypeAbc

Modifier type.

Source code in wake/ir/types.py
class Modifier(TypeAbc):
    """
    Modifier type.
    """

    _parameters: typ.Tuple[TypeAbc, ...]

    def __init__(self, parameters: typ.Iterable[TypeAbc]):
        self._parameters = tuple(parameters)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Modifier):
            return False
        return self._parameters == other._parameters

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Modifier:
        type_identifier.read("t_modifier")
        parameters = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert not any(param is None for param in parameters)
        return Modifier(parameters)  # pyright: ignore reportArgumentType

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def parameters(self) -> typ.Tuple[TypeAbc, ...]:
        """
        Returns:
            Expression types of the parameters of the modifier.
        """
        return self._parameters

parameters: typ.Tuple[TypeAbc, ...] property #

Returns:

Type Description
Tuple[TypeAbc, ...]

Expression types of the parameters of the modifier.

Array class #

Bases: TypeAbc

Array type.

Source code in wake/ir/types.py
class Array(TypeAbc):
    """
    Array type.
    """

    _base_type: TypeAbc
    _length: typ.Optional[int]
    _data_location: DataLocation
    _is_pointer: bool
    _is_slice: bool

    def __init__(
        self,
        base_type: TypeAbc,
        length: typ.Optional[int],
        data_location: DataLocation,
        is_pointer: bool,
        is_slice: bool,
    ):
        self._base_type = base_type
        self._length = length
        self._data_location = data_location
        self._is_pointer = is_pointer
        self._is_slice = is_slice

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Array):
            return False
        return (
            self._base_type == other._base_type
            and self._length == other._length
            and self._data_location == other._data_location
            and self._is_pointer == other._is_pointer
            and self._is_slice == other._is_slice
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Array:
        type_identifier.read("t_array")
        base_type = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert (
            len(base_type) == 1 and base_type[0] is not None
        ), f"Unexpected array base type {type_identifier}"
        base_type = base_type[0]

        if type_identifier.startswith("dyn"):
            length = None
            type_identifier.read("dyn")
        else:
            match = NUMBER_RE.match(type_identifier.data)
            assert match is not None, f"{type_identifier} is not a valid array length"
            length = int(match.group("number"))
            type_identifier.read(match.group("number"))

        if type_identifier.startswith("_storage"):
            data_location = DataLocation.STORAGE
            type_identifier.read("_storage")
        elif type_identifier.startswith("_memory"):
            data_location = DataLocation.MEMORY
            type_identifier.read("_memory")
        elif type_identifier.startswith("_calldata"):
            data_location = DataLocation.CALLDATA
            type_identifier.read("_calldata")
        else:
            assert False, f"Unexpected array type data location {type_identifier}"

        if type_identifier.startswith("_ptr"):
            is_pointer = True
            type_identifier.read("_ptr")
        else:
            is_pointer = False

        if type_identifier.startswith("_slice"):
            is_slice = True
            type_identifier.read("_slice")
        else:
            is_slice = False

        return Array(base_type, length, data_location, is_pointer, is_slice)

    @property
    def abi_type(self) -> str:
        if self.length is not None:
            return f"{self.base_type.abi_type}[{self.length}]"
        else:
            return f"{self.base_type.abi_type}[]"

    @property
    def base_type(self) -> TypeAbc:
        """
        Returns:
            Base type of the array.
        """
        return self._base_type

    @property
    def length(self) -> typ.Optional[int]:
        """
        Returns:
            Length of the array. `None` if the array is dynamic (not fixed size).
        """
        return self._length

    @property
    def data_location(self) -> DataLocation:
        """
        Returns:
            Data location of the array.
        """
        return self._data_location

    @property
    def is_pointer(self) -> bool:
        """
        Storage references can be pointers or bound references. In general, local variables are of
        pointer type, state variables are bound references. Assignments to pointers or deleting
        them will not modify storage (that will only change the pointer). Assignment from
        non-storage objects to a variable of storage pointer type is not possible.

        For anything other than [STORAGE][wake.ir.enums.DataLocation.STORAGE], this always returns `True` because assignments
        never change the contents of the original value.

        Returns:
            Whether the array expression is a pointer to storage.
        """
        return self._is_pointer

    @property
    def is_slice(self) -> bool:
        """
        !!! example
            ```solidity
            function foo(uint[] calldata arr) public pure {
                arr[0:5]; // arr[0:5] is an array slice
            }
            ```

        Returns:
            Whether this is a slice of an array expression.
        """
        return self._is_slice

base_type: TypeAbc property #

Returns:

Type Description
TypeAbc

Base type of the array.

length: typ.Optional[int] property #

Returns:

Type Description
Optional[int]

Length of the array. None if the array is dynamic (not fixed size).

data_location: DataLocation property #

Returns:

Type Description
DataLocation

Data location of the array.

is_pointer: bool property #

Storage references can be pointers or bound references. In general, local variables are of pointer type, state variables are bound references. Assignments to pointers or deleting them will not modify storage (that will only change the pointer). Assignment from non-storage objects to a variable of storage pointer type is not possible.

For anything other than STORAGE, this always returns True because assignments never change the contents of the original value.

Returns:

Type Description
bool

Whether the array expression is a pointer to storage.

is_slice: bool property #

Example

function foo(uint[] calldata arr) public pure {
    arr[0:5]; // arr[0:5] is an array slice
}

Returns:

Type Description
bool

Whether this is a slice of an array expression.

Mapping class #

Bases: TypeAbc

Mapping type.

Source code in wake/ir/types.py
class Mapping(TypeAbc):
    """
    Mapping type.
    """

    _key_type: TypeAbc
    _value_type: TypeAbc

    def __init__(self, key_type: TypeAbc, value_type: TypeAbc):
        self._key_type = key_type
        self._value_type = value_type

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Mapping):
            return False
        return (
            self._key_type == other._key_type and self._value_type == other._value_type
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Mapping:
        type_identifier.read("t_mapping")
        key_value = _parse_list(type_identifier, reference_resolver, cu_hash)
        assert len(key_value) == 2, f"{type_identifier} is not a valid mapping"
        assert key_value[0] is not None, f"{type_identifier} is not a valid mapping"
        assert key_value[1] is not None, f"{type_identifier} is not a valid mapping"
        return Mapping(key_value[0], key_value[1])

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def key_type(self) -> TypeAbc:
        """
        Returns:
            Key type of the mapping.
        """
        return self._key_type

    @property
    def value_type(self) -> TypeAbc:
        """
        Returns:
            Value type of the mapping.
        """
        return self._value_type

key_type: TypeAbc property #

Returns:

Type Description
TypeAbc

Key type of the mapping.

value_type: TypeAbc property #

Returns:

Type Description
TypeAbc

Value type of the mapping.

Contract class #

Bases: TypeAbc

Source code in wake/ir/types.py
class Contract(TypeAbc):
    _is_super: bool
    _name: str
    _ast_id: AstNodeId
    _reference_resolver: ReferenceResolver
    _cu_hash: bytes

    def __init__(
        self,
        is_super: bool,
        name: str,
        ast_id: AstNodeId,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ):
        self._is_super = is_super
        self._name = name
        self._ast_id = ast_id
        self._reference_resolver = reference_resolver
        self._cu_hash = cu_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Contract):
            return False
        return (
            self._is_super == other._is_super
            and self._name == other._name
            and self.ir_node == other.ir_node
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Contract:
        if type_identifier.startswith("t_contract"):
            is_super = False
            type_identifier.read("t_contract")
        elif type_identifier.startswith("t_super"):
            is_super = True
            type_identifier.read("t_super")
        else:
            assert False, f"Unexpected contract type {type_identifier}"
        name = _parse_user_identifier(type_identifier)

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid contract"
        ast_id = AstNodeId(int(match.group("number")))
        type_identifier.read(match.group("number"))

        return Contract(is_super, name, ast_id, reference_resolver, cu_hash)

    @property
    def abi_type(self) -> str:
        return "address"

    @property
    def is_super(self) -> bool:
        """
        !!! warning
            Until 0.7.6, the `super` keyword ([Identifier][wake.ir.expressions.identifier.Identifier]) was of the [Contract][wake.ir.types.Contract] type with `is_super` set to `True`.
            Since 0.8.0, the `super` keyword is of the [Type][wake.ir.types.Type] type with [Contract][wake.ir.types.Contract] as the `actual_type` and `is_super` set to `True`.

        !!! warning
            When this is `True`, the `name` and `ir_node` properties refer to the current contract, not the base contract.

        !!! example
            The `name` and `ir_node` properties of the [Contract][wake.ir.types.Contract] type of the `super` expression in the following example refer to the `Foo` contract, not the `Bar` contract.
            ```solidity
            contract Foo is Bar {
                function foo() public {
                    super.foo();
                }
            }
            ```

        Returns:
            `True` if the expression is the `super` keyword.
        """
        return self._is_super

    @property
    def name(self) -> str:
        """
        Returns:
            Name of the contract.
        """
        return self._name

    @property
    def ir_node(self) -> ContractDefinition:
        """
        Returns:
            Contract definition IR node.
        """
        from wake.ir.declarations.contract_definition import ContractDefinition

        node = self._reference_resolver.resolve_node(self._ast_id, self._cu_hash)
        assert isinstance(node, ContractDefinition)
        return node

is_super: bool property #

Warning

Until 0.7.6, the super keyword (Identifier) was of the Contract type with is_super set to True. Since 0.8.0, the super keyword is of the Type type with Contract as the actual_type and is_super set to True.

Warning

When this is True, the name and ir_node properties refer to the current contract, not the base contract.

Example

The name and ir_node properties of the Contract type of the super expression in the following example refer to the Foo contract, not the Bar contract.

contract Foo is Bar {
    function foo() public {
        super.foo();
    }
}

Returns:

Type Description
bool

True if the expression is the super keyword.

name: str property #

Returns:

Type Description
str

Name of the contract.

ir_node: ContractDefinition property #

Returns:

Type Description
ContractDefinition

Contract definition IR node.

Struct class #

Bases: TypeAbc

Struct type.

Source code in wake/ir/types.py
class Struct(TypeAbc):
    """
    Struct type.
    """

    _name: str
    _ast_id: AstNodeId
    _data_location: DataLocation
    _is_pointer: bool
    _reference_resolver: ReferenceResolver
    _cu_hash: bytes

    def __init__(
        self,
        name: str,
        data_location: DataLocation,
        is_pointer: bool,
        ast_id: AstNodeId,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ):
        self._name = name
        self._data_location = data_location
        self._is_pointer = is_pointer
        self._ast_id = ast_id
        self._reference_resolver = reference_resolver
        self._cu_hash = cu_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Struct):
            return False
        return (
            self._name == other._name
            and self._data_location == other._data_location
            and self._is_pointer == other._is_pointer
            and self.ir_node == other.ir_node
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Struct:
        type_identifier.read("t_struct")
        name = _parse_user_identifier(type_identifier)

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid struct"
        ast_id = AstNodeId(int(match.group("number")))
        type_identifier.read(match.group("number"))

        if type_identifier.startswith("_storage"):
            data_location = DataLocation.STORAGE
            type_identifier.read("_storage")
        elif type_identifier.startswith("_memory"):
            data_location = DataLocation.MEMORY
            type_identifier.read("_memory")
        elif type_identifier.startswith("_calldata"):
            data_location = DataLocation.CALLDATA
            type_identifier.read("_calldata")
        else:
            assert False, f"Unexpected array type data location {type_identifier}"

        if type_identifier.startswith("_ptr"):
            is_pointer = True
            type_identifier.read("_ptr")
        else:
            is_pointer = False

        return Struct(
            name, data_location, is_pointer, ast_id, reference_resolver, cu_hash
        )

    @property
    def abi_type(self) -> str:
        return (
            "("
            + ",".join([member.type.abi_type for member in self.ir_node.members])
            + ")"
        )

    @property
    def name(self) -> str:
        """
        Returns:
            Name of the struct.
        """
        return self._name

    @property
    def data_location(self) -> DataLocation:
        """
        Returns:
            Data location of the struct.
        """
        return self._data_location

    @property
    def is_pointer(self) -> bool:
        """
        Storage references can be pointers or bound references. In general, local variables are of
        pointer type, state variables are bound references. Assignments to pointers or deleting
        them will not modify storage (that will only change the pointer). Assignment from
        non-storage objects to a variable of storage pointer type is not possible.

        For anything other than [STORAGE][wake.ir.enums.DataLocation.STORAGE], this always returns `True` because assignments
        never change the contents of the original value.

        Returns:
            Whether the struct expression is a pointer to storage.
        """
        return self._is_pointer

    @property
    def ir_node(self) -> StructDefinition:
        """
        Returns:
            Struct definition IR node.
        """
        from wake.ir.declarations.struct_definition import StructDefinition

        node = self._reference_resolver.resolve_node(self._ast_id, self._cu_hash)
        assert isinstance(node, StructDefinition)
        return node

name: str property #

Returns:

Type Description
str

Name of the struct.

data_location: DataLocation property #

Returns:

Type Description
DataLocation

Data location of the struct.

is_pointer: bool property #

Storage references can be pointers or bound references. In general, local variables are of pointer type, state variables are bound references. Assignments to pointers or deleting them will not modify storage (that will only change the pointer). Assignment from non-storage objects to a variable of storage pointer type is not possible.

For anything other than STORAGE, this always returns True because assignments never change the contents of the original value.

Returns:

Type Description
bool

Whether the struct expression is a pointer to storage.

ir_node: StructDefinition property #

Returns:

Type Description
StructDefinition

Struct definition IR node.

Enum class #

Bases: TypeAbc

Enum type.

Warning

Enum values are of the Enum type and enums are of the Type type with Enum as the actual_type.

Source code in wake/ir/types.py
class Enum(TypeAbc):
    """
    Enum type.

    !!! warning
        Enum values are of the [Enum][wake.ir.types.Enum] type and enums are of the [Type][wake.ir.types.Type] type with [Enum][wake.ir.types.Enum] as the [actual_type][wake.ir.types.Type.actual_type].
    """

    _name: str
    _ast_id: AstNodeId
    _reference_resolver: ReferenceResolver
    _cu_hash: bytes

    def __init__(
        self,
        name: str,
        ast_id: AstNodeId,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ):
        self._name = name
        self._ast_id = ast_id
        self._reference_resolver = reference_resolver
        self._cu_hash = cu_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Enum):
            return False
        return self._name == other._name and self.ir_node == other.ir_node

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Enum:
        type_identifier.read("t_enum")
        name = _parse_user_identifier(type_identifier)

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid enum"
        ast_id = AstNodeId(int(match.group("number")))
        type_identifier.read(match.group("number"))

        return Enum(name, ast_id, reference_resolver, cu_hash)

    @property
    def abi_type(self) -> str:
        return "uint8"

    @property
    def name(self) -> str:
        """
        Returns:
            Name of the enum.
        """
        return self._name

    @property
    def ir_node(self) -> EnumDefinition:
        """
        Returns:
            Enum definition IR node.
        """
        from wake.ir.declarations.enum_definition import EnumDefinition

        node = self._reference_resolver.resolve_node(self._ast_id, self._cu_hash)
        assert isinstance(node, EnumDefinition)
        return node

name: str property #

Returns:

Type Description
str

Name of the enum.

ir_node: EnumDefinition property #

Returns:

Type Description
EnumDefinition

Enum definition IR node.

Magic class #

Bases: TypeAbc

Magic type represents Solidity language built-in objects.

Source code in wake/ir/types.py
class Magic(TypeAbc):
    """
    Magic type represents Solidity language built-in objects.
    """

    _kind: MagicTypeKind
    _meta_argument_type: typ.Optional[TypeAbc]

    def __init__(self, kind: MagicTypeKind, meta_argument_type: typ.Optional[TypeAbc]):
        self._kind = kind
        self._meta_argument_type = meta_argument_type

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Magic):
            return False
        return (
            self._kind == other._kind
            and self._meta_argument_type == other._meta_argument_type
        )

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Magic:
        type_identifier.read("t_magic_")

        for magic_kind in MagicTypeKind:
            if type_identifier.startswith(magic_kind):
                kind = MagicTypeKind(magic_kind)
                type_identifier.read(magic_kind)

                if kind == MagicTypeKind.META_TYPE:
                    type_identifier.read("_")
                    meta_argument_type = TypeAbc.from_type_identifier(
                        type_identifier, reference_resolver, cu_hash
                    )
                    assert meta_argument_type is not None
                    meta_argument_type = meta_argument_type
                else:
                    meta_argument_type = None

                return Magic(kind, meta_argument_type)

        assert False, f"Unexpected magic type {type_identifier}"

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def kind(self) -> MagicTypeKind:
        """
        Returns:
            Kind of the magic type.
        """
        return self._kind

    @property
    def meta_argument_type(self) -> typ.Optional[TypeAbc]:
        """
        Is only set for [MagicTypeKind.META_TYPE][wake.ir.enums.MagicTypeKind.META_TYPE] kind.
        !!! example
            [Contract][wake.ir.types.Contract] in `:::solidity type(IERC20)`, [UInt][wake.ir.types.UInt] in `:::solidity type(uint)`.

        Returns:
            Type of the meta expression argument.
        """
        return self._meta_argument_type

kind: MagicTypeKind property #

Returns:

Type Description
MagicTypeKind

Kind of the magic type.

meta_argument_type: typ.Optional[TypeAbc] property #

Is only set for MagicTypeKind.META_TYPE kind.

Example

Contract in type(IERC20), UInt in type(uint).

Returns:

Type Description
Optional[TypeAbc]

Type of the meta expression argument.

UserDefinedValueType class #

Bases: TypeAbc

Source code in wake/ir/types.py
class UserDefinedValueType(TypeAbc):
    _name: str
    _ast_id: AstNodeId
    _reference_resolver: ReferenceResolver
    _cu_hash: bytes

    def __init__(
        self,
        name: str,
        ast_id: AstNodeId,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ):
        self._name = name
        self._ast_id = ast_id
        self._reference_resolver = reference_resolver
        self._cu_hash = cu_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UserDefinedValueType):
            return False
        return self._name == other._name and self.ir_node == other.ir_node

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> UserDefinedValueType:
        type_identifier.read("t_userDefinedValueType")
        name = _parse_user_identifier(type_identifier)

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid enum"
        ast_id = AstNodeId(int(match.group("number")))
        type_identifier.read(match.group("number"))

        return UserDefinedValueType(name, ast_id, reference_resolver, cu_hash)

    @property
    def abi_type(self) -> str:
        return self.ir_node.underlying_type.type.abi_type

    @property
    def name(self) -> str:
        """
        Returns:
            Name of the user defined value type.
        """
        return self._name

    @property
    def ir_node(self) -> UserDefinedValueTypeDefinition:
        """
        Returns:
            User defined value type definition IR node.
        """
        from wake.ir.declarations.user_defined_value_type_definition import (
            UserDefinedValueTypeDefinition,
        )

        node = self._reference_resolver.resolve_node(self._ast_id, self._cu_hash)
        assert isinstance(node, UserDefinedValueTypeDefinition)
        return node

name: str property #

Returns:

Type Description
str

Name of the user defined value type.

ir_node: UserDefinedValueTypeDefinition property #

Returns:

Type Description
UserDefinedValueTypeDefinition

User defined value type definition IR node.

Module class #

Bases: TypeAbc

Module type.

Note

It is probably currently not possible to create an expression of this type.

Source code in wake/ir/types.py
class Module(TypeAbc):
    """
    Module type.
    !!! note
        It is probably currently not possible to create an expression of this type.
    """

    _source_unit_id: int
    _reference_resolver: ReferenceResolver
    _cu_hash: bytes

    def __init__(
        self, source_unit_id: int, reference_resolver: ReferenceResolver, cu_hash: bytes
    ):
        self._source_unit_id = source_unit_id
        self._reference_resolver = reference_resolver
        self._cu_hash = cu_hash

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Module):
            return False
        return self.file == other.file

    @classmethod
    def from_type_identifier(
        cls,
        type_identifier: StringReader,
        reference_resolver: ReferenceResolver,
        cu_hash: bytes,
    ) -> Module:
        type_identifier.read("t_module_")

        match = NUMBER_RE.match(type_identifier.data)
        assert match is not None, f"{type_identifier} is not a valid module"
        source_unit_id = int(match.group("number"))
        type_identifier.read(match.group("number"))

        return Module(source_unit_id, reference_resolver, cu_hash)

    @property
    def abi_type(self) -> str:
        raise NotImplementedError

    @property
    def file(self) -> Path:
        """
        Returns:
            Path representing the source file (module).
        """
        return self._reference_resolver.resolve_source_file_id(
            self._source_unit_id, self._cu_hash
        )

file: Path property #

Returns:

Type Description
Path

Path representing the source file (module).