web3 7.5.0__py3-none-any.whl → 7.6.1__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.
@@ -49,6 +49,8 @@ from hexbytes import (
49
49
  from web3._utils.abi import (
50
50
  fallback_func_abi_exists,
51
51
  find_constructor_abi_element_by_type,
52
+ get_abi_element_signature,
53
+ get_name_from_abi_element_identifier,
52
54
  is_array_type,
53
55
  receive_func_abi_exists,
54
56
  )
@@ -96,7 +98,6 @@ from web3.exceptions import (
96
98
  InvalidEventABI,
97
99
  LogTopicError,
98
100
  MismatchedABI,
99
- NoABIEventsFound,
100
101
  NoABIFound,
101
102
  NoABIFunctionsFound,
102
103
  Web3AttributeError,
@@ -121,10 +122,10 @@ from web3.types import (
121
122
  TxReceipt,
122
123
  )
123
124
  from web3.utils.abi import (
125
+ _get_any_abi_signature_with_name,
124
126
  check_if_arguments_can_be_encoded,
125
127
  get_abi_element,
126
128
  get_abi_element_info,
127
- get_event_abi,
128
129
  )
129
130
 
130
131
  if TYPE_CHECKING:
@@ -153,26 +154,55 @@ class BaseContractEvent:
153
154
 
154
155
  address: ChecksumAddress = None
155
156
  event_name: str = None
157
+ name: str = None
158
+ abi_element_identifier: ABIElementIdentifier = None
159
+ signature: str = None
156
160
  w3: Union["Web3", "AsyncWeb3"] = None
157
161
  contract_abi: ABI = None
158
162
  abi: ABIEvent = None
163
+ argument_names: Tuple[str, ...] = tuple()
164
+ argument_types: Tuple[str, ...] = tuple()
165
+ args: Any = None
166
+ kwargs: Any = None
159
167
 
160
- def __init__(self, *argument_names: Tuple[str], abi: ABIEvent) -> None:
161
- self.abi = abi
162
- self.name = type(self).__name__
168
+ def __init__(self, *argument_names: str, abi: Optional[ABIEvent] = None) -> None:
169
+ self.abi_element_identifier = type(self).__name__
170
+ self.name = get_name_from_abi_element_identifier(self.abi_element_identifier)
171
+ self.event_name = self.name
163
172
 
164
- if argument_names is None:
165
- # https://github.com/python/mypy/issues/6283
166
- self.argument_names = tuple() # type: ignore
167
- else:
173
+ if abi:
174
+ self.abi = abi
175
+
176
+ self.signature = abi_to_signature(self.abi)
177
+
178
+ if argument_names:
168
179
  self.argument_names = argument_names
169
180
 
181
+ event_inputs = self.abi.get("inputs", [])
182
+ self.argument_names = tuple([input.get("name", None) for input in event_inputs])
183
+ self.argument_types = tuple([input["type"] for input in event_inputs])
184
+
170
185
  def __repr__(self) -> str:
171
- return f"<Event {abi_to_signature(self.abi)}>"
186
+ if self.abi:
187
+ return f"<Event {abi_to_signature(self.abi)}>"
188
+ return f"<Event {get_abi_element_signature(self.abi_element_identifier)}>"
172
189
 
173
- @classmethod
190
+ @combomethod
174
191
  def _get_event_abi(cls) -> ABIEvent:
175
- return get_event_abi(cls.contract_abi, event_name=cls.event_name)
192
+ if cls.abi:
193
+ return cls.abi
194
+
195
+ return cast(
196
+ ABIEvent,
197
+ get_abi_element(
198
+ filter_abi_by_type("event", cls.contract_abi),
199
+ cls.abi_element_identifier,
200
+ abi_codec=cls.w3.codec,
201
+ ),
202
+ )
203
+
204
+ def _set_event_info(self) -> None:
205
+ self.abi = self._get_event_abi()
176
206
 
177
207
  @combomethod
178
208
  def process_receipt(
@@ -275,9 +305,7 @@ class BaseContractEvent:
275
305
  def factory(
276
306
  cls, class_name: str, **kwargs: Any
277
307
  ) -> Union["ContractEvent", "AsyncContractEvent"]:
278
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(
279
- abi=kwargs.get("abi")
280
- )
308
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
281
309
 
282
310
  @staticmethod
283
311
  def check_for_forbidden_api_filter_arguments(
@@ -367,12 +395,10 @@ class BaseContractEvent:
367
395
 
368
396
  _filters = dict(**argument_filters)
369
397
 
370
- event_abi = self._get_event_abi()
371
-
372
- self.check_for_forbidden_api_filter_arguments(event_abi, _filters)
398
+ self.check_for_forbidden_api_filter_arguments(self.abi, _filters)
373
399
 
374
400
  _, event_filter_params = construct_event_filter_params(
375
- self._get_event_abi(),
401
+ self.abi,
376
402
  self.w3.codec,
377
403
  contract_address=self.address,
378
404
  argument_filters=_filters,
@@ -435,48 +461,36 @@ class BaseContractEvents:
435
461
  contract_event_type: Union[Type["ContractEvent"], Type["AsyncContractEvent"]],
436
462
  address: Optional[ChecksumAddress] = None,
437
463
  ) -> None:
438
- if abi:
439
- self.abi = abi
440
- self._events = filter_abi_by_type("event", self.abi)
441
- for event in self._events:
442
- setattr(
443
- self,
444
- event["name"],
445
- contract_event_type.factory(
446
- event["name"],
447
- w3=w3,
448
- contract_abi=self.abi,
449
- address=address,
450
- event_name=event["name"],
451
- abi=event,
452
- ),
453
- )
464
+ self.abi = abi
465
+ self.w3 = w3
466
+ self.address = address
467
+ _events: Sequence[ABIEvent] = None
454
468
 
455
- def __getattr__(self, event_name: str) -> Type["BaseContractEvent"]:
456
- if "_events" not in self.__dict__:
457
- raise NoABIEventsFound(
458
- "The abi for this contract contains no event definitions. ",
459
- "Are you sure you provided the correct contract abi?",
460
- )
461
- elif event_name not in self.__dict__["_events"]:
462
- raise ABIEventNotFound(
463
- f"The event '{event_name}' was not found in this contract's abi. ",
464
- "Are you sure you provided the correct contract abi?",
469
+ if self.abi:
470
+ _events = sorted(
471
+ filter_abi_by_type("event", self.abi),
472
+ key=lambda evt: (evt["name"], len(evt.get("inputs", []))),
465
473
  )
466
- else:
467
- return super().__getattribute__(event_name)
474
+ for event in _events:
475
+ abi_signature = abi_to_signature(event)
476
+ event_factory = contract_event_type.factory(
477
+ abi_signature,
478
+ w3=self.w3,
479
+ contract_abi=self.abi,
480
+ address=self.address,
481
+ abi=event,
482
+ )
468
483
 
469
- def __getitem__(self, event_name: str) -> Type["BaseContractEvent"]:
470
- return getattr(self, event_name)
484
+ # Set event name on instance if it does not already exist
485
+ if event["name"] not in self.__dict__:
486
+ setattr(self, event["name"], event_factory)
471
487
 
472
- def __iter__(self) -> Iterable[Type["BaseContractEvent"]]:
473
- """
474
- Iterate over supported
488
+ # Set underscore prefixed event signature on instance
489
+ # Handles ambiguity in overloaded contract events
490
+ setattr(self, f"_{abi_signature}", event_factory)
475
491
 
476
- :return: Iterable of :class:`ContractEvent`
477
- """
478
- for event in self._events:
479
- yield self[event["name"]]
492
+ if _events:
493
+ self._events = _events
480
494
 
481
495
  def __hasattr__(self, event_name: str) -> bool:
482
496
  try:
@@ -494,52 +508,78 @@ class BaseContractFunction:
494
508
  """
495
509
 
496
510
  address: ChecksumAddress = None
511
+ fn_name: str = None
512
+ name: str = None
513
+ signature: str = None
497
514
  abi_element_identifier: ABIElementIdentifier = None
498
515
  w3: Union["Web3", "AsyncWeb3"] = None
499
516
  contract_abi: ABI = None
500
517
  abi: ABIFunction = None
501
518
  transaction: TxParams = None
502
519
  arguments: Tuple[Any, ...] = None
503
- decode_tuples: Optional[bool] = False
520
+ decode_tuples: Optional[bool] = None
521
+ argument_names: Tuple[str, ...] = tuple()
522
+ argument_types: Tuple[str, ...] = tuple()
504
523
  args: Any = None
505
524
  kwargs: Any = None
506
525
 
507
526
  def __init__(self, abi: Optional[ABIFunction] = None) -> None:
508
- self.abi = abi
509
- self.fn_name = type(self).__name__
527
+ if not self.abi_element_identifier:
528
+ self.abi_element_identifier = type(self).__name__
510
529
 
511
- def _set_function_info(self) -> None:
512
- if not self.abi:
513
- self.abi = cast(
530
+ self.name = get_name_from_abi_element_identifier(self.abi_element_identifier)
531
+ self.fn_name = self.name
532
+
533
+ if abi:
534
+ self.abi = abi
535
+
536
+ self.signature = abi_to_signature(self.abi)
537
+
538
+ event_inputs = self.abi.get("inputs", [])
539
+ self.argument_names = tuple([input.get("name", None) for input in event_inputs])
540
+ self.argument_types = tuple([input["type"] for input in event_inputs])
541
+
542
+ @combomethod
543
+ def _get_abi(cls) -> ABIFunction:
544
+ if not cls.args and not cls.kwargs:
545
+ # If no args or kwargs are provided, get the ABI element by name
546
+ return cast(
514
547
  ABIFunction,
515
548
  get_abi_element(
516
- self.contract_abi,
517
- self.abi_element_identifier,
518
- *self.args,
519
- abi_codec=self.w3.codec,
520
- **self.kwargs,
549
+ cls.contract_abi,
550
+ get_abi_element_signature(cls.abi_element_identifier),
551
+ abi_codec=cls.w3.codec,
521
552
  ),
522
553
  )
523
554
 
524
- if self.abi_element_identifier in [
525
- FallbackFn,
526
- ReceiveFn,
527
- ]:
528
- self.selector = encode_hex(b"")
529
- elif is_text(self.abi_element_identifier):
530
- self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
531
- else:
532
- raise Web3TypeError("Unsupported function identifier")
555
+ return cast(
556
+ ABIFunction,
557
+ get_abi_element(
558
+ cls.contract_abi,
559
+ get_name_from_abi_element_identifier(cls.abi_element_identifier),
560
+ *cls.args,
561
+ abi_codec=cls.w3.codec,
562
+ **cls.kwargs,
563
+ ),
564
+ )
533
565
 
566
+ def _set_function_info(self) -> None:
567
+ self.selector = encode_hex(b"")
534
568
  if self.abi_element_identifier in [
569
+ "fallback",
570
+ "receive",
535
571
  FallbackFn,
536
572
  ReceiveFn,
537
573
  ]:
574
+ self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
538
575
  self.arguments = None
539
- else:
576
+ elif is_text(self.abi_element_identifier):
577
+ self.selector = encode_hex(function_abi_to_4byte_selector(self.abi))
540
578
  self.arguments = get_normalized_abi_inputs(
541
579
  self.abi, *self.args, **self.kwargs
542
580
  )
581
+ else:
582
+ raise Web3TypeError("Unsupported function identifier")
543
583
 
544
584
  def _get_call_txparams(self, transaction: Optional[TxParams] = None) -> TxParams:
545
585
  if transaction is None:
@@ -671,20 +711,20 @@ class BaseContractFunction:
671
711
  if self.arguments is not None:
672
712
  _repr += f" bound to {self.arguments!r}"
673
713
  return _repr + ">"
674
- return f"<Function {self.fn_name}>"
714
+ return f"<Function {get_abi_element_signature(self.abi_element_identifier)}>"
675
715
 
676
716
  @classmethod
677
717
  def factory(
678
718
  cls, class_name: str, **kwargs: Any
679
719
  ) -> Union["ContractFunction", "AsyncContractFunction"]:
680
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(
681
- abi=kwargs.get("abi")
682
- )
720
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
683
721
 
684
722
 
685
723
  class BaseContractFunctions:
686
724
  """Class containing contract function objects"""
687
725
 
726
+ _functions: Sequence[ABIFunction] = None
727
+
688
728
  def __init__(
689
729
  self,
690
730
  abi: ABI,
@@ -698,32 +738,38 @@ class BaseContractFunctions:
698
738
  self.abi = abi
699
739
  self.w3 = w3
700
740
  self.address = address
741
+ _functions: Sequence[ABIFunction] = None
701
742
 
702
743
  if self.abi:
703
- self._functions = filter_abi_by_type("function", self.abi)
704
- for func in self._functions:
705
- setattr(
706
- self,
707
- func["name"],
708
- contract_function_class.factory(
709
- func["name"],
710
- w3=self.w3,
711
- contract_abi=self.abi,
712
- address=self.address,
713
- decode_tuples=decode_tuples,
714
- abi_element_identifier=func["name"],
715
- ),
744
+ # Function with least number of inputs is first
745
+ # This ensures ambiguity will always be deterministic
746
+ # Prefer function without arguments if present, otherwise
747
+ # just use the first available
748
+ _functions = sorted(
749
+ filter_abi_by_type("function", self.abi),
750
+ key=lambda fn: (fn["name"], len(fn.get("inputs", []))),
751
+ )
752
+ for func in _functions:
753
+ abi_signature = abi_to_signature(func)
754
+ function_factory = contract_function_class.factory(
755
+ abi_signature,
756
+ w3=self.w3,
757
+ contract_abi=self.abi,
758
+ address=self.address,
759
+ decode_tuples=decode_tuples,
760
+ abi=func,
716
761
  )
717
762
 
718
- def __iter__(self) -> Iterable["ABIFunction"]:
719
- if not hasattr(self, "_functions") or not self._functions:
720
- return
763
+ # Set function name on instance if it does not already exist
764
+ if func["name"] not in self.__dict__:
765
+ setattr(self, func["name"], function_factory)
721
766
 
722
- for func in self._functions:
723
- yield self[func["name"]]
767
+ # Set function signature on instance
768
+ # Handles ambiguity in overloaded contract functions
769
+ setattr(self, f"_{abi_signature}", function_factory)
724
770
 
725
- def __getitem__(self, function_name: str) -> ABIFunction:
726
- return getattr(self, function_name)
771
+ if _functions:
772
+ self._functions = _functions
727
773
 
728
774
  def __hasattr__(self, function_name: str) -> bool:
729
775
  try:
@@ -813,7 +859,7 @@ class BaseContract:
813
859
  @combomethod
814
860
  def all_functions(
815
861
  self,
816
- ) -> "BaseContractFunction":
862
+ ) -> List["BaseContractFunction"]:
817
863
  """
818
864
  Return all functions in the contract.
819
865
  """
@@ -843,7 +889,7 @@ class BaseContract:
843
889
  return self.get_function_by_identifier(fns, "signature")
844
890
 
845
891
  @combomethod
846
- def find_functions_by_name(self, fn_name: str) -> "BaseContractFunction":
892
+ def find_functions_by_name(self, fn_name: str) -> List["BaseContractFunction"]:
847
893
  """
848
894
  Return all functions with matching name.
849
895
  Raises a Web3ValueError if there is no match or more than one is found.
@@ -1080,12 +1126,14 @@ class BaseContract:
1080
1126
  address: Optional[ChecksumAddress] = None,
1081
1127
  ) -> "BaseContractFunction":
1082
1128
  if abi and fallback_func_abi_exists(abi):
1129
+ fallback_abi = filter_abi_by_type("fallback", abi)[0]
1083
1130
  return function_type.factory(
1084
1131
  "fallback",
1085
1132
  w3=w3,
1086
1133
  contract_abi=abi,
1087
1134
  address=address,
1088
1135
  abi_element_identifier=FallbackFn,
1136
+ abi=fallback_abi,
1089
1137
  )()
1090
1138
 
1091
1139
  return cast(function_type, NonExistentFallbackFunction()) # type: ignore
@@ -1098,12 +1146,14 @@ class BaseContract:
1098
1146
  address: Optional[ChecksumAddress] = None,
1099
1147
  ) -> "BaseContractFunction":
1100
1148
  if abi and receive_func_abi_exists(abi):
1149
+ receive_abi = filter_abi_by_type("receive", abi)[0]
1101
1150
  return function_type.factory(
1102
1151
  "receive",
1103
1152
  w3=w3,
1104
1153
  contract_abi=abi,
1105
1154
  address=address,
1106
1155
  abi_element_identifier=ReceiveFn,
1156
+ abi=receive_abi,
1107
1157
  )()
1108
1158
 
1109
1159
  return cast(function_type, NonExistentReceiveFunction()) # type: ignore
@@ -1138,6 +1188,9 @@ class BaseContract:
1138
1188
  *args: Sequence[Any],
1139
1189
  **kwargs: Dict[str, Any],
1140
1190
  ) -> ABIElement:
1191
+ if not args and not kwargs:
1192
+ fn_identifier = get_abi_element_signature(fn_identifier)
1193
+
1141
1194
  return get_abi_element(
1142
1195
  cls.abi,
1143
1196
  fn_identifier,
@@ -1152,8 +1205,13 @@ class BaseContract:
1152
1205
  event_name: Optional[str] = None,
1153
1206
  argument_names: Optional[Sequence[str]] = None,
1154
1207
  ) -> ABIEvent:
1155
- return get_event_abi(
1156
- abi=cls.abi, event_name=event_name, argument_names=argument_names
1208
+ return cast(
1209
+ ABIEvent,
1210
+ get_abi_element(
1211
+ abi=cls.abi,
1212
+ abi_element_identifier=event_name,
1213
+ argument_names=argument_names,
1214
+ ),
1157
1215
  )
1158
1216
 
1159
1217
  @combomethod
@@ -1219,7 +1277,9 @@ class BaseContractCaller:
1219
1277
 
1220
1278
  def __getattr__(self, function_name: str) -> Any:
1221
1279
  function_names = [
1222
- fn["name"] for fn in self._functions if fn.get("type") == "function"
1280
+ get_name_from_abi_element_identifier(fn["name"])
1281
+ for fn in self._functions
1282
+ if fn.get("type") == "function"
1223
1283
  ]
1224
1284
  if self.abi is None:
1225
1285
  raise NoABIFound(
@@ -1230,7 +1290,7 @@ class BaseContractCaller:
1230
1290
  "The ABI for this contract contains no function definitions. ",
1231
1291
  "Are you sure you provided the correct contract ABI?",
1232
1292
  )
1233
- elif function_name not in function_names:
1293
+ elif get_name_from_abi_element_identifier(function_name) not in function_names:
1234
1294
  functions_available = ", ".join(function_names)
1235
1295
  raise ABIFunctionNotFound(
1236
1296
  f"The function '{function_name}' was not found in this contract's ABI.",
@@ -1239,11 +1299,17 @@ class BaseContractCaller:
1239
1299
  "Did you mean to call one of those functions?",
1240
1300
  )
1241
1301
  else:
1242
- return super().__getattribute__(function_name)
1302
+ function_identifier = function_name
1243
1303
 
1244
- def __hasattr__(self, event_name: str) -> bool:
1304
+ if "(" not in function_name:
1305
+ function_identifier = _get_any_abi_signature_with_name(
1306
+ function_name, self._functions
1307
+ )
1308
+ return super().__getattribute__(function_identifier)
1309
+
1310
+ def __hasattr__(self, function_name: str) -> bool:
1245
1311
  try:
1246
- return event_name in self.__dict__["_events"]
1312
+ return function_name in self.__dict__["_functions"]
1247
1313
  except ABIFunctionNotFound:
1248
1314
  return False
1249
1315