genlayer-test 0.4.0__py3-none-any.whl → 0.5.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 (47) hide show
  1. {genlayer_test-0.4.0.dist-info → genlayer_test-0.5.0.dist-info}/METADATA +257 -24
  2. genlayer_test-0.5.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 +205 -0
  7. gltest/{glchain/contract.py → contracts/contract_factory.py} +47 -144
  8. gltest/contracts/contract_functions.py +62 -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/logging.py +17 -0
  15. gltest/types.py +1 -0
  16. gltest_cli/config/constants.py +2 -0
  17. gltest_cli/config/plugin.py +121 -49
  18. gltest_cli/config/pytest_context.py +9 -0
  19. gltest_cli/config/types.py +73 -8
  20. gltest_cli/config/user.py +71 -28
  21. gltest_cli/logging.py +4 -3
  22. tests/examples/contracts/football_prediction_market.py +1 -1
  23. tests/examples/tests/test_football_prediction_market.py +2 -2
  24. tests/examples/tests/test_intelligent_oracle_factory.py +8 -24
  25. tests/examples/tests/test_llm_erc20.py +5 -5
  26. tests/examples/tests/test_llm_erc20_analyze.py +50 -0
  27. tests/examples/tests/test_log_indexer.py +23 -11
  28. tests/examples/tests/test_multi_file_contract.py +2 -2
  29. tests/examples/tests/test_multi_file_contract_legacy.py +2 -2
  30. tests/examples/tests/test_multi_read_erc20.py +14 -12
  31. tests/examples/tests/test_multi_tenant_storage.py +11 -7
  32. tests/examples/tests/test_read_erc20.py +1 -1
  33. tests/examples/tests/test_storage.py +4 -4
  34. tests/examples/tests/test_storage_legacy.py +5 -3
  35. tests/examples/tests/test_user_storage.py +20 -10
  36. tests/examples/tests/test_wizard_of_coin.py +1 -1
  37. tests/gltest_cli/config/test_config_integration.py +432 -0
  38. tests/gltest_cli/config/test_general_config.py +406 -0
  39. tests/gltest_cli/config/test_plugin.py +164 -1
  40. tests/gltest_cli/config/test_user.py +61 -1
  41. genlayer_test-0.4.0.dist-info/RECORD +0 -66
  42. gltest/glchain/__init__.py +0 -16
  43. {genlayer_test-0.4.0.dist-info → genlayer_test-0.5.0.dist-info}/WHEEL +0 -0
  44. {genlayer_test-0.4.0.dist-info → genlayer_test-0.5.0.dist-info}/entry_points.txt +0 -0
  45. {genlayer_test-0.4.0.dist-info → genlayer_test-0.5.0.dist-info}/licenses/LICENSE +0 -0
  46. {genlayer_test-0.4.0.dist-info → genlayer_test-0.5.0.dist-info}/top_level.txt +0 -0
  47. /gltest/{glchain/account.py → accounts.py} +0 -0
@@ -2,24 +2,22 @@ from enum import Enum
2
2
  from dataclasses import dataclass, field
3
3
  from pathlib import Path
4
4
  from typing import Dict, List, Optional
5
- from genlayer_py.chains import localnet, testnet_asimov
5
+ from genlayer_py.chains import localnet, studionet, testnet_asimov
6
6
  from genlayer_py.types import GenLayerChain
7
7
  from urllib.parse import urlparse
8
-
9
-
10
- class NetworkConfig(str, Enum):
11
- LOCALNET = "localnet"
12
- TESTNET_ASIMOV = "testnet_asimov"
8
+ from gltest_cli.config.constants import PRECONFIGURED_NETWORKS
13
9
 
14
10
 
15
11
  @dataclass
16
12
  class PluginConfig:
17
13
  contracts_dir: Optional[Path] = None
14
+ artifacts_dir: Optional[Path] = None
18
15
  rpc_url: Optional[str] = None
