web3 7.4.0__py3-none-any.whl → 7.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  2. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  3. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  4. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  5. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  6. web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
  7. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  8. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  9. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  10. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  11. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  12. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  13. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  14. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  15. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  16. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  17. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  18. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  19. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  20. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  21. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  22. web3/_utils/method_formatters.py +108 -1
  23. web3/_utils/module_testing/__init__.py +4 -0
  24. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  25. web3/_utils/rpc_abi.py +3 -0
  26. web3/contract/async_contract.py +45 -2
  27. web3/contract/base_contract.py +248 -63
  28. web3/contract/contract.py +41 -0
  29. web3/contract/utils.py +48 -0
  30. web3/geth.py +59 -0
  31. web3/main.py +4 -0
  32. web3/manager.py +10 -0
  33. web3/types.py +87 -2
  34. {web3-7.4.0.dist-info → web3-7.5.0.dist-info}/METADATA +2 -1
  35. {web3-7.4.0.dist-info → web3-7.5.0.dist-info}/RECORD +38 -37
  36. {web3-7.4.0.dist-info → web3-7.5.0.dist-info}/WHEEL +1 -1
  37. {web3-7.4.0.dist-info → web3-7.5.0.dist-info}/LICENSE +0 -0
  38. {web3-7.4.0.dist-info → web3-7.5.0.dist-info}/top_level.txt +0 -0
@@ -38,6 +38,8 @@ from eth_utils import (
38
38
  get_normalized_abi_inputs,
39
39
  is_list_like,
40
40
  is_text,
41
+ keccak,
42
+ to_bytes,
41
43
  to_tuple,
42
44
  )
43
45
  from hexbytes import (
@@ -66,6 +68,7 @@ from web3._utils.empty import (
66
68
  empty,
67
69
  )
68
70
  from web3._utils.encoding import (
71
+ hexstr_if_str,
69
72
  to_4byte_hex,
70
73
  to_hex,
71
74
  )
@@ -130,8 +133,14 @@ if TYPE_CHECKING:
130
133
  Web3,
131
134
  )
132
135
 
133
- from .async_contract import AsyncContractFunction # noqa: F401
134
- from .contract import ContractFunction # noqa: F401
136
+ from .async_contract import ( # noqa: F401
137
+ AsyncContractEvent,
138
+ AsyncContractFunction,
139
+ )
140
+ from .contract import ( # noqa: F401
141
+ ContractEvent,
142
+ ContractFunction,
143
+ )
135
144
 
136
145
 
137
146
  class BaseContractEvent:
@@ -148,14 +157,18 @@ class BaseContractEvent:
148
157
  contract_abi: ABI = None
149
158
  abi: ABIEvent = None
150
159
 
151
- def __init__(self, *argument_names: Tuple[str]) -> None:
160
+ def __init__(self, *argument_names: Tuple[str], abi: ABIEvent) -> None:
161
+ self.abi = abi
162
+ self.name = type(self).__name__
163
+
152
164
  if argument_names is None:
153
165
  # https://github.com/python/mypy/issues/6283
154
166
  self.argument_names = tuple() # type: ignore
155
167
  else:
156
168
  self.argument_names = argument_names
157
169
 
158
- self.abi = self._get_event_abi()
170
+ def __repr__(self) -> str:
171
+ return f"<Event {abi_to_signature(self.abi)}>"
159
172
 
160
173
  @classmethod
161
174
  def _get_event_abi(cls) -> ABIEvent:
@@ -165,8 +178,9 @@ class BaseContractEvent:
165
178
  def process_receipt(
166
179
  self, txn_receipt: TxReceipt, errors: EventLogErrorFlags = WARN
167
180
  ) -> Iterable[EventData]:
168
- return self._parse_logs(txn_receipt, errors)
181
+ return self._parse_logs(txn_receipt=txn_receipt, errors=errors)
169
182
 
183
+ @combomethod
170
184
  @to_tuple
171
185
  def _parse_logs(
172
186
  self, txn_receipt: TxReceipt, errors: EventLogErrorFlags
@@ -258,8 +272,12 @@ class BaseContractEvent:
258
272
  return event_filter_params
259
273
 
260
274
  @classmethod
261
- def factory(cls, class_name: str, **kwargs: Any) -> PropertyCheckingFactory:
262
- return PropertyCheckingFactory(class_name, (cls,), kwargs)
275
+ def factory(
276
+ cls, class_name: str, **kwargs: Any
277
+ ) -> Union["ContractEvent", "AsyncContractEvent"]:
278
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)(
279
+ abi=kwargs.get("abi")
280
+ )
263
281
 
