genlayer-test 0.5.1__py3-none-any.whl → 0.7.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: 0.5.1
3
+ Version: 0.7.0
4
4
  Summary: GenLayer Testing Suite
5
5
  Author: GenLayer
6
6
  License-Expression: MIT
@@ -365,17 +365,24 @@ Key features demonstrated in this contract:
365
365
 
366
366
  ### Contract Deployment
367
367
 
368
+ The GenLayer Testing Suite provides two methods for deploying contracts:
369
+
370
+ 1. **`deploy()`** - Returns the deployed contract instance (recommended for most use cases)
371
+ 2. **`deploy_contract_tx()`** - Returns only the transaction receipt
372
+
368
373
  Here's how to deploy the Storage contract:
369
374
 
370
375
  ```python
371
376
  from gltest import get_contract_factory, get_default_account
377
+ from gltest.assertions import tx_execution_succeeded
378
+ from gltest.utils import extract_contract_address
372
379
 
373
380
  def test_deployment():
374
381
  # Get the contract factory for your contract
375
382
  # it will search in the contracts directory
376
383
  factory = get_contract_factory("Storage")
377
384
 
378
- # Deploy the contract with constructor arguments
385
+ # Method 1: Deploy the contract with constructor arguments (recommended)
379
386
  contract = factory.deploy(
380
387
  args=["initial_value"], # Constructor arguments
381
388
  account=get_default_account(), # Account to deploy from
@@ -384,6 +391,18 @@ def test_deployment():
384
391
 
385
392
  # Contract is now deployed and ready to use
386
393
  assert contract.address is not None
394
+
395
+ # Method 2: Deploy and get only the receipt
396
+ receipt = factory.deploy_contract_tx(
397
+ args=["initial_value"],
398
+ account=get_default_account(),
399
+ )
400
+
401
+ # Verify deployment succeeded
402
+ assert tx_execution_succeeded(receipt)
403
+
404
+ # Get the contract address
405
+ contract_address = extract_contract_address(receipt)
387
406
  ```
388
407
 
389
408
  ### Read Methods
@@ -1,4 +1,4 @@
1
- genlayer_test-0.5.1.dist-info/licenses/LICENSE,sha256=che_H4vE0QUx3HvWrAa1_jDEVInift0U6VO15-QqEls,1064
1
+ genlayer_test-0.7.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,15 +6,16 @@ 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=Kj6UuTpRXkA4c5ystKfwgXuCiRJ1VMkbgvP7Auvznhw,184
9
+ gltest/types.py,sha256=LJogYw6jQGoZnmE9Kx-YjNBRHWe3-mGsVIFP6R-zwSw,636
10
+ gltest/utils.py,sha256=-gHhjrS7i_GhDG3sKOq2qsTtYBt4HHgXHEXh-3RB_rI,573
10
11
  gltest/artifacts/__init__.py,sha256=qTt3TE19gVNWnQLUlt5aDe4nNvJ2YJ1jzDkMmYIsCG0,194
11
12
  gltest/artifacts/contract.py,sha256=KChpmfjZod_0dVB8y-dvWz6IVm7QlIJsgG2ArtvVDaU,6457
12
13
  gltest/contracts/__init__.py,sha256=A9bvEtYOoqoHS8TLlFBfmNOnfwdsJPEf-AZuikagCHM,166
13
- gltest/contracts/contract.py,sha256=hYuugaV_gNFk58mGyLRPKkKVKIjsCDlVpKLvwRBf7sM,6743
14
- gltest/contracts/contract_factory.py,sha256=8MhiTD3ZBcFY6tbe1Xn26JryVWFfVZhXKN4AYXnHmdY,7615
15
- gltest/contracts/contract_functions.py,sha256=5gNQYzN47rCJtmcMtUkl4ZK-yDifdZup2YhtlFlrx7E,2203
14
+ gltest/contracts/contract.py,sha256=jLF_ojSM6IIbdGO2_DhsO79r2wZ2Z8eBAJRrZk2qVTI,7748
15
+ gltest/contracts/contract_factory.py,sha256=PpWh4mKf1hDMv_yms5lwFV_EoXxXiuNfdXbwD74hbAU,8929
16
+ gltest/contracts/contract_functions.py,sha256=W6Dpw1z-n9EeJxlNtIbeVnzpv4BPABhtgmgjnS8-P0w,2573
16
17
  gltest/contracts/method_stats.py,sha256=zWWjvf7K5VC4yrHpDIR717VF7LYp1RaZ1Hr_RZvWxJA,5150
17
- gltest/contracts/stats_collector.py,sha256=fuCc8L8hd0tsVGzH4adtZWwPa7ORf0A0zR5Dt1w92Qk,9033
18
+ gltest/contracts/stats_collector.py,sha256=iwsnoYo5aZbI4SVMH7dR-5CQgkglExXfsvaUDpwcdss,9286
18
19
  gltest/contracts/utils.py,sha256=TTXgcXn9BuRIlKJrjwmU7R3l1IgXsXk2luM-r3lfbbg,296
19
20
  gltest/helpers/__init__.py,sha256=I7HiTu_H7_hP65zY6Wl02r-5eAMr2eZvqBVmusuQLX4,180
20
21
  gltest/helpers/fixture_snapshot.py,sha256=bMgvlEVQBGIQzj7NOyosXWlphI1H2C1o75Zo0C-kGfQ,1931
@@ -33,11 +34,13 @@ tests/conftest.py,sha256=RKdoE5_zcMimeojAoA_GSFI9du4pMzMi1vZ1njtfoAs,28
33
34
  tests/examples/contracts/football_prediction_market.py,sha256=9xwU8f3q73Hae-ByHy_wauhMPLRnLZd4XKNrClnjOJM,3248
34
35
  tests/examples/contracts/intelligent_oracle.py,sha256=cZNGbjKMXY-pimVmPIKIlS963Gd3L1JAipq0VBR1J5Q,12360
35
36
  tests/examples/contracts/intelligent_oracle_factory.py,sha256=6UApEdPzcUXR18wMpaQXtWpEKeQjU0zDkI5ezghuJUo,1526
37
+ tests/examples/contracts/invalid_deploy.py,sha256=cHXV0aq8gfbWdvjchnjSeoZDCCC-5iNdLGUkJi-tQfo,228
36
38
  tests/examples/contracts/llm_erc20.py,sha256=pOvSUszCtC_f5yDX0tZnj494Ce10uESZ09JLLE8V67o,2534
37
39
  tests/examples/contracts/log_indexer.py,sha256=Nlf8XUt13ujam3k6hbbVrPHK-KJ366Csz1TBjc4P07g,1901
38
40
  tests/examples/contracts/multi_read_erc20.py,sha256=28qYqn191Ro3rP7YJtZwL6Sc7JDXTeh8_QoqvdVPdqM,864
39
41
  tests/examples/contracts/multi_tenant_storage.py,sha256=ceq3X4E7pkvSdy7eQMhAoHme4KFedjAcpkfUYBmcgVM,1941
40
42
  tests/examples/contracts/read_erc20.py,sha256=RgH269F0x482WuLLYcacBnZsGJjhgJp6sG_33cV6Z-w,454
43
+ tests/examples/contracts/simple_time_contract.py,sha256=-9p_umlzUW5X2NL6IknO6Khs_114izSWifP5D2bWCMo,2512
41
44
  tests/examples/contracts/storage.py,sha256=3DD3qvzb0JkVcBtu240e5kaZWgkh-bu6YExqBUYvfaw,501
42
45
  tests/examples/contracts/user_storage.py,sha256=2W2Iv-hQZMkAaPl2RY_F-OeJpD4IlPxgWzN6e1bTkKE,649
43
46
  tests/examples/contracts/wizard_of_coin.py,sha256=Y8daPpoSKdM8wfbCPOIgEdpkLIA1ZMmeg6Hu5fBB-kU,1624
@@ -45,13 +48,15 @@ tests/examples/contracts/multi_file_contract/__init__.py,sha256=xDn_wS62GhCmnYoI
45
48
  tests/examples/contracts/multi_file_contract/other.py,sha256=jHDtjUL3eAUgE6yOYKFw_RfAH7kIwk8CvxUjbWHNruk,236
46
49
  tests/examples/tests/test_football_prediction_market.py,sha256=f2hfDK76WrNJZtFkTPKoPRR6bkmFLEssnlwwltSnU9A,1111
47
50
  tests/examples/tests/test_intelligent_oracle_factory.py,sha256=sL26aeb84XpJPCSUSIX3yrbwQE8GZtvNcsaKTpngFmY,7611
51
+ tests/examples/tests/test_invalid_deploy.py,sha256=pppM8_Vn4DXcWq9iyJXb0SpZnKXkIyJxoIDW5ApCS94,814
48
52
  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
53
+ tests/examples/tests/test_llm_erc20_analyze.py,sha256=rVnC3iQW_1J3P6cczEVOOG84K6ldCzlZbMO9WXQdRds,1849
50
54
  tests/examples/tests/test_log_indexer.py,sha256=46AqL7qquNc9GX2wxFxVcQXLqruMnPmxXl1yeB0-KZ4,2869
51
55
  tests/examples/tests/test_multi_file_contract.py,sha256=1Emj6ze4f-OrpxvqExeJhPHjK0cNAQW54MXf6fy2Yuc,469
52
56
  tests/examples/tests/test_multi_read_erc20.py,sha256=pSZUxoB33Z2EBtcvXxfUgwGSi_h7ryPoovkiNIfNAVQ,3713
53
57
  tests/examples/tests/test_multi_tenant_storage.py,sha256=8B3tjnRbT8ATN5obtWkGsN3nOtBU9OSjuaH9aEat3vc,2935
54
58
  tests/examples/tests/test_read_erc20.py,sha256=vLGQwguaNnT497nSq-vt4LrXj4ehn5ZSgfPt0GVFoPc,1254
59
+ tests/examples/tests/test_simple_time_contract.py,sha256=HQCJNheFyaHh73jZHyGOhNItzg045o8XsW-jLnNp-xc,3199
55
60
  tests/examples/tests/test_storage.py,sha256=y46nPjM-Jd9FVJmaNE29RPqamzxVwYtPPWE_GlXUsls,774
56
61
  tests/examples/tests/test_user_storage.py,sha256=wk0r0AXfKNgI7Eeyc8noNlJKvZBFXDbXTQE_u19XxBQ,2927
57
62
  tests/examples/tests/test_wizard_of_coin.py,sha256=AOQTanDsfZt9zIGkqZass_4BsGcVKTHzqRejN4KhSPI,854
@@ -67,8 +72,8 @@ tests/gltest_cli/config/test_config_integration.py,sha256=vPTzr3_h9UMw7m72HogBJE
67
72
  tests/gltest_cli/config/test_general_config.py,sha256=UHtSwVnso-ZwNtYM0Z4v2sCLKwyrVbHlk6b1leVfV84,14703
68
73
  tests/gltest_cli/config/test_plugin.py,sha256=COrEK5tHP1BSzanWbZHmN3EQgE9VuTcPvB95pgOvKS4,7974
69
74
  tests/gltest_cli/config/test_user.py,sha256=JxR655oUFoM9quWQO68CVPKRpT0TMpzS3bF6j6NWyT4,14401
70
- genlayer_test-0.5.1.dist-info/METADATA,sha256=2-fwx4rsyi66GTBON5dv1tcZwZFOLUOKDJE9Yf5DqRQ,31760
71
- genlayer_test-0.5.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
- genlayer_test-0.5.1.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
73
- genlayer_test-0.5.1.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
74
- genlayer_test-0.5.1.dist-info/RECORD,,
75
+ genlayer_test-0.7.0.dist-info/METADATA,sha256=o89bAkGCyeJbcOUVD1ElUDG37zLVClakUp3tvs6oKsc,32466
76
+ genlayer_test-0.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
+ genlayer_test-0.7.0.dist-info/entry_points.txt,sha256=RWPcSArBpz_G4BYioh5L8Q8hyClRbSgzLimjcWMp-BQ,94
78
+ genlayer_test-0.7.0.dist-info/top_level.txt,sha256=-qiGZxTRBytujzgVcKpxjvQ-WNeUDjXa59ceGMwMpko,24
79
+ genlayer_test-0.7.0.dist-info/RECORD,,
@@ -7,7 +7,9 @@ from gltest.types import (
7
7
  GenLayerTransaction,
8
8
  TransactionStatus,
9
9
  TransactionHashVariant,
10
+ TransactionContext,
10
11
  )
12
+ from genlayer_py.types import SimConfig
11
13
  from typing import List, Any, Optional, Dict, Callable
12
14
  from gltest_cli.config.general import get_general_config
13
15
  from .contract_functions import ContractFunction
@@ -25,14 +27,24 @@ def read_contract_wrapper(
25
27
 
26
28
  def call_method(
27
29
  transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
30
+ transaction_context: Optional[TransactionContext] = None,
28
31
  ):
29
32
  client = get_gl_client()
33
+ sim_config = None
34
+ if transaction_context:
35
+ try:
36
+ sim_config = SimConfig(**transaction_context)
37
+ except TypeError as e:
38
+ raise ValueError(
39
+ f"Invalid transaction_context keys: {sorted(transaction_context.keys())}"
40
+ ) from e
30
41
  return client.read_contract(
31
42
  address=self.address,
32
43
  function_name=method_name,
33
44
  account=self.account,
34
45
  args=args,
35
46
  transaction_hash_variant=transaction_hash_variant,
47
+ sim_config=sim_config,
36
48
  )
37
49
 
38
50
  return ContractFunction(
@@ -59,6 +71,7 @@ def write_contract_wrapper(
59
71
  wait_retries: Optional[int] = None,
60
72
  wait_triggered_transactions: bool = False,
61
73
  wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
74
+ transaction_context: Optional[TransactionContext] = None,
62
75
  ):
63
76
  """
64
77
  Transact the contract method.
@@ -80,6 +93,14 @@ def write_contract_wrapper(
80
93
  else False
81
94
  )
82
95
  client = get_gl_client()
96
+ sim_config = None
97
+ if transaction_context:
98
+ try:
99
+ sim_config = SimConfig(**transaction_context)
100
+ except TypeError as e:
101
+ raise ValueError(
102
+ f"Invalid transaction_context keys: {sorted(transaction_context.keys())}"
103
+ ) from e
83
104
  tx_hash = client.write_contract(
84
105
  address=self.address,
85
106
  function_name=method_name,
@@ -88,6 +109,7 @@ def write_contract_wrapper(
88
109
  consensus_max_rotations=consensus_max_rotations,
89
110
  leader_only=leader_only,
90
111
  args=args,
112
+ sim_config=sim_config,
91
113
  )
92
114
  receipt = client.wait_for_transaction_receipt(
93
115
  transaction_hash=tx_hash,
@@ -113,6 +135,7 @@ def write_contract_wrapper(
113
135
  plugin: Optional[str] = None,
114
136
  plugin_config: Optional[Dict[str, Any]] = None,
115
137
  runs: int = 100,
138
+ genvm_datetime: Optional[str] = None,
116
139
  ):
117
140
  """
118
141
  Analyze the contract method using StatsCollector.
@@ -129,6 +152,7 @@ def write_contract_wrapper(
129
152
  config=config,
130
153
  plugin=plugin,
131
154
  plugin_config=plugin_config,
155
+ genvm_datetime=genvm_datetime,
132
156
  )
133
157
  sim_results = collector.run_simulations(sim_config, runs)
134
158
  return collector.analyze_results(sim_results, runs, sim_config)
@@ -15,12 +15,15 @@ from gltest.clients import (
15
15
  get_gl_hosted_studio_client,
16
16
  get_local_client,
17
17
  )
18
+ from genlayer_py.types import SimConfig
18
19
  from .contract import Contract
19
20
  from gltest.logging import logger
20
- from gltest.types import TransactionStatus
21
+ from gltest.types import TransactionStatus, GenLayerTransaction, CalldataEncodable
21
22
  from gltest.assertions import tx_execution_failed
22
23
  from gltest.exceptions import DeploymentError
23
24
  from gltest_cli.config.general import get_general_config
25
+ from gltest.utils import extract_contract_address
26
+ from gltest.types import TransactionContext
24
27
 
25
28
 
26
29
  @dataclass
@@ -104,7 +107,7 @@ class ContractFactory:
104
107
 
105
108
  def deploy(
106
109
  self,
107
- args: List[Any] = [],
110
+ args: Optional[List[CalldataEncodable]] = None,
108
111
  account: Optional[LocalAccount] = None,
109
112
  consensus_max_rotations: Optional[int] = None,
110
113
  wait_interval: Optional[int] = None,
@@ -112,9 +115,46 @@ class ContractFactory:
112
115
  wait_transaction_status: TransactionStatus = TransactionStatus.ACCEPTED,
113
116
  wait_triggered_transactions: bool = False,
114
117
  wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
118
+ transaction_context: Optional[TransactionContext] = None,
115
119
  ) -> Contract:
116
120
  """
117
- Deploy the contract
121
+ Deploy the contract and return a Contract instance (convenience method).
122
+
123
+ This is a convenience method that handles receipt validation
124
+ and contract instantiation automatically.
125
+ """
126
+ receipt = self.deploy_contract_tx(
127
+ args=args,
128
+ account=account,
129
+ consensus_max_rotations=consensus_max_rotations,
130
+ wait_interval=wait_interval,
131
+ wait_retries=wait_retries,
132
+ wait_transaction_status=wait_transaction_status,
133
+ wait_triggered_transactions=wait_triggered_transactions,
134
+ wait_triggered_transactions_status=wait_triggered_transactions_status,
135
+ transaction_context=transaction_context,
136
+ )
137
+
138
+ if tx_execution_failed(receipt):
139
+ raise DeploymentError(f"Deployment transaction failed: {receipt}")
140
+
141
+ contract_address = extract_contract_address(receipt)
142
+ return self.build_contract(contract_address=contract_address, account=account)
143
+
144
+ def deploy_contract_tx(
145
+ self,
146
+ args: Optional[List[CalldataEncodable]] = None,
147
+ account: Optional[LocalAccount] = None,
148
+ consensus_max_rotations: Optional[int] = None,
149
+ wait_interval: Optional[int] = None,
150
+ wait_retries: Optional[int] = None,
151
+ wait_transaction_status: TransactionStatus = TransactionStatus.ACCEPTED,
152
+ wait_triggered_transactions: bool = False,
153
+ wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
154
+ transaction_context: Optional[TransactionContext] = None,
155
+ ) -> GenLayerTransaction:
156
+ """
157
+ Deploy the contract and return the transaction receipt.
118
158
  """
119
159
  general_config = get_general_config()
120
160
  actual_wait_interval = (
@@ -135,12 +175,21 @@ class ContractFactory:
135
175
 
136
176
  client = get_gl_client()
137
177
  try:
178
+ sim_config = None
179
+ if transaction_context:
180
+ try:
181
+ sim_config = SimConfig(**transaction_context)
182
+ except TypeError as e:
183
+ raise ValueError(
184
+ f"Invalid transaction_context keys: {sorted(transaction_context.keys())}"
185
+ ) from e
138
186
  tx_hash = client.deploy_contract(
139
187
  code=self.contract_code,
140
188
  args=args,
141
189
  account=account,
142
190
  consensus_max_rotations=consensus_max_rotations,
143
191
  leader_only=leader_only,
192
+ sim_config=sim_config,
144
193
  )
145
194
  tx_receipt = client.wait_for_transaction_receipt(
146
195
  transaction_hash=tx_hash,
@@ -148,11 +197,6 @@ class ContractFactory:
148
197
  interval=actual_wait_interval,
149
198
  retries=actual_wait_retries,
150
199
  )
151
- if tx_execution_failed(tx_receipt):
152
- raise ValueError(
153
- f"Deployment transaction finalized with error: {tx_receipt}"
154
- )
155
-
156
200
  if wait_triggered_transactions:
157
201
  triggered_transactions = tx_receipt.get("triggered_transactions", [])
158
202
  for triggered_transaction in triggered_transactions:
@@ -162,26 +206,7 @@ class ContractFactory:
162
206
  interval=actual_wait_interval,
163
207
  retries=actual_wait_retries,
164
208
  )
165
-
166
- if (
167
- "tx_data_decoded" in tx_receipt
168
- and "contract_address" in tx_receipt["tx_data_decoded"]
169
- ):
170
- contract_address = tx_receipt["tx_data_decoded"]["contract_address"]
171
- elif "data" in tx_receipt and "contract_address" in tx_receipt["data"]:
172
- contract_address = tx_receipt["data"]["contract_address"]
173
- else:
174
- raise ValueError("Transaction receipt missing contract address")
175
-
176
- schema = self._get_schema_with_fallback()
177
- if schema is None:
178
- raise ValueError(
179
- "Failed to get schema from all clients (default, hosted studio, and local)"
180
- )
181
-
182
- return Contract.new(
183
- address=contract_address, schema=schema, account=account
184
- )
209
+ return tx_receipt
185
210
  except Exception as e:
186
211
  raise DeploymentError(
187
212
  f"Failed to deploy contract {self.contract_name}: {str(e)}"
@@ -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, TransactionHashVariant
3
+ from gltest.types import TransactionStatus, TransactionHashVariant, TransactionContext
4
4
 
5
5
 
6
6
  @dataclass
@@ -14,10 +14,14 @@ class ContractFunction:
14
14
  def call(
15
15
  self,
16
16
  transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
17
+ transaction_context: Optional[TransactionContext] = None,
17
18
  ):
18
19
  if not self.read_only:
19
20
  raise ValueError("call() not implemented for non-readonly method")
20
- return self.call_method(transaction_hash_variant=transaction_hash_variant)
21
+ return self.call_method(
22
+ transaction_hash_variant=transaction_hash_variant,
23
+ transaction_context=transaction_context,
24
+ )
21
25
 
22
26
  def transact(
23
27
  self,
@@ -28,6 +32,7 @@ class ContractFunction:
28
32
  wait_retries: Optional[int] = None,
29
33
  wait_triggered_transactions: bool = False,
30
34
  wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
35
+ transaction_context: Optional[TransactionContext] = None,
31
36
  ):
32
37
  if self.read_only:
33
38
  raise ValueError("Cannot transact read-only method")
@@ -39,6 +44,7 @@ class ContractFunction:
39
44
  wait_retries=wait_retries,
40
45
  wait_triggered_transactions=wait_triggered_transactions,
41
46
  wait_triggered_transactions_status=wait_triggered_transactions_status,
47
+ transaction_context=transaction_context,
42
48
  )
43
49
 
44
50
  def analyze(
@@ -49,6 +55,7 @@ class ContractFunction:
49
55
  plugin: Optional[str] = None,
50
56
  plugin_config: Optional[Dict[str, Any]] = None,
51
57
  runs: int = 100,
58
+ genvm_datetime: Optional[str] = None,
52
59
  ):
53
60
  if self.read_only:
54
61
  raise ValueError("Cannot analyze read-only method")
@@ -59,4 +66,5 @@ class ContractFunction:
59
66
  plugin=plugin,
60
67
  plugin_config=plugin_config,
61
68
  runs=runs,
69
+ genvm_datetime=genvm_datetime,
62
70
  )
@@ -29,6 +29,7 @@ class SimulationConfig:
29
29
  config: Optional[Dict[str, Any]] = None
30
30
  plugin: Optional[str] = None
31
31
  plugin_config: Optional[Dict[str, Any]] = None
32
+ genvm_datetime: Optional[str] = None
32
33
 
33
34
 
34
35
  @dataclass
@@ -103,7 +104,12 @@ class StatsCollector:
103
104
  self, sim_config: SimulationConfig
104
105
  ) -> Dict[str, Any]:
105
106
  """Execute a single simulation."""
106
- config_dict = {
107
+ config_dict = {}
108
+
109
+ if sim_config.genvm_datetime is not None:
110
+ config_dict["genvm_datetime"] = sim_config.genvm_datetime
111
+
112
+ validator_info = {
107
113
  "provider": sim_config.provider,
108
114
  "model": sim_config.model,
109
115
  }
@@ -113,9 +119,10 @@ class StatsCollector:
113
119
  and sim_config.plugin is not None
114
120
  and sim_config.plugin_config is not None
115
121
  ):
116
- config_dict["config"] = sim_config.config
117
- config_dict["plugin"] = sim_config.plugin
118
- config_dict["plugin_config"] = sim_config.plugin_config
122
+ validator_info["config"] = sim_config.config
123
+ validator_info["plugin"] = sim_config.plugin
124
+ validator_info["plugin_config"] = sim_config.plugin_config
125
+ config_dict["validators"] = [validator_info]
119
126
 
120
127
  return self.client.simulate_write_contract(
121
128
  address=self.contract_address,
gltest/types.py CHANGED
@@ -6,3 +6,21 @@ from genlayer_py.types import (
6
6
  CalldataEncodable,
7
7
  TransactionHashVariant,
8
8
  )
9
+ from typing import List, TypedDict, Dict, Any
10
+
11
+
12
+ class ValidatorConfig(TypedDict):
13
+ """Validator information."""
14
+
15
+ provider: str
16
+ model: str
17
+ config: Dict[str, Any]
18
+ plugin: str
19
+ plugin_config: Dict[str, Any]
20
+
21
+
22
+ class TransactionContext(TypedDict, total=False):
23
+ """Context for consensus operations."""
24
+
25
+ validators: List[ValidatorConfig] # List to create virtual validators
26
+ genvm_datetime: str # ISO format datetime string
gltest/utils.py ADDED
@@ -0,0 +1,14 @@
1
+ from genlayer_py.types import GenLayerTransaction
2
+
3
+
4
+ def extract_contract_address(receipt: GenLayerTransaction) -> str:
5
+ """Extract contract address from a deployment transaction receipt."""
6
+ if (
7
+ "tx_data_decoded" in receipt
8
+ and "contract_address" in receipt["tx_data_decoded"]
9
+ ):
10
+ return receipt["tx_data_decoded"]["contract_address"]
11
+ elif "data" in receipt and "contract_address" in receipt["data"]:
12
+ return receipt["data"]["contract_address"]
13
+ else:
14
+ raise ValueError("Transaction receipt missing contract address")
@@ -0,0 +1,10 @@
1
+ # { "Depends": "py-genlayer:test" }
2
+
3
+ import genlayer as gl
4
+
5
+
6
+ class InvalidDeploy(gl.Contract):
7
+ """Contract that always fails during deployment"""
8
+
9
+ def __init__(self):
10
+ raise Exception("This is an invalid deploy")
@@ -0,0 +1,85 @@
1
+ # {
2
+ # "Seq": [
3
+ # { "Depends": "py-lib-genlayer-embeddings:09h0i209wrzh4xzq86f79c60x0ifs7xcjwl53ysrnw06i54ddxyi" },
4
+ # { "Depends": "py-genlayer:1j12s63yfjpva9ik2xgnffgrs6v44y1f52jvj9w7xvdn7qckd379" }
5
+ # ]
6
+ # }
7
+
8
+ from datetime import datetime, timezone
9
+ from genlayer import *
10
+
11
+
12
+ class SimpleTimeContract(gl.Contract):
13
+ """
14
+ A simple contract that demonstrates time-based function availability.
15
+ """
16
+
17
+ start_date: str # ISO format datetime string
18
+ data: str
19
+ is_active: bool
20
+
21
+ def __init__(self, start_datetime_iso: str):
22
+ """
23
+ Initialize the contract with a required start date (ISO 8601).
24
+ """
25
+ self.start_date = start_datetime_iso
26
+ self.is_active = False
27
+ self.data = ""
28
+
29
+ def _days_since_start(self) -> int:
30
+ """Calculate days elapsed since start date."""
31
+ current = datetime.now(timezone.utc)
32
+ start = datetime.fromisoformat(self.start_date)
33
+ print(f"Current: {current}, Start: {start}")
34
+ delta = current - start
35
+ print(f"Delta: {delta}")
36
+ return delta.days
37
+
38
+ @gl.public.write
39
+ def activate(self):
40
+ """
41
+ Activate the contract.
42
+ Only works if current date is after start date.
43
+ """
44
+ days = self._days_since_start()
45
+
46
+ if days < 0:
47
+ raise ValueError(
48
+ f"Cannot activate before start date. Days until start: {abs(days)}"
49
+ )
50
+
51
+ self.is_active = True
52
+
53
+ @gl.public.write
54
+ def set_data(self, value: str):
55
+ """
56
+ Set data in the contract.
57
+ Only works if contract is active and within 30 days of start.
58
+ """
59
+ if not self.is_active:
60
+ raise ValueError("Contract must be activated first")
61
+
62
+ days = self._days_since_start()
63
+
64
+ if days > 30:
65
+ raise ValueError(
66
+ f"Function expired. Was available for 30 days after start, now at day {days}"
67
+ )
68
+
69
+ self.data = value
70
+
71
+ @gl.public.view
72
+ def get_status(self) -> dict:
73
+ """Get current contract status."""
74
+ days = self._days_since_start()
75
+ current = datetime.now(timezone.utc)
76
+
77
+ return {
78
+ "start_date": self.start_date,
79
+ "current_time": current.isoformat(),
80
+ "days_since_start": days,
81
+ "is_active": self.is_active,
82
+ "data": self.data,
83
+ "can_activate": days >= 0 and not self.is_active,
84
+ "can_set_data": self.is_active and 0 <= days <= 30,
85
+ }
@@ -0,0 +1,24 @@
1
+ import pytest
2
+ from gltest import get_contract_factory
3
+ from gltest.assertions import tx_execution_failed
4
+ from gltest.exceptions import DeploymentError
5
+
6
+
7
+ def test_invalid_deploy_basic_exception(setup_validators):
8
+ """Test deployment failure with basic exception"""
9
+ setup_validators()
10
+ factory = get_contract_factory("InvalidDeploy")
11
+
12
+ # Deployment should fail with exception
13
+ with pytest.raises(DeploymentError):
14
+ factory.deploy()
15
+
16
+
17
+ def test_invalid_deploy_receipt_only(setup_validators):
18
+ """Test deployment failure using deploy_contract_tx() method that returns receipt only"""
19
+ setup_validators()
20
+ factory = get_contract_factory("InvalidDeploy")
21
+
22
+ # Deploy and get receipt - should show failure
23
+ receipt = factory.deploy_contract_tx()
24
+ assert tx_execution_failed(receipt)
@@ -1,4 +1,5 @@
1
1
  from gltest import get_contract_factory, get_default_account, create_account
2
+ from datetime import datetime, timezone
2
3
 
3
4
 
4
5
  TOKEN_TOTAL_SUPPLY = 1000
@@ -21,7 +22,10 @@ def test_llm_erc20_analyze(setup_validators):
21
22
 
22
23
  # Transfer from User A to User B
23
24
  stats = contract.transfer(args=[TRANSFER_AMOUNT, from_account_b.address]).analyze(
24
- provider="openai", model="gpt-4o", runs=3
25
+ provider="openai",
26
+ model="gpt-4o",
27
+ runs=3,
28
+ genvm_datetime=datetime.now(timezone.utc).isoformat(),
25
29
  )
26
30
 
27
31
  # Verify it's a MethodStatsSummary object
@@ -0,0 +1,90 @@
1
+ from gltest import get_contract_factory
2
+ from datetime import datetime, timedelta, timezone
3
+ from gltest.assertions import tx_execution_succeeded, tx_execution_failed
4
+
5
+
6
+ def test_simple_time_contract():
7
+ """Test all time-based functionality in a single comprehensive test."""
8
+
9
+ factory = get_contract_factory("SimpleTimeContract")
10
+
11
+ # Test 1: Deploy with past start date (10 days ago)
12
+ now = datetime.now(timezone.utc)
13
+ past_date = (now - timedelta(days=10)).isoformat()
14
+ contract = factory.deploy(args=[past_date])
15
+
16
+ # Test 1: Check initial status (10 days after start)
17
+ status = contract.get_status().call()
18
+ assert status["is_active"] == False
19
+ assert status["days_since_start"] == 10
20
+ assert status["can_activate"] == True
21
+
22
+ # Test 2: Try to activate before start date (simulate going back in time)
23
+ before_start_date = now - timedelta(days=15) # 5 days before start
24
+ before_start_date_receipt = contract.activate().transact(
25
+ transaction_context={
26
+ "genvm_datetime": before_start_date.isoformat(),
27
+ },
28
+ )
29
+ assert tx_execution_failed(before_start_date_receipt)
30
+
31
+ # Test 3: Activate after start date
32
+ activate_date = now - timedelta(days=5) # 5 days after start (15 days ago + 10)
33
+ activate_receipt = contract.activate().transact(
34
+ transaction_context={
35
+ "genvm_datetime": activate_date.isoformat(),
36
+ },
37
+ )
38
+ assert tx_execution_succeeded(activate_receipt)
39
+
40
+ # Test 4: Verify activation and check status
41
+ status = contract.get_status().call(
42
+ transaction_context={
43
+ "genvm_datetime": activate_date.isoformat(),
44
+ },
45
+ )
46
+ assert status["is_active"] == True
47
+ assert status["days_since_start"] == 5
48
+ assert status["can_set_data"] == True
49
+
50
+ # Test 5: Set data within valid period (within 30 days)
51
+ set_data_date = now - timedelta(days=2) # 8 days after start
52
+ test_data = "Test data within valid period"
53
+ set_data_receipt = contract.set_data(
54
+ args=[test_data],
55
+ ).transact(
56
+ transaction_context={
57
+ "genvm_datetime": set_data_date.isoformat(),
58
+ }
59
+ )
60
+ assert tx_execution_succeeded(set_data_receipt)
61
+
62
+ # Test 6: Verify data was set
63
+ status = contract.get_status().call(
64
+ transaction_context={
65
+ "genvm_datetime": set_data_date.isoformat(),
66
+ },
67
+ )
68
+ assert status["data"] == test_data
69
+ assert status["days_since_start"] == 8
70
+
71
+ # Test 7: Try to set data after 30 days (should fail)
72
+ expired_date = now + timedelta(days=25) # 35 days after start
73
+ expired_date_receipt = contract.set_data(
74
+ args=["Should fail - expired"],
75
+ ).transact(
76
+ transaction_context={
77
+ "genvm_datetime": expired_date.isoformat(),
78
+ }
79
+ )
80
+ assert tx_execution_failed(expired_date_receipt)
81
+
82
+ # Test 8: Check status shows expired
83
+ status = contract.get_status().call(
84
+ transaction_context={
85
+ "genvm_datetime": expired_date.isoformat(),
86
+ },
87
+ )
88
+ assert status["is_active"] == True # Still active
89
+ assert status["can_set_data"] == False # But can't set data
90
+ assert status["days_since_start"] == 35