web3 7.4.0__py3-none-any.whl → 7.6.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 (46) hide show
  1. web3/_utils/abi.py +59 -1
  2. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  3. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  4. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  5. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  6. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  7. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  8. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  9. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  10. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  11. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  12. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  13. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  14. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  15. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  16. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  17. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  18. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  19. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  20. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  21. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  22. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  23. web3/_utils/contracts.py +48 -7
  24. web3/_utils/method_formatters.py +109 -1
  25. web3/_utils/module_testing/__init__.py +4 -0
  26. web3/_utils/module_testing/eth_module.py +11 -0
  27. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  28. web3/_utils/rpc_abi.py +4 -0
  29. web3/_utils/validation.py +3 -0
  30. web3/contract/async_contract.py +191 -41
  31. web3/contract/base_contract.py +373 -152
  32. web3/contract/contract.py +182 -34
  33. web3/contract/utils.py +51 -2
  34. web3/eth/async_eth.py +11 -0
  35. web3/eth/eth.py +11 -0
  36. web3/geth.py +59 -0
  37. web3/main.py +4 -0
  38. web3/manager.py +10 -0
  39. web3/providers/eth_tester/defaults.py +1 -0
  40. web3/types.py +87 -2
  41. web3/utils/abi.py +308 -76
  42. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/METADATA +3 -2
  43. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/RECORD +46 -45
  44. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/WHEEL +1 -1
  45. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/LICENSE +0 -0
  46. {web3-7.4.0.dist-info → web3-7.6.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 (
@@ -46,7 +48,10 @@ from hexbytes import (
46
48
 
47
49
  from web3._utils.abi import (
48
50
  fallback_func_abi_exists,
51
+ filter_by_types,
49
52
  find_constructor_abi_element_by_type,
53
+ get_abi_element_signature,
54
+ get_name_from_abi_element_identifier,
50
55
  is_array_type,
51
56
  receive_func_abi_exists,
52
57
  )
@@ -66,6 +71,7 @@ from web3._utils.empty import (
66
71
  empty,
67
72
  )
68
73
  from web3._utils.encoding import (
74
+ hexstr_if_str,
69
75
  to_4byte_hex,
70
76
  to_hex,
71
77
  )
@@ -93,7 +99,6 @@ from web3.exceptions import (
93
99
  InvalidEventABI,
94
100
  LogTopicError,
95
101
  MismatchedABI,
96
- NoABIEventsFound,
97
102
  NoABIFound,
98
103
  NoABIFunctionsFound,
99
104
  Web3AttributeError,
@@ -118,10 +123,10 @@ from web3.types import (
118
123
  TxReceipt,
119
124
  )
120
125
  from web3.utils.abi import (
126
+ _get_any_abi_signature_with_name,
121
127
  check_if_arguments_can_be_encoded,
122
128
  get_abi_element,
123
129
  get_abi_element_info,
124
- get_event_abi,
125
130
  )
126
131
 
127
132
  if TYPE_CHECKING:
@@ -130,8 +135,14 @@ if TYPE_CHECKING:
130
135
  Web3,
131
136
  )
132
137
 
133
- from .async_contract import AsyncContractFunction # noqa: F401
134
- from .contract import ContractFunction # noqa: F401
138
+ from .async_contract import ( # noqa: F401
139
+ AsyncContractEvent,
140
+ AsyncContractFunction,
141
+ )
142
+ from .contract import ( # noqa: F401
143
+ ContractEvent,
144
+ ContractFunction,
145
+ )
135
146
 
136
147
 
137
148
  class BaseContractEvent:
@@ -144,29 +155,56 @@ class BaseContractEvent:
144
155
 
145
156
  address: ChecksumAddress = None
146
157
  event_name: str = None
158
+ abi_element_identifier: ABIElementIdentifier = None
147
159
  w3: Union["Web3", "AsyncWeb3"] = None
148
160
  contract_abi: ABI = None
149
161
  abi: ABIEvent = None
162
+ argument_types: Tuple[str] = None
163
+ args: Any = None
164
+ kwargs: Any = None
150
165
 
151
166
  def __init__(self, *argument_names: Tuple[str]) -> None:
167
+ self.event_name = get_name_from_abi_element_identifier(type(self).__name__)
168
+ self.abi_element_identifier = type(self).__name__
169
+ abi = self._get_event_abi()
170
+ self.name = abi_to_signature(abi)
171
+ self.abi = abi
172
+
152
173
  if argument_names is None:
153
174
  # https://github.com/python/mypy/issues/6283
154
175
  self.argument_names = tuple() # type: ignore
155
176
  else:
156
177
  self.argument_names = argument_names
157
178
 
158
- self.abi = self._get_event_abi()
179
+ def __repr__(self) -> str:
180
+ if self.abi:
181
+ return f"<Event {abi_to_signature(self.abi)}>"
182
+ return f"<Event {get_abi_element_signature(self.abi_element_identifier)}>"
159
183
 
160
- @classmethod
184
+ @combomethod
161
185
  def _get_event_abi(cls) -> ABIEvent:
162
- return get_event_abi(cls.contract_abi, event_name=cls.event_name)
186
+ if cls.abi:
187
+ return cls.abi
188
+
189
+ return cast(
190
+ ABIEvent,
191
+ get_abi_element(
192
+ filter_abi_by_type("event", cls.contract_abi),
193
+ cls.abi_element_identifier,
194
+ abi_codec=cls.w3.codec,
195
+ ),
196
+ )
197
+
198
+ def _set_event_info(self) -> None:
199
+ self.abi = self._get_event_abi()
163
200
 
164
201
  @combomethod
165
202
  def process_receipt(
166
203
  self, txn_receipt: TxReceipt, errors: EventLogErrorFlags = WARN
167
204
  ) -> Iterable[EventData]:
168
- return self._parse_logs(txn_receipt, errors)
205
+ return self._parse_logs(txn_receipt=txn_receipt, errors=errors)
169
206
 
207
+ @combomethod
170
208
  @to_tuple
171
209
  def _parse_logs(
172
210
  self, txn_receipt: TxReceipt, errors: EventLogErrorFlags
@@ -258,8 +296,10 @@ class BaseContractEvent:
258
296
  return event_filter_params
259
297
 
260
298
  @classmethod
261
- def factory(cls, class_name: str, **kwargs: Any) -> PropertyCheckingFactory:
262
- return PropertyCheckingFactory(class_name, (cls,), kwargs)
299
+ def factory(
300
+ cls, class_name: str, **kwargs: Any
301
+ ) -> Union["ContractEvent", "AsyncContractEvent"]:
302
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
263
303
 
264
304
  @staticmethod
265
305
  def check_for_forbidden_api_filter_arguments(
@@ -349,12 +389,10 @@ class BaseContractEvent:
349
389
 
350
390
  _filters = dict(**argument_filters)
351
391
 
352
- event_abi = self._get_event_abi()
353
-
354
- self.check_for_forbidden_api_filter_arguments(event_abi, _filters)
392
+ self.check_for_forbidden_api_filter_arguments(self.abi, _filters)
355
393
 
356
394
  _, event_filter_params = construct_event_filter_params(
357
- self._get_event_abi(),
395
+ self.abi,
358
396
  self.w3.codec,
359
397
  contract_address=self.address,
360
398
  argument_filters=_filters,
@@ -414,50 +452,29 @@ class BaseContractEvents:
414
452
  self,
415
453
  abi: ABI,
416
454
  w3: Union["Web3", "AsyncWeb3"],
417
- contract_event_type: Type["BaseContractEvent"],
455
+ contract_event_type: Union[Type["ContractEvent"], Type["AsyncContractEvent"]],
418
456
  address: Optional[ChecksumAddress] = None,
419
457
  ) -> None:
420
- if abi:
421
- self.abi = abi
422
- self._events = filter_abi_by_type("event", self.abi)
423
- for event in self._events:
424
- setattr(
425
- self,
426
- event["name"],
427
- contract_event_type.factory(
428
- event["name"],
429
- w3=w3,
430
- contract_abi=self.abi,
431
- address=address,
432
- event_name=event["name"],
433
- ),
434
- )
435
-
436
- def __getattr__(self, event_name: str) -> Type["BaseContractEvent"]:
437
- if "_events" not in self.__dict__:
438
- raise NoABIEventsFound(
439
- "The abi for this contract contains no event definitions. ",
440
- "Are you sure you provided the correct contract abi?",
441
- )
442
- elif event_name not in self.__dict__["_events"]:
443
- raise ABIEventNotFound(
444
- f"The event '{event_name}' was not found in this contract's abi. ",
445
- "Are you sure you provided the correct contract abi?",
446
- )
447
- else:
448
- return super().__getattribute__(event_name)
449
-
450
- def __getitem__(self, event_name: str) -> Type["BaseContractEvent"]:
451
- return getattr(self, event_name)
458
+ self.abi = abi
459
+ self.w3 = w3
460
+ self.address = address
461
+ _events: Sequence[ABIEvent] = None
452
462
 
453
- def __iter__(self) -> Iterable[Type["BaseContractEvent"]]:
454
- """
455
- Iterate over supported
463
+ if self.abi:
464
+ _events = filter_abi_by_type("event", abi)
465
+ for event in _events:
466
+ abi_signature = abi_to_signature(event)
467
+ event_factory = contract_event_type.factory(
468
+ abi_signature,
469
+ w3=self.w3,
470
+ contract_abi=self.abi,
471
+ address=self.address,
472
+ event_name=event["name"],
473
+ )
474
+ setattr(self, abi_signature, event_factory)
456
475
 
457
- :return: Iterable of :class:`ContractEvent`
458
- """
459
- for event in self._events:
460
- yield self[event["name"]]
476
+ if _events:
477
+ self._events = _events
461
478
 
462
479
  def __hasattr__(self, event_name: str) -> bool:
463
480
  try:
@@ -475,52 +492,80 @@ class BaseContractFunction:
475
492
  """
476
493
 
477
494
  address: ChecksumAddress = None
495
+ fn_name: str = None
496
+ name: str = None
478
497
  abi_element_identifier: ABIElementIdentifier = None
479
498
  w3: Union["Web3", "AsyncWeb3"] = None
480
499
  contract_abi: ABI = None
481
500
  abi: ABIFunction = None
482
501
  transaction: TxParams = None
483
502
  arguments: Tuple[Any, ...] = None
484
- decode_tuples: Optional[bool] = False
503
+ decode_tuples: Optional[bool] = None
485
504
  args: Any = None
486
505
  kwargs: Any = None
487
506
 
488
507
  def __init__(self, abi: Optional[ABIFunction] = None) -> None:
489
- self.abi = abi
490
- self.fn_name = type(self).__name__
508
+ if not self.abi_element_identifier:
509
+ self.abi_element_identifier = type(self).__name__
510
+
511
+ self.fn_name = get_name_from_abi_element_identifier(self.abi_element_identifier)
512
+ self.abi = cast(
513
+ ABIFunction,
514
+ get_abi_element(
515
+ filter_by_types(
516
+ ["function", "constructor", "fallback", "receive"],
517
+ self.contract_abi,
518
+ ),
519
+ self.abi_element_identifier,
520
+ ),
521
+ )
522
+ self.name = abi_to_signature(self.abi)
491
523
 
492
- def _set_function_info(self) -> None:
493
- if not self.abi:
494
- self.abi = cast(
524
+ @combomethod
525
+ def _get_abi(cls) -> ABIFunction:
526
+ if not cls.args and not cls.kwargs:
527
+ # If no args or kwargs are provided, get the ABI element by name
528
+ return cast(
495
529
  ABIFunction,
496
530
  get_abi_element(
497
- self.contract_abi,
498
- self.abi_element_identifier,
499
- *self.args,
500
- abi_codec=self.w3.codec,
501
- **self.kwargs,
531
+ cls.contract_abi,
532
+ get_abi_element_signature(cls.abi_element_identifier),
533
+ abi_codec=cls.w3.codec,
502
534
  ),
503
535
  )
504
536
 
505
- if self.abi_element_identifier in [
506
- FallbackFn,
507
- ReceiveFn,
508
- ]:
509
- self.selector = encode_hex(b"")
510
- elif is_text(self.abi_element_identifier):
511
- self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
512
- else:
513
- raise Web3TypeError("Unsupported function identifier")
537
+ return cast(
538
+ ABIFunction,
539
+ get_abi_element(
540
+ cls.contract_abi,
541
+ get_name_from_abi_element_identifier(cls.abi_element_identifier),
542
+ *cls.args,
543
+ abi_codec=cls.w3.codec,
544
+ **cls.kwargs,
545
+ ),
546
+ )
514
547
 
548
+ def _set_function_info(self) -> None:
549
+ self.selector = encode_hex(b"")
515
550
  if self.abi_element_identifier in [
551
+ "fallback",
552
+ "receive",
516
553
  FallbackFn,
517
554
  ReceiveFn,
518
555
  ]:
556
+ self.abi = self._get_abi()
557
+
558
+ self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
519
559
  self.arguments = None
520
- else:
560
+ elif is_text(self.abi_element_identifier):
561
+ self.abi = self._get_abi()
562
+
563
+ self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
521
564
  self.arguments = get_normalized_abi_inputs(
522
565
  self.abi, *self.args, **self.kwargs
523
566
  )
567
+ else:
568
+ raise Web3TypeError("Unsupported function identifier")
524
569
 
525
570
  def _get_call_txparams(self, transaction: Optional[TxParams] = None) -> TxParams:
526
571
  if transaction is None:
@@ -652,18 +697,20 @@ class BaseContractFunction:
652
697
  if self.arguments is not None:
653
698
  _repr += f" bound to {self.arguments!r}"
654
699
  return _repr + ">"
655
- return f"<Function {self.fn_name}>"
700
+ return f"<Function {get_abi_element_signature(self.abi_element_identifier)}>"
656
701
 
657
702
  @classmethod
658
703
  def factory(
659
704
  cls, class_name: str, **kwargs: Any
660
705
  ) -> Union["ContractFunction", "AsyncContractFunction"]:
661
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
706
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
662
707
 
663
708
 
664
709
  class BaseContractFunctions:
665
710
  """Class containing contract function objects"""
666
711
 
712
+ _functions: Sequence[ABIFunction] = None
713
+
667
714
  def __init__(
668
715
  self,
669
716
  abi: ABI,
@@ -677,32 +724,26 @@ class BaseContractFunctions:
677
724
  self.abi = abi
678
725
  self.w3 = w3
679
726
  self.address = address
727
+ _functions: Sequence[ABIFunction] = None
680
728
 
681
729
  if self.abi:
682
- self._functions = filter_abi_by_type("function", self.abi)
683
- for func in self._functions:
730
+ _functions = filter_abi_by_type("function", self.abi)
731
+ for func in _functions:
732
+ abi_signature = abi_to_signature(func)
684
733
  setattr(
685
734
  self,
686
- func["name"],
735
+ abi_signature,
687
736
  contract_function_class.factory(
688
- func["name"],
737
+ abi_signature,
689
738
  w3=self.w3,
690
739
  contract_abi=self.abi,
691
740
  address=self.address,
692
741
  decode_tuples=decode_tuples,
693
- abi_element_identifier=func["name"],
694
742
  ),
695
743
  )
696
744
 
697
- def __iter__(self) -> Iterable["ABIFunction"]:
698
- if not hasattr(self, "_functions") or not self._functions:
699
- return
700
-
701
- for func in self._functions:
702
- yield self[func["name"]]
703
-
704
- def __getitem__(self, function_name: str) -> ABIFunction:
705
- return getattr(self, function_name)
745
+ if _functions:
746
+ self._functions = _functions
706
747
 
707
748
  def __hasattr__(self, function_name: str) -> bool:
708
749
  try:
@@ -766,7 +807,7 @@ class BaseContract:
766
807
  ) -> HexStr:
767
808
  """
768
809
  Encodes the arguments using the Ethereum ABI for the contract function
769
- that matches the given name and arguments..
810
+ that matches the given name and arguments.
770
811
 
771
812
  :param data: defaults to function selector
772
813
  """
@@ -786,16 +827,27 @@ class BaseContract:
786
827
 
787
828
  return encode_abi(cls.w3, element_info["abi"], element_info["arguments"], data)
788
829
 
830
+ #
831
+ # Functions API
832
+ #
789
833
  @combomethod
790
834
  def all_functions(
791
835
  self,
792
- ) -> "BaseContractFunction":
836
+ ) -> List["BaseContractFunction"]:
837
+ """
838
+ Return all functions in the contract.
839
+ """
793
840
  return self.find_functions_by_identifier(
794
841
  self.abi, self.w3, self.address, lambda _: True
795
842
  )
796
843
 
797
844
  @combomethod
798
845
  def get_function_by_signature(self, signature: str) -> "BaseContractFunction":
846
+ """
847
+ Return a distinct function with matching signature.
848
+ Raises a Web3ValueError if the signature is invalid or if there is no match or
849
+ more than one is found.
850
+ """
799
851
  if " " in signature:
800
852
  raise Web3ValueError(
801
853
  "Function signature should not contain any spaces. "
@@ -811,7 +863,12 @@ class BaseContract:
811
863
  return self.get_function_by_identifier(fns, "signature")
812
864
 
813
865
  @combomethod
814
- def find_functions_by_name(self, fn_name: str) -> "BaseContractFunction":
866
+ def find_functions_by_name(self, fn_name: str) -> List["BaseContractFunction"]:
867
+ """
868
+ Return all functions with matching name.
869
+ Raises a Web3ValueError if there is no match or more than one is found.
870
+ """
871
+
815
872
  def callable_check(fn_abi: ABIFunction) -> bool:
816
873
  return fn_abi["name"] == fn_name
817
874
 
@@ -821,6 +878,10 @@ class BaseContract:
821
878
 
822
879
  @combomethod
823
880
  def get_function_by_name(self, fn_name: str) -> "BaseContractFunction":
881
+ """
882
+ Return a distinct function with matching name.
883
+ Raises a Web3ValueError if there is no match or more than one is found.
884
+ """
824
885
  fns = self.find_functions_by_name(fn_name)
825
886
  return self.get_function_by_identifier(fns, "name")
826
887
 
@@ -828,6 +889,11 @@ class BaseContract:
828
889
  def get_function_by_selector(
829
890
  self, selector: Union[bytes, int, HexStr]
830
891
  ) -> "BaseContractFunction":
892
+ """
893
+ Return a distinct function with matching 4byte selector.
894
+ Raises a Web3ValueError if there is no match or more than one is found.
895
+ """
896
+
831
897
  def callable_check(fn_abi: ABIFunction) -> bool:
832
898
  return encode_hex(function_abi_to_4byte_selector(fn_abi)) == to_4byte_hex(
833
899
  selector
@@ -842,6 +908,9 @@ class BaseContract:
842
908
  def decode_function_input(
843
909
  self, data: HexStr
844
910
  ) -> Tuple["BaseContractFunction", Dict[str, Any]]:
911
+ """
912
+ Return a Tuple of the function selector and decoded arguments.
913
+ """
845
914
  func = self.get_function_by_selector(HexBytes(data)[:4])
846
915
  arguments = decode_transaction_data(
847
916
  func.abi, data, normalizers=BASE_RETURN_NORMALIZERS
@@ -850,6 +919,11 @@ class BaseContract:
850
919
 
851
920
  @combomethod
852
921
  def find_functions_by_args(self, *args: Any) -> "BaseContractFunction":
922
+ """
923
+ Return all functions with matching args, checking each argument can be encoded
924
+ with the type.
925
+ """
926
+
853
927
  def callable_check(fn_abi: ABIFunction) -> bool:
854
928
  return check_if_arguments_can_be_encoded(
855
929
  fn_abi,
@@ -864,77 +938,119 @@ class BaseContract:
864
938
 
865
939
  @combomethod
866
940
  def get_function_by_args(self, *args: Any) -> "BaseContractFunction":
941
+ """
942
+ Return a distinct function with matching args, checking each argument can be
943
+ encoded with the type.
944
+ Raises a Web3ValueError if there is no match or more than one is found.
945
+ """
867
946
  fns = self.find_functions_by_args(*args)
868
947
  return self.get_function_by_identifier(fns, "args")
869
948
 
870
949
  #
871
- # Private Helpers
950
+ # Events API
872
951
  #
873
- _return_data_normalizers: Tuple[Callable[..., Any], ...] = tuple()
952
+ @combomethod
953
+ def all_events(self) -> List["BaseContractEvent"]:
954
+ """
955
+ Return all events in the contract.
956
+ """
957
+ return self.find_events_by_identifier(
958
+ self.abi, self.w3, self.address, lambda _: True
959
+ )
874
960
 
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,
961
+ @combomethod
962
+ def get_event_by_signature(self, signature: str) -> "BaseContractEvent":
963
+ """
964
+ Return a distinct event with matching signature.
965
+ Raises a Web3ValueError if the signature is invalid or if there is no match or
966
+ more than one is found.
967
+ """
968
+
969
+ def callable_check(event_abi: ABIEvent) -> bool:
970
+ return abi_to_signature(event_abi) == signature.replace(" ", "")
971
+
972
+ events = self.find_events_by_identifier(
973
+ self.abi, self.w3, self.address, callable_check
891
974
  )
975
+ return self.get_event_by_identifier(events, "signature")
892
976
 
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,
977
+ @combomethod
978
+ def find_events_by_name(self, event_name: str) -> List["BaseContractEvent"]:
979
+ """
980
+ Return all events with matching name.
981
+ Raises a Web3ValueError if there is no match or more than one is found.
982
+ """
983
+
984
+ def callable_check(fn_abi: ABIFunction) -> bool:
985
+ return fn_abi["name"] == event_name
986
+
987
+ return self.find_events_by_identifier(
988
+ self.abi, self.w3, self.address, callable_check
906
989
  )
907
990
 
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
991
+ @combomethod
992
+ def get_event_by_name(self, event_name: str) -> "BaseContractEvent":
993
+ """
994
+ Return a distinct event with matching name.
995
+ Raises a Web3ValueError if there is no match or more than one is found.
996
+ """
997
+ events = self.find_events_by_name(event_name)
998
+ return self.get_event_by_identifier(events, "name")
999
+
1000
+ @combomethod
1001
+ def find_events_by_selector(
1002
+ self, selector: Union[bytes, int, HexStr]
1003
+ ) -> List["BaseContractEvent"]:
1004
+ """
1005
+ Return all events with matching selector.
1006
+ Raises a Web3ValueError if there is no match or more than one is found.
1007
+ """
1008
+
1009
+ def callable_check(event_abi: ABIEvent) -> bool:
1010
+ return encode_hex(
1011
+ keccak(text=abi_to_signature(event_abi).replace(" ", ""))
1012
+ ) == encode_hex(hexstr_if_str(to_bytes, selector))
1013
+
1014
+ return self.find_events_by_identifier(
1015
+ self.abi, self.w3, self.address, callable_check
916
1016
  )
917
1017
 
918
1018
  @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)
1019
+ def get_event_by_selector(
1020
+ self, selector: Union[bytes, int, HexStr]
1021
+ ) -> "BaseContractEvent":
1022
+ """
1023
+ Return a distinct event with matching keccak selector.
1024
+ Raises a Web3ValueError if there is no match or more than one is found.
1025
+ """
1026
+ events = self.find_events_by_selector(selector)
1027
+ return self.get_event_by_identifier(events, "selector")
923
1028
 
924
- if constructor_abi:
925
- arguments = get_normalized_abi_inputs(constructor_abi, *args, **kwargs)
1029
+ @combomethod
1030
+ def find_events_by_topic(self, topic: HexStr) -> List["BaseContractEvent"]:
1031
+ """
1032
+ Return all events with matching topic.
1033
+ Raises a Web3ValueError if there is no match or more than one is found.
1034
+ """
926
1035
 
927
- deploy_data = add_0x_prefix(
928
- encode_abi(cls.w3, constructor_abi, arguments, data=cls.bytecode)
1036
+ def callable_check(event_abi: ABIEvent) -> bool:
1037
+ return (
1038
+ encode_hex(keccak(text=abi_to_signature(event_abi).replace(" ", "")))
1039
+ == topic
929
1040
  )
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
1041
 
935
- deploy_data = to_hex(cls.bytecode)
1042
+ return self.find_events_by_identifier(
1043
+ self.abi, self.w3, self.address, callable_check
1044
+ )
936
1045
 
937
- return deploy_data
1046
+ @combomethod
1047
+ def get_event_by_topic(self, topic: HexStr) -> "BaseContractEvent":
1048
+ """
1049
+ Return a distinct event with matching topic.
1050
+ Raises a Web3ValueError if there is no match or more than one is found.
1051
+ """
1052
+ events = self.find_events_by_topic(topic)
1053
+ return self.get_event_by_identifier(events, "topic")
938
1054
 
939
1055
  @combomethod
940
1056
  def find_functions_by_identifier(
@@ -956,6 +1072,26 @@ class BaseContract:
956
1072
  "This method should be implemented in the inherited class"
957
1073
  )
958
1074
 
1075
+ @combomethod
1076
+ def find_events_by_identifier(
1077
+ cls,
1078
+ contract_abi: ABI,
1079
+ w3: Union["Web3", "AsyncWeb3"],
1080
+ address: ChecksumAddress,
1081
+ callable_check: Callable[..., Any],
1082
+ ) -> List[Any]:
1083
+ raise NotImplementedError(
1084
+ "This method should be implemented in the inherited class"
1085
+ )
1086
+
1087
+ @combomethod
1088
+ def get_event_by_identifier(
1089
+ cls, fns: Sequence["BaseContractEvent"], identifier: str
1090
+ ) -> "BaseContractEvent":
1091
+ raise NotImplementedError(
1092
+ "This method should be implemented in the inherited class"
1093
+ )
1094
+
959
1095
  @staticmethod
960
1096
  def get_fallback_function(
961
1097
  abi: ABI,
@@ -992,6 +1128,83 @@ class BaseContract:
992
1128
 
993
1129
  return cast(function_type, NonExistentReceiveFunction()) # type: ignore
994
1130
 
1131
+ #
1132
+ # Private Helpers
1133
+ #
1134
+ _return_data_normalizers: Tuple[Callable[..., Any], ...] = tuple()
1135
+
1136
+ @classmethod
1137
+ def _prepare_transaction(
1138
+ cls,
1139
+ abi_element_identifier: ABIElementIdentifier,
1140
+ fn_args: Optional[Any] = None,
1141
+ fn_kwargs: Optional[Any] = None,
1142
+ transaction: Optional[TxParams] = None,
1143
+ ) -> TxParams:
1144
+ return prepare_transaction(
1145
+ cls.address,
1146
+ cls.w3,
1147
+ abi_element_identifier=abi_element_identifier,
1148
+ contract_abi=cls.abi,
1149
+ transaction=transaction,
1150
+ fn_args=fn_args,
1151
+ fn_kwargs=fn_kwargs,
1152
+ )
1153
+
1154
+ @classmethod
1155
+ def _find_matching_fn_abi(
1156
+ cls,
1157
+ fn_identifier: Optional[ABIElementIdentifier] = None,
1158
+ *args: Sequence[Any],
1159
+ **kwargs: Dict[str, Any],
1160
+ ) -> ABIElement:
1161
+ if not args and not kwargs:
1162
+ fn_identifier = get_abi_element_signature(fn_identifier)
1163
+
1164
+ return get_abi_element(
1165
+ cls.abi,
1166
+ fn_identifier,
1167
+ *args,
1168
+ abi_codec=cls.w3.codec,
1169
+ **kwargs,
1170
+ )
1171
+
1172
+ @classmethod
1173
+ def _get_event_abi(
1174
+ cls,
1175
+ event_name: Optional[str] = None,
1176
+ argument_names: Optional[Sequence[str]] = None,
1177
+ ) -> ABIEvent:
1178
+ return cast(
1179
+ ABIEvent,
1180
+ get_abi_element(
1181
+ abi=cls.abi,
1182
+ abi_element_identifier=event_name,
1183
+ argument_names=argument_names,
1184
+ ),
1185
+ )
1186
+
1187
+ @combomethod
1188
+ def _encode_constructor_data(
1189
+ cls, *args: Sequence[Any], **kwargs: Dict[str, Any]
1190
+ ) -> HexStr:
1191
+ constructor_abi = find_constructor_abi_element_by_type(cls.abi)
1192
+
1193
+ if constructor_abi:
1194
+ arguments = get_normalized_abi_inputs(constructor_abi, *args, **kwargs)
1195
+
1196
+ deploy_data = add_0x_prefix(
1197
+ encode_abi(cls.w3, constructor_abi, arguments, data=cls.bytecode)
1198
+ )
1199
+ else:
1200
+ if args or kwargs:
1201
+ msg = "Constructor args were provided, but no constructor function was provided." # noqa: E501
1202
+ raise Web3TypeError(msg)
1203
+
1204
+ deploy_data = to_hex(cls.bytecode)
1205
+
1206
+ return deploy_data
1207
+
995
1208
 
996
1209
  class BaseContractCaller:
997
1210
  """
@@ -1034,7 +1247,9 @@ class BaseContractCaller:
1034
1247
 
1035
1248
  def __getattr__(self, function_name: str) -> Any:
1036
1249
  function_names = [
1037
- fn["name"] for fn in self._functions if fn.get("type") == "function"
1250
+ get_name_from_abi_element_identifier(fn["name"])
1251
+ for fn in self._functions
1252
+ if fn.get("type") == "function"
1038
1253
  ]
1039
1254
  if self.abi is None:
1040
1255
  raise NoABIFound(
@@ -1045,7 +1260,7 @@ class BaseContractCaller:
1045
1260
  "The ABI for this contract contains no function definitions. ",
1046
1261
  "Are you sure you provided the correct contract ABI?",
1047
1262
  )
1048
- elif function_name not in function_names:
1263
+ elif get_name_from_abi_element_identifier(function_name) not in function_names:
1049
1264
  functions_available = ", ".join(function_names)
1050
1265
  raise ABIFunctionNotFound(
1051
1266
  f"The function '{function_name}' was not found in this contract's ABI.",
@@ -1054,11 +1269,17 @@ class BaseContractCaller:
1054
1269
  "Did you mean to call one of those functions?",
1055
1270
  )
1056
1271
  else:
1057
- return super().__getattribute__(function_name)
1272
+ function_identifier = function_name
1058
1273
 
1059
- def __hasattr__(self, event_name: str) -> bool:
1274
+ if "(" not in function_name:
1275
+ function_identifier = _get_any_abi_signature_with_name(
1276
+ function_name, self._functions
1277
+ )
1278
+ return super().__getattribute__(function_identifier)
1279
+
1280
+ def __hasattr__(self, function_name: str) -> bool:
1060
1281
  try:
1061
- return event_name in self.__dict__["_events"]
1282
+ return function_name in self.__dict__["_functions"]
1062
1283
  except ABIFunctionNotFound:
1063
1284
  return False
1064
1285