eth-prototype 1.3.0b1__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.
- {eth_prototype-1.3.0b1.dist-info → eth_prototype-1.3.1.dist-info}/METADATA +9 -7
- eth_prototype-1.3.1.dist-info/RECORD +18 -0
- {eth_prototype-1.3.0b1.dist-info → eth_prototype-1.3.1.dist-info}/WHEEL +1 -1
- ethproto/aa_bundler.py +20 -1
- ethproto/build_artifacts.py +118 -1
- ethproto/w3wrappers.py +40 -13
- ethproto/wrappers.py +1 -1
- eth_prototype-1.3.0b1.dist-info/RECORD +0 -18
- {eth_prototype-1.3.0b1.dist-info → eth_prototype-1.3.1.dist-info/licenses}/AUTHORS.rst +0 -0
- {eth_prototype-1.3.0b1.dist-info → eth_prototype-1.3.1.dist-info/licenses}/LICENSE.txt +0 -0
- {eth_prototype-1.3.0b1.dist-info → eth_prototype-1.3.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: eth-prototype
|
3
|
-
Version: 1.3.
|
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
|
@@ -26,14 +26,16 @@ Requires-Dist: boto3; extra == "defender"
|
|
26
26
|
Provides-Extra: gmpy2
|
27
27
|
Requires-Dist: gmpy2; extra == "gmpy2"
|
28
28
|
Provides-Extra: testing
|
29
|
-
Requires-Dist:
|
30
|
-
Requires-Dist:
|
29
|
+
Requires-Dist: boto3; extra == "testing"
|
30
|
+
Requires-Dist: factory-boy; extra == "testing"
|
31
31
|
Requires-Dist: gmpy2; extra == "testing"
|
32
|
+
Requires-Dist: pytest; extra == "testing"
|
32
33
|
Requires-Dist: pytest-cov; extra == "testing"
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist: boto3; extra == "testing"
|
34
|
+
Requires-Dist: pytest-mock; extra == "testing"
|
35
35
|
Requires-Dist: pytest-recording; extra == "testing"
|
36
|
-
Requires-Dist:
|
36
|
+
Requires-Dist: setuptools; extra == "testing"
|
37
|
+
Requires-Dist: web3[tester]==7.*; extra == "testing"
|
38
|
+
Dynamic: license-file
|
37
39
|
|
38
40
|
# eth-prototype
|
39
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,,
|
ethproto/aa_bundler.py
CHANGED
@@ -14,6 +14,7 @@ from eth_utils import add_0x_prefix, function_signature_to_4byte_selector
|
|
14
14
|
from hexbytes import HexBytes
|
15
15
|
from web3 import Web3
|
16
16
|
from web3.constants import ADDRESS_ZERO
|
17
|
+
from web3.types import TxParams
|
17
18
|
|
18
19
|
from .contracts import RevertError
|
19
20
|
|
@@ -40,7 +41,7 @@ NonceMode = Enum(
|
|
40
41
|
],
|
41
42
|
)
|
42
43
|
|
43
|
-
AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE",
|
44
|
+
AA_BUNDLER_NONCE_MODE = env.enum("AA_BUNDLER_NONCE_MODE", default="FIXED_KEY_LOCAL_NONCE", enum=NonceMode)
|
44
45
|
AA_BUNDLER_NONCE_KEY = env.int("AA_BUNDLER_NONCE_KEY", 0)
|
45
46
|
AA_BUNDLER_MAX_GETNONCE_RETRIES = env.int("AA_BUNDLER_MAX_GETNONCE_RETRIES", 3)
|
46
47
|
|
@@ -94,6 +95,16 @@ class Tx:
|
|
94
95
|
from_: HexAddress = ADDRESS_ZERO
|
95
96
|
chain_id: int = None
|
96
97
|
|
98
|
+
@classmethod
|
99
|
+
def from_tx_params(cls, params: TxParams) -> "Tx":
|
100
|
+
return cls(
|
101
|
+
target=params["to"],
|
102
|
+
data=HexBytes(params["data"]),
|
103
|
+
value=params["value"],
|
104
|
+
from_=params.get("from", ADDRESS_ZERO),
|
105
|
+
chain_id=params.get("chainId", None),
|
106
|
+
)
|
107
|
+
|
97
108
|
def as_execute_args(self):
|
98
109
|
return [self.target, self.value, self.data]
|
99
110
|
|
@@ -320,6 +331,14 @@ class Bundler:
|
|
320
331
|
self.base_gas_price_factor = base_gas_price_factor
|
321
332
|
self.executor_pk = executor_pk
|
322
333
|
|
334
|
+
def __str__(self):
|
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}, "
|
339
|
+
f"base_gas_price_factor={self.base_gas_price_factor})"
|
340
|
+
)
|
341
|
+
|
323
342
|
def get_nonce_and_key(self, tx: Tx, fetch=False):
|
324
343
|
nonce_key = tx.nonce_key
|
325
344
|
nonce = tx.nonce
|
ethproto/build_artifacts.py
CHANGED
@@ -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
|
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
|
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
|
|
@@ -111,7 +112,7 @@ def transact(provider, function, tx_kwargs):
|
|
111
112
|
}
|
112
113
|
)
|
113
114
|
signed_tx = from_.sign_transaction(tx)
|
114
|
-
tx_hash = provider.w3.eth.send_raw_transaction(signed_tx.
|
115
|
+
tx_hash = provider.w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
115
116
|
elif W3_TRANSACT_MODE == "defender-async":
|
116
117
|
from .defender_relay import send_transaction
|
117
118
|
|
@@ -211,8 +212,8 @@ class W3EnvAddressBook(AddressBook):
|
|
211
212
|
if isinstance(name, (Account, LocalAccount)):
|
212
213
|
return name
|
213
214
|
if name is None:
|
214
|
-
return self.ZERO
|
215
|
-
if
|
215
|
+
return list(self.signers.values())[0] if self.signers else self.ZERO
|
216
|
+
if isinstance(name, str) and name.startswith("0x"):
|
216
217
|
return name
|
217
218
|
if name in self.name_to_address:
|
218
219
|
return self.name_to_address[name]
|
@@ -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.
|
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(
|
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
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
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)
|
@@ -483,7 +510,7 @@ class W3Provider(BaseProvider):
|
|
483
510
|
for lib, _ in contract_def.libraries():
|
484
511
|
if lib not in libraries:
|
485
512
|
library_def = self.get_contract_factory(lib)
|
486
|
-
library = self.construct(library_def)
|
513
|
+
library = self.construct(library_def, transact_kwargs={"from": eth_wrapper.owner})
|
487
514
|
libraries[lib] = library.address
|
488
515
|
|
489
516
|
if libraries:
|
ethproto/wrappers.py
CHANGED
@@ -303,7 +303,7 @@ class ETHWrapper:
|
|
303
303
|
constructor_args = None
|
304
304
|
initialize_args = None
|
305
305
|
|
306
|
-
def __init__(self, owner=
|
306
|
+
def __init__(self, owner=None, *init_params, **kwargs):
|
307
307
|
self.provider_key = kwargs.get("provider_key", None)
|
308
308
|
init_params = self._parse_init_params(init_params, kwargs)
|
309
309
|
self.provider.init_eth_wrapper(self, owner, init_params, kwargs)
|
@@ -1,18 +0,0 @@
|
|
1
|
-
ethproto/__init__.py,sha256=YWkAFysBp4tZjLWWB2FFmp5yG23pUYhQvgQW9b3soXs,579
|
2
|
-
ethproto/aa_bundler.py,sha256=vVEiorWPpLq7ev8-wKUQ8fpgDZpaOz9LGlKGNFH0faU,14987
|
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=4ZEnJFrc8bV1qHG4dNdom4FL1gEUhgoJORY6cGzlJDk,21549
|
7
|
-
ethproto/wadray.py,sha256=JBsu5KcyU9k70bDK03T2IY6qPVFO30WbYPhwrAHdXao,8262
|
8
|
-
ethproto/wrappers.py,sha256=9qDwRDOXw3wquzvGfIsub-VPWm98GBWP7dHLFOUPWzg,17307
|
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.0b1.dist-info/AUTHORS.rst,sha256=Ui-05yYXtDZxna6o1yNcfdm8Jt68UIDQ01osiLxlYlU,95
|
14
|
-
eth_prototype-1.3.0b1.dist-info/LICENSE.txt,sha256=U_Q6_nDYDwZPIuhttHi37hXZ2qU2-HlV2geo9hzHXFw,1087
|
15
|
-
eth_prototype-1.3.0b1.dist-info/METADATA,sha256=Ipe-jiFcOwqo89hto7kwoZ52UetWAmwC7i8y_5km0_g,2583
|
16
|
-
eth_prototype-1.3.0b1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
17
|
-
eth_prototype-1.3.0b1.dist-info/top_level.txt,sha256=Dl0X7m6N1hxeo4JpGpSNqWC2gtsN0731g-DL1J0mpjc,9
|
18
|
-
eth_prototype-1.3.0b1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|