19
16
  default_wait_interval: Optional[int] = None
20
17
  default_wait_retries: Optional[int] = None
21
18
  network_name: Optional[str] = None
22
19
  test_with_mocks: bool = False
20
+ leader_only: bool = False
23
21
 
24
22
 
25
23
  @dataclass
@@ -28,6 +26,7 @@ class NetworkConfigData:
28
26
  url: Optional[str] = None
29
27
  accounts: Optional[List[str]] = None
30
28
  from_account: Optional[str] = None
29
+ leader_only: bool = False
31
30
 
32
31
  def __post_init__(self):
33
32
  if self.id is not None and not isinstance(self.id, int):
@@ -46,10 +45,13 @@ class NetworkConfigData:
46
45
  @dataclass
47
46
  class PathConfig:
48
47
  contracts: Optional[Path] = None
48
+ artifacts: Optional[Path] = None
49
49
 
50
50
  def __post_init__(self):
51
51
  if self.contracts is not None and not isinstance(self.contracts, (str, Path)):
52
52
  raise ValueError("contracts must be a string or Path")
53
+ if self.artifacts is not None and not isinstance(self.artifacts, (str, Path)):
54
+ raise ValueError("artifacts must be a string or Path")
53
55
 
54
56
 
55
57
  @dataclass
@@ -93,10 +95,29 @@ class GeneralConfig:
93
95
  def set_contracts_dir(self, contracts_dir: Path):
94
96
  self.plugin_config.contracts_dir = contracts_dir
95
97
 
98
+ def get_artifacts_dir(self) -> Path:
99
+ if self.plugin_config.artifacts_dir is not None:
100
+ return self.plugin_config.artifacts_dir
101
+ return self.user_config.paths.artifacts
102
+
103
+ def set_artifacts_dir(self, artifacts_dir: Path):
104
+ self.plugin_config.artifacts_dir = artifacts_dir
105
+
106
+ def get_analysis_dir(self) -> Path:
107
+ artifacts_dir = self.get_artifacts_dir()
108
+ return artifacts_dir / "analysis"
109
+
110
+ def get_networks_keys(self) -> List[str]:
111
+ return list(self.user_config.networks.keys())
112
+
96
113
  def get_rpc_url(self) -> str:
97
114
  if self.plugin_config.rpc_url is not None:
98
115
  return self.plugin_config.rpc_url
99
116
  network_name = self.get_network_name()
117
+ if network_name not in self.user_config.networks:
118
+ raise ValueError(
119
+ f"Unknown network: {network_name}, possible values: {self.get_networks_keys()}"
120
+ )
100
121
  return self.user_config.networks[network_name].url
101
122
 
102
123
  def get_default_account_key(self, network_name: Optional[str] = None) -> str:
@@ -110,16 +131,37 @@ class GeneralConfig:
110
131
  return self.user_config.networks[self.user_config.default_network].accounts
111
132
 
112
133
  def get_chain(self) -> GenLayerChain:
134
+ network_name = self.get_network_name()
135
+ if network_name not in self.user_config.networks:
136
+ raise ValueError(
137
+ f"Unknown network: {network_name}, possible values: {self.get_networks_keys()}"
138
+ )
139
+
140
+ # Reserved network names
141
+ chain_map_by_name = {
142
+ "localnet": localnet,
143
+ "studionet": studionet,
144
+ "testnet_asimov": testnet_asimov,
145
+ }
146
+
147
+ if network_name in chain_map_by_name:
148
+ return chain_map_by_name[network_name]
149
+
150
+ if network_name in PRECONFIGURED_NETWORKS:
151
+ raise ValueError(
152
+ f"Network {network_name} should be handled by reserved mapping"
153
+ )
154
+
155
+ # Custom networks
113
156
  chain_map_by_id = {
114
157
  61999: localnet,
115
158
  4221: testnet_asimov,
116
159
  }
117
- network_name = self.get_network_name()
118
160
  network_id = self.user_config.networks[network_name].id
