eth-prototype 1.5.0b4__tar.gz → 1.5.2__tar.gz

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 (73) hide show
  1. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/PKG-INFO +1 -1
  2. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/PKG-INFO +1 -1
  3. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/SOURCES.txt +1 -0
  4. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/aa_bundler.py +67 -43
  5. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/test_utils/vcr_utils.py +9 -3
  6. eth_prototype-1.5.2/tests/cassettes/test_aa_bundler/test_build_user_operation.yaml +48 -0
  7. eth_prototype-1.5.2/tests/cassettes/test_aa_bundler/test_build_user_operation_execute_user_op.yaml +145 -0
  8. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/conftest.py +1 -1
  9. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_aa_bundler.py +57 -0
  10. eth_prototype-1.5.0b4/tests/cassettes/test_aa_bundler/test_build_user_operation.yaml +0 -48
  11. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.coveragerc +0 -0
  12. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.github/workflows/publish.yaml +0 -0
  13. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.github/workflows/test.yaml +0 -0
  14. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.gitignore +0 -0
  15. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.isort.cfg +0 -0
  16. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.pre-commit-config.yaml +0 -0
  17. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/.readthedocs.yml +0 -0
  18. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/AUTHORS.rst +0 -0
  19. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/CHANGELOG.rst +0 -0
  20. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/LICENSE.txt +0 -0
  21. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/README.md +0 -0
  22. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/Makefile +0 -0
  23. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/_static/.gitignore +0 -0
  24. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/authors.rst +0 -0
  25. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/changelog.rst +0 -0
  26. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/conf.py +0 -0
  27. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/index.rst +0 -0
  28. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/license.rst +0 -0
  29. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/readme.rst +0 -0
  30. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/docs/requirements.txt +0 -0
  31. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/pyproject.toml +0 -0
  32. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/setup.cfg +0 -0
  33. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/setup.py +0 -0
  34. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/dependency_links.txt +0 -0
  35. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/not-zip-safe +0 -0
  36. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/requires.txt +0 -0
  37. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/eth_prototype.egg-info/top_level.txt +0 -0
  38. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/__init__.py +0 -0
  39. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/build_artifacts.py +0 -0
  40. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/contracts.py +0 -0
  41. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/defender_relay.py +0 -0
  42. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/test_utils/__init__.py +0 -0
  43. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/test_utils/factories.py +0 -0
  44. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/test_utils/hardhat.py +0 -0
  45. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/w3wrappers.py +0 -0
  46. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/wadray.py +0 -0
  47. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/src/ethproto/wrappers.py +0 -0
  48. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/__init__.py +0 -0
  49. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/README.md +0 -0
  50. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/artifacts2/TestCurrency.sol/TestCurrency.json +0 -0
  51. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/Count.sol +0 -0
  52. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/Counter.sol +0 -0
  53. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/CounterUpgradeable.sol +0 -0
  54. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/CounterUpgradeableWithLibrary.sol +0 -0
  55. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/CounterWithLibrary.sol +0 -0
  56. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/Datatypes.sol +0 -0
  57. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/EventLauncher.sol +0 -0
  58. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/TestCurrency.sol +0 -0
  59. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/TestCurrencyUUPS.sol +0 -0
  60. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/contracts/TestNFT.sol +0 -0
  61. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/hardhat.config.js +0 -0
  62. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/package-lock.json +0 -0
  63. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/package.json +0 -0
  64. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/verifiable-binaries/@anotherOrg/aPkg/1.0.2/build/contracts/TestCurrency.sol/TestCurrency.json +0 -0
  65. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/verifiable-binaries/@org/pkg/0.2.1/build/contracts/TestCurrency.json +0 -0
  66. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/hardhat-project/verifiable-binaries/@org/pkg/0.3.0/build/contracts/TestCurrency.json +0 -0
  67. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_build_artifacts.py +0 -0
  68. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_contracts.py +0 -0
  69. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_defender.py +0 -0
  70. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_time_control.py +0 -0
  71. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_w3.py +0 -0
  72. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tests/test_wadray.py +0 -0
  73. {eth_prototype-1.5.0b4 → eth_prototype-1.5.2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.5.0b4
3
+ Version: 1.5.2
4
4
  Summary: Prototype Ethereum Smart Contracts in Python
5
5
  Home-page: https://github.com/gnarvaja/eth-prototype
6
6
  Author: Guillermo M. Narvaja
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.5.0b4
3
+ Version: 1.5.2
4
4
  Summary: Prototype Ethereum Smart Contracts in Python
5
5
  Home-page: https://github.com/gnarvaja/eth-prototype
6
6
  Author: Guillermo M. Narvaja
@@ -50,6 +50,7 @@ tests/test_time_control.py
50
50
  tests/test_w3.py
51
51
  tests/test_wadray.py
52
52
  tests/cassettes/test_aa_bundler/test_build_user_operation.yaml
53
+ tests/cassettes/test_aa_bundler/test_build_user_operation_execute_user_op.yaml
53
54
  tests/hardhat-project/README.md
54
55
  tests/hardhat-project/hardhat.config.js
55
56
  tests/hardhat-project/package-lock.json
@@ -3,14 +3,13 @@ from collections import defaultdict
3
3
  from dataclasses import dataclass, replace
4
4
  from enum import Enum
5
5
  from threading import local
6
- from warnings import warn
7
6
 
8
7
  from environs import Env
9
8
  from eth_abi import encode
10
9
  from eth_abi.packed import encode_packed
11
10
  from eth_account import Account
12
11
  from eth_account.messages import encode_defunct
13
- from eth_typing import HexAddress
12
+ from eth_typing import ChecksumAddress, HexAddress
14
13
  from eth_utils import add_0x_prefix, function_signature_to_4byte_selector
15
14
  from hexbytes import HexBytes
16
15
  from requests import HTTPError
@@ -48,8 +47,8 @@ NonceMode = Enum(
48
47
 
49
48
  AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", enum=NonceMode)
50
49
  AA_BUNDLER_NONCE_KEY = env.int("AA_BUNDLER_NONCE_KEY", 0)
51
- AA_BUNDLER_MAX_GETNONCE_RETRIES = env.int("AA_BUNDLER_MAX_GETNONCE_RETRIES", 3)
52
50
  AA_BUNDLER_ALCHEMY_GAS_POLICY_ID = env.str("AA_BUNDLER_ALCHEMY_GAS_POLICY_ID", None)
51
+ AA_BUNDLER_USE_EXECUTE_USER_OP = env.bool("AA_BUNDLER_USE_EXECUTE_USER_OP", False)
53
52
 
54
53
  GET_NONCE_ABI = [
55
54
  {
@@ -87,6 +86,12 @@ class BundlerError(Exception):
87
86
  pass
88
87
 
89
88
 
89
+ class NonceError(BundlerRevertError):
90
+ """Raised when the bundler returns an AA25 invalid account nonce error."""
91
+
92
+ pass
93
+
94
+
90
95
  @dataclass(frozen=True)
91
96
  class UserOpEstimation:
92
97
  """eth_estimateUserOperationGas response"""
@@ -148,6 +153,12 @@ class UserOperation:
148
153
  EXECUTE_ARG_TYPES = ["address", "uint256", "bytes"]
149
154
  EXECUTE_SELECTOR = function_signature_to_4byte_selector(f"execute({','.join(EXECUTE_ARG_TYPES)})")
150
155
 
156
+ EXECUTE_USEROP_ARG_TYPES = ["address", "address", "uint256", "bytes"]
157
+ EXECUTE_USEROP_SELECTOR = function_signature_to_4byte_selector(
158
+ # PackedUserOperation struct
159
+ "executeUserOp((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes),bytes32)"
160
+ )
161
+
151
162
  sender: HexBytes
152
163
  nonce: int
153
164
  call_data: HexBytes
@@ -169,13 +180,24 @@ class UserOperation:
169
180
  paymaster_post_op_gas_limit: int = 0
170
181
 
171
182
  @classmethod
172
- def from_tx(cls, tx: Tx, nonce):
183
+ def from_tx(cls, tx: Tx, nonce, execute_user_op_context=None):
184
+ if execute_user_op_context is not None:
185
+ call_data = add_0x_prefix(
186
+ (
187
+ cls.EXECUTE_USEROP_SELECTOR
188
+ + encode(
189
+ cls.EXECUTE_USEROP_ARG_TYPES, [execute_user_op_context, tx.target, tx.value, tx.data]
190
+ )
191
+ ).hex()
192
+ )
193
+ else:
194
+ call_data = add_0x_prefix(
195
+ (cls.EXECUTE_SELECTOR + encode(cls.EXECUTE_ARG_TYPES, tx.as_execute_args())).hex()
196
+ )
173
197
  return cls(
174
198
  sender=get_sender(tx),
175
199
  nonce=nonce,
176
- call_data=add_0x_prefix(
177
- (cls.EXECUTE_SELECTOR + encode(cls.EXECUTE_ARG_TYPES, tx.as_execute_args())).hex()
178
- ),
200
+ call_data=call_data,
179
201
  )
180
202
 
181
203
  def as_reduced_dict(self):
@@ -346,18 +368,9 @@ def consume_nonce(nonce_key, nonce):
346
368
  NONCE_CACHE[nonce_key] = max(NONCE_CACHE[nonce_key], nonce + 1)
347
369
 
348
370
 
349
- def check_nonce_error(resp, retry_nonce):
350
- """Returns the next nonce if resp contains a nonce error and retries weren't exhausted
351
- Raises RevertError otherwise
352
- """
353
- if "AA25" in resp["error"]["message"] and AA_BUNDLER_MAX_GETNONCE_RETRIES > 0:
354
- # Retry fetching the nonce
355
- if retry_nonce == AA_BUNDLER_MAX_GETNONCE_RETRIES:
356
- raise BundlerRevertError(resp["error"]["message"], response=resp)
357
- warn(f'{resp["error"]["message"]} error, I will retry fetching the nonce')
358
- return (retry_nonce or 0) + 1
359
- else:
360
- raise BundlerRevertError(resp["error"]["message"], response=resp)
371
+ def is_nonce_error(resp):
372
+ """Check if a bundler response contains an AA25 nonce error."""
373
+ return "error" in resp and "AA25" in resp["error"]["message"]
361
374
 
362
375
 
363
376
  def get_sender(tx):
@@ -386,6 +399,7 @@ class Bundler:
386
399
  executor_pk: HexBytes = AA_BUNDLER_EXECUTOR_PK,
387
400
  overrides: StateOverride = AA_BUNDLER_STATE_OVERRIDES,
388
401
  alchemy_gas_policy_id: str = AA_BUNDLER_ALCHEMY_GAS_POLICY_ID,
402
+ use_execute_user_op: bool = AA_BUNDLER_USE_EXECUTE_USER_OP,
389
403
  ):
390
404
  self.w3 = w3
391
405
  self.bundler = Web3(Web3.HTTPProvider(bundler_url), middleware=[]) if bundler_url else w3
@@ -397,7 +411,7 @@ class Bundler:
397
411
  self.gas_limit_factor = gas_limit_factor
398
412
  self.priority_gas_price_factor = priority_gas_price_factor
399
413
  self.base_gas_price_factor = base_gas_price_factor
400
- self.executor_pk = executor_pk
414
+ self.account = Account.from_key(executor_pk) if executor_pk else None
401
415
  self.max_fee_per_gas = max_fee_per_gas
402
416
 
403
417
  # stateOverrideSet mapping to use when calling eth_estimateUserOperationGas
@@ -407,15 +421,23 @@ class Bundler:
407
421
  if alchemy_gas_policy_id is None and bundler_type == "alchemy":
408
422
  raise BundlerError("Must provide alchemy_gas_policy_id when using alchemy bundler_type")
409
423
  self.alchemy_gas_policy_id = alchemy_gas_policy_id
424
+ self.use_execute_user_op = use_execute_user_op
410
425
 
411
426
  def __str__(self):
412
427
  return (
413
428
  f"Bundler(type={self.bundler_type}, entrypoint={self.entrypoint}, nonce_mode={self.nonce_mode}, "
414
429
  f"fixed_nonce_key={self.fixed_nonce_key}, verification_gas_factor={self.verification_gas_factor}, "
415
430
  f"gas_limit_factor={self.gas_limit_factor}, priority_gas_price_factor={self.priority_gas_price_factor}, "
416
- f"base_gas_price_factor={self.base_gas_price_factor}, max_fee_per_gas={self.max_fee_per_gas})"
431
+ f"base_gas_price_factor={self.base_gas_price_factor}, max_fee_per_gas={self.max_fee_per_gas}), "
432
+ f"use_execute_user_op={self.use_execute_user_op}, signer={self.account.address if self.account else None}"
417
433
  )
418
434
 
435
+ @property
436
+ def execute_user_op_context(self) -> ChecksumAddress:
437
+ if self.use_execute_user_op and self.account:
438
+ return self.account.address
439
+ return None
440
+
419
441
  def get_nonce_and_key(self, tx: Tx, fetch=False):
420
442
  nonce_key = tx.nonce_key
421
443
  nonce = tx.nonce
@@ -552,22 +574,23 @@ class Bundler:
552
574
  total_fee = int(resp["result"]["standard"]["maxFeePerGas"], 16)
553
575
  base_fee = total_fee - priority_fee
554
576
  max_priority_fee_per_gas = int(priority_fee * self.priority_gas_price_factor)
555
- max_fee_per_gas = min(max_priority_fee_per_gas + base_fee, self.max_fee_per_gas)
577
+ max_fee_per_gas = max_priority_fee_per_gas + base_fee
556
578
  return GasPrice(max_priority_fee_per_gas=max_priority_fee_per_gas, max_fee_per_gas=max_fee_per_gas)
557
579
 
558
580
  def generic_gas_price(self):
559
581
  base_fee = self.get_base_fee()
560
582
  priority_fee = self.w3.eth.max_priority_fee
561
583
  max_priority_fee_per_gas = int(priority_fee * self.priority_gas_price_factor)
562
- max_fee_per_gas = min(max_priority_fee_per_gas + base_fee, self.max_fee_per_gas)
584
+ max_fee_per_gas = max_priority_fee_per_gas + base_fee
563
585
  return GasPrice(max_priority_fee_per_gas=max_priority_fee_per_gas, max_fee_per_gas=max_fee_per_gas)
564
586
 
565
- def build_user_operation(self, tx: Tx, retry_nonce=None) -> UserOperation:
566
- nonce_key, nonce = self.get_nonce_and_key(tx, fetch=retry_nonce is not None)
567
- # Consume the nonce, even if the userop may fail later
587
+ def build_user_operation(self, tx: Tx, enable_cap=True) -> UserOperation:
588
+ nonce_key, nonce = self.get_nonce_and_key(tx)
568
589
  consume_nonce(nonce_key, nonce)
569
590
 
570
- user_operation = UserOperation.from_tx(tx, make_nonce(nonce_key, nonce))
591
+ user_operation = UserOperation.from_tx(
592
+ tx, make_nonce(nonce_key, nonce), execute_user_op_context=self.execute_user_op_context
593
+ )
571
594
 
572
595
  if self.bundler_type == "alchemy":
573
596
  estimation_and_paymaster = self.alchemy_estimation(user_operation)
@@ -584,7 +607,11 @@ class Bundler:
584
607
  user_operation = user_operation.add_estimation(estimation)
585
608
 
586
609
  gas_price = self.pimlico_gas_price()
587
-
610
+ if enable_cap:
611
+ gas_price = GasPrice(
612
+ max_priority_fee_per_gas=gas_price.max_priority_fee_per_gas,
613
+ max_fee_per_gas=min(gas_price.max_fee_per_gas, self.max_fee_per_gas),
614
+ )
588
615
  user_operation = user_operation.add_gas_price(gas_price)
589
616
 
590
617
  elif self.bundler_type == "generic":
@@ -593,7 +620,11 @@ class Bundler:
593
620
  user_operation = user_operation.add_estimation(estimation)
594
621
 
595
622
  gas_price = self.generic_gas_price()
596
-
623
+ if enable_cap:
624
+ gas_price = GasPrice(
625
+ max_priority_fee_per_gas=gas_price.max_priority_fee_per_gas,
626
+ max_fee_per_gas=min(gas_price.max_fee_per_gas, self.max_fee_per_gas),
627
+ )
597
628
  user_operation = user_operation.add_gas_price(gas_price)
598
629
 
599
630
  else:
@@ -601,25 +632,18 @@ class Bundler:
601
632
 
602
633
  return user_operation
603
634
 
604
- def send_transaction(self, tx: Tx, retry_nonce=None):
605
- user_operation = self.build_user_operation(tx, retry_nonce).sign(
606
- self.executor_pk, tx.chain_id, self.entrypoint
607
- )
635
+ def send_transaction(self, tx: Tx):
636
+ user_operation = self.build_user_operation(tx).sign(self.account.key, tx.chain_id, self.entrypoint)
637
+ return self.send_user_operation(user_operation)
608
638
 
639
+ def send_user_operation(self, user_operation: UserOperation):
609
640
  resp = self.bundler.provider.make_request(
610
641
  "eth_sendUserOperation", [user_operation.as_dict(), self.entrypoint]
611
642
  )
612
643
  if "error" in resp:
613
- try:
614
- next_nonce = check_nonce_error(resp, retry_nonce)
615
- except BundlerRevertError as e:
616
- raise BundlerRevertError(
617
- e.message,
618
- userop=user_operation,
619
- response=e.response,
620
- )
621
- return self.send_transaction(tx, retry_nonce=next_nonce)
622
-
644
+ if is_nonce_error(resp):
645
+ raise NonceError(resp["error"]["message"], userop=user_operation, response=resp)
646
+ raise BundlerRevertError(resp["error"]["message"], userop=user_operation, response=resp)
623
647
  return {"userOpHash": resp["result"]}
624
648
 
625
649
  def get_user_operation(self, user_op_hash):
@@ -18,6 +18,12 @@ def json_rpc_matcher(r1, r2):
18
18
  r1_body = json.loads(r1.body)
19
19
  r2_body = json.loads(r2.body)
20
20
 
21
- assert r1_body["jsonrpc"] == r2_body["jsonrpc"] == "2.0"
22
- assert r1_body["method"] == r2_body["method"]
23
- assert r1_body["params"] == r2_body["params"]
21
+ assert (
22
+ r1_body["jsonrpc"] == r2_body["jsonrpc"] == "2.0"
23
+ ), f"JSON-RPC mismatch: {r1_body['jsonrpc']} != {r2_body['jsonrpc']}"
24
+ assert (
25
+ r1_body["method"] == r2_body["method"]
26
+ ), f"Method mismatch: {r1_body['method']} != {r2_body['method']}"
27
+ assert (
28
+ r1_body["params"] == r2_body["params"]
29
+ ), f"Param mismatch: {r1_body['params']} != {r2_body['params']}"
@@ -0,0 +1,48 @@
1
+ interactions:
2
+ - request:
3
+ body: '{"jsonrpc": "2.0", "method": "alchemy_requestGasAndPaymasterAndData", "params":
4
+ [{"policyId": "d80ed67a-d8bc-4cd1-90ad-b50e0a58c93e", "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
5
+ "dummySignature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
6
+ "userOperation": {"sender": "0xE8B412158c205B0F605e0FC09dCdA27d3F140FE9", "nonce":
7
+ "0xae85c374ae0606ed34d0ee009a9ca43a757a8a46a324510000000000000000", "callData":
8
+ "0xb61d27f60000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000007ace242f32208d836a2245df957c08547059bf45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000",
9
+ "signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"},
10
+ "overrides": {"maxFeePerGas": {"multiplier": 1}, "maxPriorityFeePerGas": {"multiplier":
11
+ 1}, "callGasLimit": {"multiplier": 1}, "verificationGasLimit": {"multiplier":
12
+ 1}}, "stateOverrideSet": {}}], "id": 0}'
13
+ headers:
14
+ Accept:
15
+ - "*/*"
16
+ Accept-Encoding:
17
+ - gzip, deflate
18
+ Connection:
19
+ - keep-alive
20
+ Content-Length:
21
+ - "1328"
22
+ Content-Type:
23
+ - application/json
24
+ User-Agent:
25
+ - web3.py/7.14.0/web3.providers.rpc.rpc.HTTPProvider
26
+ method: POST
27
+ uri: https://bundler.example.com
28
+ response:
29
+ body:
30
+ string: '{"jsonrpc":"2.0","id":0,"result":{"callGasLimit":"0xd912","paymasterVerificationGasLimit":"0x9b37","paymasterPostOpGasLimit":"0x0","verificationGasLimit":"0xac76","maxPriorityFeePerGas":"0x7aef40a00","paymaster":"0x2cc0c7981D846b9F2a16276556f6e8cb52BfB633","maxFeePerGas":"0xbaad142eb6","paymasterData":"0x00000000000000006982f2961b02e25cb873537d0a2383b9665992667628a8d22f8223e7951b2f6d2aeae55e03af6a6ec9774cf4303980e7e3b2ecab9b65a8bcce8aabe4fd89b81c0facdb4f1b","preVerificationGas":"0xbd54"}}'
31
+ headers:
32
+ content-length:
33
+ - "493"
34
+ content-type:
35
+ - application/json
36
+ date:
37
+ - Wed, 04 Feb 2026 07:07:42 GMT
38
+ server:
39
+ - istio-envoy
40
+ x-alchemy-trace-id:
41
+ - c905c727e8b9bd9f1495b4112264ea51
42
+ - 2a016524-94c2-4e82-bdb5-7a4c7035b07e
43
+ x-envoy-upstream-service-time:
44
+ - "122"
45
+ status:
46
+ code: 200
47
+ message: OK
48
+ version: 1
@@ -0,0 +1,145 @@
1
+ interactions:
2
+ - request:
3
+ body: '{"jsonrpc": "2.0", "method": "eth_estimateUserOperationGas", "params":
4
+ [{"sender": "0xE8B412158c205B0F605e0FC09dCdA27d3F140FE9", "nonce": "0xae85c374ae0606ed34d0ee009a9ca43a757a8a46a324520000000000000000",
5
+ "callData": "0x8dd7712f00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000007ace242f32208d836a2245df957c08547059bf45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000",
6
+ "callGasLimit": "0x0", "verificationGasLimit": "0x0", "preVerificationGas":
7
+ "0x0", "maxPriorityFeePerGas": "0x0", "maxFeePerGas": "0x0", "signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
8
+ "paymaster": null, "paymasterData": "0x", "paymasterVerificationGasLimit": "0x0",
9
+ "paymasterPostOpGasLimit": "0x0"}, "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
10
+ {}], "id": 0}'
11
+ headers:
12
+ Accept:
13
+ - "*/*"
14
+ Accept-Encoding:
15
+ - gzip, deflate
16
+ Connection:
17
+ - keep-alive
18
+ Content-Length:
19
+ - "1142"
20
+ Content-Type:
21
+ - application/json
22
+ User-Agent:
23
+ - web3.py/7.14.1/web3.providers.rpc.rpc.HTTPProvider
24
+ method: POST
25
+ uri: https://bundler.example.com
26
+ response:
27
+ body:
28
+ string: '{"jsonrpc":"2.0","id":0,"result":{"preVerificationGas":"0xc0fe","verificationGasLimit":"0x16471","callGasLimit":"0x10282","paymasterVerificationGasLimit":"0x0","paymasterPostOpGasLimit":"0x0"}}'
29
+ headers:
30
+ Access-Control-Allow-Origin:
31
+ - "*"
32
+ CF-RAY:
33
+ - 9d9cbd935996199f-EZE
34
+ Connection:
35
+ - keep-alive
36
+ Content-Encoding:
37
+ - gzip
38
+ Content-Type:
39
+ - application/json; charset=utf-8
40
+ Date:
41
+ - Mon, 09 Mar 2026 20:13:29 GMT
42
+ Server:
43
+ - cloudflare
44
+ Strict-Transport-Security:
45
+ - max-age=15552000; includeSubDomains
46
+ Transfer-Encoding:
47
+ - chunked
48
+ Vary:
49
+ - Origin
50
+ X-Ratelimit-Limit:
51
+ - "500"
52
+ X-Ratelimit-Remaining:
53
+ - "499"
54
+ X-Ratelimit-Reset:
55
+ - "60"
56
+ X-Request-Id:
57
+ - 7a1b3d8ced87e1a3dcc23a965e1ddcde
58
+ alt-svc:
59
+ - h3=":443"; ma=86400
60
+ cf-cache-status:
61
+ - DYNAMIC
62
+ status:
63
+ code: 200
64
+ message: OK
65
+ - request:
66
+ body: '{"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": ["latest",
67
+ false], "id": 0}'
68
+ headers:
69
+ Accept:
70
+ - "*/*"
71
+ Accept-Encoding:
72
+ - gzip, deflate
73
+ Connection:
74
+ - keep-alive
75
+ Content-Length:
76
+ - "90"
77
+ Content-Type:
78
+ - application/json
79
+ User-Agent:
80
+ - web3.py/7.14.1/web3.providers.rpc.rpc.HTTPProvider
81
+ method: POST
82
+ uri: http://example.org
83
+ response:
84
+ body:
85
+ string: '{"jsonrpc":"2.0","id":0,"result":{"baseFeePerGas":"0x13f86bb293","difficulty":"0x1","extraData":"0xd78301100883626f7288676f312e32362e30856c696e75780000000000000000f9019480f90190c0c0c101c102c180c104c105c106c107c108c109c10ac0c0c0c10ec10fc110c102c10bc0c114c111c115c117c115c0c0c11bc11ac11dc11ec113c120c121c103c112c119c0c126c127c128c122c12ac12bc12cc12dc12ec12fc130c22423c125c132c134c131c136c133c138c3293918c13ac13bc13cc2373dc13ec13fc140c141c142c143c144c145c146c147c148c149c14ac14bc14cc14dc14ec14fc135c150c152c153c154c155c156c157c158c159c15ac15bc15cc15dc15ec15fc160c161c162c163c164c165c166c167c168c169c16ac16bc16cc16dc16ec16fc170c171c172c173c174c175c176c177c23251c178c17ac17bc17cc17dc17ec17fc28180c28181c28182c28183c28184c28185c28186c28187c28188c28189c179c2818ac2818cc2818dc2818ec2818fc0c54a3e4d4f5bc28190c0c0c0c116c28192c0c28199c2819ac2819bc2819cc0c0c0c28198c0c281a1c281a3c4819781a4c0c281a5c0cb4c5a818f498181818c3d4ec481a98193c2818bc0c781a30d81a581aac281adc281aec0c281aac281b1c281b2e86ba6f942b45fbb80cbad2a1f6415b852acdc1b66be009842c008040d35cb7a64243595c7592b9585c5968146d06641d706048693018dceff7518c415a484a501","gasLimit":"0x6679122","gasUsed":"0x2a9e13d","hash":"0xf83cca6bb72ef19942916f9f8904253f2ccd1e1095c41f5e540b17441d1fce14","logsBloom":"0xeff9d76bf3fbdb92cffa64a3ffef72ddff2f7bb7dfbaa6fafdbf7fb5dc7d7e6eefedf84dffb3fcdc6fdfdbdf7c8da7c95f7bf73f4a5fefbcfbf57bfbd36eeff9ef96ef8fffbeffeafdee4c6efffffeb1fddf9fcf7bf73dafffebaff7fee2fffa7ff53dfcfffef9bbf95bb319fe76fb7fd7bf3ffffff7f6f9d4ff7f769fbfdfbfff5fdfbbffef9ffff7b6cf2ff573b3ff5f3fecaffff0fedd63f2f962f5fef5efafefd77ffffdbfff6cfbdbbff6c5fff3fbbfdfdf7a9a7df1b6eaf6ff7f7bfff7beecffd397fe63cb6fb97ffeefe33e5d6e4eff7bff4dffffff5bceabfb2fec7a7fbdf5ebff7fd5f8ffefcd3bfef3fdbffc73efeadf07bf6bffe77d75ff3ffdbd","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x5017ce4","parentHash":"0x5247d1c9bd425e54921e060f482c4d3c51249a18705cc566a09359ab9ee0662d","receiptsRoot":"0x512d8d584fd2e6d3e68b4e2e9842cec14b6f4de9aaeb54ba9e20ae5694eaf31c","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x35133","stateRoot":"0x58bb5d2af652309718b50bdc7923b1fa7d07a9c6f9d1171c215d1734bfd6db80","timestamp":"0x69af29e9","transactions":["0x78f348510a3f130c38858ce2ac16c138a9798675d1e2ca1fa9f264c9a9106fdc","0x4a1c96c7543b6b4a4bca5ce3042489f524f0e91b4dceadcd1e06d28d688ce358","0x04ff780bc77f2a306b0e922216166d94c721b24bc421e7832d0ba7b963c59c4b","0xfd27c4e08a66024e5bf188d2843da11020bede5ecdfb97bd5088ed1252a1b52a","0xe16145eb9886fb467c8dc796a84181348e7901c79d60d37f1f407920c9f83137","0xfa52bae697ed6bf8f2a0a06ddb7bf07ac8c225264f1426af8331323d2e26b90f","0x2ff5148cb54dc23e4bfc5eab9cb723fd8ee99ddd1bc2f598190a2bff3a0652c6","0x69c5344eda2c2ebf3acb20247b48d77b7e1a4dfb296a40d9e2d79b41f872cec4","0x02f62e541e1cc011fbff2688e95aa211907af4f26b16dda2ef75eb36fd865005","0x470d6889cbe7c9d2bb46bf62362e68d9c89f8153903c041bfba167184260c70b","0xa04156fad73c835a6077e8c30a20b033b8a46eec29c0430b4d932769227b2179","0xe7e932178275614fb4944c4c344b78684686dc7eadba68c937cc4e86c1b1c5b0","0x131927f1d6087f755c601536ac04d60f48ffcb407fd3f446b62ccffaa884c881","0x0ec77359fd8531fe2480c2f5f1a513809c61d3ebc049c119af0f0104d28b73ae","0xd522956638cb789cb77f4d4a5301022956207435c8f10a0817bf1760676d9cbd","0x26d88df750e0f6ac7016dc2ba2804a70d76030a9c1bcd07e266df1dba54df133","0xb5c7a8aa4844bcd9e30e7960c83cf6b25938d7d418685ccb21a34a4be6d68f5f","0xc8d96200ab2b4bfb8833be03090154851f952978bee5ace5b4c737247918ab0c","0xca6ec0bf6abea8e2b07fe839141fbdd0f41e115e94a806bb93eec192d7fea011","0x267a15a38acea012ad41ac8d2a599d84c9d958932c969420c29ffda173ef435d","0x9d208d89fffc4474158ae08b6493b3b8a0258acc1292aa720dfe3b9105c9f96f","0x259c8755d72e2962db1ffd05468a6df8a30666c3fdb3568c6f9af35dbfa9d907","0x97f132a85b9a69bf9138695c4cb050654ed23082f1b806a9dd53ac3e389baa30","0xee1c3a1cb8aadc80b7e58b5ba01e6e69410dc65b1b535de6d8afb72209b5dee0","0x0ccebcf383f09457bf0beb96a86ace25bc7fa41d193730d5878aca1d464a0283","0xd39f77e418380107688d024b1f3752744122032069a54d9247f1e89e5a33daf8","0x8ef08e565c8ce6368e90db40eaaee5605cc6e31625a469ab2fb09f3e9e2cfef7","0x891d4de48e0fef3bbef3165e5e5bcf6320a4578e84e63970be5a81868b822ca8","0x8144be28808073cd9e3c62769ce2f1a4bc25cd27d3c75c581fbab86bf4279c84","0x2bd913e9a50ba721ec4f1c0b943fb9156d1fd23fd356ed921de84bfcd93609d5","0x8d857b39dc459f330145e1c2f5d9929013210137d36b4ab1bd7f61af8d95b88e","0xab34328061c0926edb51779b6d8505fc7dda02b047d6c2da1a86a6996343bbb2","0x41ac901fe60c65ab115b44dea4262dab5766b016991cdee22b601f01c3ae1bc8","0xb8f80ba5e260dc4a5fc5c0d77876b85b495c8316e068297aa3853328a5bdd267","0xf7d63a354d962502fe4df994f0e165d32fb711b282bbcdb20e5db74360da12bc","0xd900e275a31a97f5f0d66caefab743992fc5cd281622edee50ca446a367eac55","0xd0d2d8242a04c78f739be2c899c4d17dc964c4daf0a0b76ee8bb3d964f17d831","0x391d6d3d83231a3dfbe1d377cf581744c9ad6dabdb32ba8ff786d359be1bdfa1","0x79ffa7f5adc8e80d9ccd7fabccbe5432400b7678355ef92a44083ae73680c5b3","0xc29fde5f7e7003c299c3b86d3fdf308cfcfcbc79c9e3f13df25704431b0f5455","0xdeb90f17a972f688bd343426d454e05be03325672bcaaeb71fbdfec1a4b913a4","0x023cf36f8acbcf7e4fe893a85f09fd8ae484510195557fca9afd3f7696258607","0x5e7ef923c594abbf97f02d1ac33635311c2bc2d4189093d9f68eb8826ab4df81","0x53fe5b02ed01c959322adde8375f52dc3009409b30d03bee59be502318a3d7e1","0xbd1567cf06d5b62723a5cd18f1ec4d59c2db1f9156080deae13cc97c9e3b70eb","0x9af1223d3f49ce55961ce7ed20874acd92bebbab55b3ebd03c5957875b4ac3ed","0x3a08a920ebd1a7e53a6087cb119e89dffee2782844d262afe442a00f9280bdb5","0xec79aa9268e6a0d0de429ec4e071dde2ada47a8bec6039e7d9122a99216b6cd9","0x3082f7e8102bd4c0eaae2455d203d4eca1bab69dd6a221720df9493392a66876","0xc624a5b76ec49269c1ac3406ff965cf612f10d4f3a4837b41b0ec041edf33e2a","0x4bd20915c9f72a85490a6ee373f13a067b31d96ba97a1e850b16fe566ec8d525","0x4e18a85c0819cdbcacd83ab093bc7b8634665f660d6f9bcb7ab3b41b738ec595","0xddebc670f2d1f427aa628a140d266c6efedc1f68156b06cb6d22c0ab2358df21","0xb9ced02960f025fc834d6a46bcbf09f826f7076e3fe9d09c86bac8fb0fb7c4c4","0x9d730e115c3da9a95142348389a694665072c2ca20e2e7f7307b1151034a8a9b","0x4ae2a59e87b26a6bf440ec30b2b0aa82a58f16994dd01187336baa1bba66ae37","0xdb59ee82c977d3f558d2dccb8e4454be4a0a2a78065ff39cd5068694794dfdce","0x41500c036ac90fb8ec72b925bf67b6476004404490a5d043b241b98d715e9791","0x3a3a9bc3896023a43e31a3347efe7ee4e72d3cfa2def51c1133332abbceb6fa1","0x0e145757883daf0e4ea0adce9ff0405a7b1ba5ffe722158ecaab915a51b8401f","0xcdab875b0b59e7ccf5e232eeb27ed347041cfac019876525dc92fd5e21d26d8a","0x43adf7ab781deca5266fb0cf414fe5d03a061b397b791bd184199176fef8c2d3","0x22f55a0e2009d61c683290ee2049fd5e5597efd18728234fed930af352aab733","0x83641ba81683c0b7c2d0ae370c33bf5d057ad12cef7971ef3178c1924f8139ff","0x362a6fc24e6f5b32bec342f88b7c75b9890a947e7a52a311862a2f696aef3c1b","0x84e14da0b004593b02058e1512efd29a59c7607e4a16377ff44fec39bad548ae","0x0ef846ea59891938d84f474c466314c7b8b2eb1483d8b60ed2022d858bb014db","0xf91e04043aa180545b1d81f4c3f073964338c5bbaccf2bba93ce1d691e5cdab0","0x325a1b4e4b1597f36a160110c11a4ceac4d1f406f05697533e52ad6e91d82452","0x52b56a4ac5a1075c9dd529ce166be8470dc8239e18c44e7c5a311317ba4d86d0","0xd557ccfb66fed4a6498c27ba9e9b58baff7e28e947572b768c6fb5a5272c87b6","0xb4425b5e20c870854068b29936085bfd28fecd806a9e5cc773245b7f83b21c12","0xc45a0ef8e9ab93580b4c1fd2ba4e5ede7c908acd2b8a0a84e422b065bd31d597","0x954a319fd59430262d0f162d59351f5a9c8eb3795850406a55d29775dcfac2ab","0x696ced7c5e713e276a921585b279f3b9b79f2a7947249a8baad2c2ff95fa4e94","0x7226f989ba542b6452bafe9027bb79d6dd231ebf33c7c1ec89bb507f96ab4710","0xffef08c497469e800ab0ddb7cb436099db360a087a9ce54d3a00174fd6954fbb","0x88b8451f159b14307b9b2dd9638515751f58575072442f777dd790ad8cd8bc9b","0xe176e5466f6b74867c883b06cb44e89f68427f0750f6d3da875b2fe3b16fd94f","0xf7abdab8f0f9c0ca5d5f37ac4ccf56e719a8a709b7aadd4fdba799aefce953dd","0x808fc8fb9b2a975dda2a6fb1c267353aa3ca8ff68906a786252cb0c26cef2f51","0x5abf8deabb0eaf018fb98942bb1fa5abe3bde0b4ea052ec0d65c176533df326c","0xe9aa5214d0af6a6423e0db881b8bf476054aaa3e9f4c440420bf429508c7f1e2","0x55a93d6283d64c9c1c68f6d89a22127c2c1c4b1d273cca31fc8fdb3e7366ffc1","0x1464f8bec0a28323c4260f7e281ac50a9b14eec8d8dfb6eadd3999601bcfe4e6","0xbf058bf1b118b9e0a666a11d6e996777a4df6dc4e946f9be938afaf45ecda4bf","0x30df95fff326ae0b9bf35ad6326569a6457049d89cdf018b1f7ff647f2298e35","0xd6f35f0ffd7cc0fa0987eed4ce1a2e5894388d3745d11d5b37a0af7ba5861a7b","0x2db309f7a8d211a0c83626b73e3f3cf92b3a94ac853dc22d4a8a2fab439ec1d3","0x2b744f371f48b9e50042e5d777acd086b4a6ddfcf7fd9498a92c66899b959efa","0x59f1cbb116e2c6f6cfb44df6c493210edf7158c8219b94413ef5c4e5ea5b1cd5","0x175b9b4170a8e21597a2be9f0929e82a3e919d163b347f0a71436ab0bfef8159","0x9b4335f2f0cf42fcaa9f6e07a19ae24b87dab0244b3f33a5e0277df58fc9a5db","0x6df065193352e54128987aaffb9d0bf7684e3c299a8b63678468f3ab935dadfb","0xb9d27db50ed7b340f6778fc3e5adb4e6bbcc7b88dbf7cb804341f39dddfc0c2a","0xcf833dcdc1371bd3396b49f5a779cb53d76a40abe450a84314ac7faf5b1672d1","0x1a6dceb23130f08a12480bf100982d3ebba86edad9e171e721e3caa10d0dbea6","0x3ce1bdbbcd2b5442cf1135705f962831f5cfe7eb30d1ca38bbaeff1f093da733","0x091addd21ed2d8f353c214ab829bbbc36beb8cb6f9105ca4d6044259a63cbe54","0xa8fb046cfbc0eff4cd8751b642636e019a96a5804408672ddc11872ff3767634","0xe9a01497d6f5c7cb8eb671432a00a3d1241cbb15ae67475370678866e174639e","0xe72b4cabf1b5f440b97bb5b08af7f5c4e076bc5bb892b041aa717ab4f7ad940f","0xee2c84461922a9c87b91fe977241336c4e83444df7945a6a32d2efcc65301ef6","0x99d2af9f2cf33989b8bb61058888fdc92daf5e5915f5e7f20e0822aebd85e7a0","0x74a0729ff29d309d2be1c1454314a01ef59b02c16c5ebb1e18ec2b281db7168c","0x4abbae051f9dfacc9f40bc914ef9c2405028fd9b53118f374cc6fe4e0d7e69f3","0x4708e152441d546a0bca6bf008c0557be545ffbecee07f72cb579d0939d7e719","0x79e3885e38c80bc3976bc68b3f256dfe30b7a4137d77e76e7ca57a7940008da0","0xc5e889e112303f0610a1c51bd946338c86eec9186bf227e7f676e16b0bab2606","0x71fc377d24c84e91672e654e4d1f992edb8595bd228aee278aed837b76360b1c","0x3b1b78dcd86424435b191d0a239371bd568d23a300acd63d722bd16149404f05","0x40af5242fbb80539032bc434fd04b6c72123b7fc78a834e16754174b5bece393","0x281d215c0c68c8ab036f3889f13c8bd8a39cc1a40f61e71029cfc7b35c1c4169","0x8839502dfbeae7edad0d838cebd6f85ed96d68c4555f52f0dfa73b79369cc01d","0x6c90cdd18882acc5dbc8ebb112e23acfe5e59b8030910a48cf63c64b29232f65","0x9a6d28c9131e66d441492b73f4c1bde1918bad5cbf13e306aab030b035c569ba","0x5e8fabc0521728f9507b7b785e0108cb59b2524eff0ee8cf5216b494f3a8210f","0x251bd3466bb8964137948790611d70c831f41188f2249758ab07b2d6743555ef","0x28e4bf2dad6738ae27f6bd46093c12b1a72cea5405a2abcd425ce2796764468d","0x1a9e898af876a0ba32b916f6cbcbd3eb107f2cea61a0b14dc65f514a180e69fa","0xc4597100ca103148816d10abae96123cee1da5a4441c83a372be3ac29b9ea6cf","0x725b613a43bf536c3606772e7681b33133affe47ed8496a59b7891a1a5a3d074","0x70b8855ea5e935cbd711c0a9583efff6cbceef7e754fd2bd0e1c4d0c441b3403","0xa1dfb87b2e985781faef9eda689c6d7333bc828f82164591bd77d4d7e357d123","0x880e4ee6eb34f48a413095ccddbb9930a0d1fa9cd52d5d5e1097aaeb4a2accb3","0x7a51b337529732e5718a6f1bdf7bbc5c65f0c1a025c8a14925ce772ef42eb65c","0x731148c4ec5358b1f27dda1b6c30e3829f47568ccf87bebcd1f036db55db40c8","0xd8bca217dfb6a3e644993a750db36c3c48c288acbd56acd9103607ecbeacdfb0","0x2c652c5df3a74ad225ac7f604adff7af0f338f9f581cc4e7ebba9d1e55864cda","0x4236e682c35417d4cbae601eaae0fb11eadd3bd47e4109a7a552b1eafed1f602","0xd74b73cdf7bddcc054283315b9476b5047e3fa5f3c5b33968d83e66ca74a7636","0x6f7a97cfe4d4bd0d8a02713b37fc08687d79ae582256be30cffd8a3ed249ef00","0x8a937cda049b4abbb82bdbf1fb9064366dad095859e483d6dd0ad7acabf82324","0x1bb8c6e821f25ce13868d16549cf7275baf131193734ea64947d00d81c84d4d7","0xcea3201f457277a31a2628be93d16a56c65734467c17a91a30a9973fa10a9e3c","0xa3cc92527df20f955d9caf42055d9f579d13a52ede001e72360bec5431450dea","0x77481b1e01ce979a2cf0e8f1364e06c4ca90a43f4afdfd62275074c10c45a312","0x75c80d5252b74e9f774524599d85daedef1fbb857e8f2d49a0c0a64af07d57e3","0x738ec5a9d7e8041ab4fa4cd03932488aa211ee1a78ebc0ac96e683f92c2e944f","0xd362dc2f44d89319cc66cf41f66cbce6701a166186c479deabc7028585b6874c","0xed6c01ce27c2e1b7bf5edec72adf61ed6930ef8fb94a99e7a8907685d9ab69de","0xa9e9406c0bbe777e5f1a0c432e7074526413de23817a273b3130386613a09fe9","0x299fd4bb647947fb82d5251f4a7875ca93baed653000f6a0d1e663bb9a19e871","0x4cbdc8dde68f24715f0bb12434e2c7da9b563e87fa794b5b06ff604b9283b661","0x9fb7ad615eb3897c2a174f09357bb7d9e46da74c0327798a6ef22de3db26672c","0x4bc8c1fbf8c7e8eb4947f81c892fa4ab05b862c21452b3ce09343e223cce5600","0x0ca2e24dc3f633db5be2eb457c2d08e7046dfd26385569454a30bf9bb68086de","0x5d0f38927839055393bdc29db06f33977b7b9f2fce88011497e43f665ea7fb00","0x0a5a8d6248285a31d07f055a298ce1be10c811330687b3ea5361e7e9af9ca137","0xc890fd8cd64b4613ab5017bcfd2fcc315c7bee1f66b16008fa874b9a10a6ae49","0xdd00d71f230d027a971837be866635b21b2ae81a9d24a13bfb7c49eaef90ad4f","0xf0c346123a67a404e0a2ff4dcacce72f9158da320466b16dac3c500f1c14eb43","0xb73abafc5e44e9ecf4162e3f3a7fbe230eadc0ca77dca3c1e7cb15d31ef07dfd","0xf870a34c2a16a8da8b60029ad865c49aff76efcebff22551ca625235fda5533b","0x434102f5594ee556f97fd8d6d137af6855d29c19bbf6ef6f883ff227c2c2768c","0x43760dcf7f75527f47a8e6459c98189974a3afa0e88255b336e923f61df910b8","0xe330967fd19b86b296c3bafec36a9982a73a5da26179d3299861817ff1488741","0x72229da46cbf86a9b1073afd5fbcd1b5ddf212e48ae9dc36c19ffe86078f38ba","0x5a3484926f8ec055d2907cb7485770fdfc000fc1491d4d877220e56fe0f26e93","0x60ab7b3862691c435d687d93b308bf26e84814577ad81dfebbf991c74c9ec084","0xbb6f3dbbcd4e74be26bc8aedfc31e14ef7b16a5df00f7b295c70fee0ebbb64b4","0xc60b62cf3a24d63fa6623b83d6ce73fefe88db98a774bbd3e40e519c18f2826e","0x5870da28a9c62863f2c8c69f089e5cb42f014417ec049317c067b095cd14aa55","0xaac4d88ad74608dee51d99b24bdc20a8d972de46618f263fb468cd0b1fda2b98","0xc012bf7df189c6d6d400a54601bb194afae44d2ed240f6b1fb1238192de43c34","0xc4da572920d3556ad88fb7ec85e45f59d05105c8ff5cee4b5f558e8ae5e26ae7","0x39bddcc04b58eaeaf8129486176641761c3b4ec3798b170247becd0a5ed58740","0x66c86f9cccfdc3be0dc633992af708453d0696ae0c260cfdecb4fd8b87f4e732","0x76f4f0bcd5d8455a5d63bcce638d69c1e64a2060f6f16b557073bdbceb2261fd","0x0a9377625b91d7f99cde941183444f4610586f09c1ab42554fb5d8efcfd33717","0x47761379617939eaa5de427c28338f196c897d9f1755494fa9f3e9a6a4bb1df6","0x41b2f63da50e1673421895676f8e60647d72b55f34ee53661e82d985f72438a7","0x6899ec69bb18c340887f37ea2827acd359971a3b7a8eacb681c3dbb9a771316a","0xe7d59cc977a303af5cd7151a08a1ab55dfd7fa73644394b249a6924292e5ccd0","0x72a614ac7626e8ad1eadf561596a043029eaf95717fdf2d672f4900c8482622a","0x08dcb548d11d22841cf533f6b6634b52c84b317e5fe857eecc660000e7ff4f12","0x2c5b4f0f4773905bec6703d98076b3c32c2307ab2528d4d1e1805b13ca20de22","0xd1fe00337258d21c18cf3c09d5e3147753f379874b37f04f9b4502699959bcfa","0xf6535ded4720be9b193e3b5f578f027472460cc1a5ba608e6c5c6f384ca7fb8b","0xe2a4e373c3392f9d7cd3499869665a90ab672a59c9091bee2ce38544302748df"],"transactionsRoot":"0x60860a2c7338c6bf98a597ae6a5d887719f62e14192ea8d191d17dfd864783e1","uncles":[]}}'
86
+ headers:
87
+ content-encoding:
88
+ - gzip
89
+ content-type:
90
+ - application/json
91
+ date:
92
+ - Mon, 9 Mar 2026 20:13:30 GMT
93
+ server:
94
+ - istio-envoy
95
+ transfer-encoding:
96
+ - chunked
97
+ x-alchemy-trace-id:
98
+ - ef65ed03-ca15-440d-b1c5-bcc9c1950905
99
+ x-envoy-upstream-service-time:
100
+ - "4"
101
+ status:
102
+ code: 200
103
+ message: OK
104
+ - request:
105
+ body: '{"jsonrpc": "2.0", "method": "eth_maxPriorityFeePerGas", "params": [],
106
+ "id": 1}'
107
+ headers:
108
+ Accept:
109
+ - "*/*"
110
+ Accept-Encoding:
111
+ - gzip, deflate
112
+ Connection:
113
+ - keep-alive
114
+ Content-Length:
115
+ - "79"
116
+ Content-Type:
117
+ - application/json
118
+ User-Agent:
119
+ - web3.py/7.14.1/web3.providers.rpc.rpc.HTTPProvider
120
+ method: POST
121
+ uri: http://example.org
122
+ response:
123
+ body:
124
+ string: '{"jsonrpc":"2.0","id":1,"result":"0x645849ef5"}
125
+
126
+ '
127
+ headers:
128
+ content-encoding:
129
+ - gzip
130
+ content-type:
131
+ - application/json
132
+ date:
133
+ - Mon, 9 Mar 2026 20:13:32 GMT
134
+ server:
135
+ - istio-envoy
136
+ transfer-encoding:
137
+ - chunked
138
+ x-alchemy-trace-id:
139
+ - 4cf67e9f-baf5-41b8-9353-a886051644dc
140
+ x-envoy-upstream-service-time:
141
+ - "12"
142
+ status:
143
+ code: 200
144
+ message: OK
145
+ version: 1
@@ -46,7 +46,7 @@ def pytest_recording_configure(config, vcr: VCR):
46
46
  @pytest.fixture(autouse=True)
