eth-prototype 1.1.1__tar.gz → 1.1.2__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 (62) hide show
  1. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.github/workflows/test.yaml +2 -2
  2. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/PKG-INFO +1 -1
  3. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/PKG-INFO +1 -1
  4. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/SOURCES.txt +1 -0
  5. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/aa_bundler.py +67 -35
  6. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/w3wrappers.py +4 -4
  7. eth_prototype-1.1.2/tests/hardhat-project/contracts/EventLauncher.sol +16 -0
  8. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_aa_bundler.py +51 -11
  9. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_w3.py +29 -0
  10. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.coveragerc +0 -0
  11. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.github/workflows/publish.yaml +0 -0
  12. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.gitignore +0 -0
  13. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.isort.cfg +0 -0
  14. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.pre-commit-config.yaml +0 -0
  15. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/.readthedocs.yml +0 -0
  16. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/AUTHORS.rst +0 -0
  17. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/CHANGELOG.rst +0 -0
  18. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/LICENSE.txt +0 -0
  19. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/README.md +0 -0
  20. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/Makefile +0 -0
  21. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/_static/.gitignore +0 -0
  22. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/authors.rst +0 -0
  23. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/changelog.rst +0 -0
  24. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/conf.py +0 -0
  25. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/index.rst +0 -0
  26. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/license.rst +0 -0
  27. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/readme.rst +0 -0
  28. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/docs/requirements.txt +0 -0
  29. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/pyproject.toml +0 -0
  30. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/setup.cfg +0 -0
  31. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/setup.py +0 -0
  32. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/dependency_links.txt +0 -0
  33. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/not-zip-safe +0 -0
  34. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/requires.txt +0 -0
  35. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/eth_prototype.egg-info/top_level.txt +0 -0
  36. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/__init__.py +0 -0
  37. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/build_artifacts.py +0 -0
  38. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/contracts.py +0 -0
  39. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/defender_relay.py +0 -0
  40. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/wadray.py +0 -0
  41. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/src/ethproto/wrappers.py +0 -0
  42. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/conftest.py +0 -0
  43. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/README.md +0 -0
  44. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/artifacts2/TestCurrency.sol/TestCurrency.json +0 -0
  45. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/Count.sol +0 -0
  46. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/Counter.sol +0 -0
  47. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/CounterUpgradeable.sol +0 -0
  48. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/CounterUpgradeableWithLibrary.sol +0 -0
  49. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/CounterWithLibrary.sol +0 -0
  50. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/Datatypes.sol +0 -0
  51. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/TestCurrency.sol +0 -0
  52. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/TestCurrencyUUPS.sol +0 -0
  53. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/contracts/TestNFT.sol +0 -0
  54. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/hardhat.config.js +0 -0
  55. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/package-lock.json +0 -0
  56. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/hardhat-project/package.json +0 -0
  57. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_build_artifacts.py +0 -0
  58. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_contracts.py +0 -0
  59. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_defender.py +0 -0
  60. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_time_control.py +0 -0
  61. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tests/test_wadray.py +0 -0
  62. {eth_prototype-1.1.1 → eth_prototype-1.1.2}/tox.ini +0 -0
@@ -2,9 +2,9 @@ name: Tests
2
2
 
3
3
  on:
4
4
  push:
5
- branches: ["main"]
5
+ branches: ["main", "v1.1.x"]
6
6
  pull_request:
7
- branches: ["main"]
7
+ branches: ["main", "v1.1.x"]
8
8
 
9
9
  jobs:
10
10
  build:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eth-prototype
3
- Version: 1.1.1
3
+ Version: 1.1.2
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eth-prototype
3
- Version: 1.1.1
3
+ Version: 1.1.2
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
@@ -55,6 +55,7 @@ tests/hardhat-project/contracts/CounterUpgradeable.sol
55
55
  tests/hardhat-project/contracts/CounterUpgradeableWithLibrary.sol