119
161
  if network_id not in chain_map_by_id:
120
162
  known = ", ".join(map(str, chain_map_by_id.keys()))
121
163
  raise ValueError(
122
- f"Unknown network: {network_name}, possible values: {known}"
164
+ f"Unknown network id: {network_id}, possible values: {known}"
123
165
  )
124
166
  return chain_map_by_id[network_id]
125
167
 
@@ -141,8 +183,31 @@ class GeneralConfig:
141
183
  def get_test_with_mocks(self) -> bool:
142
184
  return self.plugin_config.test_with_mocks
143
185
 
186
+ def get_leader_only(self) -> bool:
187
+ if self.plugin_config.leader_only:
188
+ return True
189
+ network_name = self.get_network_name()
190
+ if network_name in self.user_config.networks:
191
+ network_config = self.user_config.networks[network_name]
192
+ return network_config.leader_only
193
+ return False
194
+
144
195
  def check_local_rpc(self) -> bool:
145
196
  SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
146
197
  rpc_url = self.get_rpc_url()
147
198
  domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
148
199
  return domain in SUPPORTED_RPC_DOMAINS
200
+
201
+ def check_studio_based_rpc(self) -> bool:
202
+ SUPPORTED_RPC_DOMAINS = ["localhost", "127.0.0.1"]
203
+ rpc_url = self.get_rpc_url()
204
+ domain = urlparse(rpc_url).netloc.split(":")[0] # Extract domain without port
205
+
206
+ if domain in SUPPORTED_RPC_DOMAINS:
207
+ return True
208
+
209
+ # Check .genlayer.com or .genlayerlabs.com subdomains
210
+ if domain.endswith(".genlayer.com") or domain.endswith(".genlayerlabs.com"):
211
+ return True
212
+
213
+ return False
gltest_cli/config/user.py CHANGED
@@ -4,20 +4,21 @@ import re
4
4
  from dotenv import load_dotenv
5
5
  from pathlib import Path
6
6
  from functools import lru_cache
7
- from gltest.glchain.account import create_accounts
7
+ from gltest.accounts import create_accounts
8
8
  from gltest_cli.config.constants import (
9
9
  GLTEST_CONFIG_FILE,
10
10
  DEFAULT_NETWORK,
11
- DEFAULT_RPC_URL,
12
11
  DEFAULT_ENVIRONMENT,
13
12
  DEFAULT_CONTRACTS_DIR,
14
- DEFAULT_NETWORK_ID,
13
+ DEFAULT_ARTIFACTS_DIR,
14
+ PRECONFIGURED_NETWORKS,
15
15
  )
16
+ from genlayer_py.chains import localnet, studionet, testnet_asimov
16
17
  from gltest_cli.config.types import UserConfig, NetworkConfigData, PathConfig
17
18
 
18
19
  VALID_ROOT_KEYS = ["networks", "paths", "environment"]
19
- VALID_NETWORK_KEYS = ["id", "url", "accounts", "from"]
20
- VALID_PATHS_KEYS = ["contracts"]
20
+ VALID_NETWORK_KEYS = ["id", "url", "accounts", "from", "leader_only"]
21
+ VALID_PATHS_KEYS = ["contracts", "artifacts"]
21
22
 
22
23
 
23
24
  @lru_cache(maxsize=1)
@@ -25,16 +26,35 @@ def get_default_user_config() -> UserConfig:
25
26
  accounts = create_accounts(n_accounts=10)
26
27
  accounts_private_keys = [account.key.hex() for account in accounts]
27
28
 
29
+ networks = {
30
+ "localnet": NetworkConfigData(
31
+ id=localnet.id,
32
+ url=localnet.rpc_urls["default"]["http"][0],
33
+ accounts=accounts_private_keys,
34
+ from_account=accounts_private_keys[0],
35
+ leader_only=False,
36
+ ),
37
+ "studionet": NetworkConfigData(
38
+ id=studionet.id,
39
+ url=studionet.rpc_urls["default"]["http"][0],
40
+ accounts=accounts_private_keys,
41
+ from_account=accounts_private_keys[0],
42
+ leader_only=False,
43
+ ),
44
+ "testnet_asimov": NetworkConfigData(
45
+ id=testnet_asimov.id,
46
+ url=testnet_asimov.rpc_urls["default"]["http"][0],
47
+ accounts=None,
48
+ from_account=None,
49
+ leader_only=False,
50
+ ),
51
+ }
52
+
28
53
  return UserConfig(
29
- networks={
30
- DEFAULT_NETWORK: NetworkConfigData(
31
- id=DEFAULT_NETWORK_ID,
32
- url=DEFAULT_RPC_URL,
33
- accounts=accounts_private_keys,
34
- from_account=accounts_private_keys[0],
35
- ),
36
- },
37
- paths=PathConfig(contracts=DEFAULT_CONTRACTS_DIR),
54
+ networks=networks,
55
+ paths=PathConfig(
56
+ contracts=DEFAULT_CONTRACTS_DIR, artifacts=DEFAULT_ARTIFACTS_DIR
57
+ ),
38
58
  environment=DEFAULT_ENVIRONMENT,
39
59
  default_network=DEFAULT_NETWORK,
40
60
  )
@@ -42,11 +62,26 @@ def get_default_user_config() -> UserConfig:
42
62
 
43
63
  def resolve_env_vars(obj):
44
64
  if isinstance(obj, str):
45
- return re.sub(
46
- r"\${(\w+)}",
47
- lambda m: os.getenv(m.group(1), f"<UNSET:{m.group(1)}>"),
48
- obj,
49
- )
65
+
66
+ def replace_env_var(m):
67
+ try:
68
+ var_name = m.group(1)
69
+ if var_name is None:
70
+ raise ValueError(
71
+ f"Invalid environment variable pattern: {m.group(0)}"
72
+ )
73
+ var_value = os.getenv(var_name)
74
+ if var_value is None:
75
+ raise ValueError(
76
+ f"Environment variable {var_name} is not set, please check your environment file"
77
+ )
78
+ return var_value
79
+ except IndexError as e:
80
+ raise ValueError(
81
+ f"Invalid environment variable pattern: {m.group(0)}"
82
+ ) from e
83
+
84
+ return re.sub(r"\${(\w+)}", replace_env_var, obj)
50
85
  elif isinstance(obj, dict):
51
86
  return {k: resolve_env_vars(v) for k, v in obj.items()}
52
87
  elif isinstance(obj, list):
@@ -81,9 +116,13 @@ def validate_network_config(network_name: str, network_config: dict):
81
116
 
82
117
  if "from" in network_config and not isinstance(network_config["from"], str):
83
118
  raise ValueError(f"network {network_name} from must be a string")
119
+ if "leader_only" in network_config and not isinstance(
120
+ network_config["leader_only"], bool
121
+ ):
122
+ raise ValueError(f"network {network_name} leader_only must be a boolean")
84
123
 
85
- # For non-default networks, url and accounts are required
86
- if network_name != DEFAULT_NETWORK:
124
+ # For non-preconfigured networks, url and accounts are required
125
+ if network_name not in PRECONFIGURED_NETWORKS:
87
126
  if "id" not in network_config:
88
127
  raise ValueError(f"network {network_name} must have an id")
89
128
  if "url" not in network_config:
@@ -131,7 +170,6 @@ def validate_raw_user_config(config: dict):
131
170
  def load_user_config(path: str) -> UserConfig:
132
171
  with open(path, "r") as f:
133
172
  raw_config = yaml.safe_load(f) or {}
134
-
135
173
  validate_raw_user_config(raw_config)
