genlayer-test 1.0.0__py3-none-any.whl → 2.1.0__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: genlayer-test
3
- Version: 1.0.0
3
+ Version: 2.1.0
4
4
  Summary: GenLayer Testing Suite
5
5
  Author: GenLayer
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: pytest
17
17
  Requires-Dist: setuptools>=77.0
18
- Requires-Dist: genlayer-py==0.7.1
18
+ Requires-Dist: genlayer-py==0.7.2
19
19
  Requires-Dist: colorama>=0.4.6
20
20
  Requires-Dist: pyyaml
21
21
  Requires-Dist: python-dotenv
@@ -114,6 +114,7 @@ networks:
114
114
 
115
115
  localnet: # Local development network configuration
116
116
  url: "http://127.0.0.1:4000/api"
117
+ leader_only: false # Set to true to run all contracts in leader-only mode by default
117
118
 
118
119
  testnet_asimov: # Test network configuration
119
120
  id: 4221
@@ -125,6 +126,7 @@ networks:
125
126
 
126
127
  paths:
127
128
  contracts: "contracts" # Path to your contracts directory
129
+ artifacts: "artifacts" # Path to your artifacts directory
128
130
 
129
131
  environment: .env # Path to your environment file containing private keys and other secrets
130
132
  ```
@@ -137,12 +139,14 @@ Key configuration sections:
137
139
  - `url`: The RPC endpoint for the network
138
140
  - `id`: Chain ID
139
141
  - `accounts`: List of account private keys (using environment variables)
142
+ - `leader_only`: Leader only mode
140
143
  - Special case for `localnet`:
141
144
  - If a network is named `localnet`, missing fields will be filled with default values
142
145
  - For all other network names, `id`, `url`, and `accounts` are required fields
143
146
 
144
147
  2. **Paths**: Define important directory paths
145
148
  - `contracts`: Location of your contract files
149
+ - `artifacts`: Location of your artifacts files (analysis results will be stored here)
146
150
 
147
151
  3. **Environment**: Path to your `.env` file containing sensitive information like private keys
148
152
 
@@ -231,6 +235,20 @@ def test_with_mocked_llm(setup_validators):
231
235
 
232
236
  Note: This feature is only available when running tests on localnet.
233
237
 
238
+ 11. Run tests with leader-only mode enabled
239
+ ```bash
240
+ $ gltest --leader-only
241
+ ```
242
+ The `--leader-only` flag configures all contract deployments and write operations to run only on the leader node. This is useful for:
243
+ - Faster test execution by avoiding consensus
244
+ - Testing specific leader-only scenarios
245
+ - Development and debugging purposes
246
+ - Reducing computational overhead in test environments
247
+
248
+ When this flag is enabled, all contracts deployed and all write transactions will automatically use leader-only mode, regardless of individual method parameters.
249
+
250
+ **Note:** Leader-only mode is only available for studio-based networks (localhost, 127.0.0.1, *.genlayer.com, *.genlayerlabs.com). When enabled on other networks, it will have no effect and a warning will be logged.
251
+
234
252
  ## 🚀 Key Features
235
253
 
236
254
  - **Pytest Integration** – Extends pytest to support intelligent contract testing, making it familiar and easy to adopt.
@@ -251,8 +269,9 @@ Before diving into the examples, let's understand the basic project structure:
251
269
  genlayer-example/
252
270
  ├── contracts/ # Contract definitions
253
271
  │ └── storage.py # Example storage contract
254
- └── test/ # Test files
255
- └── test_contract.py # Contract test cases
272
+ ├── test/ # Test files
273
+ └── test_contract.py # Contract test cases
274
+ └── gltest.config.yaml # Configuration file
256
275
  ```
257
276
 
258
277
  ### Storage Contract Example
@@ -311,7 +330,6 @@ def test_deployment():
311
330
  args=["initial_value"], # Constructor arguments
312
331
  account=get_default_account(), # Account to deploy from
313
332
  consensus_max_rotations=3, # Optional: max consensus rotations
314
- leader_only=False, # Optional: whether to run only on leader
315
333
  )
316
334
 