264
282
  @staticmethod
265
283
  def check_for_forbidden_api_filter_arguments(
@@ -414,7 +432,7 @@ class BaseContractEvents:
414
432
  self,
415
433
  abi: ABI,
416
434
  w3: Union["Web3", "AsyncWeb3"],
417
- contract_event_type: Type["BaseContractEvent"],
435
+ contract_event_type: Union[Type["ContractEvent"], Type["AsyncContractEvent"]],
418
436
  address: Optional[ChecksumAddress] = None,
419
437
  ) -> None:
420
438
  if abi:
@@ -430,6 +448,7 @@ class BaseContractEvents:
430
448
  contract_abi=self.abi,
431
449
  address=address,
432
450
  event_name=event["name"],
451
+ abi=event,
433
452
  ),
434
453
  )
435
454
 
@@ -658,7 +677,9 @@ class BaseContractFunction:
658
677
  def factory(
659
678
  cls, class_name: str, **kwargs: Any
660
679
  ) -> Union["ContractFunction", "AsyncContractFunction"]:
661
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
680
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)(
681
+ abi=kwargs.get("abi")
682
+ )
662
683
 
663
684
 
664
685
  class BaseContractFunctions:
@@ -766,7 +787,7 @@ class BaseContract:
766
787
  ) -> HexStr:
767
788
  """
768
789
  Encodes the arguments using the Ethereum ABI for the contract function
769
- that matches the given name and arguments..
790
+ that matches the given name and arguments.
770
791
 
771
792
  :param data: defaults to function selector
772
793
  """
@@ -786,16 +807,27 @@ class BaseContract:
786
807
 
787
808
  return encode_abi(cls.w3, element_info["abi"], element_info["arguments"], data)
788
809
 
810
+ #
811
+ # Functions API
812
+ #
789
813
  @combomethod
790
814
  def all_functions(
791
815
  self,
792
816
  ) -> "BaseContractFunction":
817
+ """
818
+ Return all functions in the contract.
819
+ """
793
820
  return self.find_functions_by_identifier(
794
821
  self.abi, self.w3, self.address, lambda _: True
795
822
  )
796
823
 
797
824
  @combomethod
798
825
  def get_function_by_signature(self, signature: str) -> "BaseContractFunction":
826
+ """
827
+ Return a distinct function with matching signature.
828
+ Raises a Web3ValueError if the signature is invalid or if there is no match or
829
+ more than one is found.
830
+ """
799
831
  if " " in signature:
