genlayer-test 0.1.0b6__py3-none-any.whl → 0.1.2__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.0b6.dist-info → genlayer_test-0.1.2.dist-info}/METADATA +19 -2
- genlayer_test-0.1.2.dist-info/RECORD +53 -0
- {genlayer_test-0.1.0b6.dist-info → genlayer_test-0.1.2.dist-info}/WHEEL +1 -1
- gltest/artifacts/__init__.py +1 -1
- gltest/artifacts/contract.py +14 -5
- gltest/assertions.py +4 -3
- gltest/exceptions.py +9 -1
- gltest/glchain/account.py +1 -0
- gltest/helpers/__init__.py +1 -1
- gltest/helpers/fixture_snapshot.py +9 -1
- tests/artifact/contracts/not_ic_contract.py +22 -0
- tests/artifact/test_contract_definition.py +92 -0
- tests/assertions/test_assertions.py +31 -0
- tests/examples/contracts/football_prediction_market.py +97 -0
- tests/examples/contracts/intelligent_oracle.py +369 -0
- tests/examples/contracts/intelligent_oracle_factory.py +47 -0
- tests/examples/contracts/llm_erc20.py +69 -0
- tests/examples/contracts/log_indexer.py +67 -0
- tests/examples/contracts/multi_file_contract/__init__.py +20 -0
- tests/examples/contracts/multi_file_contract/other.py +14 -0
- tests/examples/contracts/multi_read_erc20.py +28 -0
- tests/examples/contracts/multi_tenant_storage.py +48 -0
- tests/examples/contracts/read_erc20.py +14 -0
- tests/examples/contracts/storage.py +22 -0
- tests/examples/contracts/user_storage.py +24 -0
- tests/examples/contracts/wizard_of_coin.py +56 -0
- tests/examples/tests/test_football_prediction_market.py +19 -0
- tests/examples/tests/test_intelligent_oracle_factory.py +127 -0
- tests/examples/tests/test_llm_erc20.py +41 -0
- tests/examples/tests/test_log_indexer.py +63 -0
- tests/examples/tests/test_multi_file_contract.py +15 -0
- tests/examples/tests/test_multi_file_contract_legacy.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_storage_legacy.py +23 -0
- tests/examples/tests/test_user_storage.py +57 -0
- tests/examples/tests/test_wizard_of_coin.py +12 -0
- tests/plugin/conftest.py +1 -0
- genlayer_test-0.1.0b6.dist-info/RECORD +0 -24
- tests/conftest.py +0 -1
- {genlayer_test-0.1.0b6.dist-info → genlayer_test-0.1.2.dist-info}/entry_points.txt +0 -0
- {genlayer_test-0.1.0b6.dist-info → genlayer_test-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {genlayer_test-0.1.0b6.dist-info → genlayer_test-0.1.2.dist-info}/top_level.txt +0 -0
- /tests/{test_plugin_hooks.py → plugin/test_plugin_hooks.py} +0 -0
@@ -0,0 +1,369 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
import json
|
4
|
+
from enum import Enum
|
5
|
+
from datetime import datetime, timezone
|
6
|
+
from urllib.parse import urlparse
|
7
|
+
from genlayer import *
|
8
|
+
|
9
|
+
|
10
|
+
class Status(Enum):
|
11
|
+
ACTIVE = "Active"
|
12
|
+
RESOLVED = "Resolved"
|
13
|
+
ERROR = "Error"
|
14
|
+
|
15
|
+
|
16
|
+
class IntelligentOracle(gl.Contract):
|
17
|
+
# Declare persistent storage fields
|
18
|
+
prediction_market_id: str
|
19
|
+
title: str
|
20
|
+
description: str
|
21
|
+
potential_outcomes: DynArray[str]
|
22
|
+
rules: DynArray[str]
|
23
|
+
data_source_domains: DynArray[str]
|
24
|
+
resolution_urls: DynArray[str]
|
25
|
+
earliest_resolution_date: str # Store as ISO format string
|
26
|
+
status: str # Store as string since Enum isn't supported
|
27
|
+
analysis: str # Store analysis results
|
28
|
+
outcome: str
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
prediction_market_id: str,
|
33
|
+
title: str,
|
34
|
+
description: str,
|
35
|
+
potential_outcomes: list[str],
|
36
|
+
rules: list[str],
|
37
|
+
data_source_domains: list[str],
|
38
|
+
resolution_urls: list[str],
|
39
|
+
earliest_resolution_date: str,
|
40
|
+
):
|
41
|
+
if (
|
42
|
+
not prediction_market_id
|
43
|
+
or not title
|
44
|
+
or not description
|
45
|
+
or not potential_outcomes
|
46
|
+
or not rules
|
47
|
+
or not earliest_resolution_date
|
48
|
+
):
|
49
|
+
raise ValueError("Missing required fields.")
|
50
|
+
|
51
|
+
if not resolution_urls and not data_source_domains:
|
52
|
+
raise ValueError("Missing resolution URLs or data source domains.")
|
53
|
+
|
54
|
+
if len(resolution_urls) > 0 and len(data_source_domains) > 0:
|
55
|
+
raise ValueError(
|
56
|
+
"Cannot provide both resolution URLs and data source domains."
|
57
|
+
)
|
58
|
+
|
59
|
+
if len(potential_outcomes) < 2:
|
60
|
+
raise ValueError("At least two potential outcomes are required.")
|
61
|
+
|
62
|
+
if len(potential_outcomes) != len(set(potential_outcomes)):
|
63
|
+
raise ValueError("Potential outcomes must be unique.")
|
64
|
+
|
65
|
+
self.prediction_market_id = prediction_market_id
|
66
|
+
self.title = title
|
67
|
+
self.description = description
|
68
|
+
|
69
|
+
for outcome in potential_outcomes:
|
70
|
+
self.potential_outcomes.append(outcome.strip())
|
71
|
+
|
72
|
+
for rule in rules:
|
73
|
+
self.rules.append(rule)
|
74
|
+
|
75
|
+
for datasource in data_source_domains:
|
76
|
+
self.data_source_domains.append(
|
77
|
+
datasource.strip()
|
78
|
+
.lower()
|
79
|
+
.replace("http://", "")
|
80
|
+
.replace("https://", "")
|
81
|
+
.replace("www.", "")
|
82
|
+
)
|
83
|
+
|
84
|
+
for url in resolution_urls:
|
85
|
+
self.resolution_urls.append(url.strip())
|
86
|
+
|
87
|
+
self.earliest_resolution_date = earliest_resolution_date
|
88
|
+
self.status = Status.ACTIVE.value
|
89
|
+
|
90
|
+
self.outcome = ""
|
91
|
+
|
92
|
+
@gl.public.view
|
93
|
+
def _check_evidence_domain(self, evidence: str) -> bool:
|
94
|
+
try:
|
95
|
+
parsed_url = urlparse(evidence)
|
96
|
+
evidence_domain = parsed_url.netloc.lower().replace("www.", "")
|
97
|
+
return evidence_domain in self.data_source_domains
|
98
|
+
except Exception:
|
99
|
+
return False
|
100
|
+
|
101
|
+
@gl.public.write
|
102
|
+
def resolve(self, evidence_url: str = "") -> None:
|
103
|
+
if self.status == Status.RESOLVED.value:
|
104
|
+
raise ValueError("Cannot resolve an already resolved oracle.")
|
105
|
+
|
106
|
+
current_time = datetime.now().astimezone().date()
|
107
|
+
earliest_time = datetime.fromisoformat(self.earliest_resolution_date).date()
|
108
|
+
if current_time < earliest_time:
|
109
|
+
raise ValueError("Cannot resolve before the earliest resolution date.")
|
110
|
+
|
111
|
+
if len(self.resolution_urls) > 0 and evidence_url:
|
112
|
+
raise ValueError(
|
113
|
+
"An evidence URL was provided but the oracle is configured to use resolution URLs already provided."
|
114
|
+
)
|
115
|
+
|
116
|
+
if len(self.resolution_urls) == 0 and not evidence_url:
|
117
|
+
raise ValueError(
|
118
|
+
"No evidence URL provided and the oracle is not configured to use resolution URLs."
|
119
|
+
)
|
120
|
+
|
121
|
+
if evidence_url:
|
122
|
+
is_valid = self._check_evidence_domain(evidence_url)
|
123
|
+
if not is_valid:
|
124
|
+
raise ValueError(
|
125
|
+
"The evidence URL does not match any of the data source domains."
|
126
|
+
)
|
127
|
+
|
128
|
+
analyzed_outputs = []
|
129
|
+
resources_to_check = (
|
130
|
+
self.resolution_urls if len(self.resolution_urls) > 0 else [evidence_url]
|
131
|
+
)
|
132
|
+
|
133
|
+
title = self.title
|
134
|
+
description = self.description
|
135
|
+
potential_outcomes = list(self.potential_outcomes)
|
136
|
+
rules = list(self.rules)
|
137
|
+
earliest_resolution_date = self.earliest_resolution_date
|
138
|
+
|
139
|
+
for resource_url in resources_to_check:
|
140
|
+
|
141
|
+
def evaluate_single_source() -> str:
|
142
|
+
resource_web_data = gl.get_webpage(resource_url, mode="text")
|
143
|
+
print(resource_web_data)
|
144
|
+
|
145
|
+
task = f"""
|
146
|
+
You are an AI Validator tasked with resolving a prediction market.
|
147
|
+
Your goal is to determine the correct outcome based on the user-defined rules,
|
148
|
+
the provided webpage HTML content, the resolution date, and the list of potential outcomes.
|
149
|
+
|
150
|
+
### Inputs
|
151
|
+
<title>
|
152
|
+
{title}
|
153
|
+
</title>
|
154
|
+
|
155
|
+
<description>
|
156
|
+
{description}
|
157
|
+
</description>
|
158
|
+
|
159
|
+
<potential_outcomes>
|
160
|
+
{potential_outcomes}
|
161
|
+
</potential_outcomes>
|
162
|
+
|
163
|
+
<rules>
|
164
|
+
{rules}
|
165
|
+
</rules>
|
166
|
+
|
167
|
+
<source_url>
|
168
|
+
{resource_url}
|
169
|
+
</source_url>
|
170
|
+
|
171
|
+
<webpage_content>
|
172
|
+
{resource_web_data}
|
173
|
+
</webpage_content>
|
174
|
+
|
175
|
+
<current_date>
|
176
|
+
{datetime.now().astimezone()}
|
177
|
+
</current_date>
|
178
|
+
|
179
|
+
<earliest_resolution_date>
|
180
|
+
{earliest_resolution_date}
|
181
|
+
</earliest_resolution_date>
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
### **Your Task:**
|
187
|
+
1. **Analyze the Inputs:**
|
188
|
+
- Carefully read and interpret the user-defined rules.
|
189
|
+
- Parse the HTML content to extract meaningful information relevant to the rules.
|
190
|
+
- Determine if the source pertains to the event that is being predicted.
|
191
|
+
- Determine if the event has occurred yet.
|
192
|
+
|
193
|
+
2. **Provide Reasoning:**
|
194
|
+
- Write a clear, self-contained reasoning for the outcome.
|
195
|
+
- Reference specific parts of the rules and the extracted data that support your decision.
|
196
|
+
- Ensure that someone reading the reasoning can understand it without needing additional information.
|
197
|
+
|
198
|
+
3. **Determine The Outcome:**
|
199
|
+
- Based on your analysis, decide which potential outcome is correct.
|
200
|
+
- If an outcome can be determined, but the outcome is not in the list of potential outcomes, the outcome should be `ERROR`.
|
201
|
+
- If the information is insufficient or inconclusive, or the event has not occurred yet, and you cannot confidently determine an outcome based on this source, the outcome should be `UNDETERMINED`.
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
### **Output Format:**
|
207
|
+
|
208
|
+
Provide your response in **valid JSON** format with the following structure:
|
209
|
+
|
210
|
+
```json
|
211
|
+
{{
|
212
|
+
"valid_source": "true | false",
|
213
|
+
"event_has_occurred": "true | false",
|
214
|
+
"reasoning": "Your detailed reasoning here",
|
215
|
+
"outcome": "Chosen outcome from the potential outcomes list, `UNDETERMINED` if no outcome can be determined based on this source, `ERROR` if the outcome is not in the potential outcomes list"
|
216
|
+
}}
|
217
|
+
```
|
218
|
+
|
219
|
+
### **Constraints and Considerations:**
|
220
|
+
|
221
|
+
- **Accuracy:** Base your decision strictly on the provided inputs.
|
222
|
+
- **Objectivity:** Remain neutral and unbiased.
|
223
|
+
- **Clarity:** Make sure your reasoning is easy to understand.
|
224
|
+
- **Validity:** Ensure the JSON output is properly formatted and free of errors. Do not include trailing commas.
|
225
|
+
"""
|
226
|
+
result = gl.exec_prompt(task)
|
227
|
+
print(result)
|
228
|
+
return result
|
229
|
+
|
230
|
+
result = gl.eq_principle_prompt_comparative(
|
231
|
+
evaluate_single_source,
|
232
|
+
principle="`outcome` field must be exactly the same. All other fields must be similar",
|
233
|
+
)
|
234
|
+
|
235
|
+
result_dict = _parse_json_dict(result)
|
236
|
+
analyzed_outputs.append((resource_url, result_dict))
|
237
|
+
|
238
|
+
def evaluate_all_sources() -> str:
|
239
|
+
task = f"""
|
240
|
+
You are an AI Validator tasked with resolving a prediction market Oracle. Your goal is to determine
|
241
|
+
the correct outcome based on processed data from all of the individial data sources. Here are your inputs
|
242
|
+
|
243
|
+
### Inputs
|
244
|
+
<title>
|
245
|
+
{title}
|
246
|
+
</title>
|
247
|
+
|
248
|
+
<description>
|
249
|
+
{description}
|
250
|
+
</description>
|
251
|
+
|
252
|
+
<potential_outcomes>
|
253
|
+
{potential_outcomes}
|
254
|
+
</potential_outcomes>
|
255
|
+
|
256
|
+
<rules>
|
257
|
+
{rules}
|
258
|
+
</rules>
|
259
|
+
|
260
|
+
<processed_data>
|
261
|
+
{analyzed_outputs}
|
262
|
+
</processed_data>
|
263
|
+
|
264
|
+
<current_date>
|
265
|
+
{datetime.now().astimezone()}
|
266
|
+
</current_date>
|
267
|
+
|
268
|
+
<earliest_resolution_date>
|
269
|
+
{earliest_resolution_date}
|
270
|
+
</earliest_resolution_date>
|
271
|
+
|
272
|
+
### **Your Task:**
|
273
|
+
1. **Analyze the Inputs:**
|
274
|
+
- Carefully read and interpret the user-defined rules.
|
275
|
+
- Take into account all the processed data form the sources.
|
276
|
+
- Consider the resolution date in your analysis to ensure timeliness of the data.
|
277
|
+
|
278
|
+
2. **Determine The Outcome:**
|
279
|
+
- The output should be determined from the processed data form the resolution sources.
|
280
|
+
- Based on your analysis, decide which potential outcome is correct.
|
281
|
+
- If an outcome can be determined, but the outcome is not in the list of potential outcomes, the outcome should be `ERROR`.
|
282
|
+
- If the information is insufficient or inconclusive, and you cannot confidently determine an outcome, the outcome should be `UNDETERMINED`.
|
283
|
+
- Your response should reflect a coherent summary outcome from the previous analysis.
|
284
|
+
- If multiple sources contradict each other, refer to the rules to determine how to resolve the contradiction.
|
285
|
+
- If the rules do not provide a clear resolution, the outcome should be `ERROR`.
|
286
|
+
|
287
|
+
### **Output Format:**
|
288
|
+
|
289
|
+
Provide your response in **valid JSON** format with the following structure:
|
290
|
+
|
291
|
+
```json
|
292
|
+
{{
|
293
|
+
"relevant_sources": "List of URLs that are relevant to the outcome",
|
294
|
+
"reasoning": "Your detailed reasoning here",
|
295
|
+
"outcome": "Chosen outcome from the potential outcomes list, `UNDETERMINED` if undetermined, `ERROR` if the outcome is not in the potential outcomes list"
|
296
|
+
}}
|
297
|
+
```
|
298
|
+
|
299
|
+
### **Constraints and Considerations:**
|
300
|
+
|
301
|
+
- **Accuracy:** Base your decision strictly on the provided inputs.
|
302
|
+
- **Objectivity:** Remain neutral and unbiased.
|
303
|
+
- **Clarity:** Make sure your reason is easy to understand.
|
304
|
+
- **Validity:** Ensure the JSON output is properly formatted and free of errors. Do not include trailing commas.
|
305
|
+
|
306
|
+
"""
|
307
|
+
|
308
|
+
result = gl.exec_prompt(task)
|
309
|
+
print(result)
|
310
|
+
return result
|
311
|
+
|
312
|
+
result = gl.eq_principle_prompt_comparative(
|
313
|
+
evaluate_all_sources,
|
314
|
+
principle="`outcome` field must be exactly the same. All other fields must be similar",
|
315
|
+
)
|
316
|
+
|
317
|
+
result_dict = _parse_json_dict(result)
|
318
|
+
self.analysis = json.dumps(result_dict)
|
319
|
+
|
320
|
+
if result_dict["outcome"] == "UNDETERMINED":
|
321
|
+
return
|
322
|
+
|
323
|
+
if (
|
324
|
+
result_dict["outcome"] == "ERROR"
|
325
|
+
or result_dict["outcome"] not in self.potential_outcomes
|
326
|
+
):
|
327
|
+
self.status = Status.ERROR.value
|
328
|
+
return
|
329
|
+
|
330
|
+
self.outcome = result_dict["outcome"]
|
331
|
+
self.status = Status.RESOLVED.value
|
332
|
+
|
333
|
+
@gl.public.view
|
334
|
+
def get_dict(self) -> dict[str, str]:
|
335
|
+
return {
|
336
|
+
"title": self.title,
|
337
|
+
"description": self.description,
|
338
|
+
"potential_outcomes": list(self.potential_outcomes),
|
339
|
+
"rules": list(self.rules),
|
340
|
+
"data_source_domains": list(self.data_source_domains),
|
341
|
+
"resolution_urls": list(self.resolution_urls),
|
342
|
+
"status": self.status,
|
343
|
+
"earliest_resolution_date": self.earliest_resolution_date,
|
344
|
+
"analysis": self.analysis,
|
345
|
+
"outcome": self.outcome,
|
346
|
+
"prediction_market_id": self.prediction_market_id,
|
347
|
+
}
|
348
|
+
|
349
|
+
@gl.public.view
|
350
|
+
def get_status(self) -> str:
|
351
|
+
return self.status
|
352
|
+
|
353
|
+
|
354
|
+
def _parse_json_dict(json_str: str) -> dict:
|
355
|
+
"""
|
356
|
+
Used to sanitize the JSON output from the LLM.
|
357
|
+
Remove everything before the first '{' and after the last '}', and remove trailing commas before closing braces/brackets
|
358
|
+
"""
|
359
|
+
first_brace = json_str.find("{")
|
360
|
+
last_brace = json_str.rfind("}")
|
361
|
+
json_str = json_str[first_brace : last_brace + 1]
|
362
|
+
|
363
|
+
# Remove trailing commas before closing braces/brackets
|
364
|
+
import re
|
365
|
+
|
366
|
+
json_str = re.sub(r",(?!\s*?[\{\[\"\'\w])", "", json_str)
|
367
|
+
print(json_str)
|
368
|
+
|
369
|
+
return json.loads(json_str)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
class Registry(gl.Contract):
|
7
|
+
# Declare persistent storage fields
|
8
|
+
contract_addresses: DynArray[str]
|
9
|
+
intelligent_oracle_code: str
|
10
|
+
|
11
|
+
def __init__(self, intelligent_oracle_code: str):
|
12
|
+
self.intelligent_oracle_code = intelligent_oracle_code
|
13
|
+
|
14
|
+
@gl.public.write
|
15
|
+
def create_new_prediction_market(
|
16
|
+
self,
|
17
|
+
prediction_market_id: str,
|
18
|
+
title: str,
|
19
|
+
description: str,
|
20
|
+
potential_outcomes: list[str],
|
21
|
+
rules: list[str],
|
22
|
+
data_source_domains: list[str],
|
23
|
+
resolution_urls: list[str],
|
24
|
+
earliest_resolution_date: str,
|
25
|
+
) -> None:
|
26
|
+
registered_contracts = len(self.contract_addresses)
|
27
|
+
contract_address = gl.deploy_contract(
|
28
|
+
code=self.intelligent_oracle_code.encode("utf-8"),
|
29
|
+
args=[
|
30
|
+
prediction_market_id,
|
31
|
+
title,
|
32
|
+
description,
|
33
|
+
potential_outcomes,
|
34
|
+
rules,
|
35
|
+
data_source_domains,
|
36
|
+
resolution_urls,
|
37
|
+
earliest_resolution_date,
|
38
|
+
],
|
39
|
+
salt_nonce=registered_contracts + 1,
|
40
|
+
)
|
41
|
+
print("contract_address", contract_address)
|
42
|
+
print("contract_address type", type(contract_address))
|
43
|
+
self.contract_addresses.append(contract_address.as_hex)
|
44
|
+
|
45
|
+
@gl.public.view
|
46
|
+
def get_contract_addresses(self) -> list[str]:
|
47
|
+
return list(self.contract_addresses)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
import json
|
4
|
+
|
5
|
+
from genlayer import *
|
6
|
+
|
7
|
+
|
8
|
+
class LlmErc20(gl.Contract):
|
9
|
+
balances: TreeMap[Address, u256]
|
10
|
+
|
11
|
+
def __init__(self, total_supply: int) -> None:
|
12
|
+
self.balances[gl.message.sender_address] = u256(total_supply)
|
13
|
+
|
14
|
+
@gl.public.write
|
15
|
+
def transfer(self, amount: int, to_address: str) -> None:
|
16
|
+
input = f"""
|
17
|
+
You keep track of transactions between users and their balance in coins.
|
18
|
+
The current balance for all users in JSON format is:
|
19
|
+
{json.dumps(self.get_balances())}
|
20
|
+
The transaction to compute is: {{
|
21
|
+
sender: "{gl.message.sender_address.as_hex}",
|
22
|
+
recipient: "{Address(to_address).as_hex}",
|
23
|
+
amount: {amount},
|
24
|
+
}}
|
25
|
+
|
26
|
+
"""
|
27
|
+
task = """For every transaction, validate that the user sending the Coins has
|
28
|
+
enough balance. If any transaction is invalid, it shouldn't be processed.
|
29
|
+
Update the balances based on the valid transactions only.
|
30
|
+
Given the current balance in JSON format and the transaction provided,
|
31
|
+
please provide the result of your calculation with the following format:
|
32
|
+
{{
|
33
|
+
"transaction_success": bool, // Whether the transaction was successful
|
34
|
+
"transaction_error": str, // Empty if transaction is successful
|
35
|
+
"updated_balances": object<str, int> // Updated balances after the transaction
|
36
|
+
}}
|
37
|
+
|
38
|
+
It is mandatory that you respond only using the JSON format above,
|
39
|
+
nothing else. Don't include any other words or characters,
|
40
|
+
your output must be only JSON without any formatting prefix or suffix.
|
41
|
+
This result should be perfectly parsable by a JSON parser without errors.
|
42
|
+
"""
|
43
|
+
|
44
|
+
criteria = """
|
45
|
+
The balance of the sender should have decreased by the amount sent.
|
46
|
+
The balance of the receiver should have increased by the amount sent.
|
47
|
+
The total sum of all balances should remain the same before and after the transaction"""
|
48
|
+
|
49
|
+
final_result = (
|
50
|
+
gl.eq_principle_prompt_non_comparative(
|
51
|
+
lambda: input,
|
52
|
+
task=task,
|
53
|
+
criteria=criteria,
|
54
|
+
)
|
55
|
+
.replace("```json", "")
|
56
|
+
.replace("```", "")
|
57
|
+
)
|
58
|
+
print("final_result: ", final_result)
|
59
|
+
result_json = json.loads(final_result)
|
60
|
+
for k, v in result_json["updated_balances"].items():
|
61
|
+
self.balances[Address(k)] = v
|
62
|
+
|
63
|
+
@gl.public.view
|
64
|
+
def get_balances(self) -> dict[str, int]:
|
65
|
+
return {k.as_hex: v for k, v in self.balances.items()}
|
66
|
+
|
67
|
+
@gl.public.view
|
68
|
+
def get_balance_of(self, address: str) -> int:
|
69
|
+
return self.balances.get(Address(address), 0)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# {
|
2
|
+
# "Seq": [
|
3
|
+
# { "Depends": "py-lib-genlayermodelwrappers:test" },
|
4
|
+
# { "Depends": "py-genlayer:test" }
|
5
|
+
# ]
|
6
|
+
# }
|
7
|
+
|
8
|
+
import numpy as np
|
9
|
+
from genlayer import *
|
10
|
+
import genlayermodelwrappers
|
11
|
+
from dataclasses import dataclass
|
12
|
+
import typing
|
13
|
+
|
14
|
+
|
15
|
+
@allow_storage
|
16
|
+
@dataclass
|
17
|
+
class StoreValue:
|
18
|
+
log_id: u256
|
19
|
+
text: str
|
20
|
+
|
21
|
+
|
22
|
+
# contract class
|
23
|
+
class LogIndexer(gl.Contract):
|
24
|
+
vector_store: VecDB[np.float32, typing.Literal[384], StoreValue]
|
25
|
+
|
26
|
+
def __init__(self):
|
27
|
+
pass
|
28
|
+
|
29
|
+
def get_embedding_generator(self):
|
30
|
+
return genlayermodelwrappers.SentenceTransformer("all-MiniLM-L6-v2")
|
31
|
+
|
32
|
+
def get_embedding(
|
33
|
+
self, txt: str
|
34
|
+
) -> np.ndarray[tuple[typing.Literal[384]], np.dtypes.Float32DType]:
|
35
|
+
return self.get_embedding_generator()(txt)
|
36
|
+
|
37
|
+
@gl.public.view
|
38
|
+
def get_closest_vector(self, text: str) -> dict | None:
|
39
|
+
emb = self.get_embedding(text)
|
40
|
+
result = list(self.vector_store.knn(emb, 1))
|
41
|
+
if len(result) == 0:
|
42
|
+
return None
|
43
|
+
result = result[0]
|
44
|
+
return {
|
45
|
+
"vector": list(str(x) for x in result.key),
|
46
|
+
"similarity": str(1 - result.distance),
|
47
|
+
"id": result.value.log_id,
|
48
|
+
"text": result.value.text,
|
49
|
+
}
|
50
|
+
|
51
|
+
@gl.public.write
|
52
|
+
def add_log(self, log: str, log_id: int) -> None:
|
53
|
+
emb = self.get_embedding(log)
|
54
|
+
self.vector_store.insert(emb, StoreValue(text=log, log_id=u256(log_id)))
|
55
|
+
|
56
|
+
@gl.public.write
|
57
|
+
def update_log(self, log_id: int, log: str) -> None:
|
58
|
+
emb = self.get_embedding(log)
|
59
|
+
for elem in self.vector_store.knn(emb, 2):
|
60
|
+
if elem.value.text == log:
|
61
|
+
elem.value.log_id = u256(log_id)
|
62
|
+
|
63
|
+
@gl.public.write
|
64
|
+
def remove_log(self, id: int) -> None:
|
65
|
+
for el in self.vector_store:
|
66
|
+
if el.value.log_id == id:
|
67
|
+
el.remove()
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from genlayer import *
|
2
|
+
|
3
|
+
|
4
|
+
class MultiFileContract(gl.Contract):
|
5
|
+
other_addr: Address
|
6
|
+
|
7
|
+
def __init__(self):
|
8
|
+
with open("/contract/other.py", "rt") as f:
|
9
|
+
text = f.read()
|
10
|
+
self.other_addr = gl.deploy_contract(
|
11
|
+
code=text.encode("utf-8"), args=["123"], salt_nonce=1
|
12
|
+
)
|
13
|
+
|
14
|
+
@gl.public.write
|
15
|
+
def wait(self) -> None:
|
16
|
+
pass
|
17
|
+
|
18
|
+
@gl.public.view
|
19
|
+
def test(self) -> str:
|
20
|
+
return gl.ContractAt(self.other_addr).view().test()
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
class multi_read_erc20(gl.Contract):
|
7
|
+
balances: TreeMap[Address, TreeMap[Address, u256]]
|
8
|
+
|
9
|
+
def __init__(self):
|
10
|
+
pass
|
11
|
+
|
12
|
+
@gl.public.write
|
13
|
+
def update_token_balances(
|
14
|
+
self, account_address: str, token_contracts: list[str]
|
15
|
+
) -> None:
|
16
|
+
for token_contract in token_contracts:
|
17
|
+
contract = gl.ContractAt(Address(token_contract))
|
18
|
+
balance = contract.view().get_balance_of(account_address)
|
19
|
+
self.balances.get_or_insert_default(Address(account_address))[
|
20
|
+
Address(token_contract)
|
21
|
+
] = balance
|
22
|
+
|
23
|
+
@gl.public.view
|
24
|
+
def get_balances(self) -> dict[str, dict[str, int]]:
|
25
|
+
return {
|
26
|
+
k.as_hex: {k.as_hex: v for k, v in v.items()}
|
27
|
+
for k, v in self.balances.items()
|
28
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
class MultiTentantStorage(gl.Contract):
|
7
|
+
"""
|
8
|
+
Same functionality as UserStorage, but implemented with multiple storage contracts.
|
9
|
+
Each user is assigned to a storage contract, and all storage contracts are managed by this same contract.
|
10
|
+
This contract does not prevent users from directly interacting with the storage contracts, but it doesn't bother us for testing purposes.
|
11
|
+
This is done to test contract calls between different contracts.
|
12
|
+
"""
|
13
|
+
|
14
|
+
all_storage_contracts: DynArray[Address]
|
15
|
+
available_storage_contracts: DynArray[Address]
|
16
|
+
mappings: TreeMap[
|
17
|
+
Address, Address
|
18
|
+
] # mapping of user address to storage contract address
|
19
|
+
|
20
|
+
def __init__(self, storage_contracts: list[str]):
|
21
|
+
for el in storage_contracts:
|
22
|
+
self.all_storage_contracts.append(Address(el))
|
23
|
+
self.available_storage_contracts.append(Address(el))
|
24
|
+
|
25
|
+
@gl.public.view
|
26
|
+
def get_available_contracts(self) -> list[str]:
|
27
|
+
return [x.as_hex for x in self.available_storage_contracts]
|
28
|
+
|
29
|
+
@gl.public.view
|
30
|
+
def get_all_storages(self) -> dict[str, str]:
|
31
|
+
return {
|
32
|
+
storage_contract.as_hex: gl.ContractAt(storage_contract)
|
33
|
+
.view()
|
34
|
+
.get_storage()
|
35
|
+
for storage_contract in self.all_storage_contracts
|
36
|
+
}
|
37
|
+
|
38
|
+
@gl.public.write
|
39
|
+
def update_storage(self, new_storage: str) -> None:
|
40
|
+
# Assign user to a storage contract if not already assigned
|
41
|
+
if gl.message.sender_address not in self.mappings:
|
42
|
+
self.mappings[gl.message.sender_address] = self.available_storage_contracts[
|
43
|
+
-1
|
44
|
+
]
|
45
|
+
self.available_storage_contracts.pop()
|
46
|
+
|
47
|
+
contract_to_use = self.mappings[gl.message.sender_address]
|
48
|
+
gl.ContractAt(contract_to_use).emit(gas=100000).update_storage(new_storage)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# { "Depends": "py-genlayer:test" }
|
2
|
+
|
3
|
+
from genlayer import *
|
4
|
+
|
5
|
+
|
6
|
+
class read_erc20(gl.Contract):
|
7
|
+
token_contract: Address
|
8
|
+
|
9
|
+
def __init__(self, token_contract: str):
|
10
|
+
self.token_contract = Address(token_contract)
|
11
|
+
|
12
|
+
@gl.public.view
|
13
|
+
def get_balance_of(self, account_address: str) -> int:
|
14
|
+
return gl.ContractAt(self.token_contract).view().get_balance_of(account_address)
|