136
174
  load_dotenv(
137
175
  dotenv_path=raw_config.get("environment", DEFAULT_ENVIRONMENT), override=True
@@ -168,8 +206,8 @@ def _get_overridden_networks(raw_config: dict) -> tuple[dict, str]:
168
206
 
169
207
  networks_config = {}
170
208
  for network_name, network_config in networks.items():
171
- if network_name == DEFAULT_NETWORK:
172
- networks_config[network_name] = default_config.networks[DEFAULT_NETWORK]
209
+ if network_name in PRECONFIGURED_NETWORKS:
210
+ networks_config[network_name] = default_config.networks[network_name]
173
211
  if network_config is None:
174
212
  continue
175
213
 
@@ -182,18 +220,23 @@ def _get_overridden_networks(raw_config: dict) -> tuple[dict, str]:
182
220
  ]
183
221
  if "from" in network_config:
184
222
  networks_config[network_name].from_account = network_config["from"]
223
+ if "leader_only" in network_config:
224
+ networks_config[network_name].leader_only = network_config[
225
+ "leader_only"
226
+ ]
185
227
  continue
186
228
 
187
229
  url = network_config["url"]
188
230
  accounts = network_config["accounts"]
189
231
  from_account = network_config.get("from", accounts[0])
190
232
  network_id = network_config.get("id")
191
-
233
+ leader_only = network_config.get("leader_only", False)
192
234
  networks_config[network_name] = NetworkConfigData(
193
235
  id=network_id,
194
236
  url=url,
195
237
  accounts=accounts,
196
238
  from_account=from_account,
239
+ leader_only=leader_only,
197
240
  )
198
241
  return networks_config, user_default_network
199
242
 
@@ -208,10 +251,10 @@ def _get_overridden_environment(raw_config: dict) -> str:
208
251
  def _get_overridden_paths(raw_config: dict) -> PathConfig:
209
252
  default_config = get_default_user_config()
210
253
  if "paths" in raw_config:
254
+ paths_config = raw_config.get("paths", {})
211
255
  return PathConfig(
212
- contracts=Path(
213
- raw_config.get("paths", {}).get("contracts", DEFAULT_CONTRACTS_DIR)
214
- )
256
+ contracts=Path(paths_config.get("contracts", DEFAULT_CONTRACTS_DIR)),
257
+ artifacts=Path(paths_config.get("artifacts", DEFAULT_ARTIFACTS_DIR)),
215
258
  )
216
259
  return default_config.paths
217
260
 
gltest_cli/logging.py CHANGED
@@ -30,14 +30,15 @@ class ColoredFormatter(logging.Formatter):
30
30
 
31
31
 
32
32
  def setup_logger():
33
- logger = logging.getLogger("gltest")
34
- logger.setLevel(logging.DEBUG)
33
+ logger = logging.getLogger("gltest_cli")
34
+ log_level = logging.INFO
35
+ logger.setLevel(log_level)
35
36
 
36
37
  if logger.handlers:
37
38
  return logger
38
39
 
39
40
  console_handler = logging.StreamHandler()
40
- console_handler.setLevel(logging.DEBUG)
41
+ console_handler.setLevel(log_level)
41
42
 
42
43
  formatter = ColoredFormatter("%(levelname)s: %(message)s")
43
44
  console_handler.setFormatter(formatter)
@@ -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
+ }
@@ -27,11 +27,11 @@ def test_football_prediction_market(setup_validators):
27
27
  contract = factory.deploy(args=["2024-06-26", "Georgia", "Portugal"])
28
28
 
29
29
  # Resolve match
30
- transaction_response_call_1 = contract.resolve(args=[])
30
+ transaction_response_call_1 = contract.resolve(args=[]).transact()
31
31
  assert tx_execution_succeeded(transaction_response_call_1)
32
32
 
33
33
  # Get Updated State
34
- contract_state_2 = contract.get_resolution_data(args=[])
34
+ contract_state_2 = contract.get_resolution_data(args=[]).call()
35
35
 
36
36
  assert contract_state_2["winner"] == 1