56
56
  tests/hardhat-project/contracts/CounterWithLibrary.sol
57
57
  tests/hardhat-project/contracts/Datatypes.sol
58
+ tests/hardhat-project/contracts/EventLauncher.sol
58
59
  tests/hardhat-project/contracts/TestCurrency.sol
59
60
  tests/hardhat-project/contracts/TestCurrencyUUPS.sol
60
61
  tests/hardhat-project/contracts/TestNFT.sol
@@ -1,7 +1,9 @@
1
1
  import random
2
- from warnings import warn
2
+ from collections import defaultdict
3
3
  from enum import Enum
4
- import requests
4
+ from threading import local
5
+ from warnings import warn
6
+
5
7
  from environs import Env
6
8
  from eth_abi import encode
7
9
  from eth_account import Account
@@ -9,8 +11,8 @@ from eth_account.messages import encode_defunct
9
11
  from hexbytes import HexBytes
10
12
  from web3 import Web3
11
13
  from web3.constants import ADDRESS_ZERO
12
- from .contracts import RevertError
13
14
 
15
+ from .contracts import RevertError
14
16
 
15
17
  env = Env()
16
18
 
@@ -21,6 +23,7 @@ AA_BUNDLER_PROVIDER = env.str("AA_BUNDLER_PROVIDER", "alchemy")
21
23
  AA_BUNDLER_GAS_LIMIT_FACTOR = env.float("AA_BUNDLER_GAS_LIMIT_FACTOR", 1)
22
24
  AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR", 1)
23
25
  AA_BUNDLER_BASE_GAS_PRICE_FACTOR = env.float("AA_BUNDLER_BASE_GAS_PRICE_FACTOR", 1)
26
+ AA_BUNDLER_VERIFICATION_GAS_FACTOR = env.float("AA_BUNDLER_VERIFICATION_GAS_FACTOR", 1)
24
27
 
25
28
  NonceMode = Enum(
26
29
  "NonceMode",
@@ -50,8 +53,8 @@ GET_NONCE_ABI = [
50
53
  }
51
54
  ]
52
55
 
53
- NONCE_CACHE = {}
54
- RANDOM_NONCE_KEY = None
56
+ NONCE_CACHE = defaultdict(lambda: 0)
57
+ RANDOM_NONCE_KEY = local()
55
58
 
56
59
 
57
60
  def pack_two(a, b):
@@ -68,6 +71,10 @@ def _to_uint(x):
68
71
  raise RuntimeError(f"Invalid int value {x}")
69
72
 
70
73
 
74
+ def apply_factor(x, factor):
75
+ return int(_to_uint(x) * factor)
76
+
77
+
71
78
  def pack_user_operation(user_operation):
72
79
  # https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/interfaces/PackedUserOperation.sol