800
832
  raise Web3ValueError(
801
833
  "Function signature should not contain any spaces. "
@@ -812,6 +844,11 @@ class BaseContract:
812
844
 
813
845
  @combomethod
814
846
  def find_functions_by_name(self, fn_name: str) -> "BaseContractFunction":
847
+ """
848
+ Return all functions with matching name.
849
+ Raises a Web3ValueError if there is no match or more than one is found.
850
+ """
851
+
815
852
  def callable_check(fn_abi: ABIFunction) -> bool:
816
853
  return fn_abi["name"] == fn_name
817
854
 
@@ -821,6 +858,10 @@ class BaseContract:
821
858
 
822
859
  @combomethod
823
860
  def get_function_by_name(self, fn_name: str) -> "BaseContractFunction":
861
+ """
862
+ Return a distinct function with matching name.
863
+ Raises a Web3ValueError if there is no match or more than one is found.
864
+ """
824
865
  fns = self.find_functions_by_name(fn_name)
825
866
  return self.get_function_by_identifier(fns, "name")
826
867
 
@@ -828,6 +869,11 @@ class BaseContract:
828
869
  def get_function_by_selector(
829
870
  self, selector: Union[bytes, int, HexStr]
830
871
  ) -> "BaseContractFunction":
872
+ """
873
+ Return a distinct function with matching 4byte selector.
874
+ Raises a Web3ValueError if there is no match or more than one is found.
875
+ """
876
+
831
877
  def callable_check(fn_abi: ABIFunction) -> bool:
832
878
  return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(
833
879
  selector
@@ -842,6 +888,9 @@ class BaseContract:
842
888
  def decode_function_input(
843
889
  self, data: HexStr
844
890
  ) -> Tuple["BaseContractFunction", Dict[str, Any]]:
891
+ """
892
+ Return a Tuple of the function selector and decoded arguments.
893
+ """
845
894
  func = self.get_function_by_selector(HexBytes(data)[:4])
846
895
  arguments = decode_transaction_data(
847
896
  func.abi, data, normalizers=BASE_RETURN_NORMALIZERS
@@ -850,6 +899,11 @@ class BaseContract:
850
899
 
851
900
  @combomethod
852
901
  def find_functions_by_args(self, *args: Any) -> "BaseContractFunction":
902
+ """
903
+ Return all functions with matching args, checking each argument can be encoded
904
+ with the type.
905
+ """
906
+
853
907
  def callable_check(fn_abi: ABIFunction) -> bool:
854
908
  return check_if_arguments_can_be_encoded(
855
909
  fn_abi,
@@ -864,77 +918,119 @@ class BaseContract:
864
918
 
865
919
  @combomethod
866
920
  def get_function_by_args(self, *args: Any) -> "BaseContractFunction":
921
+ """
922
+ Return a distinct function with matching args, checking each argument can be
923
+ encoded with the type.
924
+ Raises a Web3ValueError if there is no match or more than one is found.
925
+ """
867
926
  fns = self.find_functions_by_args(*args)
868
927
  return self.get_function_by_identifier(fns, "args")
869
928
 
870
929
  #
871
- # Private Helpers
930
+ # Events API
872
931
  #
873
- _return_data_normalizers: Tuple[Callable[..., Any], ...] = tuple()
932
+ @combomethod
933
+ def all_events(self) -> List["BaseContractEvent"]:
934
+ """
935
+ Return all events in the contract.
936
+ """
937
+ return self.find_events_by_identifier(
938
+ self.abi, self.w3, self.address, lambda _: True
939
+ )
874
940
 
875
- @classmethod
876
- def _prepare_transaction(
877
- cls,
878
- abi_element_identifier: ABIElementIdentifier,
879
- fn_args: Optional[Any] = None,
880
- fn_kwargs: Optional[Any] = None,
881
- transaction: Optional[TxParams] = None,
882
- ) -> TxParams:
883
- return prepare_transaction(
884
- cls.address,
885
- cls.w3,
886
- abi_element_identifier=abi_element_identifier,
887
- contract_abi=cls.abi,
888
- transaction=transaction,
889
- fn_args=fn_args,
890
- fn_kwargs=fn_kwargs,
941
+ @combomethod
942
+ def get_event_by_signature(self, signature: str) -> "BaseContractEvent":
943
+ """
944
+ Return a distinct event with matching signature.
945
+ Raises a Web3ValueError if the signature is invalid or if there is no match or
946
+ more than one is found.
947
+ """
948
+
949
+ def callable_check(event_abi: ABIEvent) -> bool:
950
+ return abi_to_signature(event_abi) == signature.replace(" ", "")
951
+
952
+ events = self.find_events_by_identifier(
953
+ self.abi, self.w3, self.address, callable_check
891
954
  )
955
+ return self.get_event_by_identifier(events, "signature")
892
956
 
893
- @classmethod
894
- def _find_matching_fn_abi(
895
- cls,
896
- fn_identifier: Optional[ABIElementIdentifier] = None,
897
- *args: Sequence[Any],
898
- **kwargs: Dict[str, Any],
899
- ) -> ABIElement:
900
- return get_abi_element(
901
- cls.abi,
902
- fn_identifier,
903
- *args,
904
- abi_codec=cls.w3.codec,
905
- **kwargs,
957
+ @combomethod
958
+ def find_events_by_name(self, event_name: str) -> List["BaseContractEvent"]:
959
+ """
960
+ Return all events with matching name.
961
+ Raises a Web3ValueError if there is no match or more than one is found.
962
+ """
963
+
964
+ def callable_check(fn_abi: ABIFunction) -> bool:
965
+ return fn_abi["name"] == event_name
966
+
967
+ return self.find_events_by_identifier(
968
+ self.abi, self.w3, self.address, callable_check
906
969
  )
907
970
 
908
- @classmethod
909
- def _get_event_abi(
910
- cls,
911
- event_name: Optional[str] = None,
912
- argument_names: Optional[Sequence[str]] = None,
913
- ) -> ABIEvent:
914
- return get_event_abi(
915
- abi=cls.abi, event_name=event_name, argument_names=argument_names
971
+ @combomethod
972
+ def get_event_by_name(self, event_name: str) -> "BaseContractEvent":
973
+ """
974
+ Return a distinct event with matching name.
975
+ Raises a Web3ValueError if there is no match or more than one is found.
976
+ """
977
+ events = self.find_events_by_name(event_name)
978
+ return self.get_event_by_identifier(events, "name")
979
+
980
+ @combomethod
981
+ def find_events_by_selector(
982
+ self, selector: Union[bytes, int, HexStr]
983
+ ) -> List["BaseContractEvent"]:
984
+ """
985
+ Return all events with matching selector.
986
+ Raises a Web3ValueError if there is no match or more than one is found.
987
+ """
988
+
989
+ def callable_check(event_abi: ABIEvent) -> bool:
990
+ return encode_hex(
991
+ keccak(text=abi_to_signature(event_abi).replace(" ", ""))
992
+ ) == encode_hex(hexstr_if_str(to_bytes, selector))
993
+
994
+ return self.find_events_by_identifier(
995
+ self.abi, self.w3, self.address, callable_check
916
996
  )
917
997
 
918
998
  @combomethod
919
- def _encode_constructor_data(
920
- cls, *args: Sequence[Any], **kwargs: Dict[str, Any]
921
- ) -> HexStr:
922
- constructor_abi = find_constructor_abi_element_by_type(cls.abi)
999
+ def get_event_by_selector(
1000
+ self, selector: Union[bytes, int, HexStr]
1001
+ ) -> "BaseContractEvent":
1002
+ """
1003
+ Return a distinct event with matching keccak selector.
1004
+ Raises a Web3ValueError if there is no match or more than one is found.
1005
+ """
1006
+ events = self.find_events_by_selector(selector)
1007
+ return self.get_event_by_identifier(events, "selector")
923
1008
 
924
- if constructor_abi:
925
- arguments = get_normalized_abi_inputs(constructor_abi, *args, **kwargs)
1009
+ @combomethod
1010
+ def find_events_by_topic(self, topic: HexStr) -> List["BaseContractEvent"]:
1011
+ """
1012
+ Return all events with matching topic.
1013
+ Raises a Web3ValueError if there is no match or more than one is found.
1014
+ """
926
1015
 
927
- deploy_data = add_0x_prefix(
928
- encode_abi(cls.w3, constructor_abi, arguments, data=cls.bytecode)
1016
+ def callable_check(event_abi: ABIEvent) -> bool:
1017
+ return (
1018
+ encode_hex(keccak(text=abi_to_signature(event_abi).replace(" ", "")))
1019
+ == topic
929
1020
  )
930
- else:
931
- if args or kwargs:
932
- msg = "Constructor args were provided, but no constructor function was provided." # noqa: E501
933
- raise Web3TypeError(msg)
934
1021
 
935
- deploy_data = to_hex(cls.bytecode)
1022
+ return self.find_events_by_identifier(
1023
+ self.abi, self.w3, self.address, callable_check
1024
+ )
936
1025
 
937
- return deploy_data
1026
+ @combomethod
1027
+ def get_event_by_topic(self, topic: HexStr) -> "BaseContractEvent":
1028
+ """
1029
+ Return a distinct event with matching topic.
1030
+ Raises a Web3ValueError if there is no match or more than one is found.
1031
+ """
1032
+ events = self.find_events_by_topic(topic)
1033
+ return self.get_event_by_identifier(events, "topic")
938
1034
 
939
1035
  @combomethod
940
1036
  def find_functions_by_identifier(
@@ -956,6 +1052,26 @@ class BaseContract:
956
1052
  "This method should be implemented in the inherited class"
957
1053
  )
958
1054
 
1055
+ @combomethod
1056
+ def find_events_by_identifier(
1057
+ cls,
1058
+ contract_abi: ABI,
1059
+ w3: Union["Web3", "AsyncWeb3"],
1060
+ address: ChecksumAddress,
1061
+ callable_check: Callable[..., Any],
1062
+ ) -> List[Any]:
1063
+ raise NotImplementedError(
1064
+ "This method should be implemented in the inherited class"
1065
+ )
1066
+
1067
+ @combomethod
1068
+ def get_event_by_identifier(
1069
+ cls, fns: Sequence["BaseContractEvent"], identifier: str
1070
+ ) -> "BaseContractEvent":
1071
+ raise NotImplementedError(
1072
+ "This method should be implemented in the inherited class"
1073
+ )
1074
+
959
1075
  @staticmethod
960
1076
  def get_fallback_function(
961
1077
  abi: ABI,
@@ -992,6 +1108,75 @@ class BaseContract:
992
1108
 
993
1109
  return cast(function_type, NonExistentReceiveFunction()) # type: ignore
994
1110
 
1111
+ #
1112
+ # Private Helpers
1113
+ #
1114
+ _return_data_normalizers: Tuple[Callable[..., Any], ...] = tuple()
1115
+
1116
+ @classmethod
1117
+ def _prepare_transaction(
1118
+ cls,
1119
+ abi_element_identifier: ABIElementIdentifier,
1120
+ fn_args: Optional[Any] = None,
1121
+ fn_kwargs: Optional[Any] = None,
1122
+ transaction: Optional[TxParams] = None,
1123
+ ) -> TxParams:
1124
+ return prepare_transaction(
1125
+ cls.address,
1126
+ cls.w3,
1127
+ abi_element_identifier=abi_element_identifier,
1128
+ contract_abi=cls.abi,
1129
+ transaction=transaction,
1130
+ fn_args=fn_args,
1131
+ fn_kwargs=fn_kwargs,
1132
+ )
1133
+
1134
+ @classmethod
1135
+ def _find_matching_fn_abi(
1136
+ cls,
1137
+ fn_identifier: Optional[ABIElementIdentifier] = None,
1138
+ *args: Sequence[Any],
1139
+ **kwargs: Dict[str, Any],
1140
+ ) -> ABIElement:
1141
+ return get_abi_element(
1142
+ cls.abi,
1143
+ fn_identifier,
1144
+ *args,
1145
+ abi_codec=cls.w3.codec,
1146
+ **kwargs,
1147
+ )
1148
+
1149
+ @classmethod
1150
+ def _get_event_abi(
1151
+ cls,
1152
+ event_name: Optional[str] = None,
1153
+ argument_names: Optional[Sequence[str]] = None,
1154
+ ) -> ABIEvent:
1155
+ return get_event_abi(
1156
+ abi=cls.abi, event_name=event_name, argument_names=argument_names
1157
+ )
1158
+
1159
+ @combomethod
1160
+ def _encode_constructor_data(
1161
+ cls, *args: Sequence[Any], **kwargs: Dict[str, Any]
1162
+ ) -> HexStr:
1163
+ constructor_abi = find_constructor_abi_element_by_type(cls.abi)
1164
+
1165
+ if constructor_abi:
1166
+ arguments = get_normalized_abi_inputs(constructor_abi, *args, **kwargs)
1167
+
1168
+ deploy_data = add_0x_prefix(
1169
+ encode_abi(cls.w3, constructor_abi, arguments, data=cls.bytecode)
1170
+ )
1171
+ else:
1172
+ if args or kwargs:
1173
+ msg = "Constructor args were provided, but no constructor function was provided." # noqa: E501
1174
+ raise Web3TypeError(msg)
1175
+
1176
+ deploy_data = to_hex(cls.bytecode)
1177
+
1178
+ return deploy_data
1179
+
995
1180
 
996
1181
  class BaseContractCaller:
997
1182
  """
web3/contract/contract.py CHANGED
@@ -14,6 +14,7 @@ from typing import (
14
14
 
15
15
  from eth_typing import (
16
16
  ABI,
17
+ ABIEvent,
17
18
  ChecksumAddress,
18
19
  )
19
20
  from eth_utils import (
@@ -75,7 +76,9 @@ from web3.contract.utils import (
75
76
  build_transaction_for_function,
76
77
  call_contract_function,
77
78
  estimate_gas_for_function,
79
+ find_events_by_identifier,
78
80
  find_functions_by_identifier,
81
+ get_event_by_identifier,
79
82
  get_function_by_identifier,
80
83
  transact_with_contract_function,
81
84
  )
@@ -94,6 +97,9 @@ from web3.types import (
94
97
  StateOverride,
95
98
  TxParams,
96
99
  )
100
+ from web3.utils.abi import (
101
+ get_abi_element,
102
+ )
97
103
 
98
104
  if TYPE_CHECKING:
99
105
  from ens import ENS # noqa: F401
@@ -104,6 +110,17 @@ class ContractEvent(BaseContractEvent):
104
110
  # mypy types
105
111
  w3: "Web3"
106
112
 
113
+ def __call__(self) -> "ContractEvent":
114
+ clone = copy.copy(self)
115
+
116
+ if not self.abi:
117
+ self.abi = cast(
118
+ ABIEvent,
119
+ get_abi_element(self.contract_abi, self.event_name),
120
+ )
121
+
122
+ return clone
123
+
107
124
  @combomethod
108
125
  def get_logs(
109
126
  self,
@@ -238,6 +255,12 @@ class ContractEvent(BaseContractEvent):
238
255
  builder.address = self.address
239
256
  return builder
240
257
 
258
+ @classmethod
259
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)(
261
+ abi=kwargs.get("abi")
262
+ )
263
+
241
264
 
242
265
  class ContractEvents(BaseContractEvents):
243
266
  def __init__(
@@ -562,6 +585,24 @@ class Contract(BaseContract):
562
585
  ) -> "ContractFunction":
563
586
  return get_function_by_identifier(fns, identifier)
564
587
 
588
+ @combomethod
589
+ def find_events_by_identifier(
590
+ cls,
591
+ contract_abi: ABI,
592
+ w3: "Web3",
593
+ address: ChecksumAddress,
594
+ callable_check: Callable[..., Any],
595
+ ) -> List["ContractEvent"]:
596
+ return find_events_by_identifier(
597
+ contract_abi, w3, address, callable_check, ContractEvent
598
+ )
599
+
600
+ @combomethod
601
+ def get_event_by_identifier(
602
+ cls, events: Sequence["ContractEvent"], identifier: str
603
+ ) -> "ContractEvent":
604
+ return get_event_by_identifier(events, identifier)
605
+
565
606
 
566
607
  class ContractCaller(BaseContractCaller):
567
608
  # mypy types
web3/contract/utils.py CHANGED
@@ -63,6 +63,7 @@ from web3.types import (
63
63
  BlockIdentifier,
64
64
  RPCEndpoint,
65
65
  StateOverride,
66
+ TContractEvent,
66
67
  TContractFn,
67
68
  TxParams,
68
69
  )
@@ -338,6 +339,9 @@ def find_functions_by_identifier(
338
339
  callable_check: Callable[..., Any],
339
340
  function_type: Type[TContractFn],
340
341
  ) -> List[TContractFn]:
342
+ """
343
+ Given a contract ABI, return a list of TContractFunction instances.
344
+ """
341
345
  fns_abi = filter_abi_by_type("function", contract_abi)
342
346
  return [
343
347
  function_type.factory(
@@ -356,6 +360,10 @@ def find_functions_by_identifier(
356
360
  def get_function_by_identifier(
357
361
  fns: Sequence[TContractFn], identifier: str
358
362
  ) -> TContractFn:
363
+ """
364
+ Check that the provided list of TContractFunction instances contains one element and
365
+ return it.
366
+ """
359
367
  if len(fns) > 1:
360
368
  raise Web3ValueError(
361
369
  f"Found multiple functions with matching {identifier}. " f"Found: {fns!r}"
@@ -365,6 +373,46 @@ def get_function_by_identifier(
365
373
  return fns[0]
366
374
 
367
375
 
376
+ def find_events_by_identifier(
377
+ contract_abi: ABI,
378
+ w3: Union["Web3", "AsyncWeb3"],
379
+ address: ChecksumAddress,
380
+ callable_check: Callable[..., Any],
381
+ event_type: Type[TContractEvent],
382
+ ) -> List[TContractEvent]:
383
+ """
384
+ Given a contract ABI, return a list of TContractEvent instances.
385
+ """
386
+ event_abis = filter_abi_by_type("event", contract_abi)
387
+ return [
388
+ event_type.factory(
389
+ event_abi["name"],
390
+ w3=w3,
391
+ contract_abi=contract_abi,
392
+ address=address,
393
+ abi=event_abi,
394
+ )
395
+ for event_abi in event_abis
396
+ if callable_check(event_abi)
397
+ ]
398
+
399
+
400
+ def get_event_by_identifier(
401
+ events: Sequence[TContractEvent], identifier: str
402
+ ) -> TContractEvent:
403
+ """
404
+ Check that the provided list of TContractEvent instances contains one element and
405
+ return it.
406
+ """
407
+ if len(events) > 1:
408
+ raise Web3ValueError(
409
+ f"Found multiple events with matching {identifier}. " f"Found: {events!r}"
410
+ )
411
+ elif len(events) == 0:
412
+ raise Web3ValueError(f"Could not find any event with matching {identifier}")
413
+ return events[0]
414
+
415
+
368
416
  # --- async --- #
369
417
 
370
418