317
335
  # Contract is now deployed and ready to use
@@ -357,7 +375,6 @@ def test_write_methods():
357
375
  ).transact(
358
376
  value=0, # Optional: amount of native currency to send
359
377
  consensus_max_rotations=3, # Optional: max consensus rotations
360
- leader_only=False, # Optional: whether to run only on leader
361
378
  wait_interval=1, # Optional: seconds between status checks
362
379
  wait_retries=10, # Optional: max number of retries
363
380
  )
@@ -604,13 +621,11 @@ The `.analyze()` method helps you:
604
621
  # Try with increased consensus parameters
605
622
  contract = factory.deploy(
606
623
  consensus_max_rotations=5, # Increase number of consensus rotations
607
- leader_only=True, # Try leader-only mode for faster execution
608
624
  )
609
625
 
610
626
  # For critical operations, use more conservative settings
611
627
  contract = factory.deploy(
612
628
  consensus_max_rotations=10, # More rotations for better reliability
613
- leader_only=False, # Full consensus for better security
614
629
  wait_interval=3, # Longer wait between checks
615
630
  wait_retries=30 # More retries for consensus
616
631
  )
@@ -1,4 +1,4 @@
1
- genlayer_test-1.0.0.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
1
+ genlayer_test-2.1.0.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
2
2
  gltest/__init__.py,sha256=qoBV3IgDJr8uh9262XsWNMhi-ilkSgMqKVC9FVMk7cw,372
3
3
  gltest/accounts.py,sha256=HUmWguJMolggQaZNRPw-LGlRlQCjLLdUanKRowMv6pI,812
4
4
  gltest/assertions.py,sha256=0dEk0VxcHK4I7GZPHxJmz-2jaA60V499gOSR74rZbfM,1748
@@ -6,13 +6,13 @@ gltest/clients.py,sha256=1dX6wmG3QCevQRLbSaFlHymZSb-sJ5aYwet1IoX2nbA,1554
6
6
  gltest/exceptions.py,sha256=deJPmrTe5gF33qkkKF2IVJY7lc_knI7Ql3N7jZ8aLZs,510
7
7
  gltest/fixtures.py,sha256=EJXmqcC3LD03v07mepacFl58lAdhbLj6bP5rtALYISk,2507
8
8
  gltest/logging.py,sha256=jAkHsuMm-AEx1Xu1srU6W-0YzTwXJB48mCK-OVzAiN4,342
9
- gltest/types.py,sha256=BODmwTr2gAUEiO9FjiuTiWwuKvXgo4xZWstQWNUfnlw,156
9
+ gltest/types.py,sha256=Kj6UuTpRXkA4c5ystKfwgXuCiRJ1VMkbgvP7Auvznhw,184
10
10
  gltest/artifacts/__init__.py,sha256=qTt3TE19gVNWnQLUlt5aDe4nNvJ2YJ1jzDkMmYIsCG0,194
11
11
  gltest/artifacts/contract.py,sha256=KChpmfjZod_0dVB8y-dvWz6IVm7QlIJsgG2ArtvVDaU,6457
12
12
  gltest/contracts/__init__.py,sha256=A9bvEtYOoqoHS8TLlFBfmNOnfwdsJPEf-AZuikagCHM,166
13
- gltest/contracts/contract.py,sha256=NGgciplaYINL_unJvLnOSWL4f8cNlHGMOEEbc6a1kw4,6402
14
- gltest/contracts/contract_factory.py,sha256=jjzxgsLW6AiQ4h8LcZwIzdq5Uqm-xkj_JZNhN0djijc,6691
15
- gltest/contracts/contract_functions.py,sha256=E-C9RnBkz3FeJRiswUaZbcDn4HRSWitsdwegSjX7E_Q,2089
13
+ gltest/contracts/contract.py,sha256=se8wpRIu-PGkLkvqz-DWikacw4n4vl2JAJD84YVn4sc,6735
14
+ gltest/contracts/contract_factory.py,sha256=-AA0G-mQdLffQSpOzkNg_P4nRsqLYLmUJoYrrs8_dD4,7607
15
+ gltest/contracts/contract_functions.py,sha256=5gNQYzN47rCJtmcMtUkl4ZK-yDifdZup2YhtlFlrx7E,2203
16
16
  gltest/contracts/method_stats.py,sha256=zWWjvf7K5VC4yrHpDIR717VF7LYp1RaZ1Hr_RZvWxJA,5150
