genlayer-test 0.4.1__tar.gz → 1.0.0__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 (81) hide show
  1. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/PKG-INFO +54 -8
  2. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/README.md +52 -6
  3. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/PKG-INFO +54 -8
  4. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/SOURCES.txt +12 -4
  5. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/requires.txt +1 -1
  6. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/__init__.py +7 -6
  7. genlayer_test-0.4.1/gltest/glchain/client.py → genlayer_test-1.0.0/gltest/clients.py +1 -1
  8. genlayer_test-1.0.0/gltest/contracts/__init__.py +4 -0
  9. genlayer_test-1.0.0/gltest/contracts/contract.py +193 -0
  10. genlayer_test-0.4.1/gltest/glchain/contract.py → genlayer_test-1.0.0/gltest/contracts/contract_factory.py +17 -135
  11. genlayer_test-1.0.0/gltest/contracts/contract_functions.py +61 -0
  12. genlayer_test-1.0.0/gltest/contracts/method_stats.py +163 -0
  13. genlayer_test-1.0.0/gltest/contracts/stats_collector.py +259 -0
  14. genlayer_test-1.0.0/gltest/contracts/utils.py +12 -0
  15. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/fixtures.py +2 -6
  16. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/helpers/take_snapshot.py +1 -1
  17. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/constants.py +1 -0
  18. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/plugin.py +37 -0
  19. genlayer_test-1.0.0/gltest_cli/config/pytest_context.py +9 -0
  20. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/types.py +16 -0
  21. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/user.py +9 -6
  22. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/pyproject.toml +2 -2
  23. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_football_prediction_market.py +2 -2
  24. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_intelligent_oracle_factory.py +6 -6
  25. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_llm_erc20.py +5 -5
  26. genlayer_test-1.0.0/tests/examples/tests/test_llm_erc20_analyze.py +50 -0
  27. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_log_indexer.py +23 -11
  28. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_multi_file_contract.py +2 -2
  29. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_multi_file_contract_legacy.py +2 -2
  30. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_multi_read_erc20.py +14 -12
  31. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_multi_tenant_storage.py +11 -7
  32. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_read_erc20.py +1 -1
  33. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_storage.py +4 -4
  34. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_storage_legacy.py +5 -3
  35. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_user_storage.py +20 -10
  36. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/tests/test_wizard_of_coin.py +1 -1
  37. genlayer_test-1.0.0/tests/gltest_cli/config/test_general_config.py +149 -0
  38. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest_cli/config/test_plugin.py +78 -0
  39. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest_cli/config/test_user.py +51 -1
  40. genlayer_test-0.4.1/gltest/glchain/__init__.py +0 -16
  41. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/LICENSE +0 -0
  42. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/dependency_links.txt +0 -0
  43. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/entry_points.txt +0 -0
  44. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/genlayer_test.egg-info/top_level.txt +0 -0
  45. /genlayer_test-0.4.1/gltest/glchain/account.py → /genlayer_test-1.0.0/gltest/accounts.py +0 -0
  46. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/artifacts/__init__.py +0 -0
  47. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/artifacts/contract.py +0 -0
  48. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/assertions.py +0 -0
  49. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/exceptions.py +0 -0
  50. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/helpers/__init__.py +0 -0
  51. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/helpers/fixture_snapshot.py +0 -0
  52. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/logging.py +0 -0
  53. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest/types.py +0 -0
  54. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/__init__.py +0 -0
  55. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/config/general.py +0 -0
  56. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/logging.py +0 -0
  57. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/gltest_cli/main.py +0 -0
  58. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/setup.cfg +0 -0
  59. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/__init__.py +0 -0
  60. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/conftest.py +0 -0
  61. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/football_prediction_market.py +0 -0
  62. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/intelligent_oracle.py +0 -0
  63. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/intelligent_oracle_factory.py +0 -0
  64. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/llm_erc20.py +0 -0
  65. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/log_indexer.py +0 -0
  66. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/multi_file_contract/__init__.py +0 -0
  67. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/multi_file_contract/other.py +0 -0
  68. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/multi_read_erc20.py +0 -0
  69. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/multi_tenant_storage.py +0 -0
  70. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/read_erc20.py +0 -0
  71. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/storage.py +0 -0
  72. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/user_storage.py +0 -0
  73. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/examples/contracts/wizard_of_coin.py +0 -0
  74. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/__init__.py +0 -0
  75. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/artifact/__init__.py +0 -0
  76. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_1.py +0 -0
  77. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/artifact/contracts/duplicate_ic_contract_2.py +0 -0
  78. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/artifact/contracts/not_ic_contract.py +0 -0
  79. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/artifact/test_contract_definition.py +0 -0
  80. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest/assertions/test_assertions.py +0 -0
  81. {genlayer_test-0.4.1 → genlayer_test-1.0.0}/tests/gltest_cli/__init__.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: 1.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.1
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
  ```
@@ -320,7 +320,7 @@ def test_deployment():
320
320
 
321
321
  ### Read Methods
322
322
 
323
- Reading from the contract is straightforward:
323
+ Reading from the contract requires calling `.call()` on the method:
324
324
 
325
325
  ```python
326
326
  from gltest import get_contract_factory
@@ -332,7 +332,7 @@ def test_read_methods():
332
332
  contract = factory.deploy()
333
333
 
334
334
  # Call a read-only method
335
- result = contract.get_value(args=[])
335
+ result = contract.get_storage(args=[]).call()
336
336
 
337
337
  # Assert the result matches the initial value
338
338
  assert result == "initial_value"
@@ -340,7 +340,7 @@ def test_read_methods():
340
340
 
341
341
  ### Write Methods
342
342
 
343
- Writing to the contract requires transaction handling:
343
+ 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
344
 
345
345
  ```python
346
346
  from gltest import get_contract_factory
@@ -354,6 +354,7 @@ def test_write_methods():
354
354
  # Call a write method with arguments
355
355
  tx_receipt = contract.update_storage(
356
356
  args=["new_value"], # Method arguments
357
+ ).transact(
357
358
  value=0, # Optional: amount of native currency to send
358
359
  consensus_max_rotations=3, # Optional: max consensus rotations
359
360
  leader_only=False, # Optional: whether to run only on leader
@@ -365,7 +366,7 @@ def test_write_methods():
365
366
  assert tx_execution_succeeded(tx_receipt)
366
367
 
367
368
  # Verify the value was updated
368
- assert contract.get_storage() == "new_value"
369
+ assert contract.get_storage().call() == "new_value"
369
370
  ```
370
371
 
371
372
  ### Assertions
@@ -503,6 +504,50 @@ Fixtures help maintain clean, DRY test code by:
503
504
  - Ensuring consistent test environments
504
505
  - Managing resource cleanup automatically
505
506
  - Providing appropriate scoping for performance
507
+ ### Statistical Analysis with `.analyze()`
508
+
509
+ 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:
510
+
511
+ ```python
512
+ from gltest import get_contract_factory
513
+
514
+ def test_analyze_method():
515
+ factory = get_contract_factory("LlmContract")
516
+ contract = factory.deploy()
517
+
518
+ # Analyze a write method's behavior across multiple runs
519
+ analysis = contract.process_with_llm(args=["input_data"]).analyze(
520
+ provider="openai", # LLM provider
521
+ model="gpt-4o", # Model to use
522
+ runs=100, # Number of simulation runs (default: 100)
523
+ config=None, # Optional: provider-specific config
524
+ plugin=None, # Optional: plugin name
525
+ plugin_config=None, # Optional: plugin configuration
526
+ )
527
+
528
+ # Access analysis results
529
+ print(f"Method: {analysis.method}")
530
+ print(f"Success rate: {analysis.success_rate:.2f}%")
531
+ print(f"Reliability score: {analysis.reliability_score:.2f}%")
532
+ print(f"Unique states: {analysis.unique_states}")
533
+ print(f"Execution time: {analysis.execution_time:.1f}s")
534
+
535
+ # The analysis returns a MethodStatsSummary object with:
536
+ # - method: The contract method name
537
+ # - args: Arguments passed to the method
538
+ # - total_runs: Total number of simulation runs
539
+ # - successful_runs: Number of successful executions
540
+ # - failed_runs: Number of failed executions
541
+ # - unique_states: Number of unique contract states observed
542
+ # - reliability_score: Percentage of runs with the most common state
543
+ # - execution_time: Total time for all simulations
544
+ ```
545
+
546
+ The `.analyze()` method helps you:
547
+ - Test non-deterministic contract methods
548
+ - Measure consistency of LLM-based operations
549
+ - Identify edge cases and failure patterns
550
+ - Benchmark performance across multiple runs
506
551
 
507
552
  ## 📝 Best Practices
508
553
 
@@ -546,6 +591,7 @@ Fixtures help maintain clean, DRY test code by:
546
591
  ```python
547
592
  tx_receipt = contract.set_value(
548
593
  args=["new_value"],
594
+ ).transact(
549
595
  wait_interval=2, # Increase wait interval between status checks
550
596
  wait_retries=20, # Increase number of retry attempts
551
597
  )
@@ -36,8 +36,8 @@ contract = factory.deploy(account=other_account)
36
36
  assert contract.account == other_account
37
37
 
38
38
  # Interact with the contract
39
- result = contract.get_value() # Read method
40
- tx_receipt = contract.set_value(args=["new_value"]) # Write method
39
+ result = contract.get_value().call() # Read method
40
+ tx_receipt = contract.set_value(args=["new_value"]).transact() # Write method
41
41
 
42
42
  assert tx_execution_succeeded(tx_receipt)
43
43
  ```
@@ -297,7 +297,7 @@ def test_deployment():
297
297
 
298
298
  ### Read Methods
299
299
 
300
- Reading from the contract is straightforward:
300
+ Reading from the contract requires calling `.call()` on the method:
301
301
 
302
302
  ```python
303
303
  from gltest import get_contract_factory
@@ -309,7 +309,7 @@ def test_read_methods():
309
309
  contract = factory.deploy()
310
310
 
311
311
  # Call a read-only method
312
- result = contract.get_value(args=[])
312
+ result = contract.get_storage(args=[]).call()
313
313
 
314
314
  # Assert the result matches the initial value
315
315
  assert result == "initial_value"
@@ -317,7 +317,7 @@ def test_read_methods():
317
317
 
318
318
  ### Write Methods
319
319
 
320
- Writing to the contract requires transaction handling:
320
+ 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()`:
321
321
 
322
322
  ```python
323
323
  from gltest import get_contract_factory
@@ -331,6 +331,7 @@ def test_write_methods():
331
331
  # Call a write method with arguments
332
332
  tx_receipt = contract.update_storage(
333
333
  args=["new_value"], # Method arguments
334
+ ).transact(
334
335
  value=0, # Optional: amount of native currency to send
335
336
  consensus_max_rotations=3, # Optional: max consensus rotations
336
337
  leader_only=False, # Optional: whether to run only on leader
@@ -342,7 +343,7 @@ def test_write_methods():
342
343
  assert tx_execution_succeeded(tx_receipt)
343
344
 
344
345
  # Verify the value was updated
345
- assert contract.get_storage() == "new_value"
346
+ assert contract.get_storage().call() == "new_value"
346
347
  ```
347
348
 
348
349
  ### Assertions
@@ -480,6 +481,50 @@ Fixtures help maintain clean, DRY test code by:
480
481
  - Ensuring consistent test environments
481
482
  - Managing resource cleanup automatically
482
483
  - Providing appropriate scoping for performance
484
+ ### Statistical Analysis with `.analyze()`
485
+
486
+ 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:
487
+
488
+ ```python
489
+ from gltest import get_contract_factory
490
+
491
+ def test_analyze_method():
492
+ factory = get_contract_factory("LlmContract")
493
+ contract = factory.deploy()
494
+
495
+ # Analyze a write method's behavior across multiple runs
496
+ analysis = contract.process_with_llm(args=["input_data"]).analyze(
497
+ provider="openai", # LLM provider
498
+ model="gpt-4o", # Model to use
499
+ runs=100, # Number of simulation runs (default: 100)
500
+ config=None, # Optional: provider-specific config
501
+ plugin=None, # Optional: plugin name
502
+ plugin_config=None, # Optional: plugin configuration
503
+ )
504
+
505
+ # Access analysis results
506
+ print(f"Method: {analysis.method}")
507
+ print(f"Success rate: {analysis.success_rate:.2f}%")
508
+ print(f"Reliability score: {analysis.reliability_score:.2f}%")
509
+ print(f"Unique states: {analysis.unique_states}")
510
+ print(f"Execution time: {analysis.execution_time:.1f}s")
511
+
512
+ # The analysis returns a MethodStatsSummary object with:
513
+ # - method: The contract method name
514
+ # - args: Arguments passed to the method
515
+ # - total_runs: Total number of simulation runs
516
+ # - successful_runs: Number of successful executions
517
+ # - failed_runs: Number of failed executions
518
+ # - unique_states: Number of unique contract states observed
519
+ # - reliability_score: Percentage of runs with the most common state
520
+ # - execution_time: Total time for all simulations
521
+ ```
522
+
523
+ The `.analyze()` method helps you:
524
+ - Test non-deterministic contract methods
525
+ - Measure consistency of LLM-based operations
526
+ - Identify edge cases and failure patterns
527
+ - Benchmark performance across multiple runs
483
528
 
484
529
  ## 📝 Best Practices
485
530
 
@@ -523,6 +568,7 @@ Fixtures help maintain clean, DRY test code by:
523
568
  ```python
524
569
  tx_receipt = contract.set_value(
525
570
  args=["new_value"],
571
+ ).transact(
526
572
  wait_interval=2, # Increase wait interval between status checks
527
573
  wait_retries=20, # Increase number of retry attempts
528
574
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genlayer-test
3
- Version: 0.4.1
3
+ Version: 1.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.1
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
  ```
@@ -320,7 +320,7 @@ def test_deployment():
320
320
 
321
321
  ### Read Methods
322
322
 
323
- Reading from the contract is straightforward:
323
+ Reading from the contract requires calling `.call()` on the method:
324
324
 
325
325
  ```python
326
326
  from gltest import get_contract_factory
@@ -332,7 +332,7 @@ def test_read_methods():
332
332
  contract = factory.deploy()
333
333
 
334
334
  # Call a read-only method
335
- result = contract.get_value(args=[])
335
+ result = contract.get_storage(args=[]).call()
336
336
 
337
337
  # Assert the result matches the initial value
338
338
  assert result == "initial_value"
@@ -340,7 +340,7 @@ def test_read_methods():
340
340
 
341
341
  ### Write Methods
342
342
 
343
- Writing to the contract requires transaction handling:
343
+ 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
344
 
345
345
  ```python
346
346
  from gltest import get_contract_factory
@@ -354,6 +354,7 @@ def test_write_methods():
354
354
  # Call a write method with arguments
355
355
  tx_receipt = contract.update_storage(
356
356
  args=["new_value"], # Method arguments
357
+ ).transact(
357
358
  value=0, # Optional: amount of native currency to send
358
359
  consensus_max_rotations=3, # Optional: max consensus rotations
359
360
  leader_only=False, # Optional: whether to run only on leader
@@ -365,7 +366,7 @@ def test_write_methods():
365
366
  assert tx_execution_succeeded(tx_receipt)
366
367
 
367
368
  # Verify the value was updated
368
- assert contract.get_storage() == "new_value"
369
+ assert contract.get_storage().call() == "new_value"
369
370
  ```
370
371
 
371
372
  ### Assertions
@@ -503,6 +504,50 @@ Fixtures help maintain clean, DRY test code by:
503
504
  - Ensuring consistent test environments
504
505
  - Managing resource cleanup automatically
505
506
  - Providing appropriate scoping for performance
507
+ ### Statistical Analysis with `.analyze()`
508
+
509
+ 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:
510
+
511
+ ```python
512
+ from gltest import get_contract_factory
513
+
514
+ def test_analyze_method():
515
+ factory = get_contract_factory("LlmContract")
516
+ contract = factory.deploy()
517
+
518
+ # Analyze a write method's behavior across multiple runs
519
+ analysis = contract.process_with_llm(args=["input_data"]).analyze(
520
+ provider="openai", # LLM provider
521
+ model="gpt-4o", # Model to use
522
+ runs=100, # Number of simulation runs (default: 100)
523
+ config=None, # Optional: provider-specific config
524
+ plugin=None, # Optional: plugin name
525
+ plugin_config=None, # Optional: plugin configuration
526
+ )
527
+
528
+ # Access analysis results
529
+ print(f"Method: {analysis.method}")
530
+ print(f"Success rate: {analysis.success_rate:.2f}%")
531
+ print(f"Reliability score: {analysis.reliability_score:.2f}%")
532
+ print(f"Unique states: {analysis.unique_states}")
533
+ print(f"Execution time: {analysis.execution_time:.1f}s")
534
+
535
+ # The analysis returns a MethodStatsSummary object with:
536
+ # - method: The contract method name
537
+ # - args: Arguments passed to the method
538
+ # - total_runs: Total number of simulation runs
539
+ # - successful_runs: Number of successful executions
540
+ # - failed_runs: Number of failed executions
541
+ # - unique_states: Number of unique contract states observed
542
+ # - reliability_score: Percentage of runs with the most common state
543
+ # - execution_time: Total time for all simulations
544
+ ```
545
+
546
+ The `.analyze()` method helps you:
547
+ - Test non-deterministic contract methods
548
+ - Measure consistency of LLM-based operations
549
+ - Identify edge cases and failure patterns
550
+ - Benchmark performance across multiple runs
506
551
 
507
552
  ## 📝 Best Practices
508
553
 
@@ -546,6 +591,7 @@ Fixtures help maintain clean, DRY test code by:
546
591
  ```python
547
592
  tx_receipt = contract.set_value(
548
593
  args=["new_value"],
594
+ ).transact(
549
595
  wait_interval=2, # Increase wait interval between status checks
550
596
  wait_retries=20, # Increase number of retry attempts
551
597
  )
@@ -8,17 +8,22 @@ genlayer_test.egg-info/entry_points.txt
8
8
  genlayer_test.egg-info/requires.txt
9
9
  genlayer_test.egg-info/top_level.txt
10
10
  gltest/__init__.py
11
+ gltest/accounts.py
11
12
  gltest/assertions.py
13
+ gltest/clients.py
12
14
  gltest/exceptions.py
13
15
  gltest/fixtures.py
14
16
  gltest/logging.py
15
17
  gltest/types.py
16
18
  gltest/artifacts/__init__.py
17
19
  gltest/artifacts/contract.py
18
- gltest/glchain/__init__.py
19
- gltest/glchain/account.py
20
- gltest/glchain/client.py
21
- gltest/glchain/contract.py
20
+ gltest/contracts/__init__.py
21
+ gltest/contracts/contract.py
22
+ gltest/contracts/contract_factory.py
23
+ gltest/contracts/contract_functions.py
24
+ gltest/contracts/method_stats.py
25
+ gltest/contracts/stats_collector.py
26
+ gltest/contracts/utils.py
22
27
  gltest/helpers/__init__.py
23
28
  gltest/helpers/fixture_snapshot.py
24
29
  gltest/helpers/take_snapshot.py
@@ -28,6 +33,7 @@ gltest_cli/config/__init__.py
28
33
  gltest_cli/config/constants.py
29
34
  gltest_cli/config/general.py
30
35
  gltest_cli/config/plugin.py
36
+ gltest_cli/config/pytest_context.py
31
37
  gltest_cli/config/types.py
32
38
  gltest_cli/config/user.py
33
39
  tests/__init__.py
@@ -48,6 +54,7 @@ tests/examples/contracts/multi_file_contract/other.py
48
54
  tests/examples/tests/test_football_prediction_market.py
49
55
  tests/examples/tests/test_intelligent_oracle_factory.py
50
56
  tests/examples/tests/test_llm_erc20.py
57
+ tests/examples/tests/test_llm_erc20_analyze.py
51
58
  tests/examples/tests/test_log_indexer.py
52
59
  tests/examples/tests/test_multi_file_contract.py
53
60
  tests/examples/tests/test_multi_file_contract_legacy.py
@@ -66,5 +73,6 @@ tests/gltest/artifact/contracts/duplicate_ic_contract_2.py
66
73
  tests/gltest/artifact/contracts/not_ic_contract.py
67
74
  tests/gltest/assertions/test_assertions.py
68
75
  tests/gltest_cli/__init__.py
76
+ tests/gltest_cli/config/test_general_config.py
69
77
  tests/gltest_cli/config/test_plugin.py
70
78
  tests/gltest_cli/config/test_user.py
@@ -1,6 +1,6 @@
1
1
  pytest
2
2
  setuptools>=77.0
3
- genlayer-py==0.6.1
3
+ genlayer-py==0.7.1
4
4
  colorama>=0.4.6
5
5
  pyyaml
6
6
  python-dotenv
@@ -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,193 @@
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
+ leader_only: bool = False,
50
+ wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
51
+ wait_interval: Optional[int] = None,
52
+ wait_retries: Optional[int] = None,
53
+ wait_triggered_transactions: bool = True,
54
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.FINALIZED,
55
+ ):
56
+ """
57
+ Transact the contract method.
58
+ """
59
+ general_config = get_general_config()
60
+ actual_wait_interval = (
61
+ wait_interval
62
+ if wait_interval is not None
63
+ else general_config.get_default_wait_interval()
64
+ )
65
+ actual_wait_retries = (
66
+ wait_retries
67
+ if wait_retries is not None
68
+ else general_config.get_default_wait_retries()
69
+ )
70
+ client = get_gl_client()
71
+ tx_hash = client.write_contract(
72
+ address=self.address,
73
+ function_name=method_name,
74
+ account=self.account,
75
+ value=value,
76
+ consensus_max_rotations=consensus_max_rotations,
77
+ leader_only=leader_only,
78
+ args=args,
79
+ )
80
+ receipt = client.wait_for_transaction_receipt(
81
+ transaction_hash=tx_hash,
82
+ status=wait_transaction_status,
83
+ interval=actual_wait_interval,
84
+ retries=actual_wait_retries,
85
+ )
86
+ if wait_triggered_transactions:
87
+ triggered_transactions = receipt["triggered_transactions"]
88
+ for triggered_transaction in triggered_transactions:
89
+ client.wait_for_transaction_receipt(
90
+ transaction_hash=triggered_transaction,
91
+ status=wait_triggered_transactions_status,
92
+ interval=actual_wait_interval,
93
+ retries=actual_wait_retries,
94
+ )
95
+ return receipt
96
+
97
+ def analyze_method(
98
+ provider: str,
99
+ model: str,
100
+ config: Optional[Dict[str, Any]] = None,
101
+ plugin: Optional[str] = None,
102
+ plugin_config: Optional[Dict[str, Any]] = None,
103
+ runs: int = 100,
104
+ ):
105
+ """
106
+ Analyze the contract method using StatsCollector.
107
+ """
108
+ collector = StatsCollector(
109
+ contract_address=self.address,
110
+ method_name=method_name,
111
+ account=self.account,
112
+ args=args,
113
+ )
114
+ sim_config = SimulationConfig(
115
+ provider=provider,
116
+ model=model,
117
+ config=config,
118
+ plugin=plugin,
119
+ plugin_config=plugin_config,
120
+ )
121
+ sim_results = collector.run_simulations(sim_config, runs)
122
+ return collector.analyze_results(sim_results, runs, sim_config)
123
+
124
+ return ContractFunction(
125
+ method_name=method_name,
126
+ read_only=False,
127
+ transact_method=transact_method,
128
+ analyze_method=analyze_method,
129
+ )
130
+
131
+
132
+ def contract_function_factory(method_name: str, read_only: bool) -> Callable:
133
+ """
134
+ Create a function that interacts with a specific contract method.
135
+ """
136
+ if read_only:
137
+ return lambda self, args=None: read_contract_wrapper(self, method_name, args)
138
+ return lambda self, args=None: write_contract_wrapper(self, method_name, args)
139
+
140
+
141
+ @dataclass
142
+ class Contract:
143
+ """
144
+ Class to interact with a contract, its methods
145
+ are implemented dynamically at build time.
146
+ """
147
+
148
+ address: str
149
+ account: Optional[LocalAccount] = None
150
+ _schema: Optional[Dict[str, Any]] = None
151
+
152
+ @classmethod
153
+ def new(
154
+ cls,
155
+ address: str,
156
+ schema: Dict[str, Any],
157
+ account: Optional[LocalAccount] = None,
158
+ ) -> "Contract":
159
+ """
160
+ Build the methods from the schema.
161
+ """
162
+ if not isinstance(schema, dict) or "methods" not in schema:
163
+ raise ValueError("Invalid schema: must contain 'methods' field")
164
+ instance = cls(address=address, _schema=schema, account=account)
165
+ instance._build_methods_from_schema()
166
+ return instance
167
+
168
+ def _build_methods_from_schema(self):
169
+ """
170
+ Build the methods from the schema.
171
+ """
172
+ if self._schema is None:
173
+ raise ValueError("No schema provided")
174
+ for method_name, method_info in self._schema["methods"].items():
175
+ if not isinstance(method_info, dict) or "readonly" not in method_info:
176
+ raise ValueError(
177
+ f"Invalid method info for '{method_name}': must contain 'readonly' field"
178
+ )
179
+ method_func = contract_function_factory(
180
+ method_name, method_info["readonly"]
181
+ )
182
+ bound_method = types.MethodType(method_func, self)
183
+ setattr(self, method_name, bound_method)
184
+
185
+ def connect(self, account: LocalAccount) -> "Contract":
186
+ """
187
+ Create a new instance of the contract with the same methods and a different account.
188
+ """
189
+ new_contract = self.__class__(
190
+ address=self.address, account=account, _schema=self._schema
191
+ )
192
+ new_contract._build_methods_from_schema()
193
+ return new_contract