eth-prototype 1.4.1__py3-none-any.whl → 1.5.0b2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.4.1
3
+ Version: 1.5.0b2
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,7 +1,7 @@
1
- eth_prototype-1.4.1.dist-info/licenses/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
2
- eth_prototype-1.4.1.dist-info/licenses/LICENSE.txt,sha256=U_Q6_nDYDwZPIuhttHi37hXZ2qU2-HlV2geo9hzHXFw,1087
1
+ eth_prototype-1.5.0b2.dist-info/licenses/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
2
+ eth_prototype-1.5.0b2.dist-info/licenses/LICENSE.txt,sha256=U_Q6_nDYDwZPIuhttHi37hXZ2qU2-HlV2geo9hzHXFw,1087
3
3
  ethproto/__init__.py,sha256=YWkAFysBp4tZjLWWB2FFmp5yG23pUYhQvgQW9b3soXs,579
4
- ethproto/aa_bundler.py,sha256=W47fIA_w7orUu7yczfHPaHpUJRFh5op0YZHPgW3Iwik,17420
4
+ ethproto/aa_bundler.py,sha256=mzEB0s5DX58wKsi9xb8dB9En9nK3D3ExrI3OOsqJb7M,22521
5
5
  ethproto/build_artifacts.py,sha256=3t-MTSFZU1pQtDwf65zNJkceWleLRlzcEo8CFpu4_6s,10031
6
6
  ethproto/contracts.py,sha256=R9lWKPe77jkPBXx798qhnVPWSFF2nhz4AaYowipNIBc,22625
7
7
  ethproto/defender_relay.py,sha256=05A8TfRZwiBhCpo924Pf9CjfKSir2Wvgg1p_asFxJbw,1777
@@ -12,7 +12,7 @@ ethproto/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
12
12
  ethproto/test_utils/factories.py,sha256=G8DnUDG_yThRxMTCkymzcjm9lR_ni0_ZmTsb3sEfIdI,1805
13
13
  ethproto/test_utils/hardhat.py,sha256=HzTqIznu6zVd_-doL96ftFJ235ktDCQen1QDQbNuwfM,2361
14
14
  ethproto/test_utils/vcr_utils.py,sha256=1FH2sgJlElSjWkJLuO3C7E2J-4HKyFvjAqkCnGRZJyk,797
15
- eth_prototype-1.4.1.dist-info/METADATA,sha256=5AYCz9-qpNclDeljRLe4nRHZhTap4Ky1FhOHXUKwFyY,2695
16
- eth_prototype-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- eth_prototype-1.4.1.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
18
- eth_prototype-1.4.1.dist-info/RECORD,,
15
+ eth_prototype-1.5.0b2.dist-info/METADATA,sha256=AYseONs04OnkUFgVa4t_z0669tv-uS5Nrc-dbeAxZkg,2697
16
+ eth_prototype-1.5.0b2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
17
+ eth_prototype-1.5.0b2.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
18
+ eth_prototype-1.5.0b2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
ethproto/aa_bundler.py CHANGED
@@ -7,11 +7,13 @@ from warnings import warn
7
7
 
8
8
  from environs import Env
9
9
  from eth_abi import encode
10
+ from eth_abi.packed import encode_packed
10
11
  from eth_account import Account
11
12
  from eth_account.messages import encode_defunct
12
13
  from eth_typing import HexAddress
13
14
  from eth_utils import add_0x_prefix, function_signature_to_4byte_selector
14
15
  from hexbytes import HexBytes
16
+ from requests import HTTPError
15
17
  from web3 import Web3
16
18
  from web3.constants import ADDRESS_ZERO
17
19
  from web3.types import StateOverride, TxParams
@@ -24,7 +26,7 @@ AA_BUNDLER_URL = env.str("AA_BUNDLER_URL", env.str("WEB3_PROVIDER_URI", "http://
24
26
  AA_BUNDLER_SENDER = env.str("AA_BUNDLER_SENDER", None)
25
27
  AA_BUNDLER_ENTRYPOINT = env.str("AA_BUNDLER_ENTRYPOINT", "0x0000000071727De22E5E9d8BAf0edAc6f37da032")
26
28
  AA_BUNDLER_EXECUTOR_PK = env.str("AA_BUNDLER_EXECUTOR_PK", None)
27
- AA_BUNDLER_PROVIDER = env.str("AA_BUNDLER_PROVIDER", "alchemy")
29
+ AA_BUNDLER_PROVIDER = env.str("AA_BUNDLER_PROVIDER", "generic")
28
30
  AA_BUNDLER_GAS_LIMIT_FACTOR = env.float("AA_BUNDLER_GAS_LIMIT_FACTOR", 1)
29
31
  AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR", 1)
30
32
  AA_BUNDLER_BASE_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_BASE_GAS_PRICE_FACTOR", 1)
@@ -47,7 +49,7 @@ NonceMode = Enum(
47
49
  AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", enum=NonceMode)
48
50
  AA_BUNDLER_NONCE_KEY = env.int("AA_BUNDLER_NONCE_KEY", 0)
49
51
  AA_BUNDLER_MAX_GETNONCE_RETRIES = env.int("AA_BUNDLER_MAX_GETNONCE_RETRIES", 3)
50
-
52
+ AA_BUNDLER_ALCHEMY_GAS_POLICY_ID = env.str("AA_BUNDLER_ALCHEMY_GAS_POLICY_ID", None)
51
53
 
52
54
  GET_NONCE_ABI = [
53
55
  {
@@ -81,6 +83,10 @@ class BundlerRevertError(RevertError):
81
83
  self.response = response
82
84
 
83
85
 
86
+ class BundlerError(Exception):
87
+ pass
88
+
89
+
84
90
  @dataclass(frozen=True)
85
91
  class UserOpEstimation:
86
92
  """eth_estimateUserOperationGas response"""
@@ -97,6 +103,21 @@ class GasPrice:
97
103
  max_fee_per_gas: int
98
104
 
99
105
 
106
+ @dataclass(frozen=True)
107
+ class PaymasterAndData:
108
+ paymaster: HexAddress
109
+ paymaster_data: HexBytes
110
+ paymaster_verification_gas_limit: int
111
+ paymaster_post_op_gas_limit: int
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class AlchemyGasAndPaymasterAndData:
116
+ estimation: UserOpEstimation
117
+ gas_price: GasPrice
118
+ paymaster_and_data: PaymasterAndData
119
+
120
+
100
121
  @dataclass(frozen=True)
101
122
  class Tx:
102
123
  target: HexAddress
@@ -141,7 +162,11 @@ class UserOperation:
141
162
  signature: HexBytes = DUMMY_SIGNATURE
142
163
 
143
164
  init_code: HexBytes = HexBytes("0x")
144
- paymaster_and_data: HexBytes = HexBytes("0x")
165
+
166
+ paymaster: HexAddress = None
167
+ paymaster_data: HexBytes = HexBytes("0x")
168
+ paymaster_verification_gas_limit: int = 0
169
+ paymaster_post_op_gas_limit: int = 0
145
170
 
146
171
  @classmethod
147
172
  def from_tx(cls, tx: Tx, nonce):
@@ -172,6 +197,10 @@ class UserOperation:
172
197
  "maxPriorityFeePerGas": "0x%x" % self.max_priority_fee_per_gas,
173
198
  "maxFeePerGas": "0x%x" % self.max_fee_per_gas,
174
199
  "signature": add_0x_prefix(self.signature.hex()),
200
+ "paymaster": self.paymaster,
201
+ "paymasterData": self.paymaster_data,
202
+ "paymasterVerificationGasLimit": "0x%x" % self.paymaster_verification_gas_limit,
203
+ "paymasterPostOpGasLimit": "0x%x" % self.paymaster_post_op_gas_limit,
175
204
  }
176
205
 
177
206
  def add_estimation(self, estimation: UserOpEstimation) -> "UserOperation":
@@ -189,6 +218,15 @@ class UserOperation:
189
218
  max_fee_per_gas=gas_price.max_fee_per_gas,
190
219
  )
191
220
 
221
+ def add_paymaster_and_data(self, paymaster_and_data: PaymasterAndData) -> "UserOperation":
222
+ return replace(
223
+ self,
224
+ paymaster=paymaster_and_data.paymaster,
225
+ paymaster_data=paymaster_and_data.paymaster_data,
226
+ paymaster_verification_gas_limit=paymaster_and_data.paymaster_verification_gas_limit,
227
+ paymaster_post_op_gas_limit=paymaster_and_data.paymaster_post_op_gas_limit,
228
+ )
229
+
192
230
  def sign(self, private_key: HexBytes, chain_id, entrypoint) -> "UserOperation":
193
231
  signature = Account.sign_message(
194
232
  encode_defunct(
@@ -225,7 +263,21 @@ class PackedUserOperation:
225
263
  pre_verification_gas=user_operation.pre_verification_gas,
226
264
  gas_fees=pack_two(user_operation.max_priority_fee_per_gas, user_operation.max_fee_per_gas),
227
265
  init_code=user_operation.init_code,
228
- paymaster_and_data=user_operation.paymaster_and_data,
266
+ paymaster_and_data=(
267
+ HexBytes(
268
+ encode_packed(
269
+ ["address", "uint128", "uint128", "bytes"],
270
+ [
271
+ user_operation.paymaster,
272
+ user_operation.paymaster_verification_gas_limit,
273
+ user_operation.paymaster_post_op_gas_limit,
274
+ user_operation.paymaster_data,
275
+ ],
276
+ )
277
+ )
278
+ if user_operation.paymaster is not None
279
+ else HexBytes("0x")
280
+ ).to_0x_hex(),
229
281
  signature=user_operation.signature,
230
282
  )
231
283
 
@@ -333,6 +385,7 @@ class Bundler:
333
385
  max_fee_per_gas: int = AA_BUNDLER_MAX_FEE_PER_GAS,
334
386
  executor_pk: HexBytes = AA_BUNDLER_EXECUTOR_PK,
335
387
  overrides: StateOverride = AA_BUNDLER_STATE_OVERRIDES,
388
+ alchemy_gas_policy_id: str = AA_BUNDLER_ALCHEMY_GAS_POLICY_ID,
336
389
  ):
337
390
  self.w3 = w3
338
391
  self.bundler = Web3(Web3.HTTPProvider(bundler_url), middleware=[])
@@ -351,6 +404,10 @@ class Bundler:
351
404
  # https://docs.alchemy.com/reference/eth-estimateuseroperationgas
352
405
  self.overrides = overrides
353
406
 
407
+ if alchemy_gas_policy_id is None and bundler_type == "alchemy":
408
+ raise BundlerError("Must provide alchemy_gas_policy_id when using alchemy bundler_type")
409
+ self.alchemy_gas_policy_id = alchemy_gas_policy_id
410
+
354
411
  def __str__(self):
355
412
  return (
356
413
  f"Bundler(type={self.bundler_type}, entrypoint={self.entrypoint}, nonce_mode={self.nonce_mode}, "
@@ -404,14 +461,68 @@ class Bundler:
404
461
  ),
405
462
  )
406
463
 
407
- def alchemy_gas_price(self):
408
- resp = self.bundler.provider.make_request("rundler_maxPriorityFeePerGas", [])
409
- if "error" in resp:
410
- raise BundlerRevertError(resp["error"]["message"], response=resp)
411
- max_priority_fee_per_gas = int(int(resp["result"], 16) * self.priority_gas_price_factor)
412
- max_fee_per_gas = min(max_priority_fee_per_gas + self.get_base_fee(), self.max_fee_per_gas)
464
+ def alchemy_estimation(self, user_operation: UserOperation) -> AlchemyGasAndPaymasterAndData:
465
+ try:
466
+ resp = self.bundler.provider.make_request(
467
+ "alchemy_requestGasAndPaymasterAndData",
468
+ [
469
+ {
470
+ "policyId": self.alchemy_gas_policy_id,
471
+ "entryPoint": self.entrypoint,
472
+ "dummySignature": DUMMY_SIGNATURE,
473
+ "userOperation": user_operation.as_reduced_dict(),
474
+ "overrides": {
475
+ "maxFeePerGas": {"multiplier": self.base_gas_price_factor},
476
+ "maxPriorityFeePerGas": {"multiplier": self.priority_gas_price_factor},
477
+ "callGasLimit": {"multiplier": self.gas_limit_factor},
478
+ "verificationGasLimit": {"multiplier": self.verification_gas_factor},
479
+ },
480
+ }
481
+ ],
482
+ )
483
+ except HTTPError as e:
484
+ raise BundlerRevertError(
485
+ f"HTTP error while requesting gas and paymaster data: {str(e)}",
486
+ userop=user_operation,
487
+ response=e.response,
488
+ ) from e
413
489
 
414
- return GasPrice(max_priority_fee_per_gas=max_priority_fee_per_gas, max_fee_per_gas=max_fee_per_gas)
490
+ if "error" in resp:
491
+ raise BundlerRevertError(resp["error"]["message"], userop=user_operation, response=resp)
492
+
493
+ # {
494
+ # "callGasLimit": "0x3dab",
495
+ # "paymasterVerificationGasLimit": "0x9afa",
496
+ # "paymasterPostOpGasLimit": "0x0",
497
+ # "verificationGasLimit": "0xac33",
498
+ # "maxPriorityFeePerGas": "0x7aef40a00",
499
+ # "paymaster": "0x2cc0c7981D846b9F2a16276556f6e8cb52BfB633",
500
+ # "maxFeePerGas": "0xaf9fe62e48",
501
+ # "paymasterData": "0xabcd...",
502
+ # "preVerificationGas": "0xb8ec"
503
+ # }
504
+
505
+ estimation = UserOpEstimation(
506
+ pre_verification_gas=int(resp["result"]["preVerificationGas"], 16),
507
+ verification_gas_limit=int(resp["result"]["verificationGasLimit"], 16),
508
+ call_gas_limit=int(resp["result"]["callGasLimit"], 16),
509
+ paymaster_verification_gas_limit=int(resp["result"]["paymasterVerificationGasLimit"], 16),
510
+ )
511
+ gas_price = GasPrice(
512
+ max_priority_fee_per_gas=int(resp["result"]["maxPriorityFeePerGas"], 16),
513
+ max_fee_per_gas=int(resp["result"]["maxFeePerGas"], 16),
514
+ )
515
+ paymaster_and_data = PaymasterAndData(
516
+ paymaster=resp["result"]["paymaster"],
517
+ paymaster_data=HexBytes(resp["result"]["paymasterData"]),
518
+ paymaster_verification_gas_limit=int(resp["result"]["paymasterVerificationGasLimit"], 16),
519
+ paymaster_post_op_gas_limit=int(resp["result"]["paymasterPostOpGasLimit"], 16),
520
+ )
521
+ return AlchemyGasAndPaymasterAndData(
522
+ estimation=estimation,
523
+ gas_price=gas_price,
524
+ paymaster_and_data=paymaster_and_data,
525
+ )
415
526
 
416
527
  def generic_gas_price(self):
417
528
  base_fee = self.get_base_fee()
@@ -426,21 +537,29 @@ class Bundler:
426
537
  consume_nonce(nonce_key, nonce)
427
538
 
428
539
  user_operation = UserOperation.from_tx(tx, make_nonce(nonce_key, nonce))
429
- estimation = self.estimate_user_operation_gas(user_operation)
430
-
431
- user_operation = user_operation.add_estimation(estimation)
432
540
 
433
541
  if self.bundler_type == "alchemy":
434
- gas_price = self.alchemy_gas_price()
542
+ estimation_and_paymaster = self.alchemy_estimation(user_operation)
543
+
544
+ user_operation = user_operation.add_estimation(estimation_and_paymaster.estimation)
545
+ user_operation = user_operation.add_gas_price(estimation_and_paymaster.gas_price)
546
+ user_operation = user_operation.add_paymaster_and_data(
547
+ estimation_and_paymaster.paymaster_and_data
548
+ )
435
549
 
436
550
  elif self.bundler_type == "generic":
551
+ estimation = self.estimate_user_operation_gas(user_operation)
552
+
553
+ user_operation = user_operation.add_estimation(estimation)
554
+
437
555
  gas_price = self.generic_gas_price()
438
556
 
557
+ user_operation = user_operation.add_gas_price(gas_price)
558
+
439
559
  else:
440
- warn(f"Unknown bundler_type: {self.bundler_type}")
441
- gas_price = GasPrice(0, 0)
560
+ raise BundlerError(f"Unknown bundler_type: {self.bundler_type}")
442
561
 
443
- return user_operation.add_gas_price(gas_price)
562
+ return user_operation
444
563
 
445
564
  def send_transaction(self, tx: Tx, retry_nonce=None):
446
565
  user_operation = self.build_user_operation(tx, retry_nonce).sign(