17
17
  gltest/contracts/stats_collector.py,sha256=fuCc8L8hd0tsVGzH4adtZWwPa7ORf0A0zR5Dt1w92Qk,9033
18
18
  gltest/contracts/utils.py,sha256=TTXgcXn9BuRIlKJrjwmU7R3l1IgXsXk2luM-r3lfbbg,296
@@ -24,13 +24,13 @@ gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
24
24
  gltest_cli/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  gltest_cli/config/constants.py,sha256=3iSK337AeupyYl_6Sf8MR_o91XfLmul8m1rVmi3Fvmo,342
26
26
  gltest_cli/config/general.py,sha256=ezpoGsT8grO9zQH6RugV14b1GzeFt-htYToHQBJhNvY,186
27
- gltest_cli/config/plugin.py,sha256=gMK1xrkJ4G0ddCjKAFB7lEXecdvoZiwndwwI4Srn66w,4857
27
+ gltest_cli/config/plugin.py,sha256=sByVyPt9IikLFg4PiVe7xFrO56mVwyDdn4nzYa_HTNo,5411
28
28
  gltest_cli/config/pytest_context.py,sha256=Ze8JSkrwMTCE8jIhpzU_71CEXg92SiEPvSgNTp-gbS4,243
29
- gltest_cli/config/types.py,sha256=w7KyINjM3_n4MYoRPfMBgudptjXjnSKPficAsqqSUTY,6344
30
- gltest_cli/config/user.py,sha256=VSS7llCUJc2GSIexdL73FqkLthdiECwmQONO3-WYi7o,8242
29
+ gltest_cli/config/types.py,sha256=KDAA_vNqI7o_sYMj21ks3WaIXGrbLVb76E17TSlWUwI,7241
30
+ gltest_cli/config/user.py,sha256=WNX_Wi2eJpjW0UstQuQiDjzRpOIOwx9iiXOx4ntt93U,8759
31
31
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  tests/conftest.py,sha256=RKdoE5_zcMimeojAoA_GSFI9du4pMzMi1vZ1njtfoAs,28
33
- tests/examples/contracts/football_prediction_market.py,sha256=0Lm2x2F1DhmUP1fcfzGQAfc50tWFcaHliwyAzXIRFVw,3247
33
+ tests/examples/contracts/football_prediction_market.py,sha256=9xwU8f3q73Hae-ByHy_wauhMPLRnLZd4XKNrClnjOJM,3248
34
34
  tests/examples/contracts/intelligent_oracle.py,sha256=cZNGbjKMXY-pimVmPIKIlS963Gd3L1JAipq0VBR1J5Q,12360
35
35
  tests/examples/contracts/intelligent_oracle_factory.py,sha256=8lBEn3Atb0yUpXwlvnShlcRxCBTXCrrkoITDHWoWuHU,1499
36
36
  tests/examples/contracts/llm_erc20.py,sha256=pOvSUszCtC_f5yDX0tZnj494Ce10uESZ09JLLE8V67o,2534
@@ -44,7 +44,7 @@ tests/examples/contracts/wizard_of_coin.py,sha256=Y8daPpoSKdM8wfbCPOIgEdpkLIA1ZM
44
44
  tests/examples/contracts/multi_file_contract/__init__.py,sha256=CCdaK5p12GDf35hgbBWURNM5KUn6SWAcuyieTmZwVWE,548
45
45
  tests/examples/contracts/multi_file_contract/other.py,sha256=jHDtjUL3eAUgE6yOYKFw_RfAH7kIwk8CvxUjbWHNruk,236
46
46
  tests/examples/tests/test_football_prediction_market.py,sha256=f2hfDK76WrNJZtFkTPKoPRR6bkmFLEssnlwwltSnU9A,1111
