eth-prototype 1.4.1__py3-none-any.whl → 1.5.0b1__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.0b1
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.0b1.dist-info/licenses/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
2
+ eth_prototype-1.5.0b1.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=aECUJJzvdEDJCzM8qTET9m309xPnQfvfWVQskkIHEss,22162
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.0b1.dist-info/METADATA,sha256=7nJRJXEAzIvwO4C8xNG47vlspDOkhaGQs5crBAWy2TQ,2697
16
+ eth_prototype-1.5.0b1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
17
+ eth_prototype-1.5.0b1.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
18
+ eth_prototype-1.5.0b1.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,6 +7,7 @@ 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
@@ -24,7 +25,7 @@ AA_BUNDLER_URL = env.str("AA_BUNDLER_URL", env.str("WEB3_PROVIDER_URI", "http://
24
25
  AA_BUNDLER_SENDER = env.str("AA_BUNDLER_SENDER", None)
25
26
  AA_BUNDLER_ENTRYPOINT = env.str("AA_BUNDLER_ENTRYPOINT", "0x0000000071727De22E5E9d8BAf0edAc6f37da032")
26
27
  AA_BUNDLER_EXECUTOR_PK = env.str("AA_BUNDLER_EXECUTOR_PK", None)
27
- AA_BUNDLER_PROVIDER = env.str("AA_BUNDLER_PROVIDER", "alchemy")
28
+ AA_BUNDLER_PROVIDER = env.str("AA_BUNDLER_PROVIDER", "generic")
28
29
  AA_BUNDLER_GAS_LIMIT_FACTOR = env.float("AA_BUNDLER_GAS_LIMIT_FACTOR", 1)
29
30
  AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR", 1)
30
31
  AA_BUNDLER_BASE_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_BASE_GAS_PRICE_FACTOR", 1)
@@ -47,7 +48,7 @@ NonceMode = Enum(
47
48
  AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", enum=NonceMode)
48
49
  AA_BUNDLER_NONCE_KEY = env.int("AA_BUNDLER_NONCE_KEY", 0)
49
50
  AA_BUNDLER_MAX_GETNONCE_RETRIES = env.int("AA_BUNDLER_MAX_GETNONCE_RETRIES", 3)
50
-
51
+ AA_BUNDLER_ALCHEMY_GAS_POLICY_ID = env.str("AA_BUNDLER_ALCHEMY_GAS_POLICY_ID", None)
51
52
 
52
53
  GET_NONCE_ABI = [
53
54
  {
@@ -81,6 +82,10 @@ class BundlerRevertError(RevertError):
81
82
  self.response = response
82
83
 
83
84
 
85
+ class BundlerError(Exception):
86
+ pass
87
+
88
+
84
89
  @dataclass(frozen=True)
85
90
  class UserOpEstimation:
86
91
  """eth_estimateUserOperationGas response"""
@@ -97,6 +102,21 @@ class GasPrice:
97
102
  max_fee_per_gas: int
98
103
 
99
104
 
105
+ @dataclass(frozen=True)
106
+ class PaymasterAndData:
107
+ paymaster: HexAddress
108
+ paymaster_data: HexBytes
109
+ paymaster_verification_gas_limit: int
110
+ paymaster_post_op_gas_limit: int
111
+
112
+
113
+ @dataclass(frozen=True)
114
+ class AlchemyGasAndPaymasterAndData:
115
+ estimation: UserOpEstimation
116
+ gas_price: GasPrice
117
+ paymaster_and_data: PaymasterAndData
118
+
119
+
100
120
  @dataclass(frozen=True)
101
121
  class Tx:
102
122
  target: HexAddress
@@ -141,7 +161,11 @@ class UserOperation:
141
161
  signature: HexBytes = DUMMY_SIGNATURE
142
162
 
143
163
  init_code: HexBytes = HexBytes("0x")
144
- paymaster_and_data: HexBytes = HexBytes("0x")
164
+
165
+ paymaster: HexAddress = None
166
+ paymaster_data: HexBytes = HexBytes("0x")
167
+ paymaster_verification_gas_limit: int = 0
168
+ paymaster_post_op_gas_limit: int = 0
145
169
 
146
170
  @classmethod
147
171
  def from_tx(cls, tx: Tx, nonce):
@@ -172,6 +196,10 @@ class UserOperation:
172
196
  "maxPriorityFeePerGas": "0x%x" % self.max_priority_fee_per_gas,
173
197
  "maxFeePerGas": "0x%x" % self.max_fee_per_gas,
174
198
  "signature": add_0x_prefix(self.signature.hex()),
199
+ "paymaster": self.paymaster,
200
+ "paymasterData": self.paymaster_data,
201
+ "paymasterVerificationGasLimit": "0x%x" % self.paymaster_verification_gas_limit,
202
+ "paymasterPostOpGasLimit": "0x%x" % self.paymaster_post_op_gas_limit,
175
203
  }
176
204
 
177
205
  def add_estimation(self, estimation: UserOpEstimation) -> "UserOperation":
@@ -189,6 +217,15 @@ class UserOperation:
189
217
  max_fee_per_gas=gas_price.max_fee_per_gas,
190
218
  )
191
219
 
220
+ def add_paymaster_and_data(self, paymaster_and_data: PaymasterAndData) -> "UserOperation":
221
+ return replace(
222
+ self,
223
+ paymaster=paymaster_and_data.paymaster,
224
+ paymaster_data=paymaster_and_data.paymaster_data,
225
+ paymaster_verification_gas_limit=paymaster_and_data.paymaster_verification_gas_limit,
226
+ paymaster_post_op_gas_limit=paymaster_and_data.paymaster_post_op_gas_limit,
227
+ )
228
+
192
229
  def sign(self, private_key: HexBytes, chain_id, entrypoint) -> "UserOperation":
193
230
  signature = Account.sign_message(
194
231
  encode_defunct(
@@ -225,7 +262,21 @@ class PackedUserOperation:
225
262
  pre_verification_gas=user_operation.pre_verification_gas,
226
263
  gas_fees=pack_two(user_operation.max_priority_fee_per_gas, user_operation.max_fee_per_gas),
227
264
  init_code=user_operation.init_code,
228
- paymaster_and_data=user_operation.paymaster_and_data,
265
+ paymaster_and_data=(
266
+ HexBytes(
267
+ encode_packed(
268
+ ["address", "uint128", "uint128", "bytes"],
269
+ [
270
+ user_operation.paymaster,
271
+ user_operation.paymaster_verification_gas_limit,
272
+ user_operation.paymaster_post_op_gas_limit,
273
+ user_operation.paymaster_data,
274
+ ],
275
+ )
276
+ )
277
+ if user_operation.paymaster is not None
278
+ else HexBytes("0x")
279
+ ).to_0x_hex(),
229
280
  signature=user_operation.signature,
230
281
  )
231
282
 
@@ -333,6 +384,7 @@ class Bundler:
333
384
  max_fee_per_gas: int = AA_BUNDLER_MAX_FEE_PER_GAS,
334
385
  executor_pk: HexBytes = AA_BUNDLER_EXECUTOR_PK,
335
386
  overrides: StateOverride = AA_BUNDLER_STATE_OVERRIDES,
387
+ alchemy_gas_policy_id: str = AA_BUNDLER_ALCHEMY_GAS_POLICY_ID,
336
388
  ):
337
389
  self.w3 = w3
338
390
  self.bundler = Web3(Web3.HTTPProvider(bundler_url), middleware=[])
@@ -351,6 +403,10 @@ class Bundler:
351
403
  # https://docs.alchemy.com/reference/eth-estimateuseroperationgas
352
404
  self.overrides = overrides
353
405
 
406
+ if alchemy_gas_policy_id is None and bundler_type == "alchemy":
407
+ raise BundlerError("Must provide alchemy_gas_policy_id when using alchemy bundler_type")
408
+ self.alchemy_gas_policy_id = alchemy_gas_policy_id
409
+
354
410
  def __str__(self):
355
411
  return (
356
412
  f"Bundler(type={self.bundler_type}, entrypoint={self.entrypoint}, nonce_mode={self.nonce_mode}, "
@@ -404,14 +460,61 @@ class Bundler:
404
460
  ),
405
461
  )
406
462
 
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)
463
+ def alchemy_estimation(self, user_operation: UserOperation) -> AlchemyGasAndPaymasterAndData:
464
+ resp = self.bundler.provider.make_request(
465
+ "alchemy_requestGasAndPaymasterAndData",
466
+ [
467
+ {
468
+ "policyId": self.alchemy_gas_policy_id,
469
+ "entryPoint": self.entrypoint,
470
+ "dummySignature": DUMMY_SIGNATURE,
471
+ "userOperation": user_operation.as_reduced_dict(),
472
+ "overrides": {
473
+ "maxFeePerGas": {"multiplier": self.base_gas_price_factor},
474
+ "maxPriorityFeePerGas": {"multiplier": self.priority_gas_price_factor},
475
+ "callGasLimit": {"multiplier": self.gas_limit_factor},
476
+ "verificationGasLimit": {"multiplier": self.verification_gas_factor},
477
+ },
478
+ }
479
+ ],
480
+ )
413
481
 
414
- return GasPrice(max_priority_fee_per_gas=max_priority_fee_per_gas, max_fee_per_gas=max_fee_per_gas)
482
+ if "error" in resp:
483
+ raise BundlerRevertError(resp["error"]["message"], userop=user_operation, response=resp)
484
+
485
+ # {
486
+ # "callGasLimit": "0x3dab",
487
+ # "paymasterVerificationGasLimit": "0x9afa",
488
+ # "paymasterPostOpGasLimit": "0x0",
489
+ # "verificationGasLimit": "0xac33",
490
+ # "maxPriorityFeePerGas": "0x7aef40a00",
491
+ # "paymaster": "0x2cc0c7981D846b9F2a16276556f6e8cb52BfB633",
492
+ # "maxFeePerGas": "0xaf9fe62e48",
493
+ # "paymasterData": "0xabcd...",
494
+ # "preVerificationGas": "0xb8ec"
495
+ # }
496
+
497
+ estimation = UserOpEstimation(
498
+ pre_verification_gas=int(resp["result"]["preVerificationGas"], 16),
499
+ verification_gas_limit=int(resp["result"]["verificationGasLimit"], 16),
500
+ call_gas_limit=int(resp["result"]["callGasLimit"], 16),
501
+ paymaster_verification_gas_limit=int(resp["result"]["paymasterVerificationGasLimit"], 16),
502
+ )
503
+ gas_price = GasPrice(
504
+ max_priority_fee_per_gas=int(resp["result"]["maxPriorityFeePerGas"], 16),
505
+ max_fee_per_gas=int(resp["result"]["maxFeePerGas"], 16),
506
+ )
507
+ paymaster_and_data = PaymasterAndData(
508
+ paymaster=resp["result"]["paymaster"],
509
+ paymaster_data=HexBytes(resp["result"]["paymasterData"]),
510
+ paymaster_verification_gas_limit=int(resp["result"]["paymasterVerificationGasLimit"], 16),
511
+ paymaster_post_op_gas_limit=int(resp["result"]["paymasterPostOpGasLimit"], 16),
512
+ )
513
+ return AlchemyGasAndPaymasterAndData(
514
+ estimation=estimation,
515
+ gas_price=gas_price,
516
+ paymaster_and_data=paymaster_and_data,
517
+ )
415
518
 
416
519
  def generic_gas_price(self):
417
520
  base_fee = self.get_base_fee()
@@ -426,21 +529,29 @@ class Bundler:
426
529
  consume_nonce(nonce_key, nonce)
427
530
 
428
531
  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
532
 
433
533
  if self.bundler_type == "alchemy":
434
- gas_price = self.alchemy_gas_price()
534
+ estimation_and_paymaster = self.alchemy_estimation(user_operation)
535
+
536
+ user_operation = user_operation.add_estimation(estimation_and_paymaster.estimation)
537
+ user_operation = user_operation.add_gas_price(estimation_and_paymaster.gas_price)
538
+ user_operation = user_operation.add_paymaster_and_data(
539
+ estimation_and_paymaster.paymaster_and_data
540
+ )
435
541
 
436
542
  elif self.bundler_type == "generic":
543
+ estimation = self.estimate_user_operation_gas(user_operation)
544
+
545
+ user_operation = user_operation.add_estimation(estimation)
546
+
437
547
  gas_price = self.generic_gas_price()
438
548
 
549
+ user_operation = user_operation.add_gas_price(gas_price)
550
+
439
551
  else:
440
- warn(f"Unknown bundler_type: {self.bundler_type}")
441
- gas_price = GasPrice(0, 0)
552
+ raise BundlerError(f"Unknown bundler_type: {self.bundler_type}")
442
553
 
443
- return user_operation.add_gas_price(gas_price)
554
+ return user_operation
444
555
 
445
556
  def send_transaction(self, tx: Tx, retry_nonce=None):
446
557
  user_operation = self.build_user_operation(tx, retry_nonce).sign(