eth-prototype 1.3.0b2__py3-none-any.whl → 1.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.3.0b2
3
+ Version: 1.3.1
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
@@ -35,6 +35,7 @@ Requires-Dist: pytest-mock; extra == "testing"
35
35
  Requires-Dist: pytest-recording; extra == "testing"
36
36
  Requires-Dist: setuptools; extra == "testing"
37
37
  Requires-Dist: web3[tester]==7.*; extra == "testing"
38
+ Dynamic: license-file
38
39
 
39
40
  # eth-prototype
40
41
 
@@ -0,0 +1,18 @@
1
+ eth_prototype-1.3.1.dist-info/licenses/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
2
+ eth_prototype-1.3.1.dist-info/licenses/LICENSE.txt,sha256=U_Q6_nDYDwZPIuhttHi37hXZ2qU2-HlV2geo9hzHXFw,1087
3
+ ethproto/__init__.py,sha256=YWkAFysBp4tZjLWWB2FFmp5yG23pUYhQvgQW9b3soXs,579
4
+ ethproto/aa_bundler.py,sha256=hwUWkd-9upeUb0_ZJ2mPs8mAt-lmp-2F6xkO8GA1fTw,15793
5
+ ethproto/build_artifacts.py,sha256=whIXEqnh5f89UYu4Cb3KDigGV7juUCbDnfZkg-SYMKA,9878
6
+ ethproto/contracts.py,sha256=rNVbCK1hURy7lWKhzSdXgVWo3wx9O_Ghk-6PfgOsRNk,18662
7
+ ethproto/defender_relay.py,sha256=05A8TfRZwiBhCpo924Pf9CjfKSir2Wvgg1p_asFxJbw,1777
8
+ ethproto/w3wrappers.py,sha256=aA5yQ25d01s8gtWkGSxSEZlVRdq6JM1ceS3cfvSj4uM,22614
9
+ ethproto/wadray.py,sha256=JBsu5KcyU9k70bDK03T2IY6qPVFO30WbYPhwrAHdXao,8262
10
+ ethproto/wrappers.py,sha256=Mj2sgZmcLVmqsnNab6PqIXtNMMPyRVvUj2_8ButEd4w,17304
11
+ ethproto/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ ethproto/test_utils/factories.py,sha256=G8DnUDG_yThRxMTCkymzcjm9lR_ni0_ZmTsb3sEfIdI,1805
13
+ ethproto/test_utils/hardhat.py,sha256=HzTqIznu6zVd_-doL96ftFJ235ktDCQen1QDQbNuwfM,2361
14
+ ethproto/test_utils/vcr_utils.py,sha256=1FH2sgJlElSjWkJLuO3C7E2J-4HKyFvjAqkCnGRZJyk,797
15
+ eth_prototype-1.3.1.dist-info/METADATA,sha256=BjVmjZbubsPyMmDHyDtIqgYc5-T4uS1Xzd_lrMd2gZA,2650
16
+ eth_prototype-1.3.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
17
+ eth_prototype-1.3.1.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
18
+ eth_prototype-1.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
ethproto/aa_bundler.py CHANGED
@@ -41,7 +41,7 @@ NonceMode = Enum(
41
41
  ],
42
42
  )
43
43
 
44
- AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", type=NonceMode)
44
+ AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", enum=NonceMode)
45
45
  AA_BUNDLER_NONCE_KEY = env.int("AA_BUNDLER_NONCE_KEY", 0)
46
46
  AA_BUNDLER_MAX_GETNONCE_RETRIES = env.int("AA_BUNDLER_MAX_GETNONCE_RETRIES", 3)
47
47
 
@@ -333,9 +333,9 @@ class Bundler:
333
333
 
334
334
  def __str__(self):