47
- tests/examples/tests/test_intelligent_oracle_factory.py,sha256=wEcVAY0HHDf8oyVQ1kEY-2YfRJipkEJYooqW1wWwXls,8317
47
+ tests/examples/tests/test_intelligent_oracle_factory.py,sha256=uOpoMIktXltfPhnLwlothXUZy6s0luPSXiIeZkPJ2hI,7729
48
48
  tests/examples/tests/test_llm_erc20.py,sha256=zb5F_7NgvZXhvqL2nULwzuTT6LGDprSy0WgrdjY7pZc,2096
49
49
  tests/examples/tests/test_llm_erc20_analyze.py,sha256=ccWQenEOG-U4TJdu-0i-3qNM4xRRCHSr3Iwz7pxknws,1729
50
50
  tests/examples/tests/test_log_indexer.py,sha256=46AqL7qquNc9GX2wxFxVcQXLqruMnPmxXl1yeB0-KZ4,2869
@@ -65,11 +65,12 @@ tests/gltest/artifact/contracts/duplicate_ic_contract_2.py,sha256=bSWsUVjBy5cGtI
65
65
  tests/gltest/artifact/contracts/not_ic_contract.py,sha256=hQyGnYiiVceYdLI2WrvcFgPqzy1S4-YMb9FPhiHEGSA,510
66
66
  tests/gltest/assertions/test_assertions.py,sha256=qzVrOdOM4xYtIy1sFHVAD_-naDHOequ23tEN0MELh0k,10781
67
67
  tests/gltest_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- tests/gltest_cli/config/test_general_config.py,sha256=T2haLj41-6tbEfybdXnEycIQQFNkdc_O07qmg_37dqM,5730
69
- tests/gltest_cli/config/test_plugin.py,sha256=GYY56VR39-uI2WXK-rlzTxB5Ui2h9f5oF_kO0iNljRo,5862
70
- tests/gltest_cli/config/test_user.py,sha256=40nEC-gM03Q86SnmJpkGfMRvKKtEkfx53qaGHj8XXGQ,13988
71
- genlayer_test-1.0.0.dist-info/METADATA,sha256=g1QNA8KP65HqSApiaNYcunHwz1Ch2IO_RIkFcbVk5Vo,24143
72
- genlayer_test-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
73
- genlayer_test-1.0.0.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
74
- genlayer_test-1.0.0.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
75
- genlayer_test-1.0.0.dist-info/RECORD,,
68
+ tests/gltest_cli/config/test_config_integration.py,sha256=vPTzr3_h9UMw7m72HogBJE2ZPhRduXoLSq18Z7FoCWQ,10105
69
+ tests/gltest_cli/config/test_general_config.py,sha256=UHtSwVnso-ZwNtYM0Z4v2sCLKwyrVbHlk6b1leVfV84,14703
70
+ tests/gltest_cli/config/test_plugin.py,sha256=87kJpSYcWbNuKDqfc_jiN7hoRnFkMEnOOTINwuXKBY0,7981
71
+ tests/gltest_cli/config/test_user.py,sha256=JxR655oUFoM9quWQO68CVPKRpT0TMpzS3bF6j6NWyT4,14401
72
+ genlayer_test-2.1.0.dist-info/METADATA,sha256=Mj2oQbtObBd2TgEWNPGYIyZKmfnMxswurXuu2tFHas4,24978
73
+ genlayer_test-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ genlayer_test-2.1.0.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
75
+ genlayer_test-2.1.0.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
76
+ genlayer_test-2.1.0.dist-info/RECORD,,
@@ -2,7 +2,12 @@ import types
2
2
  from eth_account.signers.local import LocalAccount
3
3
  from dataclasses import dataclass
4
4
  from gltest.clients import get_gl_client
5
- from gltest.types import CalldataEncodable, GenLayerTransaction, TransactionStatus
5
+ from gltest.types import (
6
+ CalldataEncodable,
7
+ GenLayerTransaction,
8
+ TransactionStatus,
9
+ TransactionHashVariant,
10
+ )
6
11
  from typing import List, Any, Optional, Dict, Callable
