genlayer-test 0.4.1__py3-none-any.whl → 2.0.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.
Files changed (44) hide show
  1. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/METADATA +75 -14
  2. genlayer_test-2.0.0.dist-info/RECORD +76 -0
  3. gltest/__init__.py +7 -6
  4. gltest/{glchain/client.py → clients.py} +1 -1
  5. gltest/contracts/__init__.py +4 -0
  6. gltest/contracts/contract.py +197 -0
  7. gltest/{glchain/contract.py → contracts/contract_factory.py} +22 -137
  8. gltest/contracts/contract_functions.py +59 -0
  9. gltest/contracts/method_stats.py +163 -0
  10. gltest/contracts/stats_collector.py +259 -0
  11. gltest/contracts/utils.py +12 -0
  12. gltest/fixtures.py +2 -6
  13. gltest/helpers/take_snapshot.py +1 -1
  14. gltest_cli/config/constants.py +1 -0
  15. gltest_cli/config/plugin.py +53 -0
  16. gltest_cli/config/pytest_context.py +9 -0
  17. gltest_cli/config/types.py +41 -0
  18. gltest_cli/config/user.py +21 -8
  19. tests/examples/contracts/football_prediction_market.py +1 -1
  20. tests/examples/tests/test_football_prediction_market.py +2 -2
  21. tests/examples/tests/test_intelligent_oracle_factory.py +6 -6
  22. tests/examples/tests/test_llm_erc20.py +5 -5
  23. tests/examples/tests/test_llm_erc20_analyze.py +50 -0
  24. tests/examples/tests/test_log_indexer.py +23 -11
  25. tests/examples/tests/test_multi_file_contract.py +2 -2
  26. tests/examples/tests/test_multi_file_contract_legacy.py +2 -2
  27. tests/examples/tests/test_multi_read_erc20.py +14 -12
  28. tests/examples/tests/test_multi_tenant_storage.py +11 -7
  29. tests/examples/tests/test_read_erc20.py +1 -1
  30. tests/examples/tests/test_storage.py +4 -4
  31. tests/examples/tests/test_storage_legacy.py +5 -3
  32. tests/examples/tests/test_user_storage.py +20 -10
  33. tests/examples/tests/test_wizard_of_coin.py +1 -1
  34. tests/gltest_cli/config/test_config_integration.py +432 -0
  35. tests/gltest_cli/config/test_general_config.py +406 -0
  36. tests/gltest_cli/config/test_plugin.py +167 -0
  37. tests/gltest_cli/config/test_user.py +61 -1
  38. genlayer_test-0.4.1.dist-info/RECORD +0 -67
  39. gltest/glchain/__init__.py +0 -16
  40. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/WHEEL +0 -0
  41. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/entry_points.txt +0 -0
  42. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/licenses/LICENSE +0 -0
  43. {genlayer_test-0.4.1.dist-info → genlayer_test-2.0.0.dist-info}/top_level.txt +0 -0
  44. /gltest/{glchain/account.py → accounts.py} +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genlayer-test
3
- Version: 0.4.1
3
+ Version: 2.0.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.6.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
@@ -59,8 +59,8 @@ contract = factory.deploy(account=other_account)
59
59
  assert contract.account == other_account
60
60
 
61
61
  # Interact with the contract
62
- result = contract.get_value() # Read method
63
- tx_receipt = contract.set_value(args=["new_value"]) # Write method
62
+ result = contract.get_value().call() # Read method
63
+ tx_receipt = contract.set_value(args=["new_value"]).transact() # Write method
64
64
 
65
65
  assert tx_execution_succeeded(tx_receipt)
66
66
  ```
@@ -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
@@ -320,7 +338,7 @@ def test_deployment():
320
338
 
321
339
  ### Read Methods
322
340
 
323
- Reading from the contract is straightforward:
341
+ Reading from the contract requires calling `.call()` on the method:
324
342
 
325
343
  ```python
326
344
  from gltest import get_contract_factory
@@ -332,7 +350,7 @@ def test_read_methods():
332
350
  contract = factory.deploy()
333
351
 
334
352
  # Call a read-only method
335
- result = contract.get_value(args=[])
353
+ result = contract.get_storage(args=[]).call()
336
354
 
337
355
  # Assert the result matches the initial value
338
356
  assert result == "initial_value"
@@ -340,7 +358,7 @@ def test_read_methods():
340
358
 
341
359
  ### Write Methods
342
360
 
343
- Writing to the contract requires transaction handling:
361
+ Writing to the contract requires calling `.transact()` on the method. Method arguments are passed to the write method, while transaction parameters are passed to `.transact()`:
344
362
 
345
363
  ```python
346
364
  from gltest import get_contract_factory
@@ -354,9 +372,9 @@ def test_write_methods():
354
372
  # Call a write method with arguments
355
373
  tx_receipt = contract.update_storage(
356
374
  args=["new_value"], # Method arguments
375
+ ).transact(
357
376
  value=0, # Optional: amount of native currency to send
358
377
  consensus_max_rotations=3, # Optional: max consensus rotations
359
- leader_only=False, # Optional: whether to run only on leader
360
378
  wait_interval=1, # Optional: seconds between status checks
361
379
  wait_retries=10, # Optional: max number of retries
362
380
  )
@@ -365,7 +383,7 @@ def test_write_methods():
365
383
  assert tx_execution_succeeded(tx_receipt)
366
384
 
367
385
  # Verify the value was updated
368
- assert contract.get_storage() == "new_value"
386
+ assert contract.get_storage().call() == "new_value"
369
387
  ```
370
388
 
371
389
  ### Assertions
@@ -503,6 +521,50 @@ Fixtures help maintain clean, DRY test code by:
503
521
  - Ensuring consistent test environments
504
522
  - Managing resource cleanup automatically
505
523
  - Providing appropriate scoping for performance
524
+ ### Statistical Analysis with `.analyze()`
525
+
526
+ The GenLayer Testing Suite provides a powerful `.analyze()` method for write operations that performs statistical analysis through multiple simulation runs. This is particularly useful for testing LLM-based contracts where outputs may vary:
527
+
528
+ ```python
529
+ from gltest import get_contract_factory
530
+
531
+ def test_analyze_method():
532
+ factory = get_contract_factory("LlmContract")
533
+ contract = factory.deploy()
534
+
535
+ # Analyze a write method's behavior across multiple runs
536
+ analysis = contract.process_with_llm(args=["input_data"]).analyze(
537
+ provider="openai", # LLM provider
538
+ model="gpt-4o", # Model to use
539
+ runs=100, # Number of simulation runs (default: 100)
540
+ config=None, # Optional: provider-specific config
541
+ plugin=None, # Optional: plugin name
542
+ plugin_config=None, # Optional: plugin configuration
543
+ )
544
+
545
+ # Access analysis results
546
+ print(f"Method: {analysis.method}")
547
+ print(f"Success rate: {analysis.success_rate:.2f}%")
548
+ print(f"Reliability score: {analysis.reliability_score:.2f}%")
549
+ print(f"Unique states: {analysis.unique_states}")
550
+ print(f"Execution time: {analysis.execution_time:.1f}s")
551
+
552
+ # The analysis returns a MethodStatsSummary object with:
553
+ # - method: The contract method name
554
+ # - args: Arguments passed to the method
555
+ # - total_runs: Total number of simulation runs
556
+ # - successful_runs: Number of successful executions
557
+ # - failed_runs: Number of failed executions
558
+ # - unique_states: Number of unique contract states observed
559
+ # - reliability_score: Percentage of runs with the most common state
560
+ # - execution_time: Total time for all simulations
561
+ ```
562
+
563
+ The `.analyze()` method helps you:
564
+ - Test non-deterministic contract methods
565
+ - Measure consistency of LLM-based operations
566
+ - Identify edge cases and failure patterns
567
+ - Benchmark performance across multiple runs
506
568
 
507
569
  ## 📝 Best Practices
508
570
 
@@ -546,6 +608,7 @@ Fixtures help maintain clean, DRY test code by:
546
608
  ```python
547
609
  tx_receipt = contract.set_value(
548
610
  args=["new_value"],
611
+ ).transact(
549
612
  wait_interval=2, # Increase wait interval between status checks
550
613
  wait_retries=20, # Increase number of retry attempts
551
614
  )
@@ -558,13 +621,11 @@ Fixtures help maintain clean, DRY test code by:
558
621
  # Try with increased consensus parameters
559
622
  contract = factory.deploy(
560
623
  consensus_max_rotations=5, # Increase number of consensus rotations
561
- leader_only=True, # Try leader-only mode for faster execution
562
624
  )
563
625
 
564
626
  # For critical operations, use more conservative settings
565
627
  contract = factory.deploy(
566
628
  consensus_max_rotations=10, # More rotations for better reliability
567
- leader_only=False, # Full consensus for better security
568
629
  wait_interval=3, # Longer wait between checks
569
630
  wait_retries=30 # More retries for consensus
570
631
  )
@@ -0,0 +1,76 @@
1
+ genlayer_test-2.0.0.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
2
+ gltest/__init__.py,sha256=qoBV3IgDJr8uh9262XsWNMhi-ilkSgMqKVC9FVMk7cw,372
3
+ gltest/accounts.py,sha256=HUmWguJMolggQaZNRPw-LGlRlQCjLLdUanKRowMv6pI,812
4
+ gltest/assertions.py,sha256=0dEk0VxcHK4I7GZPHxJmz-2jaA60V499gOSR74rZbfM,1748
5
+ gltest/clients.py,sha256=1dX6wmG3QCevQRLbSaFlHymZSb-sJ5aYwet1IoX2nbA,1554
6
+ gltest/exceptions.py,sha256=deJPmrTe5gF33qkkKF2IVJY7lc_knI7Ql3N7jZ8aLZs,510
7
+ gltest/fixtures.py,sha256=EJXmqcC3LD03v07mepacFl58lAdhbLj6bP5rtALYISk,2507
8
+ gltest/logging.py,sha256=jAkHsuMm-AEx1Xu1srU6W-0YzTwXJB48mCK-OVzAiN4,342
9
+ gltest/types.py,sha256=BODmwTr2gAUEiO9FjiuTiWwuKvXgo4xZWstQWNUfnlw,156
10
+ gltest/artifacts/__init__.py,sha256=qTt3TE19gVNWnQLUlt5aDe4nNvJ2YJ1jzDkMmYIsCG0,194
11
+ gltest/artifacts/contract.py,sha256=KChpmfjZod_0dVB8y-dvWz6IVm7QlIJsgG2ArtvVDaU,6457
12
+ gltest/contracts/__init__.py,sha256=A9bvEtYOoqoHS8TLlFBfmNOnfwdsJPEf-AZuikagCHM,166
13
+ gltest/contracts/contract.py,sha256=cYr5B5Sy-YRtEk6PvlLGkRy1X0cwf1EmsCHb6xPL-Rw,6524
14
+ gltest/contracts/contract_factory.py,sha256=4xoODlCu2jHh3Z2e6OmWWnAiXT99yFu_7DhVvCUbKZE,6812
15
+ gltest/contracts/contract_functions.py,sha256=Dpgu9rUSMrczBgNSlEaZ47RUpKzV8vucnc6OyxExMnM,2017
16
+ gltest/contracts/method_stats.py,sha256=zWWjvf7K5VC4yrHpDIR717VF7LYp1RaZ1Hr_RZvWxJA,5150
17
+ gltest/contracts/stats_collector.py,sha256=fuCc8L8hd0tsVGzH4adtZWwPa7ORf0A0zR5Dt1w92Qk,9033
18
+ gltest/contracts/utils.py,sha256=TTXgcXn9BuRIlKJrjwmU7R3l1IgXsXk2luM-r3lfbbg,296
19
+ gltest/helpers/__init__.py,sha256=I7HiTu_H7_hP65zY6Wl02r-5eAMr2eZvqBVmusuQLX4,180
20
+ gltest/helpers/fixture_snapshot.py,sha256=bMgvlEVQBGIQzj7NOyosXWlphI1H2C1o75Zo0C-kGfQ,1931
21
+ gltest/helpers/take_snapshot.py,sha256=-QkaBvFG4ZsNKv_nCSEsy5Ze1pICOHxVhReSeQmZUlY,1276
22
+ gltest_cli/logging.py,sha256=YRWIGwCJIkaB747oQvmS2tzF-B7zymdEMJznrlfyQYA,1245
23
+ gltest_cli/main.py,sha256=Ti2-0Ev1x5_cM0D1UKqdgaDt80CDHEQGtdRne2qLm4M,53
24
+ gltest_cli/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ gltest_cli/config/constants.py,sha256=3iSK337AeupyYl_6Sf8MR_o91XfLmul8m1rVmi3Fvmo,342
26
+ gltest_cli/config/general.py,sha256=ezpoGsT8grO9zQH6RugV14b1GzeFt-htYToHQBJhNvY,186
27
+ gltest_cli/config/plugin.py,sha256=zWNEJWjywI-T5B5Bc-8OyrGt7rUVlpNP8zrlsORhVgw,5412
28
+ gltest_cli/config/pytest_context.py,sha256=Ze8JSkrwMTCE8jIhpzU_71CEXg92SiEPvSgNTp-gbS4,243
29
+ gltest_cli/config/types.py,sha256=KDAA_vNqI7o_sYMj21ks3WaIXGrbLVb76E17TSlWUwI,7241
30
+ gltest_cli/config/user.py,sha256=WNX_Wi2eJpjW0UstQuQiDjzRpOIOwx9iiXOx4ntt93U,8759
31
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ tests/conftest.py,sha256=RKdoE5_zcMimeojAoA_GSFI9du4pMzMi1vZ1njtfoAs,28
33
+ tests/examples/contracts/football_prediction_market.py,sha256=9xwU8f3q73Hae-ByHy_wauhMPLRnLZd4XKNrClnjOJM,3248
34
+ tests/examples/contracts/intelligent_oracle.py,sha256=cZNGbjKMXY-pimVmPIKIlS963Gd3L1JAipq0VBR1J5Q,12360
35
+ tests/examples/contracts/intelligent_oracle_factory.py,sha256=8lBEn3Atb0yUpXwlvnShlcRxCBTXCrrkoITDHWoWuHU,1499
36
+ tests/examples/contracts/llm_erc20.py,sha256=pOvSUszCtC_f5yDX0tZnj494Ce10uESZ09JLLE8V67o,2534
37
+ tests/examples/contracts/log_indexer.py,sha256=Nlf8XUt13ujam3k6hbbVrPHK-KJ366Csz1TBjc4P07g,1901
38
+ tests/examples/contracts/multi_read_erc20.py,sha256=28qYqn191Ro3rP7YJtZwL6Sc7JDXTeh8_QoqvdVPdqM,864
39
+ tests/examples/contracts/multi_tenant_storage.py,sha256=5F3MCKbzyHMFqLRT9hZNCd3RzjSJvAKVJwLFMeazwog,1906
40
+ tests/examples/contracts/read_erc20.py,sha256=RgH269F0x482WuLLYcacBnZsGJjhgJp6sG_33cV6Z-w,454
41
+ tests/examples/contracts/storage.py,sha256=3DD3qvzb0JkVcBtu240e5kaZWgkh-bu6YExqBUYvfaw,501
42
+ tests/examples/contracts/user_storage.py,sha256=2W2Iv-hQZMkAaPl2RY_F-OeJpD4IlPxgWzN6e1bTkKE,649
43
+ tests/examples/contracts/wizard_of_coin.py,sha256=Y8daPpoSKdM8wfbCPOIgEdpkLIA1ZMmeg6Hu5fBB-kU,1624
44
+ tests/examples/contracts/multi_file_contract/__init__.py,sha256=CCdaK5p12GDf35hgbBWURNM5KUn6SWAcuyieTmZwVWE,548
45
+ tests/examples/contracts/multi_file_contract/other.py,sha256=jHDtjUL3eAUgE6yOYKFw_RfAH7kIwk8CvxUjbWHNruk,236
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
48
+ tests/examples/tests/test_llm_erc20.py,sha256=zb5F_7NgvZXhvqL2nULwzuTT6LGDprSy0WgrdjY7pZc,2096
49
+ tests/examples/tests/test_llm_erc20_analyze.py,sha256=ccWQenEOG-U4TJdu-0i-3qNM4xRRCHSr3Iwz7pxknws,1729
50
+ tests/examples/tests/test_log_indexer.py,sha256=46AqL7qquNc9GX2wxFxVcQXLqruMnPmxXl1yeB0-KZ4,2869
51
+ tests/examples/tests/test_multi_file_contract.py,sha256=ZC_zmEE-QtXZ1vxptFSWZw18fRZ5M3zqS7ZCZH1gIfc,569
52
+ tests/examples/tests/test_multi_file_contract_legacy.py,sha256=UTI7Thc8Wg-lotH9JiB5hp9ba6O-yFy1Ss-yuhlgljA,584
53
+ tests/examples/tests/test_multi_read_erc20.py,sha256=pSZUxoB33Z2EBtcvXxfUgwGSi_h7ryPoovkiNIfNAVQ,3713
54
+ tests/examples/tests/test_multi_tenant_storage.py,sha256=TjxN9ks5Wd0QkIszFKNSveNBdJ-wOb7O-fNwGQauXeY,2825
55
+ tests/examples/tests/test_read_erc20.py,sha256=vLGQwguaNnT497nSq-vt4LrXj4ehn5ZSgfPt0GVFoPc,1254
56
+ tests/examples/tests/test_storage.py,sha256=y46nPjM-Jd9FVJmaNE29RPqamzxVwYtPPWE_GlXUsls,774
57
+ tests/examples/tests/test_storage_legacy.py,sha256=STcoRKYP0JcwwNjFSTMpYGuOyXcw2DxGlemUivWp2B0,755
58
+ tests/examples/tests/test_user_storage.py,sha256=wk0r0AXfKNgI7Eeyc8noNlJKvZBFXDbXTQE_u19XxBQ,2927
59
+ tests/examples/tests/test_wizard_of_coin.py,sha256=AOQTanDsfZt9zIGkqZass_4BsGcVKTHzqRejN4KhSPI,854
60
+ tests/gltest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ tests/gltest/artifact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ tests/gltest/artifact/test_contract_definition.py,sha256=6R8THNFKKpG7brULzp63vT1_pPd_JFNp3ZS08CJJWrg,3642
63
+ tests/gltest/artifact/contracts/duplicate_ic_contract_1.py,sha256=bSWsUVjBy5cGtI72cjnkstsMzuUJbB3IG5JjTxOF-dc,500
64
+ tests/gltest/artifact/contracts/duplicate_ic_contract_2.py,sha256=bSWsUVjBy5cGtI72cjnkstsMzuUJbB3IG5JjTxOF-dc,500
65
+ tests/gltest/artifact/contracts/not_ic_contract.py,sha256=hQyGnYiiVceYdLI2WrvcFgPqzy1S4-YMb9FPhiHEGSA,510
66
+ tests/gltest/assertions/test_assertions.py,sha256=qzVrOdOM4xYtIy1sFHVAD_-naDHOequ23tEN0MELh0k,10781
67
+ tests/gltest_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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.0.0.dist-info/METADATA,sha256=ykgDxxVQ0p5XRRShD6ke9amrhH4o7MacAQsUya0r5Ts,24978
73
+ genlayer_test-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ genlayer_test-2.0.0.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
75
+ genlayer_test-2.0.0.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
76
+ genlayer_test-2.0.0.dist-info/RECORD,,
gltest/__init__.py CHANGED
@@ -1,14 +1,15 @@
1
- from gltest.glchain import (
2
- create_account,
1
+ from gltest.accounts import (
2
+ get_default_account,
3
+ get_accounts,
3
4
  create_accounts,
4
- get_contract_factory,
5
+ create_account,
6
+ )
7
+ from gltest.clients import (
5
8
  get_gl_client,
6
- get_accounts,
7
- get_default_account,
8
9
  )
10
+ from gltest.contracts import get_contract_factory
9
11
 
10
12
  __all__ = [
11
- "find_contract_definition",
12
13
  "create_account",
13
14
  "create_accounts",
14
15
  "get_contract_factory",
@@ -1,6 +1,6 @@
1
1
  from genlayer_py.chains import localnet
2
2
  from genlayer_py import create_client
3
- from .account import get_default_account
3
+ from gltest.accounts import get_default_account
4
4
  from functools import lru_cache
5
5
  from gltest_cli.config.general import get_general_config
6
6
 
@@ -0,0 +1,4 @@
1
+ from .contract import Contract
2
+ from .contract_factory import ContractFactory, get_contract_factory
3
+
4
+ __all__ = ["Contract", "ContractFactory", "get_contract_factory"]
@@ -0,0 +1,197 @@
1
+ import types
2
+ from eth_account.signers.local import LocalAccount
3
+ from dataclasses import dataclass
4
+ from gltest.clients import get_gl_client
5
+ from gltest.types import CalldataEncodable, GenLayerTransaction, TransactionStatus
6
+ from typing import List, Any, Optional, Dict, Callable
7
+ from gltest_cli.config.general import get_general_config
8
+ from .contract_functions import ContractFunction
9
+ from .stats_collector import StatsCollector, SimulationConfig
10
+
11
+
12
+ def read_contract_wrapper(
13
+ self,
14
+ method_name: str,
15
+ args: Optional[List[CalldataEncodable]] = None,
16
+ ) -> Any:
17
+ """
18
+ Wrapper to the contract read method.
19
+ """
20
+
21
+ def call_method():
22
+ client = get_gl_client()
23
+ return client.read_contract(
24
+ address=self.address,
25
+ function_name=method_name,
26
+ account=self.account,
27
+ args=args,
28
+ )
29
+
30
+ return ContractFunction(
31
+ method_name=method_name,
32
+ read_only=True,
33
+ call_method=call_method,
34
+ )
35
+
36
+
37
+ def write_contract_wrapper(
38
+ self,
39
+ method_name: str,
40
+ args: Optional[List[CalldataEncodable]] = None,
41
+ ) -> GenLayerTransaction:
42
+ """
43
+ Wrapper to the contract write method.
44
+ """
45
+
46
+ def transact_method(
47
+ value: int = 0,
48
+ consensus_max_rotations: Optional[int] = None,
49
+ wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
50
+ wait_interval: Optional[int] = None,
51
+ wait_retries: Optional[int] = None,
52
+ wait_triggered_transactions: bool = True,
53
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.FINALIZED,
54
+ ):
55
+ """
56
+ Transact the contract method.
57
+ """
58
+ general_config = get_general_config()
59
+ actual_wait_interval = (
60
+ wait_interval
61
+ if wait_interval is not None
62
+ else general_config.get_default_wait_interval()
63
+ )
64
+ actual_wait_retries = (
65
+ wait_retries
66
+ if wait_retries is not None
67
+ else general_config.get_default_wait_retries()
68
+ )
69
+ leader_only = (
70
+ general_config.get_leader_only()
71
+ if general_config.check_studio_based_rpc()
72
+ else False
73
+ )
74
+ client = get_gl_client()
75
+ tx_hash = client.write_contract(
76
+ address=self.address,
77
+ function_name=method_name,
78
+ account=self.account,
79
+ value=value,
80
+ consensus_max_rotations=consensus_max_rotations,
81
+ leader_only=leader_only,
82
+ args=args,
83
+ )
84
+ receipt = client.wait_for_transaction_receipt(
85
+ transaction_hash=tx_hash,
86
+ status=wait_transaction_status,
87
+ interval=actual_wait_interval,
88
+ retries=actual_wait_retries,
89
+ )
90
+ if wait_triggered_transactions:
91
+ triggered_transactions = receipt["triggered_transactions"]
92
+ for triggered_transaction in triggered_transactions:
93
+ client.wait_for_transaction_receipt(
94
+ transaction_hash=triggered_transaction,
95
+ status=wait_triggered_transactions_status,
96
+ interval=actual_wait_interval,
97
+ retries=actual_wait_retries,
98
+ )
99
+ return receipt
100
+
101
+ def analyze_method(
102
+ provider: str,
103
+ model: str,
104
+ config: Optional[Dict[str, Any]] = None,
105
+ plugin: Optional[str] = None,
106
+ plugin_config: Optional[Dict[str, Any]] = None,
107
+ runs: int = 100,
108
+ ):
109
+ """
110
+ Analyze the contract method using StatsCollector.
111
+ """
112
+ collector = StatsCollector(
113
+ contract_address=self.address,
114
+ method_name=method_name,
115
+ account=self.account,
116
+ args=args,
117
+ )
118
+ sim_config = SimulationConfig(
119
+ provider=provider,
120
+ model=model,
121
+ config=config,
122
+ plugin=plugin,
123
+ plugin_config=plugin_config,
124
+ )
125
+ sim_results = collector.run_simulations(sim_config, runs)
126
+ return collector.analyze_results(sim_results, runs, sim_config)
127
+
128
+ return ContractFunction(
129
+ method_name=method_name,
130
+ read_only=False,
131
+ transact_method=transact_method,
132
+ analyze_method=analyze_method,
133
+ )
134
+
135
+
136
+ def contract_function_factory(method_name: str, read_only: bool) -> Callable:
137
+ """
138
+ Create a function that interacts with a specific contract method.
139
+ """
140
+ if read_only:
141
+ return lambda self, args=None: read_contract_wrapper(self, method_name, args)
142
+ return lambda self, args=None: write_contract_wrapper(self, method_name, args)
143
+
144
+
145
+ @dataclass
146
+ class Contract:
147
+ """
148
+ Class to interact with a contract, its methods
149
+ are implemented dynamically at build time.
150
+ """
151
+
152
+ address: str
153
+ account: Optional[LocalAccount] = None
154
+ _schema: Optional[Dict[str, Any]] = None
155
+
156
+ @classmethod
157
+ def new(
158
+ cls,
159
+ address: str,
160
+ schema: Dict[str, Any],
161
+ account: Optional[LocalAccount] = None,
162
+ ) -> "Contract":
163
+ """
164
+ Build the methods from the schema.
165
+ """
166
+ if not isinstance(schema, dict) or "methods" not in schema:
167
+ raise ValueError("Invalid schema: must contain 'methods' field")
168
+ instance = cls(address=address, _schema=schema, account=account)
169
+ instance._build_methods_from_schema()
170
+ return instance
171
+
172
+ def _build_methods_from_schema(self):
173
+ """
174
+ Build the methods from the schema.
175
+ """
176
+ if self._schema is None:
177
+ raise ValueError("No schema provided")
178
+ for method_name, method_info in self._schema["methods"].items():
179
+ if not isinstance(method_info, dict) or "readonly" not in method_info:
180
+ raise ValueError(
181
+ f"Invalid method info for '{method_name}': must contain 'readonly' field"
182
+ )
183
+ method_func = contract_function_factory(
184
+ method_name, method_info["readonly"]
185
+ )
186
+ bound_method = types.MethodType(method_func, self)
187
+ setattr(self, method_name, bound_method)
188
+
189
+ def connect(self, account: LocalAccount) -> "Contract":
190
+ """
191
+ Create a new instance of the contract with the same methods and a different account.
192
+ """
193
+ new_contract = self.__class__(
194
+ address=self.address, account=account, _schema=self._schema
195
+ )
196
+ new_contract._build_methods_from_schema()
197
+ return new_contract