73
80
  return {
@@ -133,10 +140,9 @@ def fetch_nonce(w3, account, entry_point, nonce_key):
133
140
 
134
141
 
135
142
  def get_random_nonce_key():
136
- global RANDOM_NONCE_KEY
137
- if RANDOM_NONCE_KEY is None:
138
- RANDOM_NONCE_KEY = random.randint(1, 2**192 - 1)
139
- return RANDOM_NONCE_KEY
143
+ if getattr(RANDOM_NONCE_KEY, "key", None) is None:
144
+ RANDOM_NONCE_KEY.key = random.randint(1, 2**192 - 1)
145
+ return RANDOM_NONCE_KEY.key
140
146
 
141
147
 
142
148
  def get_nonce_and_key(w3, tx, nonce_mode, entry_point=AA_BUNDLER_ENTRYPOINT, fetch=False):
@@ -152,20 +158,25 @@ def get_nonce_and_key(w3, tx, nonce_mode, entry_point=AA_BUNDLER_ENTRYPOINT, fet
152
158
  if nonce is None:
153
159
  if fetch or nonce_mode == NonceMode.FIXED_KEY_FETCH_ALWAYS:
154
160
  nonce = fetch_nonce(w3, get_sender(tx), entry_point, nonce_key)
155
- elif nonce_key not in NONCE_CACHE:
156
- nonce = 0
157
161
  else:
158
162
  nonce = NONCE_CACHE[nonce_key]
159
163
  return nonce_key, nonce
160
164
 
161
165
 
162
- def handle_response_error(resp, w3, tx, retry_nonce):
166
+ def consume_nonce(nonce_key, nonce):
167
+ NONCE_CACHE[nonce_key] = max(NONCE_CACHE[nonce_key], nonce + 1)
168
+
169
+
170
+ def check_nonce_error(resp, retry_nonce):
171
+ """Returns the next nonce if resp contains a nonce error and retries weren't exhausted
172
+ Raises RevertError otherwise
173
+ """
163
174
  if "AA25" in resp["error"]["message"] and AA_BUNDLER_MAX_GETNONCE_RETRIES > 0:
164
175
  # Retry fetching the nonce
165
176
  if retry_nonce == AA_BUNDLER_MAX_GETNONCE_RETRIES:
166
177
  raise RevertError(resp["error"]["message"])
167
178
  warn(f'{resp["error"]["message"]} error, I will retry fetching the nonce')
168
- return send_transaction(w3, tx, retry_nonce=(retry_nonce or 0) + 1)
179
+ return (retry_nonce or 0) + 1
169
180
  else:
170
181
  raise RevertError(resp["error"]["message"])
171
182
 
@@ -184,7 +195,7 @@ def get_sender(tx):
184
195
  return tx["from"]
185
196
 
186
197
 
187
- def send_transaction(w3, tx, retry_nonce=None):
198
+ def build_user_operation(w3, tx, retry_nonce=None):
188
199
  nonce_key, nonce = get_nonce_and_key(
189
200
  w3, tx, AA_BUNDLER_NONCE_MODE, entry_point=AA_BUNDLER_ENTRYPOINT, fetch=retry_nonce is not None
190
201
  )
@@ -209,42 +220,63 @@ def send_transaction(w3, tx, retry_nonce=None):
209
220
  "eth_estimateUserOperationGas", [user_operation, AA_BUNDLER_ENTRYPOINT]
210
221
  )
211
222
  if "error" in resp:
212
- return handle_response_error(resp, w3, tx, retry_nonce)
223
+ next_nonce = check_nonce_error(resp, retry_nonce)
224
+ return build_user_operation(w3, tx, retry_nonce=next_nonce)
213
225
 
214
226
  user_operation.update(resp["result"])
215
227
 
216
228
  resp = w3.provider.make_request("rundler_maxPriorityFeePerGas", [])
217
229
  if "error" in resp:
218
230
  raise RevertError(resp["error"]["message"])
219
- max_priority_fee_per_gas = int(_to_uint(resp["result"]) * AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR)
220
- user_operation["maxPriorityFeePerGas"] = hex(max_priority_fee_per_gas)
221
- user_operation["maxFeePerGas"] = hex(max_priority_fee_per_gas + get_base_fee(w3))
222
- user_operation["callGasLimit"] = hex(
223
- int(_to_uint(user_operation["callGasLimit"]) * AA_BUNDLER_GAS_LIMIT_FACTOR)
224
- )
225
- elif AA_BUNDLER_PROVIDER == "gelato":
226
- user_operation.update(
227
- {
228
- "preVerificationGas": "0x00",
229
- "callGasLimit": "0x00",
230
- "verificationGasLimit": "0x00",
231
- "maxFeePerGas": "0x00",
232
- "maxPriorityFeePerGas": "0x00",
233
- }
231
+ user_operation["maxPriorityFeePerGas"] = resp["result"]
232
+ user_operation["maxFeePerGas"] = hex(int(resp["result"], 16) + get_base_fee(w3))
233
+
234
+ elif AA_BUNDLER_PROVIDER == "generic":
235
+ resp = w3.provider.make_request(
236
+ "eth_estimateUserOperationGas", [user_operation, AA_BUNDLER_ENTRYPOINT]
234
237
  )
235
- user_operation["signature"] = sign_user_operation(
236
- AA_BUNDLER_EXECUTOR_PK, user_operation, tx["chainId"], AA_BUNDLER_ENTRYPOINT
238
+ if "error" in resp:
239
+ next_nonce = check_nonce_error(resp, retry_nonce)
240
+ return build_user_operation(w3, tx, retry_nonce=next_nonce)
241
+
242
+ user_operation.update(resp["result"])
243
+
244
+ else:
245
+ warn(f"Unknown AA_BUNDLER_PROVIDER: {AA_BUNDLER_PROVIDER}")
246
+
247
+ # Apply increase factors
248
+ user_operation["verificationGasLimit"] = hex(
249
+ apply_factor(user_operation["verificationGasLimit"], AA_BUNDLER_VERIFICATION_GAS_FACTOR)
237
250
  )
251
+ if "maxPriorityFeePerGas" in user_operation:
252
+ user_operation["maxPriorityFeePerGas"] = hex(
253
+ apply_factor(user_operation["maxPriorityFeePerGas"], AA_BUNDLER_PRIORITY_GAS_PRICE_FACTOR)
254
+ )
255
+ if "callGasLimit" in user_operation:
256
+ user_operation["callGasLimit"] = hex(
257
+ apply_factor(user_operation["callGasLimit"], AA_BUNDLER_GAS_LIMIT_FACTOR)
258
+ )
259
+
238
260
  # Remove paymaster related fields
239
261
  user_operation.pop("paymaster", None)
240
262
  user_operation.pop("paymasterData", None)
241
263
  user_operation.pop("paymasterVerificationGasLimit", None)
242
264
  user_operation.pop("paymasterPostOpGasLimit", None)
243
265
 
266
+ # Consume the nonce, even if the userop may fail later
267
+ consume_nonce(nonce_key, nonce)
268
+
269
+ return user_operation
270
+
271
+
272
+ def send_transaction(w3, tx, retry_nonce=None):
273
+ user_operation = build_user_operation(w3, tx, retry_nonce)
274
+ user_operation["signature"] = sign_user_operation(
275
+ AA_BUNDLER_EXECUTOR_PK, user_operation, tx["chainId"], AA_BUNDLER_ENTRYPOINT
276
+ )
244
277
  resp = w3.provider.make_request("eth_sendUserOperation", [user_operation, AA_BUNDLER_ENTRYPOINT])
245
278
  if "error" in resp:
246
- return handle_response_error(resp, w3, tx, retry_nonce)
279
+ next_nonce = check_nonce_error(resp, retry_nonce)
280
+ return send_transaction(w3, tx, retry_nonce=next_nonce)
247
281
 
248
- # Store nonce in the cache, so next time uses a new nonce
249
- NONCE_CACHE[nonce_key] = nonce + 1
250
282
  return {"userOpHash": resp["result"]}
@@ -444,7 +444,7 @@ class W3Provider(BaseProvider):
444
444
  def get_events(self, eth_wrapper, event_name, filter_kwargs={}):
445
445
  """Returns a list of events given a filter, like this:
446
446
 
447
- >>> provider.get_events(currencywrapper, "Transfer", dict(fromBlock=0))
447
+ >>> provider.get_events(currencywrapper, "Transfer", dict(from_block=0))
448
448
  [AttributeDict({
449
449
  'args': AttributeDict(
450
450
  {'from': '0x0000000000000000000000000000000000000000',
@@ -463,8 +463,8 @@ class W3Provider(BaseProvider):
463
463
  """
464
464
  contract = eth_wrapper.contract
465
465
  event = getattr(contract.events, event_name)
466
- if "fromBlock" not in filter_kwargs:
467
- filter_kwargs["fromBlock"] = self.get_first_block(eth_wrapper)
466
+ if "from_block" not in filter_kwargs:
467
+ filter_kwargs["from_block"] = self.get_first_block(eth_wrapper)
468
468
  event_filter = event.create_filter(**filter_kwargs)
469
469
  return event_filter.get_all_entries()
470
470
 
@@ -490,7 +490,7 @@ class W3Provider(BaseProvider):
490
490
  constructor_params, init_params = init_params
491
491
  real_contract = self.construct(eth_contract, constructor_params, {"from": eth_wrapper.owner})
492
492
  ERC1967Proxy = self.get_contract_factory("ERC1967Proxy")
493
- init_data = eth_contract.encodeABI(fn_name="initialize", args=init_params)
493
+ init_data = eth_contract.encode_abi(abi_element_identifier="initialize", args=init_params)
494
494
  proxy_contract = self.construct(
495
495
  ERC1967Proxy,
496
496
  (real_contract.address, init_data),
@@ -0,0 +1,16 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.9;
3
+
4
+ contract EventLauncher {
5
+ event Event1(uint256 value);
6
+
7
+ event Event2(uint256 value);
8
+
9
+ function launchEvent1(uint256 value) public {
10
+ emit Event1(value);
11
+ }
12
+
13
+ function launchEvent2(uint256 value) public {
14
+ emit Event2(value);
15
+ }
16
+ }
@@ -1,8 +1,11 @@
1
- import os
1
+ from queue import Queue
2
+ from threading import Event, Thread
3
+ from unittest.mock import MagicMock, patch
4
+
2
5
  from hexbytes import HexBytes
3
- from ethproto import aa_bundler
4
6
  from web3.constants import HASH_ZERO
5
- from unittest.mock import MagicMock, patch
7
+
8
+ from ethproto import aa_bundler
6
9
 
7
10
 
8
11
  def test_pack_two():
@@ -66,9 +69,7 @@ def test_hash_packed_user_operation():
66
69
 
67
70
  def test_sign_user_operation():
68
71
  signature = aa_bundler.sign_user_operation(TEST_PRIVATE_KEY, user_operation, CHAIN_ID, ENTRYPOINT)
69
- assert (
70
- signature
71
- == "0xb9b872bfe4e90f4628e8ec24879a5b01045f91da8457f3ce2b417d2e5774b508261ec1147a820e75a141cb61b884a78d7e88996ceddafb9a7016cfe7a48a1f4f1b" # noqa
72
+ assert (signature == "0xb9b872bfe4e90f4628e8ec24879a5b01045f91da8457f3ce2b417d2e5774b508261ec1147a820e75a141cb61b884a78d7e88996ceddafb9a7016cfe7a48a1f4f1b" # noqa
72
73
  )
73
74
 
74
75
 
@@ -76,9 +77,7 @@ def test_sign_user_operation_gas_diff():
76
77
  user_operation_2 = dict(user_operation)
77
78
  user_operation_2["maxPriorityFeePerGas"] -= 1
78
79
  signature = aa_bundler.sign_user_operation(TEST_PRIVATE_KEY, user_operation_2, CHAIN_ID, ENTRYPOINT)
79
- assert (
80
- signature
81
- == "0x8162479d2dbd18d7fe93a2f51e283021d6e4eae4f57d20cdd553042723a0b0ea690ab3903d45126b0047da08ab53dfdf86656e4f258ac4936ba96a759ccb77f61b" # noqa
80
+ assert (signature == "0x8162479d2dbd18d7fe93a2f51e283021d6e4eae4f57d20cdd553042723a0b0ea690ab3903d45126b0047da08ab53dfdf86656e4f258ac4936ba96a759ccb77f61b" # noqa
82
81
  )
83
82
 
84
83
 
@@ -156,8 +155,8 @@ def test_get_nonce_random_key_mode(fetch_nonce_mock, randint_mock):
156
155
  fetch_nonce_mock.assert_not_called()
157
156
  randint_mock.assert_called_with(1, 2**192 - 1)
158
157
  randint_mock.reset_mock()
159
- assert aa_bundler.RANDOM_NONCE_KEY == 444
160
- aa_bundler.RANDOM_NONCE_KEY = None # cleanup
158
+ assert aa_bundler.RANDOM_NONCE_KEY.key == 444
159
+ aa_bundler.RANDOM_NONCE_KEY.key = None # cleanup
161
160
 
162
161
 
163
162
  @patch.object(aa_bundler.random, "randint")
@@ -242,3 +241,44 @@ def test_send_transaction(get_base_fee_mock):
242
241
  get_base_fee_mock.assert_called_once_with(w3)
243
242
  assert aa_bundler.NONCE_CACHE[0] == 1
244
243
  assert ret == {"userOpHash": "0xa950a17ca1ed83e974fb1aa227360a007cb65f566518af117ffdbb04d8d2d524"}
244
+
245
+
246
+ def test_random_key_nonces_are_thread_safe():
247
+ queue = Queue()
248
+ event = Event()
249
+
250
+ def worker():
251
+ event.wait() # Get all threads running at the same time
252
+ nonce_key, nonce = aa_bundler.get_nonce_and_key(
253
+ FAIL_IF_USED,
254
+ {"from": TEST_SENDER},
255
+ nonce_mode=aa_bundler.NonceMode.RANDOM_KEY,
256
+ )
257
+ aa_bundler.consume_nonce(nonce_key, nonce)
258
+ queue.put(
259
+ aa_bundler.get_nonce_and_key(
260
+ FAIL_IF_USED,
261
+ {"from": TEST_SENDER},
262
+ nonce_mode=aa_bundler.NonceMode.RANDOM_KEY,
263
+ )
264
+ )
265
+
266
+ threads = [Thread(target=worker) for _ in range(15)]
267
+ for thread in threads:
268
+ thread.start()
269
+
270
+ # Fire all threads at once
271
+ event.set()
272
+ for thread in threads:
273
+ thread.join()
274
+
275
+ nonces = {}
276
+
277
+ while not queue.empty():
278
+ nonce_key, nonce = queue.get_nowait()
279
+ # Each thread got a different key
280
+ assert nonce_key not in nonces
281
+ nonces[nonce_key] = nonce
282
+
283
+ # All nonces are the same
284
+ assert all(nonce == 1 for nonce in nonces.values())
@@ -77,3 +77,32 @@ def test_wrapper_build_from_def():
77
77
  assert counter.value() == 0
78
78
  counter.increase()
79
79
  assert counter.value() == 1
80
+
81
+
82
+ def test_get_events():
83
+ provider = wrappers.get_provider("w3")
84
+ contract_def = provider.get_contract_def("EventLauncher")
85
+ wrapper = wrappers.ETHWrapper.build_from_def(contract_def)
86
+
87
+ launcher = wrapper()
88
+
89
+ launcher.launchEvent1(1)
90
+
91
+ cutoff_block = provider.w3.eth.get_block("latest")
92
+ launcher.launchEvent2(2)
93
+ launcher.launchEvent1(3)
94
+
95
+ all_event1 = provider.get_events(launcher, "Event1", dict(from_block=0))
96
+ assert len(all_event1) == 2
97
+
98
+ first_event1_only = provider.get_events(launcher, "Event1", dict(to_block=cutoff_block.number))
99
+ assert len(first_event1_only) == 1
100
+ assert first_event1_only[0] == all_event1[0]
101
+
102
+ last_event1_only = provider.get_events(launcher, "Event1", dict(from_block=cutoff_block.number + 1))
103
+ assert len(last_event1_only) == 1
104
+ assert last_event1_only[0] == all_event1[-1]
105
+
106
+ event2 = provider.get_events(launcher, "Event2")
107
+ assert len(event2) == 1
108
+ assert event2[0].args.value == 2
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes