eth-prototype 1.3.0b1__tar.gz → 1.3.1__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 (71) hide show
  1. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/PKG-INFO +9 -7
  2. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/setup.cfg +6 -5
  3. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/PKG-INFO +9 -7
  4. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/SOURCES.txt +4 -1
  5. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/requires.txt +6 -5
  6. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/aa_bundler.py +20 -1
  7. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/build_artifacts.py +118 -1
  8. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/w3wrappers.py +40 -13
  9. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/wrappers.py +1 -1
  10. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/conftest.py +3 -3
  11. eth_prototype-1.3.1/tests/hardhat-project/verifiable-binaries/@anotherOrg/aPkg/1.0.2/build/contracts/TestCurrency.sol/TestCurrency.json +10 -0
  12. eth_prototype-1.3.1/tests/hardhat-project/verifiable-binaries/@org/pkg/0.2.1/build/contracts/TestCurrency.json +10 -0
  13. eth_prototype-1.3.1/tests/hardhat-project/verifiable-binaries/@org/pkg/0.3.0/build/contracts/TestCurrency.json +10 -0
  14. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_build_artifacts.py +56 -0
  15. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_w3.py +78 -5
  16. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tox.ini +1 -4
  17. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.coveragerc +0 -0
  18. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.github/workflows/publish.yaml +0 -0
  19. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.github/workflows/test.yaml +0 -0
  20. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.gitignore +0 -0
  21. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.isort.cfg +0 -0
  22. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.pre-commit-config.yaml +0 -0
  23. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/.readthedocs.yml +0 -0
  24. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/AUTHORS.rst +0 -0
  25. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/CHANGELOG.rst +0 -0
  26. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/LICENSE.txt +0 -0
  27. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/README.md +0 -0
  28. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/Makefile +0 -0
  29. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/_static/.gitignore +0 -0
  30. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/authors.rst +0 -0
  31. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/changelog.rst +0 -0
  32. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/conf.py +0 -0
  33. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/index.rst +0 -0
  34. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/license.rst +0 -0
  35. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/readme.rst +0 -0
  36. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/docs/requirements.txt +0 -0
  37. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/pyproject.toml +0 -0
  38. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/setup.py +0 -0
  39. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/dependency_links.txt +0 -0
  40. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/not-zip-safe +0 -0
  41. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/eth_prototype.egg-info/top_level.txt +0 -0
  42. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/__init__.py +0 -0
  43. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/contracts.py +0 -0
  44. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/defender_relay.py +0 -0
  45. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/test_utils/__init__.py +0 -0
  46. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/test_utils/factories.py +0 -0
  47. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/test_utils/hardhat.py +0 -0
  48. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/test_utils/vcr_utils.py +0 -0
  49. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/src/ethproto/wadray.py +0 -0
  50. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/__init__.py +0 -0
  51. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/cassettes/test_aa_bundler/test_build_user_operation.yaml +0 -0
  52. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/README.md +0 -0
  53. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/artifacts2/TestCurrency.sol/TestCurrency.json +0 -0
  54. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/Count.sol +0 -0
  55. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/Counter.sol +0 -0
  56. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/CounterUpgradeable.sol +0 -0
  57. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/CounterUpgradeableWithLibrary.sol +0 -0
  58. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/CounterWithLibrary.sol +0 -0
  59. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/Datatypes.sol +0 -0
  60. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/EventLauncher.sol +0 -0
  61. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/TestCurrency.sol +0 -0
  62. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/TestCurrencyUUPS.sol +0 -0
  63. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/contracts/TestNFT.sol +0 -0
  64. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/hardhat.config.js +0 -0
  65. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/package-lock.json +0 -0
  66. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/hardhat-project/package.json +0 -0
  67. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_aa_bundler.py +0 -0
  68. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_contracts.py +0 -0
  69. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_defender.py +0 -0
  70. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_time_control.py +0 -0
  71. {eth_prototype-1.3.0b1 → eth_prototype-1.3.1}/tests/test_wadray.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.3.0b1
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: setuptools; extra == "testing"
30
- Requires-Dist: pytest; extra == "testing"
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: web3[tester]==7.*; extra == "testing"
34
- Requires-Dist: boto3; extra == "testing"
34
+ Requires-Dist: pytest-mock; extra == "testing"
35
35
  Requires-Dist: pytest-recording; extra == "testing"
36
- Requires-Dist: factory-boy; extra == "testing"
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
 
@@ -41,14 +41,15 @@ defender =
41
41
  gmpy2 =
42
42
  gmpy2
43
43
  testing =