47
47
  def vcr_config():
48
48
  return {
49
- "match_on": ["json_rpc"],
49
+ "match_on": ["method", "host", "json_rpc"],
50
50
  "allow_playback_repeats": True,
51
51
  "allowed_hosts": ["localhost", "127.0.0.1", "::1"],
52
52
  }
@@ -296,6 +296,7 @@ def test_build_user_operation(w3):
296
296
 
297
297
  userop = aa_bundler.Bundler(
298
298
  w3,
299
+ bundler_url="https://bundler.example.com/rpc",
299
300
  nonce_mode=aa_bundler.NonceMode.FIXED_KEY_LOCAL_NONCE,
300
301
  fixed_nonce_key=0xAE85C374AE0606ED34D0EE009A9CA43A757A8A46A32451,
301
302
  executor_pk=TEST_PRIVATE_KEY,
@@ -330,3 +331,59 @@ def test_build_user_operation(w3):
330
331
  ),
331
332
  "verificationGasLimit": "0xac76",
332
333
  }
334
+
335
+
336
+ @pytest.mark.vcr
337
+ def test_build_user_operation_execute_user_op(w3):
338
+ tx = aa_bundler.Tx(
339
+ value=0,
340
+ chain_id=137,
341
+ from_="0xE8B412158c205B0F605e0FC09dCdA27d3F140FE9",
342
+ target="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
343
+ data=HexBytes(
344
+ # 0x095ea7b3 -> approve(address,uint256)
345
+ "0x095ea7b30000000000000000000000007ace242f32208d836a2245df957c08547059bf45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" # noqa
346
+ ),
347
+ )
348
+
349
+ userop = aa_bundler.Bundler(
350
+ w3,
351
+ bundler_url="https://bundler.example.com/rpc",
352
+ nonce_mode=aa_bundler.NonceMode.FIXED_KEY_LOCAL_NONCE,
353
+ fixed_nonce_key=0xAE85C374AE0606ED34D0EE009A9CA43A757A8A46A32452,
354
+ executor_pk=TEST_PRIVATE_KEY,
355
+ entrypoint=ENTRYPOINT,
356
+ bundler_type="generic",
357
+ use_execute_user_op=True,
358
+ ).build_user_operation(tx)
359
+
360
+ assert userop.as_dict() == {
361
+ "callData": (
362
+ "0x8dd7712f" # executeUserOp selector
363
+ # followed by payload (address dest, uint256 value, bytes calldata)
364
+ "00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8" # private key signer
365
+ "0000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174" # dest
366
+ "0000000000000000000000000000000000000000000000000000000000000000" # value
367
+ "0000000000000000000000000000000000000000000000000000000000000080" # calldata offset
368
+ "0000000000000000000000000000000000000000000000000000000000000044" # calldata length
369
+ "095ea7b3" # Inner call starts here: approve(address,uint256)
370
+ "0000000000000000000000007ace242f32208d836a2245df957c08547059bf45"
371
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
372
+ "00000000000000000000000000000000000000000000000000000000" # padding for 32 byte alignment
373
+ ),
374
+ "callGasLimit": "0x10282",
375
+ "maxFeePerGas": "0x1a3df05188",
376
+ "maxPriorityFeePerGas": "0x645849ef5",
377
+ "nonce": "0xae85c374ae0606ed34d0ee009a9ca43a757a8a46a324520000000000000000",
378
+ "paymaster": None,
379
+ "paymasterData": "0x",
380
+ "paymasterPostOpGasLimit": "0x0",
381
+ "paymasterVerificationGasLimit": "0x0",
382
+ "preVerificationGas": "0xc0fe",
383
+ "sender": "0xE8B412158c205B0F605e0FC09dCdA27d3F140FE9",
384
+ "signature": (
385
+ "0xfffffffffffffffffffffffffffffff00000000000000000000000000000000"
386
+ "07aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
387
+ ),
388
+ "verificationGasLimit": "0x16471",
389
+ }
@@ -1,48 +0,0 @@
1
- interactions:
2
- - request:
3
- body: '{"jsonrpc": "2.0", "method": "alchemy_requestGasAndPaymasterAndData", "params":
4
- [{"policyId": "d80ed67a-d8bc-4cd1-90ad-b50e0a58c93e", "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
5
- "dummySignature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
6
- "userOperation": {"sender": "0xE8B412158c205B0F605e0FC09dCdA27d3F140FE9", "nonce":
7
- "0xae85c374ae0606ed34d0ee009a9ca43a757a8a46a324510000000000000000", "callData":
8
- "0xb61d27f60000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000007ace242f32208d836a2245df957c08547059bf45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000",
9
- "signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"},
10
- "overrides": {"maxFeePerGas": {"multiplier": 1}, "maxPriorityFeePerGas": {"multiplier":
11
- 1}, "callGasLimit": {"multiplier": 1}, "verificationGasLimit": {"multiplier":
12
- 1}}, "stateOverrideSet": {}}], "id": 0}'
13
- headers:
14
- Accept:
15
- - '*/*'
16
- Accept-Encoding:
17
- - gzip, deflate
18
- Connection:
19
- - keep-alive
20
- Content-Length:
21
- - '1328'
22
- Content-Type:
23
- - application/json
24
- User-Agent:
25
- - web3.py/7.14.0/web3.providers.rpc.rpc.HTTPProvider
26
- method: POST
27
- uri: https://polygon-mainnet.g.alchemy.com
28
- response:
29
- body:
30
- string: '{"jsonrpc":"2.0","id":0,"result":{"callGasLimit":"0xd912","paymasterVerificationGasLimit":"0x9b37","paymasterPostOpGasLimit":"0x0","verificationGasLimit":"0xac76","maxPriorityFeePerGas":"0x7aef40a00","paymaster":"0x2cc0c7981D846b9F2a16276556f6e8cb52BfB633","maxFeePerGas":"0xbaad142eb6","paymasterData":"0x00000000000000006982f2961b02e25cb873537d0a2383b9665992667628a8d22f8223e7951b2f6d2aeae55e03af6a6ec9774cf4303980e7e3b2ecab9b65a8bcce8aabe4fd89b81c0facdb4f1b","preVerificationGas":"0xbd54"}}'
31
- headers:
32
- content-length:
33
- - '493'
34
- content-type:
35
- - application/json
36
- date:
37
- - Wed, 04 Feb 2026 07:07:42 GMT
38
- server:
39
- - istio-envoy
40
- x-alchemy-trace-id:
41
- - c905c727e8b9bd9f1495b4112264ea51
42
- - 2a016524-94c2-4e82-bdb5-7a4c7035b07e
43
- x-envoy-upstream-service-time:
44
- - '122'
45
- status:
46
- code: 200
47
- message: OK
48
- version: 1
File without changes
File without changes
File without changes
File without changes