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
web3/contract/contract.py CHANGED
@@ -1,4 +1,3 @@
1
- import copy
2
1
  from typing import (
3
2
  TYPE_CHECKING,
4
3
  Any,
@@ -17,8 +16,10 @@ from eth_typing import (
17
16
  ChecksumAddress,
18
17
  )
19
18
  from eth_utils import (
19
+ abi_to_signature,
20
20
  combomethod,
21
21
  get_abi_input_names,
22
+ get_abi_input_types,
22
23
  get_all_function_abis,
23
24
  )
24
25
  from eth_utils.toolz import (
@@ -30,6 +31,9 @@ from hexbytes import (
30
31
 
31
32
  from web3._utils.abi import (
32
33
  fallback_func_abi_exists,
34
+ filter_by_types,
35
+ get_abi_element_signature,
36
+ get_name_from_abi_element_identifier,
33
37
  receive_func_abi_exists,
34
38
  )
35
39
  from web3._utils.abi_element_identifiers import (
@@ -40,6 +44,8 @@ from web3._utils.compat import (
40
44
  Self,
41
45
  )
42
46
  from web3._utils.contracts import (
47
+ copy_contract_event,
48
+ copy_contract_function,
43
49
  parse_block_identifier,
44
50
  )
45
51
  from web3._utils.datatypes import (
@@ -75,12 +81,16 @@ from web3.contract.utils import (
75
81
  build_transaction_for_function,
76
82
  call_contract_function,
77
83
  estimate_gas_for_function,
84
+ find_events_by_identifier,
78
85
  find_functions_by_identifier,
86
+ get_event_by_identifier,
79
87
  get_function_by_identifier,
80
88
  transact_with_contract_function,
81
89
  )
82
90
  from web3.exceptions import (
91
+ ABIEventNotFound,
83
92
  ABIFunctionNotFound,
93
+ NoABIEventsFound,
84
94
  NoABIFound,
85
95
  NoABIFunctionsFound,
86
96
  Web3AttributeError,
@@ -94,6 +104,11 @@ from web3.types import (
94
104
  StateOverride,
95
105
  TxParams,
96
106
  )
107
+ from web3.utils.abi import (
108
+ _get_any_abi_signature_with_name,
109
+ filter_abi_by_type,
110
+ get_abi_element,
111
+ )
97
112
 
98
113
  if TYPE_CHECKING:
99
114
  from ens import ENS # noqa: F401
@@ -104,6 +119,28 @@ class ContractEvent(BaseContractEvent):
104
119
  # mypy types
105
120
  w3: "Web3"
106
121
 
122
+ def __call__(self, *args: Any, **kwargs: Any) -> "ContractEvent":
123
+ event_abi = get_abi_element(
124
+ filter_abi_by_type("event", self.contract_abi),
125
+ self.name,
126
+ *args,
127
+ abi_codec=self.w3.codec,
128
+ **kwargs,
129
+ )
130
+ argument_types = get_abi_input_types(event_abi)
131
+ event_signature = str(
132
+ get_abi_element_signature(self.abi_element_identifier, argument_types)
133
+ )
134
+ contract_event = ContractEvent.factory(
135
+ event_signature,
136
+ w3=self.w3,
137
+ contract_abi=self.contract_abi,
138
+ address=self.address,
139
+ abi_element_identifier=event_signature,
140
+ )
141
+
142
+ return copy_contract_event(contract_event, *args, **kwargs)
143
+
107
144
  @combomethod
108
145
  def get_logs(
109
146
  self,
@@ -169,7 +206,6 @@ class ContractEvent(BaseContractEvent):
169
206
  :yield: Tuple of :class:`AttributeDict` instances
170
207
  """
171
208
  event_abi = self._get_event_abi()
172
-
173
209
  # validate ``argument_filters`` if present
174
210
  if argument_filters is not None:
175
211
  event_arg_names = get_abi_input_names(event_abi)
@@ -211,7 +247,8 @@ class ContractEvent(BaseContractEvent):
211
247
  """
212
248
  Create filter object that tracks logs emitted by this contract event.
213
249
  """
214
- filter_builder = EventFilterBuilder(self._get_event_abi(), self.w3.codec)
250
+ abi = self._get_event_abi()
251
+ filter_builder = EventFilterBuilder(abi, self.w3.codec)
215
252
  self._set_up_filter_builder(
216
253
  argument_filters,
217
254
  from_block,
@@ -221,23 +258,26 @@ class ContractEvent(BaseContractEvent):
221
258
  filter_builder,
222
259
  )
223
260
  log_filter = filter_builder.deploy(self.w3)
224
- log_filter.log_entry_formatter = get_event_data(
225
- self.w3.codec, self._get_event_abi()
226
- )
261
+ log_filter.log_entry_formatter = get_event_data(self.w3.codec, abi)
227
262
  log_filter.builder = filter_builder
228
263
 
229
264
  return log_filter
230
265
 
231
266
  @combomethod
232
267
  def build_filter(self) -> EventFilterBuilder:
268
+ abi = self._get_event_abi()
233
269
  builder = EventFilterBuilder(
234
- self._get_event_abi(),
270
+ abi,
235
271
  self.w3.codec,
236
- formatter=get_event_data(self.w3.codec, self._get_event_abi()),
272
+ formatter=get_event_data(self.w3.codec, abi),
237
273
  )
238
274
  builder.address = self.address
239
275
  return builder
240
276
 
277
+ @classmethod
278
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
279
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
280
+
241
281
 
242
282
  class ContractEvents(BaseContractEvents):
243
283
  def __init__(
@@ -245,28 +285,81 @@ class ContractEvents(BaseContractEvents):
245
285
  ) -> None:
246
286
  super().__init__(abi, w3, ContractEvent, address)
247
287
 
288
+ def __getattr__(self, event_name: str) -> "ContractEvent":
289
+ if super().__getattribute__("abi") is None:
290
+ raise NoABIFound(
291
+ "There is no ABI found for this contract.",
292
+ )
293
+ if "_events" not in self.__dict__ or len(self._events) == 0:
294
+ raise NoABIEventsFound(
295
+ "The abi for this contract contains no event definitions. ",
296
+ "Are you sure you provided the correct contract abi?",
297
+ )
298
+ elif get_name_from_abi_element_identifier(event_name) not in [
299
+ get_name_from_abi_element_identifier(event["name"])
300
+ for event in self._events
301
+ ]:
302
+ raise ABIEventNotFound(
303
+ f"The event '{event_name}' was not found in this contract's abi. ",
304
+ "Are you sure you provided the correct contract abi?",
305
+ )
306
+ else:
307
+ event_abi = get_abi_element(self._events, event_name)
308
+ argument_types = get_abi_input_types(event_abi)
309
+ event_signature = str(get_abi_element_signature(event_name, argument_types))
310
+ return super().__getattribute__(event_signature)
311
+
312
+ def __getitem__(self, event_name: str) -> "ContractEvent":
313
+ return getattr(self, event_name)
314
+
315
+ def __iter__(self) -> Iterable["ContractEvent"]:
316
+ for event in self._events:
317
+ yield self[event["name"]]
318
+
248
319
 
249
320
  class ContractFunction(BaseContractFunction):
250
321
  # mypy types
251
322
  w3: "Web3"
252
323
 
253
324
  def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction":
254
- clone = copy.copy(self)
255
- if args is None:
256
- clone.args = tuple()
257
- else:
258
- clone.args = args
325
+ element_name = self.abi_element_identifier
326
+ if element_name in ["fallback", "receive"] or len(args) + len(kwargs):
327
+ # Use only the name if a fallback, receive function
328
+ # or when args/kwargs are present to find the proper element
329
+ element_name = self.fn_name
330
+
331
+ function_abi = get_abi_element(
332
+ filter_by_types(
333
+ ["function", "constructor", "fallback", "receive"],
334
+ self.contract_abi,
335
+ ),
336
+ element_name,
337
+ *args,
338
+ abi_codec=self.w3.codec,
339
+ **kwargs,
340
+ )
259
341
 
260
- if kwargs is None:
261
- clone.kwargs = {}
262
- else:
263
- clone.kwargs = kwargs
264
- clone._set_function_info()
265
- return clone
342
+ argument_types = None
343
+ if function_abi["type"] not in ["fallback", "receive"]:
344
+ argument_types = get_abi_input_types(function_abi)
345
+
346
+ function_signature = str(
347
+ get_abi_element_signature(self.abi_element_identifier, argument_types)
348
+ )
349
+ contract_function = ContractFunction.factory(
350
+ function_signature,
351
+ w3=self.w3,
352
+ contract_abi=self.contract_abi,
353
+ address=self.address,
354
+ abi_element_identifier=function_signature,
355
+ decode_tuples=self.decode_tuples,
356
+ )
357
+
358
+ return copy_contract_function(contract_function, *args, **kwargs)
266
359
 
267
360
  @classmethod
268
361
  def factory(cls, class_name: str, **kwargs: Any) -> Self:
269
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
362
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
270
363
 
271
364
  def call(
272
365
  self,
@@ -306,11 +399,13 @@ class ContractFunction(BaseContractFunction):
306
399
 
307
400
  block_id = parse_block_identifier(self.w3, block_identifier)
308
401
 
402
+ abi_element_identifier = abi_to_signature(self.abi)
403
+
309
404
  return call_contract_function(
310
405
  self.w3,
311
406
  self.address,
312
407
  self._return_data_normalizers,
313
- self.abi_element_identifier,
408
+ abi_element_identifier,
314
409
  call_transaction,
315
410
  block_id,
316
411
  self.contract_abi,
@@ -324,11 +419,12 @@ class ContractFunction(BaseContractFunction):
324
419
 
325
420
  def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
326
421
  setup_transaction = self._transact(transaction)
422
+ abi_element_identifier = abi_to_signature(self.abi)
327
423
 
328
424
  return transact_with_contract_function(
329
425
  self.address,
330
426
  self.w3,
331
- self.abi_element_identifier,
427
+ abi_element_identifier,
332
428
  setup_transaction,
333
429
  self.contract_abi,
334
430
  self.abi,
@@ -343,10 +439,11 @@ class ContractFunction(BaseContractFunction):
343
439
  state_override: Optional[StateOverride] = None,
344
440
  ) -> int:
345
441
  setup_transaction = self._estimate_gas(transaction)
442
+ abi_element_identifier = abi_to_signature(self.abi)
346
443
  return estimate_gas_for_function(
347
444
  self.address,
348
445
  self.w3,
349
- self.abi_element_identifier,
446
+ abi_element_identifier,
350
447
  setup_transaction,
351
448
  self.contract_abi,
352
449
  self.abi,
@@ -358,11 +455,12 @@ class ContractFunction(BaseContractFunction):
358
455
 
359
456
  def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
360
457
  built_transaction = self._build_transaction(transaction)
458
+ abi_element_identifier = abi_to_signature(self.abi)
361
459
 
362
460
  return build_transaction_for_function(
363
461
  self.address,
364
462
  self.w3,
365
- self.abi_element_identifier,
463
+ abi_element_identifier,
366
464
  built_transaction,
367
465
  self.contract_abi,
368
466
  self.abi,
@@ -413,23 +511,45 @@ class ContractFunctions(BaseContractFunctions):
413
511
  ) -> None:
414
512
  super().__init__(abi, w3, ContractFunction, address, decode_tuples)
415
513
 
514
+ def __iter__(self) -> Iterable["ContractFunction"]:
515
+ if not hasattr(self, "_functions") or not self._functions:
516
+ return
517
+
518
+ for func in self._functions:
519
+ yield self[abi_to_signature(func)]
520
+
416
521
  def __getattr__(self, function_name: str) -> "ContractFunction":
417
- if self.abi is None:
522
+ if super().__getattribute__("abi") is None:
418
523
  raise NoABIFound(
419
524
  "There is no ABI found for this contract.",
420
525
  )
421
- if "_functions" not in self.__dict__:
526
+ elif "_functions" not in self.__dict__ or len(self._functions) == 0:
422
527
  raise NoABIFunctionsFound(
423
528
  "The abi for this contract contains no function definitions. ",
424
529
  "Are you sure you provided the correct contract abi?",
425
530
  )
426
- elif function_name not in self.__dict__["_functions"]:
531
+ elif get_name_from_abi_element_identifier(function_name) not in [
532
+ get_name_from_abi_element_identifier(function["name"])
533
+ for function in self._functions
534
+ ]:
427
535
  raise ABIFunctionNotFound(
428
- f"The function '{function_name}' was not found in this contract's abi.",
429
- " Are you sure you provided the correct contract abi?",
536
+ f"The function '{function_name}' was not found in this contract's "
537
+ "abi. Are you sure you provided the correct contract abi?",
430
538
  )
431
- else:
432
- return super().__getattribute__(function_name)
539
+
540
+ function_identifier = function_name
541
+
542
+ if "(" not in function_name:
543
+ function_identifier = _get_any_abi_signature_with_name(
544
+ function_name, self._functions
545
+ )
546
+
547
+ return super().__getattribute__(
548
+ function_identifier,
549
+ )
550
+
551
+ def __getitem__(self, function_name: str) -> "ContractFunction":
552
+ return getattr(self, function_name)
433
553
 
434
554
 
435
555
  class Contract(BaseContract):
@@ -503,6 +623,16 @@ class Contract(BaseContract):
503
623
  normalizers=normalizers,
504
624
  ),
505
625
  )
626
+
627
+ if contract.abi:
628
+ for abi in contract.abi:
629
+ abi_name = abi.get("name")
630
+ if abi_name in ["abi", "address"]:
631
+ raise Web3AttributeError(
632
+ f"Contract contains a reserved word `{abi_name}` "
633
+ f"and could not be instantiated."
634
+ )
635
+
506
636
  contract.functions = ContractFunctions(
507
637
  contract.abi, contract.w3, decode_tuples=contract.decode_tuples
508
638
  )
@@ -562,6 +692,24 @@ class Contract(BaseContract):
562
692
  ) -> "ContractFunction":
563
693
  return get_function_by_identifier(fns, identifier)
564
694
 
695
+ @combomethod
696
+ def find_events_by_identifier(
697
+ cls,
698
+ contract_abi: ABI,
699
+ w3: "Web3",
700
+ address: ChecksumAddress,
701
+ callable_check: Callable[..., Any],
702
+ ) -> List["ContractEvent"]:
703
+ return find_events_by_identifier(
704
+ contract_abi, w3, address, callable_check, ContractEvent
705
+ )
706
+
707
+ @combomethod
708
+ def get_event_by_identifier(
709
+ cls, events: Sequence["ContractEvent"], identifier: str
710
+ ) -> "ContractEvent":
711
+ return get_event_by_identifier(events, identifier)
712
+
565
713
 
566
714
  class ContractCaller(BaseContractCaller):
567
715
  # mypy types
@@ -586,12 +734,12 @@ class ContractCaller(BaseContractCaller):
586
734
  self._functions = get_all_function_abis(self.abi)
587
735
 
588
736
  for func in self._functions:
737
+ abi_signature = abi_to_signature(func)
589
738
  fn = ContractFunction.factory(
590
- func["name"],
739
+ abi_signature,
591
740
  w3=w3,
592
741
  contract_abi=self.abi,
593
742
  address=self.address,
594
- abi_element_identifier=func["name"],
595
743
  decode_tuples=decode_tuples,
596
744
  )
597
745
 
@@ -603,7 +751,7 @@ class ContractCaller(BaseContractCaller):
603
751
  ccip_read_enabled=ccip_read_enabled,
604
752
  )
605
753
 
606
- setattr(self, func["name"], caller_method)
754
+ setattr(self, abi_signature, caller_method)
607
755
 
608
756
  def __call__(
609
757
  self,
web3/contract/utils.py CHANGED
@@ -23,6 +23,7 @@ from eth_typing import (
23
23
  TypeStr,
24
24
  )
25
25
  from eth_utils.abi import (
26
+ abi_to_signature,
26
27
  filter_abi_by_type,
27
28
  get_abi_output_types,
28
29
  )
@@ -63,6 +64,7 @@ from web3.types import (
63
64
  BlockIdentifier,
64
65
  RPCEndpoint,
65
66
  StateOverride,
67
+ TContractEvent,
66
68
  TContractFn,
67
69
  TxParams,
68
70
  )
@@ -338,14 +340,17 @@ def find_functions_by_identifier(
338
340
  callable_check: Callable[..., Any],
339
341
  function_type: Type[TContractFn],
340
342
  ) -> List[TContractFn]:
343
+ """
344
+ Given a contract ABI, return a list of TContractFunction instances.
345
+ """
341
346
  fns_abi = filter_abi_by_type("function", contract_abi)
342
347
  return [
343
348
  function_type.factory(
344
- fn_abi["name"],
349
+ abi_to_signature(fn_abi),
345
350
  w3=w3,
346
351
  contract_abi=contract_abi,
347
352
  address=address,
348
- abi_element_identifier=fn_abi["name"],
353
+ abi_element_identifier=abi_to_signature(fn_abi),
349
354
  abi=fn_abi,
350
355
  )
351
356
  for fn_abi in fns_abi
@@ -356,6 +361,10 @@ def find_functions_by_identifier(
356
361
  def get_function_by_identifier(
357
362
  fns: Sequence[TContractFn], identifier: str
358
363
  ) -> TContractFn:
364
+ """
365
+ Check that the provided list of TContractFunction instances contains one element and
366
+ return it.
367
+ """
359
368
  if len(fns) > 1:
360
369
  raise Web3ValueError(
361
370
  f"Found multiple functions with matching {identifier}. " f"Found: {fns!r}"
@@ -365,6 +374,46 @@ def get_function_by_identifier(
365
374
  return fns[0]
366
375
 
367
376
 
377
+ def find_events_by_identifier(
378
+ contract_abi: ABI,
379
+ w3: Union["Web3", "AsyncWeb3"],
380
+ address: ChecksumAddress,
381
+ callable_check: Callable[..., Any],
382
+ event_type: Type[TContractEvent],
383
+ ) -> List[TContractEvent]:
384
+ """
385
+ Given a contract ABI, return a list of TContractEvent instances.
386
+ """
387
+ event_abis = filter_abi_by_type("event", contract_abi)
388
+ return [
389
+ event_type.factory(
390
+ event_abi["name"],
391
+ w3=w3,
392
+ contract_abi=contract_abi,
393
+ address=address,
394
+ abi=event_abi,
395
+ )
396
+ for event_abi in event_abis
397
+ if callable_check(event_abi)
398
+ ]
399
+
400
+
401
+ def get_event_by_identifier(
402
+ events: Sequence[TContractEvent], identifier: str
403
+ ) -> TContractEvent:
404
+ """
405
+ Check that the provided list of TContractEvent instances contains one element and
406
+ return it.
407
+ """
408
+ if len(events) > 1:
409
+ raise Web3ValueError(
410
+ f"Found multiple events with matching {identifier}. " f"Found: {events!r}"
411
+ )
412
+ elif len(events) == 0:
413
+ raise Web3ValueError(f"Could not find any event with matching {identifier}")
414
+ return events[0]
415
+
416
+
368
417
  # --- async --- #
369
418
 
370
419
 
web3/eth/async_eth.py CHANGED
@@ -127,6 +127,17 @@ class AsyncEth(BaseEth):
127
127
  async def accounts(self) -> Tuple[ChecksumAddress]:
128
128
  return await self._accounts()
129
129
 
130
+ # eth_blobBaseFee
131
+
132
+ _eth_blobBaseFee: Method[Callable[[], Awaitable[Wei]]] = Method(
133
+ RPC.eth_blobBaseFee,
134
+ is_property=True,
135
+ )
136
+
137
+ @property
138
+ async def blob_base_fee(self) -> Wei:
139
+ return await self._eth_blobBaseFee()
140
+
130
141
  # eth_blockNumber
131
142
 
132
143
  get_block_number: Method[Callable[[], Awaitable[BlockNumber]]] = Method(
web3/eth/eth.py CHANGED
@@ -119,6 +119,17 @@ class Eth(BaseEth):
119
119
  def accounts(self) -> Tuple[ChecksumAddress]:
120
120
  return self._accounts()
121
121
 
122
+ # eth_blobBaseFee
123
+
124
+ _eth_blobBaseFee: Method[Callable[[], Wei]] = Method(
125
+ RPC.eth_blobBaseFee,
126
+ is_property=True,
127
+ )
128
+
129
+ @property
130
+ def blob_base_fee(self) -> Wei:
131
+ return self._eth_blobBaseFee()
132
+
122
133
  # eth_blockNumber
123
134
 
124
135
  get_block_number: Method[Callable[[], BlockNumber]] = Method(
web3/geth.py CHANGED
@@ -5,6 +5,7 @@ from typing import (
5
5
  Optional,
6
6
  Protocol,
7
7
  Tuple,
8
+ Union,
8
9
  )
9
10
 
10
11
  from eth_typing.evm import (
@@ -22,12 +23,19 @@ from web3.module import (
22
23
  Module,
23
24
  )
24
25
  from web3.types import (
26
+ CallTrace,
27
+ DiffModeTrace,
25
28
  EnodeURI,
29
+ FourByteTrace,
26
30
  NodeInfo,
31
+ OpcodeTrace,
27
32
  Peer,
33
+ PrestateTrace,
34
+ TraceConfig,
28
35
  TxPoolContent,
29
36
  TxPoolInspect,
30
37
  TxPoolStatus,
38
+ _Hash32,
31
39
  )
32
40
 
33
41
 
@@ -133,9 +141,33 @@ class GethAdmin(Module):
133
141
  )
134
142
 
135
143
 
144
+ class GethDebug(Module):
145
+ """
146
+ https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
147
+ """
148
+
149
+ def trace_transaction_munger(
150
+ self,
151
+ transaction_hash: _Hash32,
152
+ trace_config: Optional[TraceConfig] = None,
153
+ ) -> Tuple[_Hash32, TraceConfig]:
154
+ return (transaction_hash, trace_config)
155
+
156
+ trace_transaction: Method[
157
+ Callable[
158
+ ...,
159
+ Union[CallTrace, PrestateTrace, OpcodeTrace, DiffModeTrace, FourByteTrace],
160
+ ]
161
+ ] = Method(
162
+ RPC.debug_traceTransaction,
163
+ mungers=[trace_transaction_munger],
164
+ )
165
+
166
+
136
167
  class Geth(Module):
137
168
  admin: GethAdmin
138
169
  txpool: GethTxPool
170
+ debug: GethDebug
139
171
 
140
172
 
141
173
  # --- async --- #
@@ -261,8 +293,35 @@ class AsyncGethAdmin(Module):
261
293
  return await self._stop_ws()
262
294
 
263
295
 
296
+ class AsyncGethDebug(Module):
297
+ """
298
+ https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
299
+ """
300
+
301
+ is_async = True
302
+
303
+ _trace_transaction: Method[
304
+ Callable[
305
+ ...,
306
+ Awaitable[
307
+ Union[
308
+ CallTrace, PrestateTrace, OpcodeTrace, FourByteTrace, DiffModeTrace
309
+ ]
310
+ ],
311
+ ]
312
+ ] = Method(RPC.debug_traceTransaction)
313
+
314
+ async def trace_transaction(
315
+ self,
316
+ transaction_hash: _Hash32,
317
+ trace_config: Optional[TraceConfig] = None,
318
+ ) -> Union[CallTrace, PrestateTrace, OpcodeTrace, FourByteTrace, DiffModeTrace]:
319
+ return await self._trace_transaction(transaction_hash, trace_config)
320
+
321
+
264
322
  class AsyncGeth(Module):
265
323
  is_async = True
266
324
 
267
325
  admin: AsyncGethAdmin
268
326
  txpool: AsyncGethTxPool
327
+ debug: AsyncGethDebug
web3/main.py CHANGED
@@ -93,9 +93,11 @@ from web3.exceptions import (
93
93
  from web3.geth import (
94
94
  AsyncGeth,
95
95
  AsyncGethAdmin,
96
+ AsyncGethDebug,
96
97
  AsyncGethTxPool,
97
98
  Geth,
98
99
  GethAdmin,
100
+ GethDebug,
99
101
  GethTxPool,
100
102
  )
101
103
  from web3.manager import (
@@ -162,6 +164,7 @@ def get_async_default_modules() -> Dict[str, Union[Type[Module], Sequence[Any]]]
162
164
  {
163
165
  "admin": AsyncGethAdmin,
164
166
  "txpool": AsyncGethTxPool,
167
+ "debug": AsyncGethDebug,
165
168
  },
166
169
  ),
167
170
  }
@@ -176,6 +179,7 @@ def get_default_modules() -> Dict[str, Union[Type[Module], Sequence[Any]]]:
176
179
  {
177
180
  "admin": GethAdmin,
178
181
  "txpool": GethTxPool,
182
+ "debug": GethDebug,
179
183
  },
180
184
  ),
181
185
  "tracing": Tracing,
web3/manager.py CHANGED
@@ -39,6 +39,7 @@ from web3.exceptions import (
39
39
  ProviderConnectionError,
40
40
  RequestTimedOut,
41
41
  TaskNotRunning,
42
+ TransactionNotFound,
42
43
  Web3RPCError,
43
44
  Web3TypeError,
44
45
  )
@@ -150,6 +151,7 @@ def _validate_response(
150
151
  error_formatters: Optional[Callable[..., Any]],
151
152
  is_subscription_response: bool = False,
152
153
  logger: Optional[logging.Logger] = None,
154
+ params: Optional[Any] = None,
153
155
  ) -> None:
154
156
  if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
155
157
  _raise_bad_response_format(
@@ -210,6 +212,13 @@ def _validate_response(
210
212
  _raise_bad_response_format(
211
213
  response, 'error["message"] is required and must be a string value.'
212
214
  )
215
+ elif error_message == "transaction not found":
216
+ transaction_hash = params[0]
217
+ web3_rpc_error = TransactionNotFound(
218
+ repr(error),
219
+ rpc_response=response,
220
+ user_message=(f"Transaction with hash {transaction_hash!r} not found."),
221
+ )
213
222
 
214
223
  # errors must include an integer code
215
224
  code = error.get("code")
@@ -360,6 +369,7 @@ class RequestManager:
360
369
  error_formatters,
361
370
  is_subscription_response=is_subscription_response,
362
371
  logger=self.logger,
372
+ params=params,
363
373
  )
364
374
 
365
375
  # format results