44
- setuptools
45
- pytest
44
+ boto3
45
+ factory-boy
46
46
  gmpy2
47
+ pytest
47
48
  pytest-cov
48
- web3[tester]==7.*
49
- boto3
49
+ pytest-mock
50
50
  pytest-recording
51
- factory-boy
51
+ setuptools
52
+ web3[tester]==7.*
52
53
 
53
54
  [options.entry_points]
54
55
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: eth-prototype
3
- Version: 1.3.0b1
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: setuptools; extra == "testing"
30
- Requires-Dist: pytest; extra == "testing"
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: web3[tester]==7.*; extra == "testing"
34
- Requires-Dist: boto3; extra == "testing"
34
+ Requires-Dist: pytest-mock; extra == "testing"
35
35
  Requires-Dist: pytest-recording; extra == "testing"
36
- Requires-Dist: factory-boy; extra == "testing"
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
 
@@ -64,4 +64,7 @@ tests/hardhat-project/contracts/Datatypes.sol
64
64
  tests/hardhat-project/contracts/EventLauncher.sol
65
65
  tests/hardhat-project/contracts/TestCurrency.sol
66
66
  tests/hardhat-project/contracts/TestCurrencyUUPS.sol
67
- tests/hardhat-project/contracts/TestNFT.sol
67
+ tests/hardhat-project/contracts/TestNFT.sol
68
+ tests/hardhat-project/verifiable-binaries/@anotherOrg/aPkg/1.0.2/build/contracts/TestCurrency.sol/TestCurrency.json
69
+ tests/hardhat-project/verifiable-binaries/@org/pkg/0.2.1/build/contracts/TestCurrency.json
70
+ tests/hardhat-project/verifiable-binaries/@org/pkg/0.3.0/build/contracts/TestCurrency.json
@@ -13,14 +13,15 @@ boto3
13
13
  gmpy2
14
14
 
15
15
  [testing]
16
- setuptools
17
- pytest
16
+ boto3
17
+ factory-boy
18
18
  gmpy2
19
+ pytest
19
20
  pytest-cov
20
- web3[tester]==7.*
21
- boto3
21
+ pytest-mock
22
22
  pytest-recording
23
- factory-boy
23
+ setuptools
24
+ web3[tester]==7.*
24
25
 
25
26
  [web3]
26
27
  web3==7.*
@@ -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", type=NonceMode)
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
@@ -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))
@@ -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
 
@@ -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.rawTransaction)
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 type(name) == str and name.startswith("0x"):
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.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)
@@ -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:
@@ -303,7 +303,7 @@ class ETHWrapper:
303
303
  constructor_args = None
304
304
  initialize_args = None
305
305
 
306
- def __init__(self, owner="owner", *init_params, **kwargs):
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)
@@ -24,11 +24,11 @@ def local_node_provider(hardhat_node):
24
24
  if os.environ.get("TEST_ENV", None) == "web3py":
25
25
  from web3 import Web3
26
26
 
27
- from ethproto import w3wrappers, wrappers
27
+ from ethproto import w3wrappers
28
28
 
29
- wrappers.register_provider("w3", w3wrappers.W3Provider(Web3(Web3.HTTPProvider(hardhat_node))))
29
+ w3wrappers.register_w3_provider("w3", Web3(Web3.HTTPProvider(hardhat_node)))
30
30
  yield
31
- wrappers.register_provider("w3", w3wrappers.W3Provider(w3))
31
+ w3wrappers.register_w3_provider("w3", w3)
32
32
  return
33
33
  yield
34
34
 
@@ -0,0 +1,10 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "TestCurrency",
4
+ "sourceName": "contracts/TestCurrency.sol",
5
+ "abi": [],
6
+ "bytecode": "0x",
7
+ "deployedBytecode": "0x",
8
+ "linkReferences": {},
9
+ "deployedLinkReferences": {}
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "TestCurrency",
4
+ "sourceName": "contracts/TestCurrency.sol",
5
+ "abi": [],
6
+ "bytecode": "0x",
7
+ "deployedBytecode": "0x",
8
+ "linkReferences": {},
9
+ "deployedLinkReferences": {}
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "TestCurrency",
4
+ "sourceName": "contracts/TestCurrency.sol",
5
+ "abi": [],
6
+ "bytecode": "0x",
7
+ "deployedBytecode": "0x",
8
+ "linkReferences": {},
9
+ "deployedLinkReferences": {}
10
+ }
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from pathlib import Path
2
3
 
3
4
  import pytest
4
5
 
@@ -122,3 +123,58 @@ def test_artifact_libraries_generator():
122
123
  artifact_with_no_libraries = library.get_artifact("contracts/Counter.sol")