335
335
  return (
336
- f"Bundler(type={self.bundler_type}, entrypoint={self.entrypoint}, nonce_mode={self.nonce_mode}"
337
- f"fixed_nonce_key={self.fixed_nonce_key}, verification_gas_factor={self.verification_gas_factor},"
338
- f"gas_limit_factor={self.gas_limit_factor}, priority_gas_price_factor={self.priority_gas_price_factor},"
336
+ f"Bundler(type={self.bundler_type}, entrypoint={self.entrypoint}, nonce_mode={self.nonce_mode}, "
337
+ f"fixed_nonce_key={self.fixed_nonce_key}, verification_gas_factor={self.verification_gas_factor}, "
338
+ f"gas_limit_factor={self.gas_limit_factor}, priority_gas_price_factor={self.priority_gas_price_factor}, "
339
339
  f"base_gas_price_factor={self.base_gas_price_factor})"
340
340
  )
341
341
 
@@ -6,10 +6,14 @@ import os.path
6
6
  import re
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
- from typing import Union, Tuple
9
+ from typing import Tuple, Union
10
10
 
11
11
  LIBRARY_PLACEHOLDER_MATCHER = re.compile(r"__\$[0-9a-f]{34}\$__")
12
12
 
13
+ CONTRACT_REF_MATCHER = re.compile(r"^(?:(?P<package>.*)/)?(?P<contract>[^@]+)(?:@(?P<version>.+))?$")
14
+
15
+ VERSION_MATCHER = re.compile(r"^\d+\.\d+\.\d+$")
16
+
13
17
 
14
18
  @dataclass
15
19
  class Artifact:
@@ -94,6 +98,7 @@ class ArtifactLibrary:
94
98
  self.lookup_paths = [Path(p).absolute() for p in paths]
95
99
  self._fullpath_cache = {}
96
100
  self._name_cache = {}
101
+ self._ref_cache = None
97
102
 
98
103
  def get_artifact(self, contract: str) -> Artifact:
99
104
  """Returns a build artifact by full contract path
@@ -139,3 +144,115 @@ class ArtifactLibrary:
139
144
  raise FileNotFoundError(f"Could not find artifact for {contract_name} on {self.lookup_paths}")
140
145
 
141
146
  return self._name_cache[contract_name]
147
+
148
+ def _load_ref_cache(self):
149
+ self._ref_cache = {}
150
+ for path in self.lookup_paths:
151
+ path = Path(path)
152
+ json_files = path.rglob("*.json")
153
+
154
+ for json_file in json_files:
155
+ # Skip build-info and debug files
156
+ if json_file.parent.stem == "build-info" or json_file.stem.endswith(".dbg"):
157
+ continue
158
+
159
+ parts = json_file.parts[len(path.parts) - 1 :]
160
+
161
+ # Find the first parent named either "build" or "artifacts"
162
+ try:
163
+ build_idx = next(i for i, part in enumerate(parts) if part in ("build", "artifacts"))
164
+ except StopIteration:
165
+ continue
166
+
167
+ if build_idx == 0:
168
+ # Our lookup path is already a build directory, we're probably looking at a hardhat build output
169
+ version = "local"
170
+ else:
171
+ # If we have a version number right before the build directory, we're probably looking at a
172
+ # verifiable binaries directory
173
+ version = parts[build_idx - 1]
174
+ if not VERSION_MATCHER.match(version):
175
+ version = "local"
176
+
177
+ if version == "local":
178
+ # For hardhat output dir, package is everything up to "contracts"
179
+ try:
180
+ package = "/".join(parts[build_idx + 1 : parts.index("contracts")])
181
+ except ValueError:
182
+ # There are exceptions, like @openzeppelin/contracts-upgradeable
183
+ # For those the package is everythin up to the contract directory
184
+ package = "/".join(parts[build_idx + 1 : -2])
185
+
186
+ else:
187
+ # For verifiable binaries dir, package is everything up to version
188
+ package = "/".join(parts[1 : build_idx - 1])
189
+
190
+ if json_file.stem not in self._ref_cache:
191
+ self._ref_cache[json_file.stem] = []
192
+ self._ref_cache[json_file.stem].append(
193
+ {
194
+ "path": json_file,
195
+ "package": package,
196
+ "version": version,
197
+ }
198
+ )
199
+
200
+ def _find_ref(self, contract_ref: str):
201
+ if self._ref_cache is None:
202
+ self._load_ref_cache()
203
+
204
+ ref_match = CONTRACT_REF_MATCHER.match(contract_ref)
205
+ if not ref_match:
206
+ raise ValueError(f"Invalid contract reference: {contract_ref}")
207
+
208
+ contract = ref_match.group("contract")
209
+ if contract not in self._ref_cache:
210
+ return None
211
+
212
+ ref = self._ref_cache[contract]
213
+
214
+ package = ref_match.group("package")
215
+ if package:
216
+ ref = [r for r in ref if r["package"] == package]
217
+
218
+ version = ref_match.group("version")
219
+ if version:
220
+ ref = [r for r in ref if r["version"] == version]
221
+
222
+ if not ref:
223
+ return None
224
+
225
+ # Sort by version ascending, forcing local to be last
226
+ ref = sorted(
227
+ ref,
228
+ key=lambda x: (
229
+ x["version"] == "local",
230
+ tuple(map(int, x["version"].split("."))) if x["version"] != "local" else None,
231
+ ),
232
+ )[-1]
233
+
234
+ return ref
235
+
236
+ def get_artifact_by_ref(self, contract_ref: str) -> Artifact:
237
+ """Returns a build artifact by looking for a matching contract reference.
238
+
239
+ This is compatible with the verifiable binaries structure.
240
+
241
+ Accepts the following kind of references:
242
+
243
+ - <ContractClass>
244
+ - <ContractClass>@<version>
245
+ - <package>/<ContractClass>
246
+ - <package>/<ContractClass>@<version>
247
+
248
+ If version is not specified it uses the latest version available.
249
+
250
+ Calling with contract_ref <ContractClass>@local is the same as calling get_artifact_by_name(<ContractClass>).
251
+ """
252
+ ref = self._find_ref(contract_ref)
253
+
254
+ if ref is None:
255
+ raise FileNotFoundError(f"Could not find artifact for {contract_ref} on {self.lookup_paths}")
256
+
257
+ with open(ref["path"]) as f:
258
+ return Artifact(**json.load(f))
ethproto/w3wrappers.py CHANGED
@@ -5,9 +5,10 @@ from typing import Iterator, List, Union
5
5
  from environs import Env
6
6
  from eth_account.account import Account, LocalAccount
7
7
  from eth_account.signers.base import BaseAccount
8
- from eth_utils.abi import event_abi_to_log_topic
8
+ from eth_utils import add_0x_prefix, event_abi_to_log_topic
9
9
  from hexbytes import HexBytes
10
10
  from web3.contract import Contract
11
+ from web3.contract.contract import ContractEvent
11
12
  from web3.exceptions import ContractLogicError, ExtraDataLengthError
12
13
  from web3.middleware import ExtraDataToPOAMiddleware
13
14
 
@@ -437,7 +438,7 @@ class W3Provider(BaseProvider):
437
438
  self.tx_kwargs = tx_kwargs or {}
438
439
 
439
440
  def get_contract_def(self, eth_contract):
440
- return self.artifact_library.get_artifact_by_name(eth_contract)
441
+ return self.artifact_library.get_artifact_by_ref(eth_contract)
441
442
 
442
443
  def get_contract_factory(self, eth_contract):
443
444
  contract_def = self.get_contract_def(eth_contract)
@@ -448,7 +449,9 @@ class W3Provider(BaseProvider):
448
449
  kwargs["from"] = from_
449
450
  return self.construct(factory, init_params, kwargs)
450
451
 
451
- def get_events(self, eth_wrapper, event_name, filter_kwargs={}):
452
+ def get_events(
453
+ self, eth_wrapper, event_names: Union[list[Union[str, ContractEvent]], str], filter_kwargs=None
454
+ ):
452
455
  """Returns a list of events given a filter, like this:
453
456
 
454
457
  >>> provider.get_events(currencywrapper, "Transfer", dict(from_block=0))
@@ -468,12 +471,36 @@ class W3Provider(BaseProvider):
468
471
  'blockNumber': 23
469
472
  })]