37
37
  assert contract_state_2["score"] == "2:0"
@@ -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=[])
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,28 +110,26 @@ def test_intelligent_oracle_factory_pattern(setup_validators):
124
110
  market_data["resolution_urls"],
125
111
  market_data["earliest_resolution_date"],
126
112
  ],
113
+ ).transact(
114
+ wait_triggered_transactions=True,
115
+ wait_triggered_transactions_status=TransactionStatus.ACCEPTED,
127
116
  )
128
117
  assert tx_execution_succeeded(create_result)
129
118
 
130
119
  # Get the latest contract address from factory
131
- registered_addresses = registry_contract.get_contract_addresses(args=[])
120
+ registered_addresses = registry_contract.get_contract_addresses(args=[]).call()
132
121
  new_market_address = registered_addresses[-1]
133
122
 
134
123
  # Build a contract object
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
 
146
130
  # Verify each market's state
147
131
  for i, market_contract in enumerate(created_market_contracts):
148
- market_state = market_contract.get_dict(args=[])
132
+ market_state = market_contract.get_dict(args=[]).call()
149
133
  expected_data = markets_data[i]
150
134
 
151
135
  # Verify key market properties
@@ -171,10 +155,10 @@ def test_intelligent_oracle_factory_pattern(setup_validators):
171
155
  for i, market_contract in enumerate(created_market_contracts):
172
156
  resolve_result = market_contract.resolve(
173
157
  args=[markets_data[i]["evidence_urls"]],
174
- )
158
+ ).transact()
175
159
  assert tx_execution_succeeded(resolve_result)
176
160
 
177
161
  # Verify market was resolved and has the correct outcome
178
- market_state = market_contract.get_dict(args=[])
162
+ market_state = market_contract.get_dict(args=[]).call()
179
163
  assert market_state["status"] == "Resolved"
180
164
  assert market_state["outcome"] == markets_data[i]["outcome"]
@@ -34,17 +34,17 @@ def test_llm_erc20(setup_validators):
34
34
  contract = factory.deploy(args=[TOKEN_TOTAL_SUPPLY])
35
35
 
36
36
  # Get Initial State
37
- contract_state_1 = contract.get_balances(args=[])
37
+ contract_state_1 = contract.get_balances(args=[]).call()
38
38
  assert contract_state_1[from_account_a.address] == TOKEN_TOTAL_SUPPLY
39
39
 
40
40
  # Transfer from User A to User B
41
41
  transaction_response_call_1 = contract.transfer(
42
42
  args=[TRANSFER_AMOUNT, from_account_b.address]
43
- )
43
+ ).transact()
44
44
  assert tx_execution_succeeded(transaction_response_call_1)
45
45
 
46
46
  # Get Updated State
47
- contract_state_2_1 = contract.get_balances(args=[])
47
+ contract_state_2_1 = contract.get_balances(args=[]).call()
48
48
  assert (
49
49
  contract_state_2_1[from_account_a.address]
50
50
  == TOKEN_TOTAL_SUPPLY - TRANSFER_AMOUNT
@@ -52,9 +52,9 @@ def test_llm_erc20(setup_validators):
52
52
  assert contract_state_2_1[from_account_b.address] == TRANSFER_AMOUNT
53
53
 
54
54
  # Get Updated State
55
- contract_state_2_2 = contract.get_balance_of(args=[from_account_a.address])
55
+ contract_state_2_2 = contract.get_balance_of(args=[from_account_a.address]).call()
56
56
  assert contract_state_2_2 == TOKEN_TOTAL_SUPPLY - TRANSFER_AMOUNT
57
57
 
58
58
  # Get Updated State
59
- contract_state_2_3 = contract.get_balance_of(args=[from_account_b.address])
59
+ contract_state_2_3 = contract.get_balance_of(args=[from_account_b.address]).call()
60
60
  assert contract_state_2_3 == TRANSFER_AMOUNT
@@ -0,0 +1,50 @@
1
+ from gltest import get_contract_factory, get_default_account, create_account
2
+
3
+
4
+ TOKEN_TOTAL_SUPPLY = 1000
5
+ TRANSFER_AMOUNT = 100
6
+
7
+
8
+ def test_llm_erc20_analyze(setup_validators):
9
+ setup_validators()
10
+ # Account Setup
11
+ from_account_a = get_default_account()
12
+ from_account_b = create_account()
13
+
14
+ # Deploy Contract
15
+ factory = get_contract_factory("LlmErc20")
16
+ contract = factory.deploy(args=[TOKEN_TOTAL_SUPPLY])
17
+
18
+ # Get Initial State
19
+ contract_state_1 = contract.get_balances(args=[]).call()
20
+ assert contract_state_1[from_account_a.address] == TOKEN_TOTAL_SUPPLY
21
+
22
+ # Transfer from User A to User B
23
+ stats = contract.transfer(args=[TRANSFER_AMOUNT, from_account_b.address]).analyze(
24
+ provider="openai", model="gpt-4o", runs=3
25
+ )
26
+
27
+ # Verify it's a MethodStatsSummary object
28
+ assert hasattr(stats, "method")
29
+ assert hasattr(stats, "args")
30
+ assert hasattr(stats, "total_runs")
31
+ assert hasattr(stats, "execution_time")
32
+ assert hasattr(stats, "provider")
33
+ assert hasattr(stats, "model")
34
+
35
+ # Check basic properties
36
+ assert stats.method == "transfer"
37
+ assert stats.args == [TRANSFER_AMOUNT, from_account_b.address]
38
+ assert stats.total_runs == 3
39
+ assert stats.provider == "openai"
40
+ assert stats.model == "gpt-4o"
41
+ assert isinstance(stats.execution_time, float)
42
+
43
+ # Check string representation
44
+ stats_str = str(stats)
45
+ assert "Method analysis summary" in stats_str
46
+ assert "Method: transfer" in stats_str
47
+ assert f"Args: [{TRANSFER_AMOUNT}, '{from_account_b.address}']" in stats_str
48
+ assert f"Total runs: {stats.total_runs}" in stats_str
49
+ assert f"Provider: {stats.provider}" in stats_str
50
+ assert f"Model: {stats.model}" in stats_str
@@ -9,56 +9,68 @@ def test_log_indexer(setup_validators):
9
9
  contract = factory.deploy(args=[])
10
10
 
11
11
  # Get closest vector when empty
12
- closest_vector_log_0 = contract.get_closest_vector(args=["I like mango"])
12
+ closest_vector_log_0 = contract.get_closest_vector(args=["I like mango"]).call()
13
13
  assert closest_vector_log_0 is None
14
14
 
15
15
  # Add log 0
16
- transaction_response_add_log_0 = contract.add_log(args=["I like to eat mango", 0])
16
+ transaction_response_add_log_0 = contract.add_log(
17
+ args=["I like to eat mango", 0]
18
+ ).transact()
17
19
  assert tx_execution_succeeded(transaction_response_add_log_0)
18
20
 
19
21
  # Get closest vector to log 0
20
- closest_vector_log_0 = contract.get_closest_vector(args=["I like mango"])
22
+ closest_vector_log_0 = contract.get_closest_vector(args=["I like mango"]).call()
21
23
  closest_vector_log_0 = closest_vector_log_0
22
24
  assert float(closest_vector_log_0["similarity"]) > 0.94
23
25
  assert float(closest_vector_log_0["similarity"]) < 0.95
24
26
 
25
27
  # Add log 1
26
- transaction_response_add_log_1 = contract.add_log(args=["I like carrots", 1])
28
+ transaction_response_add_log_1 = contract.add_log(
29
+ args=["I like carrots", 1]
30
+ ).transact()
27
31
  assert tx_execution_succeeded(transaction_response_add_log_1)
28
32
 
29
33
  # Get closest vector to log 1
30
- closest_vector_log_1 = contract.get_closest_vector(args=["I like carrots"])
34
+ closest_vector_log_1 = contract.get_closest_vector(args=["I like carrots"]).call()
31
35
  closest_vector_log_1 = closest_vector_log_1
32
36
  assert float(closest_vector_log_1["similarity"]) == 1
33
37
 
34
38
  # Update log 0
35
39
  transaction_response_update_log_0 = contract.update_log(
36
40
  args=[0, "I like to eat a lot of mangoes"]
37
- )
41
+ ).transact()
38
42
  assert tx_execution_succeeded(transaction_response_update_log_0)