123
124
  libraries = list(artifact_with_no_libraries.libraries())
124
125
  assert libraries == []
126
+
127
+
128
+ def test_artifact_library_ref_lookup():
129
+ library = ArtifactLibrary(
130
+ os.path.join(HARDHAT_PROJECT, "artifacts"),
131
+ os.path.join(HARDHAT_PROJECT, "verifiable-binaries"),
132
+ )
133
+
134
+ assert library.get_artifact_by_ref("TestCurrency") == library.get_artifact("contracts/TestCurrency.sol")
135
+
136
+ # Testing the internal _find_ref method to avoid the need of actual artifacts and to better check the results.
137
+
138
+ # No package or version specified, local is loaded
139
+ assert library._find_ref("TestCurrency") == {
140
+ "path": Path(HARDHAT_PROJECT) / "artifacts" / "contracts" / "TestCurrency.sol" / "TestCurrency.json",
141
+ "package": "",
142
+ "version": "local",
143
+ }
144
+
145
+ # Local version explicitly requested
146
+ assert library._find_ref("TestCurrency@local") == {
147
+ "path": Path(HARDHAT_PROJECT) / "artifacts" / "contracts" / "TestCurrency.sol" / "TestCurrency.json",
148
+ "package": "",
149
+ "version": "local",
150
+ }
151
+
152
+ # Specific package requested, latest version is loaded
153
+ assert library._find_ref("@org/pkg/TestCurrency") == {
154
+ "path": Path(HARDHAT_PROJECT)
155
+ / "verifiable-binaries/@org/pkg/0.3.0/build/contracts/TestCurrency.json",
156
+ "package": "@org/pkg",
157
+ "version": "0.3.0",
158
+ }
159
+
160
+ # Specific package and version requested
161
+ assert library._find_ref("@org/pkg/TestCurrency@0.2.1") == {
162
+ "path": Path(HARDHAT_PROJECT)
163
+ / "verifiable-binaries/@org/pkg/0.2.1/build/contracts/TestCurrency.json",
164
+ "package": "@org/pkg",
165
+ "version": "0.2.1",
166
+ }
167
+
168
+ # Different package, latest version too
169
+ assert library._find_ref("@anotherOrg/aPkg/TestCurrency") == {
170
+ "path": Path(HARDHAT_PROJECT)
171
+ / "verifiable-binaries/@anotherOrg/aPkg/1.0.2/build/contracts/TestCurrency.sol/TestCurrency.json",
172
+ "package": "@anotherOrg/aPkg",
173
+ "version": "1.0.2",
174
+ }
175
+
176
+ # Specific version requested, not found
177
+ assert library._find_ref("@anotherOrg/aPkg/TestCurrency@1.2.0") is None
178
+
179
+ # Specific package requested, not found
180
+ assert library._find_ref("@anotherOrg/package2/TestCurrency") is None
@@ -1,8 +1,9 @@
1
1
  import os
2
2
 
3
3
  import pytest
4
+ from web3 import Web3
4
5
 
5
- from ethproto import wrappers
6
+ from ethproto import w3wrappers, wrappers
6
7
 
