genlayer-test 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- genlayer_test-0.1.0.dist-info/METADATA +419 -0
- genlayer_test-0.1.0.dist-info/RECORD +35 -0
- {genlayer_test-0.0.1.dist-info → genlayer_test-0.1.0.dist-info}/WHEEL +1 -1
- genlayer_test-0.1.0.dist-info/entry_points.txt +5 -0
- genlayer_test-0.1.0.dist-info/licenses/LICENSE +21 -0
- genlayer_test-0.1.0.dist-info/top_level.txt +3 -0
- gltest/__init__.py +18 -0
- gltest/artifacts/__init__.py +3 -0
- gltest/artifacts/contract.py +86 -0
- gltest/assertions.py +17 -0
- gltest/exceptions.py +20 -0
- gltest/glchain/__init__.py +16 -0
- gltest/glchain/account.py +17 -0
- gltest/glchain/client.py +23 -0
- gltest/glchain/contract.py +244 -0
- gltest/helpers/__init__.py +8 -0
- gltest/helpers/fixture_snapshot.py +67 -0
- gltest/helpers/take_snapshot.py +41 -0
- gltest/plugin_config.py +42 -0
- gltest/plugin_hooks.py +51 -0
- gltest/types.py +7 -0
- gltest_cli/main.py +5 -0
- tests/examples/tests/test_football_prediction_market.py +19 -0
- tests/examples/tests/test_intelligent_oracle_factory.py +129 -0
- tests/examples/tests/test_llm_erc20.py +41 -0
- tests/examples/tests/test_log_indexer.py +64 -0
- tests/examples/tests/test_multi_file_contract.py +15 -0
- tests/examples/tests/test_multi_read_erc20.py +85 -0
- tests/examples/tests/test_multi_tenant_storage.py +58 -0
- tests/examples/tests/test_read_erc20.py +37 -0
- tests/examples/tests/test_storage.py +23 -0
- tests/examples/tests/test_user_storage.py +60 -0
- tests/examples/tests/test_wizard_of_coin.py +12 -0
- tests/plugin/conftest.py +1 -0
- tests/plugin/test_plugin_hooks.py +78 -0
- genlayer_test-0.0.1.dist-info/METADATA +0 -17
- genlayer_test-0.0.1.dist-info/RECORD +0 -4
- genlayer_test-0.0.1.dist-info/top_level.txt +0 -1
gltest/glchain/client.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
from genlayer_py.chains import localnet
|
2
|
+
from genlayer_py import create_client
|
3
|
+
from .account import default_account
|
4
|
+
from functools import lru_cache
|
5
|
+
from gltest.plugin_config import get_rpc_url
|
6
|
+
|
7
|
+
|
8
|
+
@lru_cache(maxsize=1)
|
9
|
+
def get_gl_client():
|
10
|
+
"""
|
11
|
+
Get the GenLayer client instance.
|
12
|
+
"""
|
13
|
+
return create_client(
|
14
|
+
chain=localnet, account=default_account, endpoint=get_rpc_url()
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
def get_gl_provider():
|
19
|
+
"""
|
20
|
+
Get the GenLayer provider instance.
|
21
|
+
"""
|
22
|
+
client = get_gl_client()
|
23
|
+
return client.provider
|
@@ -0,0 +1,244 @@
|
|
1
|
+
from eth_typing import (
|
2
|
+
Address,
|
3
|
+
ChecksumAddress,
|
4
|
+
)
|
5
|
+
from eth_account.signers.local import LocalAccount
|
6
|
+
from typing import Union
|
7
|
+
from dataclasses import dataclass
|
8
|
+
from gltest.artifacts import find_contract_definition
|
9
|
+
from gltest.assertions import tx_execution_failed
|
10
|
+
from gltest.exceptions import DeploymentError
|
11
|
+
from .client import get_gl_client
|
12
|
+
from gltest.types import CalldataEncodable, GenLayerTransaction, TransactionStatus
|
13
|
+
from typing import List, Any, Type, Optional, Dict, Callable
|
14
|
+
import types
|
15
|
+
from gltest.plugin_config import get_default_wait_interval, get_default_wait_retries
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class Contract:
|
20
|
+
"""
|
21
|
+
Class to interact with a contract, its methods
|
22
|
+
are implemented dynamically at build time.
|
23
|
+
"""
|
24
|
+
|
25
|
+
address: str
|
26
|
+
account: Optional[LocalAccount] = None
|
27
|
+
_schema: Optional[Dict[str, Any]] = None
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def new(
|
31
|
+
cls,
|
32
|
+
address: str,
|
33
|
+
schema: Dict[str, Any],
|
34
|
+
account: Optional[LocalAccount] = None,
|
35
|
+
) -> "Contract":
|
36
|
+
"""
|
37
|
+
Build the methods from the schema.
|
38
|
+
"""
|
39
|
+
if not isinstance(schema, dict) or "methods" not in schema:
|
40
|
+
raise ValueError("Invalid schema: must contain 'methods' field")
|
41
|
+
instance = cls(address=address, _schema=schema, account=account)
|
42
|
+
instance._build_methods_from_schema()
|
43
|
+
return instance
|
44
|
+
|
45
|
+
def _build_methods_from_schema(self):
|
46
|
+
if self._schema is None:
|
47
|
+
raise ValueError("No schema provided")
|
48
|
+
for method_name, method_info in self._schema["methods"].items():
|
49
|
+
if not isinstance(method_info, dict) or "readonly" not in method_info:
|
50
|
+
raise ValueError(
|
51
|
+
f"Invalid method info for '{method_name}': must contain 'readonly' field"
|
52
|
+
)
|
53
|
+
method_func = self.contract_method_factory(
|
54
|
+
method_name, method_info["readonly"]
|
55
|
+
)
|
56
|
+
bound_method = types.MethodType(method_func, self)
|
57
|
+
setattr(self, method_name, bound_method)
|
58
|
+
|
59
|
+
def connect(self, account: LocalAccount) -> "Contract":
|
60
|
+
"""
|
61
|
+
Create a new instance of the contract with the same methods and a different account.
|
62
|
+
"""
|
63
|
+
new_contract = self.__class__(
|
64
|
+
address=self.address, account=account, _schema=self._schema
|
65
|
+
)
|
66
|
+
new_contract._build_methods_from_schema()
|
67
|
+
return new_contract
|
68
|
+
|
69
|
+
@staticmethod
|
70
|
+
def contract_method_factory(method_name: str, read_only: bool) -> Callable:
|
71
|
+
"""
|
72
|
+
Create a function that interacts with a specific contract method.
|
73
|
+
"""
|
74
|
+
|
75
|
+
def read_contract_wrapper(
|
76
|
+
self,
|
77
|
+
args: Optional[List[CalldataEncodable]] = None,
|
78
|
+
) -> Any:
|
79
|
+
"""
|
80
|
+
Wrapper to the contract read method.
|
81
|
+
"""
|
82
|
+
client = get_gl_client()
|
83
|
+
return client.read_contract(
|
84
|
+
address=self.address,
|
85
|
+
function_name=method_name,
|
86
|
+
account=self.account,
|
87
|
+
args=args,
|
88
|
+
)
|
89
|
+
|
90
|
+
def write_contract_wrapper(
|
91
|
+
self,
|
92
|
+
args: Optional[List[CalldataEncodable]] = None,
|
93
|
+
value: int = 0,
|
94
|
+
consensus_max_rotations: Optional[int] = None,
|
95
|
+
leader_only: bool = False,
|
96
|
+
wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
|
97
|
+
wait_interval: Optional[int] = None,
|
98
|
+
wait_retries: Optional[int] = None,
|
99
|
+
wait_triggered_transactions: bool = True,
|
100
|
+
wait_triggered_transactions_status: TransactionStatus = TransactionStatus.ACCEPTED,
|
101
|
+
) -> GenLayerTransaction:
|
102
|
+
"""
|
103
|
+
Wrapper to the contract write method.
|
104
|
+
"""
|
105
|
+
if wait_interval is None:
|
106
|
+
wait_interval = get_default_wait_interval()
|
107
|
+
if wait_retries is None:
|
108
|
+
wait_retries = get_default_wait_retries()
|
109
|
+
client = get_gl_client()
|
110
|
+
tx_hash = client.write_contract(
|
111
|
+
address=self.address,
|
112
|
+
function_name=method_name,
|
113
|
+
account=self.account,
|
114
|
+
value=value,
|
115
|
+
consensus_max_rotations=consensus_max_rotations,
|
116
|
+
leader_only=leader_only,
|
117
|
+
args=args,
|
118
|
+
)
|
119
|
+
receipt = client.wait_for_transaction_receipt(
|
120
|
+
transaction_hash=tx_hash,
|
121
|
+
status=wait_transaction_status,
|
122
|
+
interval=wait_interval,
|
123
|
+
retries=wait_retries,
|
124
|
+
)
|
125
|
+
if wait_triggered_transactions:
|
126
|
+
triggered_transactions = receipt["triggered_transactions"]
|
127
|
+
for triggered_transaction in triggered_transactions:
|
128
|
+
client.wait_for_transaction_receipt(
|
129
|
+
transaction_hash=triggered_transaction,
|
130
|
+
status=wait_triggered_transactions_status,
|
131
|
+
interval=wait_interval,
|
132
|
+
retries=wait_retries,
|
133
|
+
)
|
134
|
+
return receipt
|
135
|
+
|
136
|
+
return read_contract_wrapper if read_only else write_contract_wrapper
|
137
|
+
|
138
|
+
|
139
|
+
@dataclass
|
140
|
+
class ContractFactory:
|
141
|
+
"""
|
142
|
+
A factory for deploying contracts.
|
143
|
+
"""
|
144
|
+
|
145
|
+
contract_name: str
|
146
|
+
contract_code: str
|
147
|
+
|
148
|
+
@classmethod
|
149
|
+
def from_artifact(
|
150
|
+
cls: Type["ContractFactory"], contract_name: str
|
151
|
+
) -> "ContractFactory":
|
152
|
+
"""
|
153
|
+
Create a ContractFactory instance given the contract name.
|
154
|
+
"""
|
155
|
+
contract_info = find_contract_definition(contract_name)
|
156
|
+
if contract_info is None:
|
157
|
+
raise ValueError(
|
158
|
+
f"Contract {contract_name} not found in the contracts directory"
|
159
|
+
)
|
160
|
+
return cls(
|
161
|
+
contract_name=contract_name, contract_code=contract_info.contract_code
|
162
|
+
)
|
163
|
+
|
164
|
+
def build_contract(
|
165
|
+
self,
|
166
|
+
contract_address: Union[Address, ChecksumAddress],
|
167
|
+
account: Optional[LocalAccount] = None,
|
168
|
+
) -> Contract:
|
169
|
+
"""
|
170
|
+
Build contract from address
|
171
|
+
"""
|
172
|
+
client = get_gl_client()
|
173
|
+
try:
|
174
|
+
schema = client.get_contract_schema(address=contract_address)
|
175
|
+
return Contract.new(
|
176
|
+
address=contract_address, schema=schema, account=account
|
177
|
+
)
|
178
|
+
except Exception as e:
|
179
|
+
raise ValueError(
|
180
|
+
f"Failed to build contract {self.contract_name}: {str(e)}"
|
181
|
+
) from e
|
182
|
+
|
183
|
+
def deploy(
|
184
|
+
self,
|
185
|
+
args: List[Any] = [],
|
186
|
+
account: Optional[LocalAccount] = None,
|
187
|
+
consensus_max_rotations: Optional[int] = None,
|
188
|
+
leader_only: bool = False,
|
189
|
+
wait_interval: Optional[int] = None,
|
190
|
+
wait_retries: Optional[int] = None,
|
191
|
+
wait_transaction_status: TransactionStatus = TransactionStatus.FINALIZED,
|
192
|
+
) -> Contract:
|
193
|
+
"""
|
194
|
+
Deploy the contract
|
195
|
+
"""
|
196
|
+
if wait_interval is None:
|
197
|
+
wait_interval = get_default_wait_interval()
|
198
|
+
if wait_retries is None:
|
199
|
+
wait_retries = get_default_wait_retries()
|
200
|
+
client = get_gl_client()
|
201
|
+
try:
|
202
|
+
tx_hash = client.deploy_contract(
|
203
|
+
code=self.contract_code,
|
204
|
+
args=args,
|
205
|
+
account=account,
|
206
|
+
consensus_max_rotations=consensus_max_rotations,
|
207
|
+
leader_only=leader_only,
|
208
|
+
)
|
209
|
+
tx_receipt = client.wait_for_transaction_receipt(
|
210
|
+
transaction_hash=tx_hash,
|
211
|
+
status=wait_transaction_status,
|
212
|
+
interval=wait_interval,
|
213
|
+
retries=wait_retries,
|
214
|
+
)
|
215
|
+
if (
|
216
|
+
not tx_receipt
|
217
|
+
or "data" not in tx_receipt
|
218
|
+
or "contract_address" not in tx_receipt["data"]
|
219
|
+
):
|
220
|
+
raise ValueError(
|
221
|
+
"Invalid transaction receipt: missing contract address"
|
222
|
+
)
|
223
|
+
|
224
|
+
if tx_execution_failed(tx_receipt):
|
225
|
+
raise ValueError(
|
226
|
+
f"Deployment transaction finalized with error: {tx_receipt}"
|
227
|
+
)
|
228
|
+
|
229
|
+
contract_address = tx_receipt["data"]["contract_address"]
|
230
|
+
schema = client.get_contract_schema(address=contract_address)
|
231
|
+
return Contract.new(
|
232
|
+
address=contract_address, schema=schema, account=account
|
233
|
+
)
|
234
|
+
except Exception as e:
|
235
|
+
raise DeploymentError(
|
236
|
+
f"Failed to deploy contract {self.contract_name}: {str(e)}"
|
237
|
+
) from e
|
238
|
+
|
239
|
+
|
240
|
+
def get_contract_factory(contract_name: str) -> ContractFactory:
|
241
|
+
"""
|
242
|
+
Get a ContractFactory instance for a contract.
|
243
|
+
"""
|
244
|
+
return ContractFactory.from_artifact(contract_name)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from typing import TypeVar, Callable, List, Any
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from .take_snapshot import SnapshotRestorer, take_snapshot
|
4
|
+
from gltest.exceptions import (
|
5
|
+
FixtureSnapshotError,
|
6
|
+
InvalidSnapshotError,
|
7
|
+
FixtureAnonymousFunctionError,
|
8
|
+
)
|
9
|
+
|
10
|
+
|
11
|
+
T = TypeVar("T")
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class Snapshot:
|
16
|
+
"""Represents a snapshot of the blockchain state."""
|
17
|
+
|
18
|
+
restorer: SnapshotRestorer
|
19
|
+
fixture: Callable[[], Any]
|
20
|
+
data: Any
|
21
|
+
|
22
|
+
|
23
|
+
# Global storage for snapshots
|
24
|
+
_snapshots: List[Snapshot] = []
|
25
|
+
|
26
|
+
|
27
|
+
def load_fixture(fixture: Callable[[], T]) -> T:
|
28
|
+
"""
|
29
|
+
Useful in tests for setting up the desired state of the network.
|
30
|
+
"""
|
31
|
+
if fixture.__name__ == "<lambda>":
|
32
|
+
raise FixtureAnonymousFunctionError("Fixtures must be named functions")
|
33
|
+
|
34
|
+
# Find existing snapshot for this fixture
|
35
|
+
global _snapshots
|
36
|
+
snapshot = next((s for s in _snapshots if s.fixture == fixture), None)
|
37
|
+
|
38
|
+
if snapshot is not None:
|
39
|
+
try:
|
40
|
+
snapshot.restorer.restore()
|
41
|
+
# Remove snapshots that were taken after this one
|
42
|
+
|
43
|
+
_snapshots = [
|
44
|
+
s
|
45
|
+
for s in _snapshots
|
46
|
+
if int(s.restorer.snapshot_id) <= int(snapshot.restorer.snapshot_id)
|
47
|
+
]
|
48
|
+
except Exception as e:
|
49
|
+
if isinstance(e, InvalidSnapshotError):
|
50
|
+
raise FixtureSnapshotError(e) from e
|
51
|
+
raise e
|
52
|
+
|
53
|
+
return snapshot.data
|
54
|
+
else:
|
55
|
+
# Execute the fixture and take a snapshot
|
56
|
+
data = fixture()
|
57
|
+
restorer = take_snapshot()
|
58
|
+
|
59
|
+
_snapshots.append(Snapshot(restorer=restorer, fixture=fixture, data=data))
|
60
|
+
|
61
|
+
return data
|
62
|
+
|
63
|
+
|
64
|
+
def clear_snapshots() -> None:
|
65
|
+
"""Clears every existing snapshot."""
|
66
|
+
global _snapshots
|
67
|
+
_snapshots = []
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from gltest.glchain import get_gl_provider
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Callable
|
4
|
+
from gltest.exceptions import HelperError, InvalidSnapshotError
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class SnapshotRestorer:
|
9
|
+
"""Class responsible for restoring blockchain state to a snapshot."""
|
10
|
+
|
11
|
+
restore: Callable[[], None]
|
12
|
+
snapshot_id: int
|
13
|
+
|
14
|
+
|
15
|
+
def take_snapshot() -> SnapshotRestorer:
|
16
|
+
"""
|
17
|
+
Take a snapshot of the current blockchain state and return a function to restore the state and the snapshot ID.
|
18
|
+
"""
|
19
|
+
provider = get_gl_provider()
|
20
|
+
snapshot_id = provider.make_request(method="sim_createSnapshot", params=[])[
|
21
|
+
"result"
|
22
|
+
]
|
23
|
+
if not isinstance(snapshot_id, int):
|
24
|
+
raise HelperError(
|
25
|
+
"Assertion error: the value returned by evm_snapshot should be a int"
|
26
|
+
)
|
27
|
+
|
28
|
+
def restore() -> None:
|
29
|
+
reverted = provider.make_request(
|
30
|
+
method="sim_restoreSnapshot", params=[snapshot_id]
|
31
|
+
)["result"]
|
32
|
+
|
33
|
+
if not isinstance(reverted, bool):
|
34
|
+
raise HelperError(
|
35
|
+
"Assertion error: the value returned by evm_revert should be a boolean"
|
36
|
+
)
|
37
|
+
|
38
|
+
if not reverted:
|
39
|
+
raise InvalidSnapshotError("")
|
40
|
+
|
41
|
+
return SnapshotRestorer(restore=restore, snapshot_id=snapshot_id)
|
gltest/plugin_config.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
_contracts_dir = None
|
4
|
+
_rpc_url = None
|
5
|
+
_default_wait_interval = None
|
6
|
+
_default_wait_retries = None
|
7
|
+
|
8
|
+
|
9
|
+
def set_contracts_dir(path: Path):
|
10
|
+
global _contracts_dir
|
11
|
+
_contracts_dir = path
|
12
|
+
|
13
|
+
|
14
|
+
def get_contracts_dir() -> Path:
|
15
|
+
return Path(_contracts_dir)
|
16
|
+
|
17
|
+
|
18
|
+
def set_rpc_url(rpc_url: str):
|
19
|
+
global _rpc_url
|
20
|
+
_rpc_url = rpc_url
|
21
|
+
|
22
|
+
|
23
|
+
def get_rpc_url() -> str:
|
24
|
+
return _rpc_url
|
25
|
+
|
26
|
+
|
27
|
+
def set_default_wait_interval(default_wait_interval: int):
|
28
|
+
global _default_wait_interval
|
29
|
+
_default_wait_interval = default_wait_interval
|
30
|
+
|
31
|
+
|
32
|
+
def get_default_wait_interval() -> int:
|
33
|
+
return _default_wait_interval
|
34
|
+
|
35
|
+
|
36
|
+
def set_default_wait_retries(default_wait_retries: int):
|
37
|
+
global _default_wait_retries
|
38
|
+
_default_wait_retries = default_wait_retries
|
39
|
+
|
40
|
+
|
41
|
+
def get_default_wait_retries() -> int:
|
42
|
+
return _default_wait_retries
|
gltest/plugin_hooks.py
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
from gltest.plugin_config import (
|
2
|
+
set_contracts_dir,
|
3
|
+
set_default_wait_interval,
|
4
|
+
set_default_wait_retries,
|
5
|
+
set_rpc_url,
|
6
|
+
)
|
7
|
+
from pathlib import Path
|
8
|
+
from genlayer_py.chains.localnet import SIMULATOR_JSON_RPC_URL
|
9
|
+
|
10
|
+
|
11
|
+
def pytest_addoption(parser):
|
12
|
+
group = parser.getgroup("gltest")
|
13
|
+
group.addoption(
|
14
|
+
"--contracts-dir",
|
15
|
+
action="store",
|
16
|
+
default="contracts",
|
17
|
+
help="Directory containing contract files",
|
18
|
+
)
|
19
|
+
|
20
|
+
group.addoption(
|
21
|
+
"--default-wait-interval",
|
22
|
+
action="store",
|
23
|
+
default=10000,
|
24
|
+
help="Default wait interval for waiting transaction receipts",
|
25
|
+
)
|
26
|
+
|
27
|
+
group.addoption(
|
28
|
+
"--default-wait-retries",
|
29
|
+
action="store",
|
30
|
+
default=15,
|
31
|
+
help="Default wait retries for waiting transaction receipts",
|
32
|
+
)
|
33
|
+
|
34
|
+
group.addoption(
|
35
|
+
"--rpc-url",
|
36
|
+
action="store",
|
37
|
+
default=SIMULATOR_JSON_RPC_URL,
|
38
|
+
help="RPC URL for the genlayer network",
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def pytest_configure(config):
|
43
|
+
contracts_dir = config.getoption("--contracts-dir")
|
44
|
+
default_wait_interval = config.getoption("--default-wait-interval")
|
45
|
+
default_wait_retries = config.getoption("--default-wait-retries")
|
46
|
+
rpc_url = config.getoption("--rpc-url")
|
47
|
+
|
48
|
+
set_contracts_dir(Path(contracts_dir))
|
49
|
+
set_default_wait_interval(int(default_wait_interval))
|
50
|
+
set_default_wait_retries(int(default_wait_retries))
|
51
|
+
set_rpc_url(str(rpc_url))
|
gltest/types.py
ADDED
gltest_cli/main.py
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
from gltest import get_contract_factory
|
2
|
+
from gltest.assertions import tx_execution_succeeded
|
3
|
+
|
4
|
+
|
5
|
+
def test_football_prediction_market():
|
6
|
+
# Deploy Contract
|
7
|
+
factory = get_contract_factory("PredictionMarket")
|
8
|
+
contract = factory.deploy(args=["2024-06-26", "Georgia", "Portugal"])
|
9
|
+
|
10
|
+
# Resolve match
|
11
|
+
transaction_response_call_1 = contract.resolve(args=[])
|
12
|
+
assert tx_execution_succeeded(transaction_response_call_1)
|
13
|
+
|
14
|
+
# Get Updated State
|
15
|
+
contract_state_2 = contract.get_resolution_data(args=[])
|
16
|
+
|
17
|
+
assert contract_state_2["winner"] == 1
|
18
|
+
assert contract_state_2["score"] == "2:0"
|
19
|
+
assert contract_state_2["has_resolved"] == True
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from gltest import get_contract_factory
|
4
|
+
from gltest.assertions import tx_execution_succeeded
|
5
|
+
|
6
|
+
|
7
|
+
def wait_for_contract_deployment(intelligent_oracle_contract, max_retries=10, delay=5):
|
8
|
+
"""
|
9
|
+
Wait for intelligent oracle contract to be fully deployed by attempting to call a method.
|
10
|
+
This is used to check if the triggered deployment did deploy the contract.
|
11
|
+
"""
|
12
|
+
for _ in range(max_retries):
|
13
|
+
try:
|
14
|
+
intelligent_oracle_contract.get_dict(args=[])
|
15
|
+
return True # If successful, contract is deployed
|
16
|
+
except Exception:
|
17
|
+
time.sleep(delay)
|
18
|
+
return False
|
19
|
+
|
20
|
+
|
21
|
+
def test_intelligent_oracle_factory_pattern():
|
22
|
+
# Get the intelligent oracle factory
|
23
|
+
intelligent_oracle_factory = get_contract_factory("IntelligentOracle")
|
24
|
+
|
25
|
+
# Deploy the Registry contract with the IntelligentOracle code
|
26
|
+
registry_factory = get_contract_factory("Registry")
|
27
|
+
registry_contract = registry_factory.deploy(
|
28
|
+
args=[intelligent_oracle_factory.contract_code]
|
29
|
+
)
|
30
|
+
|
31
|
+
markets_data = [
|
32
|
+
{
|
33
|
+
"prediction_market_id": "marathon2024",
|
34
|
+
"title": "Marathon Winner Prediction",
|
35
|
+
"description": "Predict the male winner of a major marathon event.",
|
36
|
+
"potential_outcomes": ["Bekele Fikre", "Tafa Mitku", "Chebii Douglas"],
|
37
|
+
"rules": [
|
38
|
+
"The outcome is based on the official race results announced by the marathon organizers."
|
39
|
+
],
|
40
|
+
"data_source_domains": ["thepostrace.com"],
|
41
|
+
"resolution_urls": [],
|
42
|
+
"earliest_resolution_date": "2024-01-01T00:00:00+00:00",
|
43
|
+
"outcome": "Tafa Mitku",
|
44
|
+
"evidence_urls": "https://thepostrace.com/en/blog/marathon-de-madrid-2024-results-and-rankings/?srsltid=AfmBOor1uG6O3_4oJ447hkah_ilOYuy0XXMvl8j70EApe1Z7Bzd94XJl",
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"prediction_market_id": "election2024",
|
48
|
+
"title": "Election Prediction",
|
49
|
+
"description": "Predict the winner of the 2024 US presidential election.",
|
50
|
+
"potential_outcomes": ["Kamala Harris", "Donald Trump"],
|
51
|
+
"rules": ["The outcome is based on official election results."],
|
52
|
+
"data_source_domains": ["bbc.com"],
|
53
|
+
"resolution_urls": [],
|
54
|
+
"earliest_resolution_date": "2024-01-01T00:00:00+00:00",
|
55
|
+
"outcome": "Donald Trump",
|
56
|
+
"evidence_urls": "https://www.bbc.com/news/election/2024/us/results",
|
57
|
+
},
|
58
|
+
]
|
59
|
+
created_market_contracts = []
|
60
|
+
|
61
|
+
# Create markets through factory
|
62
|
+
for market_data in markets_data:
|
63
|
+
create_result = registry_contract.create_new_prediction_market(
|
64
|
+
args=[
|
65
|
+
market_data["prediction_market_id"],
|
66
|
+
market_data["title"],
|
67
|
+
market_data["description"],
|
68
|
+
market_data["potential_outcomes"],
|
69
|
+
market_data["rules"],
|
70
|
+
market_data["data_source_domains"],
|
71
|
+
market_data["resolution_urls"],
|
72
|
+
market_data["earliest_resolution_date"],
|
73
|
+
],
|
74
|
+
)
|
75
|
+
assert tx_execution_succeeded(create_result)
|
76
|
+
|
77
|
+
# Get the latest contract address from factory
|
78
|
+
registered_addresses = registry_contract.get_contract_addresses(args=[])
|
79
|
+
new_market_address = registered_addresses[-1]
|
80
|
+
|
81
|
+
# Build a contract object
|
82
|
+
market_contract = intelligent_oracle_factory.build_contract(
|
83
|
+
new_market_address
|
84
|
+
)
|
85
|
+
created_market_contracts.append(market_contract)
|
86
|
+
|
87
|
+
# Wait for the new market contract to be deployed
|
88
|
+
assert wait_for_contract_deployment(
|
89
|
+
market_contract
|
90
|
+
), f"Market contract deployment timeout for {market_data['prediction_market_id']}"
|
91
|
+
|
92
|
+
# Verify all markets were registered
|
93
|
+
assert len(registered_addresses) == len(markets_data)
|
94
|
+
|
95
|
+
# Verify each market's state
|
96
|
+
for i, market_contract in enumerate(created_market_contracts):
|
97
|
+
market_state = market_contract.get_dict(args=[])
|
98
|
+
expected_data = markets_data[i]
|
99
|
+
|
100
|
+
# Verify key market properties
|
101
|
+
assert market_state["title"] == expected_data["title"]
|
102
|
+
assert market_state["description"] == expected_data["description"]
|
103
|
+
assert market_state["potential_outcomes"] == expected_data["potential_outcomes"]
|
104
|
+
assert market_state["rules"] == expected_data["rules"]
|
105
|
+
assert (
|
106
|
+
market_state["data_source_domains"] == expected_data["data_source_domains"]
|
107
|
+
)
|
108
|
+
assert market_state["resolution_urls"] == expected_data["resolution_urls"]
|
109
|
+
assert market_state["status"] == "Active"
|
110
|
+
assert (
|
111
|
+
market_state["earliest_resolution_date"]
|
112
|
+
== expected_data["earliest_resolution_date"]
|
113
|
+
)
|
114
|
+
assert (
|
115
|
+
market_state["prediction_market_id"]
|
116
|
+
== expected_data["prediction_market_id"]
|
117
|
+
)
|
118
|
+
|
119
|
+
# Resolve markets
|
120
|
+
for i, market_contract in enumerate(created_market_contracts):
|
121
|
+
resolve_result = market_contract.resolve(
|
122
|
+
args=[markets_data[i]["evidence_urls"]],
|
123
|
+
)
|
124
|
+
assert tx_execution_succeeded(resolve_result)
|
125
|
+
|
126
|
+
# Verify market was resolved and has the correct outcome
|
127
|
+
market_state = market_contract.get_dict(args=[])
|
128
|
+
assert market_state["status"] == "Resolved"
|
129
|
+
assert market_state["outcome"] == markets_data[i]["outcome"]
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from gltest import get_contract_factory, default_account, create_account
|
2
|
+
from gltest.assertions import tx_execution_succeeded
|
3
|
+
|
4
|
+
TOKEN_TOTAL_SUPPLY = 1000
|
5
|
+
TRANSFER_AMOUNT = 100
|
6
|
+
|
7
|
+
|
8
|
+
def test_llm_erc20():
|
9
|
+
# Account Setup
|
10
|
+
from_account_a = default_account
|
11
|
+
from_account_b = create_account()
|
12
|
+
|
13
|
+
# Deploy Contract
|
14
|
+
factory = get_contract_factory("LlmErc20")
|
15
|
+
contract = factory.deploy(args=[TOKEN_TOTAL_SUPPLY])
|
16
|
+
|
17
|
+
# Get Initial State
|
18
|
+
contract_state_1 = contract.get_balances(args=[])
|
19
|
+
assert contract_state_1[from_account_a.address] == TOKEN_TOTAL_SUPPLY
|
20
|
+
|
21
|
+
# Transfer from User A to User B
|
22
|
+
transaction_response_call_1 = contract.transfer(
|
23
|
+
args=[TRANSFER_AMOUNT, from_account_b.address]
|
24
|
+
)
|
25
|
+
assert tx_execution_succeeded(transaction_response_call_1)
|
26
|
+
|
27
|
+
# Get Updated State
|
28
|
+
contract_state_2_1 = contract.get_balances(args=[])
|
29
|
+
assert (
|
30
|
+
contract_state_2_1[from_account_a.address]
|
31
|
+
== TOKEN_TOTAL_SUPPLY - TRANSFER_AMOUNT
|
32
|
+
)
|
33
|
+
assert contract_state_2_1[from_account_b.address] == TRANSFER_AMOUNT
|
34
|
+
|
35
|
+
# Get Updated State
|
36
|
+
contract_state_2_2 = contract.get_balance_of(args=[from_account_a.address])
|
37
|
+
assert contract_state_2_2 == TOKEN_TOTAL_SUPPLY - TRANSFER_AMOUNT
|
38
|
+
|
39
|
+
# Get Updated State
|
40
|
+
contract_state_2_3 = contract.get_balance_of(args=[from_account_b.address])
|
41
|
+
assert contract_state_2_3 == TRANSFER_AMOUNT
|