39
43
 
40
44
  # Get closest vector to log 0
41
- closest_vector_log_0_2 = contract.get_closest_vector(args=["I like mango a lot"])
45
+ closest_vector_log_0_2 = contract.get_closest_vector(
46
+ args=["I like mango a lot"]
47
+ ).call()
42
48
  closest_vector_log_0_2 = closest_vector_log_0_2
43
49
  assert float(closest_vector_log_0_2["similarity"]) > 0.94
44
50
  assert float(closest_vector_log_0_2["similarity"]) < 0.95
45
51
 
46
52
  # Remove log 0
47
- transaction_response_remove_log_0 = contract.remove_log(args=[0])
53
+ transaction_response_remove_log_0 = contract.remove_log(args=[0]).transact()
48
54
  assert tx_execution_succeeded(transaction_response_remove_log_0)
49
55
 
50
56
  # Get closest vector to log 0
51
- closest_vector_log_0_3 = contract.get_closest_vector(args=["I like to eat mango"])
57
+ closest_vector_log_0_3 = contract.get_closest_vector(
58
+ args=["I like to eat mango"]
59
+ ).call()
52
60
  closest_vector_log_0_3 = closest_vector_log_0_3
53
61
  assert float(closest_vector_log_0_3["similarity"]) > 0.67
54
62
  assert float(closest_vector_log_0_3["similarity"]) < 0.68
55
63
 
56
64
  # Add third log
57
- transaction_response_add_log_2 = contract.add_log(args=["This is the third log", 3])
65
+ transaction_response_add_log_2 = contract.add_log(
66
+ args=["This is the third log", 3]
67
+ ).transact()
58
68
  assert tx_execution_succeeded(transaction_response_add_log_2)
59
69
 
60
70
  # Check if new item got id 2
61
- closest_vector_log_2 = contract.get_closest_vector(args=["This is the third log"])
71
+ closest_vector_log_2 = contract.get_closest_vector(
72
+ args=["This is the third log"]
73
+ ).call()
62
74
  assert float(closest_vector_log_2["similarity"]) > 0.99
63
75
  assert closest_vector_log_2["id"] == 3
64
76
  assert closest_vector_log_2["text"] == "This is the third log"
@@ -9,8 +9,8 @@ def test_multi_file_contract(setup_validators):
9
9
  factory = get_contract_factory("MultiFileContract")
10
10
  contract = factory.deploy(args=[])
11
11
 
12
- wait_response = contract.wait(args=[])
12
+ wait_response = contract.wait(args=[]).transact()
13
13
  assert tx_execution_succeeded(wait_response)
14
14
 
15
- res = contract.test(args=[])
15
+ res = contract.test(args=[]).call()
16
16
  assert res == "123"
@@ -9,8 +9,8 @@ def test_multi_file_contract_legacy(setup_validators):
9
9
  factory = get_contract_factory("MultiFileContractLegacy")
10
10
  contract = factory.deploy(args=[])
11
11
 
12
- wait_response = contract.wait(args=[])
12
+ wait_response = contract.wait(args=[]).transact()
13
13
  assert tx_execution_succeeded(wait_response)
14
14
 
15
- res = contract.test(args=[])
15
+ res = contract.test(args=[]).call()
16
16
  assert res == "123"