7
8
  pytestmark = [
8
9
  pytest.mark.skipif(os.environ.get("TEST_ENV", None) != "web3py", reason="web3py-only tests"),
@@ -36,7 +37,7 @@ class CounterUpgradeableWithLibrary(Counter):
36
37
 
37
38
  @pytest.mark.parametrize("contract_class", [Counter, CounterWithLibrary, CounterUpgradeableWithLibrary])
38
39
  def test_deploy_counter(contract_class):
39
- counter = contract_class(initial_value=0)
40
+ counter = contract_class(initial_value=0, owner="owner")
40
41
  assert counter.value() == 0
41
42
  counter.increase()
42
43
  assert counter.value() == 1
@@ -51,7 +52,7 @@ class Datatypes(wrappers.ETHWrapper):
51
52
  def test_address_arguments():
52
53
  from eth_account import Account
53
54
 
54
- wrapper = Datatypes()
55
+ wrapper = Datatypes(owner="owner")
55
56
 
56
57
  account = Account.create("TEST TEST TEST")
57
58
 
@@ -76,7 +77,7 @@ def test_wrapper_build_from_def():
76
77
  contract_def = provider.get_contract_def("Counter")
77
78
  wrapper = wrappers.ETHWrapper.build_from_def(contract_def)
78
79
 
79
- counter = wrapper(initialValue=0)
80
+ counter = wrapper(initialValue=0, owner="owner")
80
81
  assert counter.value() == 0
81
82
  counter.increase()
82
83
  assert counter.value() == 1
@@ -87,7 +88,7 @@ def test_get_events():
87
88
  contract_def = provider.get_contract_def("EventLauncher")
88
89
  wrapper = wrappers.ETHWrapper.build_from_def(contract_def)
89
90
 
90
- launcher = wrapper()
91
+ launcher = wrapper(owner="owner")
91
92
 
92
93
  launcher.launchEvent1(1)
93
94
 
@@ -109,3 +110,75 @@ def test_get_events():
109
110
  event2 = provider.get_events(launcher, "Event2")
110
111
  assert len(event2) == 1
111
112
  assert event2[0].args.value == 2
113
+
114
+
115
+ def test_get_multiple_events():
116
+ provider = wrappers.get_provider("w3")
117
+ contract_def = provider.get_contract_def("EventLauncher")
118
+ wrapper = wrappers.ETHWrapper.build_from_def(contract_def)
119
+
120
+ launcher = wrapper(owner="owner")
121
+
122
+ launcher.launchEvent1(1)
123
+ launcher.launchEvent2(2)
124
+ launcher.launchEvent1(3)
125
+
126
+ all_events = provider.get_events(launcher, ["Event1", "Event2"], dict(from_block=0))
127
+ assert len(all_events) == 3
128
+
129
+ all_events_by_reference = provider.get_events(
130
+ launcher, [launcher.contract.events.Event1, launcher.contract.events.Event2], dict(from_block=0)
131
+ )
132
+ assert len(all_events_by_reference) == 3
133
+
134
+ all_events_mixed = provider.get_events(
135
+ launcher, ["Event1", launcher.contract.events.Event2], dict(from_block=0)
136
+ )
137
+ assert len(all_events_mixed) == 3
138
+
139
+ single_event_by_reference = provider.get_events(
140
+ launcher, launcher.contract.events.Event1, dict(from_block=0)
141
+ )
142
+ assert len(single_event_by_reference) == 2
143
+
144
+
145
+ @pytest.fixture
146
+ def sign_and_send(mocker, hardhat_node):
147
+ """Sets up sign-and-send transact mode with a well-known address, returns the address"""
148
+ mocker.patch("ethproto.w3wrappers.W3_TRANSACT_MODE", "sign-and-send")
149
+ mocker.patch.dict(
150
+ os.environ,
151
+ {"W3_ADDR_HARDHAT_18": "0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0"},
152
+ )
153
+ # Force recreate the provider and its address book after patching the environment
154
+ w3wrappers.register_w3_provider("w3", Web3(Web3.HTTPProvider(hardhat_node)))
155
+ return "0xdD2FD4581271e230360230F9337D5c0430Bf44C0"
156
+
157
+
158
+ def test_sign_and_send(sign_and_send):
159
+ # Deploy a contract using sign-and-send
160
+ wrapper = Datatypes(owner="HARDHAT_18")
161
+
162
+ assert wrapper.echoAddress("HARDHAT_18") == "HARDHAT_18"
163
+ assert wrappers.get_provider("w3").address_book.get_account("HARDHAT_18") == sign_and_send
164
+
165
+
166
+ def test_sign_and_send_upgradeable(sign_and_send):
167
+ upgradeable = CounterUpgradeableWithLibrary(initial_value=0, owner="HARDHAT_18")
168
+ assert upgradeable.value() == 0
169
+ upgradeable.increase()
170
+ assert upgradeable.value() == 1
171
+
172
+
173
+ def test_sign_and_send_interact_with_existing_contract(sign_and_send):
174
+ counter = Counter(initial_value=0, owner="HARDHAT_18")
175
+ assert counter.value() == 0 # sanity check
176
+
177
+ connected = Counter.connect(counter.contract.address)
178
+
179
+ # Interactions with the connected contract work as expected
180
+ assert connected.value() == 0
181
+ connected.increase()
182
+ assert connected.value() == 1
183
+
184
+ assert counter.value() == 1 # sanity check
@@ -18,13 +18,10 @@ setenv =
18
18
  TOXINIDIR = {toxinidir}
19
19
  TEST_ENV = web3py
20
20
  WEB3_PROVIDER_URI = https://polygon-mainnet.g.alchemy.com/v2/FAKEY
21
+ W3_POA = no
21
22
  passenv =
22
23
  HOME
23
24
  WADRAY_USE_GMPY2
24
- W3_*
25
- TRANSACT_MODE
26
- WEB3_PROVIDER_URI
27
- WEB3_*
28
25
  extras =
29
26
  testing
30
27
  deps =
File without changes
File without changes