470
473
  """
471
- contract = eth_wrapper.contract
472
- event = getattr(contract.events, event_name)
473
- if "from_block" not in filter_kwargs:
474
- filter_kwargs["from_block"] = self.get_first_block(eth_wrapper)
475
- event_filter = event.create_filter(**filter_kwargs)
476
- return event_filter.get_all_entries()
474
+ if filter_kwargs is None:
475
+ filter_kwargs = {}
476
+
477
+ if isinstance(event_names, (str, ContractEvent)):
478
+ # Backwards compatibility, if we don't get a list we're getting a single event name/ref
479
+ event_names = [event_names]
480
+
481
+ topics = {}
482
+
483
+ for name in event_names:
484
+ if isinstance(name, str):
485
+ # We got a plain event name, let's get the event from the contract
486
+ event: ContractEvent = getattr(eth_wrapper.contract.events, name)
487
+ else:
488
+ # Assume we already got an event reference
489
+ event: ContractEvent = name
490
+
491
+ topics[event.topic] = event
492
+
493
+ filter_params = {
494
+ "fromBlock": filter_kwargs.get("from_block", self.get_first_block(eth_wrapper)),
495
+ "toBlock": filter_kwargs.get("to_block", "latest"),
496
+ "address": eth_wrapper.contract.address,
497
+ "topics": [list(topics.keys())],
498
+ }
499
+
500
+ logs = self.w3.eth.get_logs(filter_params)
501
+
502
+ parsed_events = [topics[add_0x_prefix(log["topics"][0].hex())].process_log(log) for log in logs]
503
+ return parsed_events
477
504
 
478
505
  def init_eth_wrapper(self, eth_wrapper, owner, init_params, kwargs):
479
506
  eth_wrapper.owner = self.address_book.get_account(owner)
@@ -1,18 +0,0 @@
1
- ethproto/__init__.py,sha256=YWkAFysBp4tZjLWWB2FFmp5yG23pUYhQvgQW9b3soXs,579
2
- ethproto/aa_bundler.py,sha256=HupCu7fRCwlE556WXDqytRU5GEFLdBoFYzjpqDHBtE4,15789
3
- ethproto/build_artifacts.py,sha256=xwCd5hJUHP82IA-y3sSfX6fV15kjCGtV19RxNRcoor0,5441
4
- ethproto/contracts.py,sha256=rNVbCK1hURy7lWKhzSdXgVWo3wx9O_Ghk-6PfgOsRNk,18662
5
- ethproto/defender_relay.py,sha256=05A8TfRZwiBhCpo924Pf9CjfKSir2Wvgg1p_asFxJbw,1777
6
- ethproto/w3wrappers.py,sha256=lmyfJLhQmPYrclmbzzsthH2cShlQb6LwavKq30jqxFE,21651
7
- ethproto/wadray.py,sha256=JBsu5KcyU9k70bDK03T2IY6qPVFO30WbYPhwrAHdXao,8262
8
- ethproto/wrappers.py,sha256=Mj2sgZmcLVmqsnNab6PqIXtNMMPyRVvUj2_8ButEd4w,17304
9
- ethproto/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- ethproto/test_utils/factories.py,sha256=G8DnUDG_yThRxMTCkymzcjm9lR_ni0_ZmTsb3sEfIdI,1805
11
- ethproto/test_utils/hardhat.py,sha256=HzTqIznu6zVd_-doL96ftFJ235ktDCQen1QDQbNuwfM,2361
12
- ethproto/test_utils/vcr_utils.py,sha256=1FH2sgJlElSjWkJLuO3C7E2J-4HKyFvjAqkCnGRZJyk,797
13
- eth_prototype-1.3.0b2.dist-info/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
14
- eth_prototype-1.3.0b2.dist-info/LICENSE.txt,sha256=U_Q6_nDYDwZPIuhttHi37hXZ2qU2-HlV2geo9hzHXFw,1087
15
- eth_prototype-1.3.0b2.dist-info/METADATA,sha256=VGLInjZc46w_OraflEsO2vG9yXmA_crJL0DVP5dFLvM,2630
16
- eth_prototype-1.3.0b2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
17
- eth_prototype-1.3.0b2.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
18
- eth_prototype-1.3.0b2.dist-info/RECORD,,