7
12
  from gltest_cli.config.general import get_general_config
8
13
  from .contract_functions import ContractFunction
@@ -18,13 +23,16 @@ def read_contract_wrapper(
18
23
  Wrapper to the contract read method.
19
24
  """
20
25
 
21
- def call_method():
26
+ def call_method(
27
+ transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
28
+ ):
22
29
  client = get_gl_client()
23
30
  return client.read_contract(
24
31
  address=self.address,
25
32
  function_name=method_name,
26
33
  account=self.account,
27
34
  args=args,
35
+ transaction_hash_variant=transaction_hash_variant,
28
36
  )
29
37
 
30
38
  return ContractFunction(
@@ -46,12 +54,11 @@ def write_contract_wrapper(
46
54
  def transact_method(
47
55
  value: int = 0,
48
56
  consensus_max_rotations: Optional[int] = None,
49
- leader_only: bool = False,
50
- wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
57
+ wait_transaction_status: TransactionStatus = TransactionStatus.ACCEPTED,
51
58
  wait_interval: Optional[int] = None,
52
59
  wait_retries: Optional[int] = None,
53
- wait_triggered_transactions: bool = True,
54
- wait_triggered_transactions_status: TransactionStatus = TransactionStatus.FINALIZED,
60
+ wait_triggered_transactions: bool = False,
61
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
55
62
  ):
56
63
  """
57
64
  Transact the contract method.
@@ -67,6 +74,11 @@ def write_contract_wrapper(
67
74
  if wait_retries is not None
68
75
  else general_config.get_default_wait_retries()
69
76
  )
77
+ leader_only = (
78
+ general_config.get_leader_only()
79
+ if general_config.check_studio_based_rpc()
80
+ else False
81
+ )
70
82
  client = get_gl_client()
71
83
  tx_hash = client.write_contract(
72
84
  address=self.address,
@@ -23,7 +23,6 @@ from gltest.exceptions import DeploymentError
23
23
  from gltest_cli.config.general import get_general_config
24
24
 
25
25
 
26
-
27
26
  @dataclass
28
27
  class ContractFactory:
29
28
  """
@@ -108,19 +107,31 @@ class ContractFactory:
108
107
  args: List[Any] = [],
109
108
  account: Optional[LocalAccount] = None,
110
109
  consensus_max_rotations: Optional[int] = None,
111
- leader_only: bool = False,
112
110
  wait_interval: Optional[int] = None,
113
111
  wait_retries: Optional[int] = None,
114
- wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
112
+ wait_transaction_status: TransactionStatus = TransactionStatus.ACCEPTED,
113
+ wait_triggered_transactions: bool = False,
114
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
115
115
  ) -> Contract:
116
116
  """
117
117
  Deploy the contract
118
118
  """
119
119
  general_config = get_general_config()
120
- if wait_interval is None:
121
- wait_interval = general_config.get_default_wait_interval()
122
- if wait_retries is None:
123
- wait_retries = general_config.get_default_wait_retries()
120
+ actual_wait_interval = (
121
+ wait_interval
122
+ if wait_interval is not None
123
+ else general_config.get_default_wait_interval()
124
+ )
125
+ actual_wait_retries = (
126
+ wait_retries
127
+ if wait_retries is not None
128
+ else general_config.get_default_wait_retries()
129
+ )
130
+ leader_only = (
131
+ general_config.get_leader_only()
132
+ if general_config.check_studio_based_rpc()
133
+ else False
134
+ )
124
135
 
125
136
  client = get_gl_client()
126
137
  try:
@@ -134,14 +145,24 @@ class ContractFactory:
134
145
  tx_receipt = client.wait_for_transaction_receipt(
135
146
  transaction_hash=tx_hash,
136
147
  status=wait_transaction_status,
137
- interval=wait_interval,
138
- retries=wait_retries,
148
+ interval=actual_wait_interval,
149
+ retries=actual_wait_retries,
139
150
  )
140
151
  if tx_execution_failed(tx_receipt):
141
152
  raise ValueError(
142
153
  f"Deployment transaction finalized with error: {tx_receipt}"
143
154
  )
144
155
 
156
+ if wait_triggered_transactions:
157
+ triggered_transactions = tx_receipt["triggered_transactions"]
158
+ for triggered_transaction in triggered_transactions:
159
+ client.wait_for_transaction_receipt(
160
+ transaction_hash=triggered_transaction,
161
+ status=wait_triggered_transactions_status,
162
+ interval=actual_wait_interval,
163
+ retries=actual_wait_retries,
164
+ )
165
+
145
166
  if (
146
167
  "tx_data_decoded" in tx_receipt
147
168
  and "contract_address" in tx_receipt["tx_data_decoded"]
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Callable, Optional, Dict, Any
3
- from gltest.types import TransactionStatus
3
+ from gltest.types import TransactionStatus, TransactionHashVariant
4
4
 
5
5
 
6
6
  @dataclass
@@ -11,28 +11,29 @@ class ContractFunction:
11
11
  analyze_method: Optional[Callable] = None
12
12
  transact_method: Optional[Callable] = None
13
13
 
14
- def call(self):
14
+ def call(
15
+ self,
16
+ transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
17
+ ):
15
18
  if not self.read_only:
16
19
  raise ValueError("call() not implemented for non-readonly method")
17
- return self.call_method()
20
+ return self.call_method(transaction_hash_variant=transaction_hash_variant)
18
21
 
19
22
  def transact(
20
23
  self,
21
24
  value: int = 0,
22
25
  consensus_max_rotations: Optional[int] = None,
23
- leader_only: bool = False,
24
- wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
26
+ wait_transaction_status: TransactionStatus = TransactionStatus.ACCEPTED,
25
27
  wait_interval: Optional[int] = None,
26
28
  wait_retries: Optional[int] = None,
27
- wait_triggered_transactions: bool = True,
28
- wait_triggered_transactions_status: TransactionStatus = TransactionStatus.FINALIZED,
29
+ wait_triggered_transactions: bool = False,
30
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
29
31
  ):
30
32
  if self.read_only:
31
33
  raise ValueError("Cannot transact read-only method")
32
34
  return self.transact_method(
33
35
  value=value,
34
36
  consensus_max_rotations=consensus_max_rotations,
35
- leader_only=leader_only,
36
37
  wait_transaction_status=wait_transaction_status,
37
38
  wait_interval=wait_interval,
38
39
  wait_retries=wait_retries,
gltest/types.py CHANGED
@@ -4,4 +4,5 @@ from genlayer_py.types import (
4
4
  GenLayerTransaction,
5
5
  TransactionStatus,
6
6
  CalldataEncodable,
7
+ TransactionHashVariant,
7
8
  )
@@ -32,14 +32,14 @@ def pytest_addoption(parser):
32
32
  group.addoption(
33
33
  "--default-wait-interval",
34
34
  action="store",
35
- default=10000,
35
+ default=3000,
36
36
  help="Default interval (ms) between transaction receipt checks",
37
37
  )
38
38
 
39
39
  group.addoption(
40
40
  "--default-wait-retries",
41
41
  action="store",
42
- default=15,
42
+ default=50,
43
43
  help="Default number of retries for transaction receipt checks",
44
44
  )
45
45
 
@@ -64,6 +64,13 @@ def pytest_addoption(parser):
64
64
  help="Test with mocks",
65
65
  )
66
66
 
67
+ group.addoption(
68
+ "--leader-only",
69
+ action="store_true",
70
+ default=False,
71
+ help="Run contracts in leader-only mode",
72
+ )
73
+
67
74
 
68
75
  def pytest_configure(config):
69
76
  general_config = get_general_config()
@@ -91,6 +98,7 @@ def pytest_configure(config):
91
98
  rpc_url = config.getoption("--rpc-url")
92
99
  network = config.getoption("--network")
93
100
  test_with_mocks = config.getoption("--test-with-mocks")
101
+ leader_only = config.getoption("--leader-only")
94
102
 
95
103
  plugin_config = PluginConfig()
96
104
  plugin_config.contracts_dir = (
@@ -104,6 +112,7 @@ def pytest_configure(config):
104
112
  plugin_config.rpc_url = rpc_url
105
113
  plugin_config.network_name = network
106
114
  plugin_config.test_with_mocks = test_with_mocks
115
+ plugin_config.leader_only = leader_only
107
116
 
108
117
  general_config.plugin_config = plugin_config
109
118
 
@@ -137,6 +146,13 @@ def pytest_sessionstart(session):
137
146
  logger.info(f" Default wait retries: {general_config.get_default_wait_retries()}")
138
147
  logger.info(f" Test with mocks: {general_config.get_test_with_mocks()}")
139
148
 
149
+ if general_config.get_leader_only() and not general_config.check_studio_based_rpc():
150
+ logger.warning(
151
+ "Leader only mode: True (enabled on non-studio network - will have no effect)"
152
+ )
153
+ else:
154
+ logger.info(f" Leader only mode: {general_config.get_leader_only()}")
155
+
140
156
 
141
157
  def pytest_runtest_setup(item):
142
158
  _pytest_context.current_item = item
@@ -21,6 +21,7 @@ class PluginConfig:
21
21
  default_wait_retries: Optional[int] = None
22
22
  network_name: Optional[str] = None
23
23
  test_with_mocks: bool = False
24
+ leader_only: bool = False
24
25
 
25
26
 
26
27
  @dataclass
@@ -29,6 +30,7 @@ class NetworkConfigData:
29
30
  url: Optional[str] = None
30
31
  accounts: Optional[List[str]] = None
31
32
  from_account: Optional[str] = None
33
+ leader_only: bool = False
32
34
 
33
35
  def __post_init__(self):
34
36
  if self.id is not None and not isinstance(self.id, int):
@@ -157,8 +159,31 @@ class GeneralConfig:
157
159
  def get_test_with_mocks(self) -> bool:
158
160
  return self.plugin_config.test_with_mocks
159
161
 
162
+ def get_leader_only(self) -> bool:
163
+ if self.plugin_config.leader_only:
164
+ return True
165
+ network_name = self.get_network_name()
166
+ if network_name in self.user_config.networks:
167
+ network_config = self.user_config.networks[network_name]
168
+ return network_config.leader_only
169
+ return False
170
+
160
171
  def check_local_rpc(self) -> bool:
161
172
  SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
162
173
  rpc_url = self.get_rpc_url()
163
174
  domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
164
175
  return domain in SUPPORTED_RPC_DOMAINS
176
+
177
+ def check_studio_based_rpc(self) -> bool:
178
+ SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
179
+ rpc_url = self.get_rpc_url()
180
+ domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
181
+
182
+ if domain in SUPPORTED_RPC_DOMAINS:
183
+ return True
184
+
185
+ # Check .genlayer.com or .genlayerlabs.com subdomains
186
+ if domain.endswith(".genlayer.com") or domain.endswith(".genlayerlabs.com"):
187
+ return True
188
+
189
+ return False
gltest_cli/config/user.py CHANGED
@@ -17,7 +17,7 @@ from gltest_cli.config.constants import (
17
17
  from gltest_cli.config.types import UserConfig, NetworkConfigData, PathConfig
18
18
 
19
19
  VALID_ROOT_KEYS = ["networks", "paths", "environment"]
20
- VALID_NETWORK_KEYS = ["id", "url", "accounts", "from"]
20
+ VALID_NETWORK_KEYS = ["id", "url", "accounts", "from", "leader_only"]
21
21
  VALID_PATHS_KEYS = ["contracts", "artifacts"]
22
22
 
23
23
 
@@ -33,6 +33,7 @@ def get_default_user_config() -> UserConfig:
33
33
  url=DEFAULT_RPC_URL,
34
34
  accounts=accounts_private_keys,
35
35
  from_account=accounts_private_keys[0],
36
+ leader_only=False,
36
37
  ),
37
38
  },
38
39
  paths=PathConfig(
@@ -84,6 +85,10 @@ def validate_network_config(network_name: str, network_config: dict):
84
85
 
85
86
  if "from" in network_config and not isinstance(network_config["from"], str):
86
87
  raise ValueError(f"network {network_name} from must be a string")
88
+ if "leader_only" in network_config and not isinstance(
89
+ network_config["leader_only"], bool
90
+ ):
91
+ raise ValueError(f"network {network_name} leader_only must be a boolean")
87
92
 
88
93
  # For non-default networks, url and accounts are required
89
94
  if network_name != DEFAULT_NETWORK:
@@ -185,18 +190,23 @@ def _get_overridden_networks(raw_config: dict) -> tuple[dict, str]:
185
190
  ]
186
191
  if "from" in network_config:
187
192
  networks_config[network_name].from_account = network_config["from"]
193
+ if "leader_only" in network_config:
194
+ networks_config[network_name].leader_only = network_config[
195
+ "leader_only"
196
+ ]
188
197
  continue
189
198
 
190
199
  url = network_config["url"]
191
200
  accounts = network_config["accounts"]
192
201
  from_account = network_config.get("from", accounts[0])
193
202
  network_id = network_config.get("id")
194
-
203
+ leader_only = network_config.get("leader_only", False)
195
204
  networks_config[network_name] = NetworkConfigData(
196
205
  id=network_id,
197
206
  url=url,
198
207
  accounts=accounts,
199
208
  from_account=from_account,
209
+ leader_only=leader_only,
200
210
  )
201
211
  return networks_config, user_default_network
202
212
 
@@ -97,4 +97,4 @@ This result should be perfectly parsable by a JSON parser without errors.
97
97
  "winner": self.winner,
98
98
  "score": self.score,
99
99
  "has_resolved": self.has_resolved,
100
- }
100
+ }
@@ -1,24 +1,10 @@
1
- import time
2
1
  import json
2
+ from gltest.types import TransactionStatus
3
3
 
4
4
  from gltest import get_contract_factory
5
5
  from gltest.assertions import tx_execution_succeeded
6
6
 
7
7
 
8
- def wait_for_contract_deployment(intelligent_oracle_contract, max_retries=10, delay=5):
9
- """
10
- Wait for intelligent oracle contract to be fully deployed by attempting to call a method.
11
- This is used to check if the triggered deployment did deploy the contract.
12
- """
13
- for _ in range(max_retries):
14
- try:
15
- intelligent_oracle_contract.get_dict(args=[]).call()
16
- return True # If successful, contract is deployed
17
- except Exception:
18
- time.sleep(delay)
19
- return False
20
-
21
-
22
8
  def create_mock_response(markets_data):
23
9
  reasoning_single_source_marathon = "The HTML content contains the results of the Madrid Marathon 2024, which occurred on April 28, 2024. Mitku Tafa won and matches the name 'Tafa Mitku' in the list of potential outcomes."
24
10
  reasoning_all_sources_marathon = "The URL indicates that the Madrid Marathon 2024 has occurred on April 28, 2024, and Mitku Tafa was the winner. The name matches one of the potential outcomes. There are no conflicting sources."
@@ -124,7 +110,10 @@ def test_intelligent_oracle_factory_pattern(setup_validators):
124
110
  market_data["resolution_urls"],
125
111
  market_data["earliest_resolution_date"],
126
112
  ],
127
- ).transact()
113
+ ).transact(
114
+ wait_triggered_transactions=True,
115
+ wait_triggered_transactions_status=TransactionStatus.ACCEPTED,
116
+ )
128
117
  assert tx_execution_succeeded(create_result)
129
118
 
130
119
  # Get the latest contract address from factory
@@ -135,11 +124,6 @@ def test_intelligent_oracle_factory_pattern(setup_validators):
135
124
  market_contract = intelligent_oracle_factory.build_contract(new_market_address)
136
125
  created_market_contracts.append(market_contract)
137
126
 
138
- # Wait for the new market contract to be deployed
139
- assert wait_for_contract_deployment(
140
- market_contract
141
- ), f"Market contract deployment timeout for {market_data['prediction_market_id']}"
142
-
143
127
  # Verify all markets were registered
144
128
  assert len(registered_addresses) == len(markets_data)
145
129