prediction-market-agent-tooling 0.67.2__py3-none-any.whl → 0.67.4__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.
- prediction_market_agent_tooling/abis/erc1155.abi.json +352 -0
- prediction_market_agent_tooling/deploy/agent.py +57 -51
- prediction_market_agent_tooling/deploy/betting_strategy.py +8 -5
- prediction_market_agent_tooling/markets/agent_market.py +24 -1
- prediction_market_agent_tooling/markets/blockchain_utils.py +5 -3
- prediction_market_agent_tooling/markets/data_models.py +23 -3
- prediction_market_agent_tooling/markets/manifold/manifold.py +2 -1
- prediction_market_agent_tooling/markets/metaculus/metaculus.py +2 -1
- prediction_market_agent_tooling/markets/omen/omen.py +2 -1
- prediction_market_agent_tooling/markets/polymarket/api.py +9 -3
- prediction_market_agent_tooling/markets/polymarket/data_models.py +5 -3
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +17 -8
- prediction_market_agent_tooling/markets/seer/data_models.py +25 -1
- prediction_market_agent_tooling/markets/seer/seer.py +85 -26
- prediction_market_agent_tooling/markets/seer/seer_contracts.py +18 -0
- prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py +122 -18
- prediction_market_agent_tooling/markets/seer/swap_pool_handler.py +4 -1
- prediction_market_agent_tooling/tools/contract.py +59 -0
- prediction_market_agent_tooling/tools/cow/cow_order.py +4 -1
- prediction_market_agent_tooling/tools/hexbytes_custom.py +9 -0
- prediction_market_agent_tooling/tools/httpx_cached_client.py +5 -3
- prediction_market_agent_tooling/tools/langfuse_client_utils.py +4 -0
- prediction_market_agent_tooling/tools/rephrase.py +1 -1
- prediction_market_agent_tooling/tools/singleton.py +11 -6
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +57 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/RECORD +30 -29
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.67.2.dist-info → prediction_market_agent_tooling-0.67.4.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import sys
|
2
2
|
import typing as t
|
3
|
+
from collections import defaultdict
|
3
4
|
from enum import Enum
|
4
5
|
from typing import Any
|
5
6
|
|
@@ -15,6 +16,7 @@ from prediction_market_agent_tooling.deploy.constants import (
|
|
15
16
|
from prediction_market_agent_tooling.gtypes import ChecksumAddress, Wei
|
16
17
|
from prediction_market_agent_tooling.loggers import logger
|
17
18
|
from prediction_market_agent_tooling.markets.agent_market import (
|
19
|
+
ConditionalFilterType,
|
18
20
|
FilterBy,
|
19
21
|
QuestionType,
|
20
22
|
SortBy,
|
@@ -22,9 +24,14 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
22
24
|
from prediction_market_agent_tooling.markets.base_subgraph_handler import (
|
23
25
|
BaseSubgraphHandler,
|
24
26
|
)
|
25
|
-
from prediction_market_agent_tooling.markets.seer.data_models import
|
27
|
+
from prediction_market_agent_tooling.markets.seer.data_models import (
|
28
|
+
SeerMarket,
|
29
|
+
SeerMarketQuestions,
|
30
|
+
SeerMarketWithQuestions,
|
31
|
+
)
|
26
32
|
from prediction_market_agent_tooling.markets.seer.subgraph_data_models import SeerPool
|
27
33
|
from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
|
34
|
+
from prediction_market_agent_tooling.tools.singleton import SingletonMeta
|
28
35
|
from prediction_market_agent_tooling.tools.utils import to_int_timestamp, utcnow
|
29
36
|
from prediction_market_agent_tooling.tools.web3_utils import unwrap_generic_value
|
30
37
|
|
@@ -85,12 +92,6 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
85
92
|
markets_field.upperBound,
|
86
93
|
markets_field.lowerBound,
|
87
94
|
markets_field.templateId,
|
88
|
-
# TODO: On the Subgraph, `questions` field is a kind of sub-query, instead of a classic list of values.
|
89
|
-
# See how it is shown on their UI: https://thegraph.com/explorer/subgraphs/B4vyRqJaSHD8dRDb3BFRoAzuBK18c1QQcXq94JbxDxWH?view=Query&chain=arbitrum-one.
|
90
|
-
# And that doesn't work with subgrounds.
|
91
|
-
# markets_field.questions.question.id,
|
92
|
-
# markets_field.questions.question.finalize_ts,
|
93
|
-
# markets_field.questions.question.best_answer,
|
94
95
|
]
|
95
96
|
if current_level < max_level:
|
96
97
|
fields.extend(
|
@@ -132,8 +133,8 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
132
133
|
def _build_where_statements(
|
133
134
|
filter_by: FilterBy,
|
134
135
|
outcome_supply_gt_if_open: Wei,
|
135
|
-
include_conditional_markets: bool = False,
|
136
136
|
question_type: QuestionType = QuestionType.ALL,
|
137
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
137
138
|
parent_market_id: HexBytes | None = None,
|
138
139
|
) -> dict[Any, Any]:
|
139
140
|
now = to_int_timestamp(utcnow())
|
@@ -153,9 +154,6 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
153
154
|
case _:
|
154
155
|
raise ValueError(f"Unknown filter {filter_by}")
|
155
156
|
|
156
|
-
if not include_conditional_markets:
|
157
|
-
and_stms["parentMarket"] = ADDRESS_ZERO.lower()
|
158
|
-
|
159
157
|
if parent_market_id:
|
160
158
|
and_stms["parentMarket"] = parent_market_id.hex().lower()
|
161
159
|
|
@@ -196,9 +194,21 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
196
194
|
}
|
197
195
|
)
|
198
196
|
|
199
|
-
#
|
197
|
+
# Build filters for conditional_filter type
|
198
|
+
conditional_filter = {}
|
199
|
+
match conditional_filter_type:
|
200
|
+
case ConditionalFilterType.ONLY_CONDITIONAL:
|
201
|
+
conditional_filter["parentMarket_not"] = ADDRESS_ZERO.lower()
|
202
|
+
case ConditionalFilterType.ONLY_NOT_CONDITIONAL:
|
203
|
+
conditional_filter["parentMarket"] = ADDRESS_ZERO.lower()
|
204
|
+
case ConditionalFilterType.ALL:
|
205
|
+
pass
|
206
|
+
case _:
|
207
|
+
raise ValueError(
|
208
|
+
f"Unknown conditional filter {conditional_filter_type}"
|
209
|
+
)
|
200
210
|
|
201
|
-
all_filters =
|
211
|
+
all_filters = outcome_filters + [and_stms, conditional_filter]
|
202
212
|
where_stms: dict[str, t.Any] = {"and": all_filters}
|
203
213
|
return where_stms
|
204
214
|
|
@@ -235,9 +245,9 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
235
245
|
limit: int | None = None,
|
236
246
|
outcome_supply_gt_if_open: Wei = Wei(0),
|
237
247
|
question_type: QuestionType = QuestionType.ALL,
|
238
|
-
|
248
|
+
conditional_filter_type: ConditionalFilterType = ConditionalFilterType.ONLY_NOT_CONDITIONAL,
|
239
249
|
parent_market_id: HexBytes | None = None,
|
240
|
-
) -> list[
|
250
|
+
) -> list[SeerMarketWithQuestions]:
|
241
251
|
sort_direction, sort_by_field = self._build_sort_params(sort_by)
|
242
252
|
|
243
253
|
where_stms = self._build_where_statements(
|
@@ -245,7 +255,7 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
245
255
|
outcome_supply_gt_if_open=outcome_supply_gt_if_open,
|
246
256
|
parent_market_id=parent_market_id,
|
247
257
|
question_type=question_type,
|
248
|
-
|
258
|
+
conditional_filter_type=conditional_filter_type,
|
249
259
|
)
|
250
260
|
|
251
261
|
# These values can not be set to `None`, but they can be omitted.
|
@@ -264,12 +274,65 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
264
274
|
)
|
265
275
|
fields = self._get_fields_for_markets(markets_field)
|
266
276
|
markets = self.do_query(fields=fields, pydantic_model=SeerMarket)
|
267
|
-
|
277
|
+
market_ids = [m.id for m in markets]
|
278
|
+
# We fetch questions from all markets and all parents in one go
|
279
|
+
parent_market_ids = [
|
280
|
+
m.parent_market.id for m in markets if m.parent_market is not None
|
281
|
+
]
|
282
|
+
q = SeerQuestionsCache(seer_subgraph_handler=self)
|
283
|
+
q.fetch_questions(list(set(market_ids + parent_market_ids)))
|
284
|
+
|
285
|
+
# Create SeerMarketWithQuestions for each market
|
286
|
+
return [
|
287
|
+
SeerMarketWithQuestions(
|
288
|
+
**m.model_dump(), questions=q.market_id_to_questions[m.id]
|
289
|
+
)
|
290
|
+
for m in markets
|
291
|
+
]
|
268
292
|
|
269
|
-
def
|
293
|
+
def get_questions_for_markets(
|
294
|
+
self, market_ids: list[HexBytes]
|
295
|
+
) -> list[SeerMarketQuestions]:
|
296
|
+
where = unwrap_generic_value(
|
297
|
+
{"market_in": [market_id.hex().lower() for market_id in market_ids]}
|
298
|
+
)
|
299
|
+
markets_field = self.seer_subgraph.Query.marketQuestions(where=where)
|
300
|
+
fields = self._get_fields_for_questions(markets_field)
|
301
|
+
questions = self.do_query(fields=fields, pydantic_model=SeerMarketQuestions)
|
302
|
+
return questions
|
303
|
+
|
304
|
+
def get_market_by_id(self, market_id: HexBytes) -> SeerMarketWithQuestions:
|
270
305
|
markets_field = self.seer_subgraph.Query.market(id=market_id.hex().lower())
|
271
306
|
fields = self._get_fields_for_markets(markets_field)
|
272
307
|
markets = self.do_query(fields=fields, pydantic_model=SeerMarket)
|
308
|
+
if len(markets) != 1:
|
309
|
+
raise ValueError(
|
310
|
+
f"Fetched wrong number of markets. Expected 1 but got {len(markets)}"
|
311
|
+
)
|
312
|
+
q = SeerQuestionsCache(self)
|
313
|
+
q.fetch_questions([market_id])
|
314
|
+
questions = q.market_id_to_questions[market_id]
|
315
|
+
s = SeerMarketWithQuestions.model_validate(
|
316
|
+
markets[0].model_dump() | {"questions": questions}
|
317
|
+
)
|
318
|
+
return s
|
319
|
+
|
320
|
+
def _get_fields_for_questions(self, questions_field: FieldPath) -> list[FieldPath]:
|
321
|
+
fields = [
|
322
|
+
questions_field.question.id,
|
323
|
+
questions_field.question.best_answer,
|
324
|
+
questions_field.question.finalize_ts,
|
325
|
+
questions_field.market.id,
|
326
|
+
]
|
327
|
+
return fields
|
328
|
+
|
329
|
+
def get_market_by_wrapped_token(self, token: ChecksumAddress) -> SeerMarket:
|
330
|
+
where_stms = {"wrappedTokens_contains": [token]}
|
331
|
+
markets_field = self.seer_subgraph.Query.markets(
|
332
|
+
where=unwrap_generic_value(where_stms)
|
333
|
+
)
|
334
|
+
fields = self._get_fields_for_markets(markets_field)
|
335
|
+
markets = self.do_query(fields=fields, pydantic_model=SeerMarket)
|
273
336
|
if len(markets) != 1:
|
274
337
|
raise ValueError(
|
275
338
|
f"Fetched wrong number of markets. Expected 1 but got {len(markets)}"
|
@@ -328,3 +391,44 @@ class SeerSubgraphHandler(BaseSubgraphHandler):
|
|
328
391
|
# We select the first one
|
329
392
|
return pools[0]
|
330
393
|
return None
|
394
|
+
|
395
|
+
|
396
|
+
class SeerQuestionsCache(metaclass=SingletonMeta):
|
397
|
+
"""A singleton cache for storing and retrieving Seer market questions.
|
398
|
+
|
399
|
+
This class provides an in-memory cache for Seer market questions, preventing
|
400
|
+
redundant subgraph queries by maintaining a mapping of market IDs to their
|
401
|
+
associated questions. It implements the singleton pattern to ensure a single
|
402
|
+
cache instance is used throughout the agent run.
|
403
|
+
|
404
|
+
Attributes:
|
405
|
+
market_id_to_questions: A dictionary mapping market IDs to lists of SeerMarketQuestions
|
406
|
+
seer_subgraph_handler: Handler for interacting with the Seer subgraph
|
407
|
+
"""
|
408
|
+
|
409
|
+
def __init__(self, seer_subgraph_handler: SeerSubgraphHandler | None = None):
|
410
|
+
self.market_id_to_questions: dict[
|
411
|
+
HexBytes, list[SeerMarketQuestions]
|
412
|
+
] = defaultdict(list)
|
413
|
+
self.seer_subgraph_handler = seer_subgraph_handler or SeerSubgraphHandler()
|
414
|
+
|
415
|
+
def fetch_questions(self, market_ids: list[HexBytes]) -> None:
|
416
|
+
filtered_list = [
|
417
|
+
market_id
|
418
|
+
for market_id in market_ids
|
419
|
+
if market_id not in self.market_id_to_questions
|
420
|
+
]
|
421
|
+
if not filtered_list:
|
422
|
+
return
|
423
|
+
|
424
|
+
questions = self.seer_subgraph_handler.get_questions_for_markets(filtered_list)
|
425
|
+
# Group questions by market_id
|
426
|
+
questions_by_market: dict[HexBytes, list[SeerMarketQuestions]] = defaultdict(
|
427
|
+
list
|
428
|
+
)
|
429
|
+
for q in questions:
|
430
|
+
questions_by_market[q.market.id].append(q)
|
431
|
+
|
432
|
+
# Update the cache with the new questions for each market
|
433
|
+
for market_id, market_questions in questions_by_market.items():
|
434
|
+
self.market_id_to_questions[market_id] = market_questions
|
@@ -71,7 +71,10 @@ class SwapPoolHandler:
|
|
71
71
|
price_outcome_token = PriceManager.build(
|
72
72
|
HexBytes(HexStr(self.market_id))
|
73
73
|
).get_token_price_from_pools(token=outcome_token)
|
74
|
-
if
|
74
|
+
if (
|
75
|
+
not price_outcome_token
|
76
|
+
or not price_outcome_token.priceOfCollateralInAskingToken
|
77
|
+
):
|
75
78
|
raise ValueError(
|
76
79
|
f"Could not find price for {outcome_token=} and {self.collateral_token_address}"
|
77
80
|
)
|
@@ -413,6 +413,20 @@ class ContractERC4626BaseClass(ContractERC20BaseClass):
|
|
413
413
|
return self.convertToShares(amount, web3=web3)
|
414
414
|
|
415
415
|
|
416
|
+
class ContractWrapped1155BaseClass(ContractERC20BaseClass):
|
417
|
+
"""Wrapped 1155 contract from Seer (https://gnosisscan.io/address/0x2f9c49974ad8b9b31424d9dc812667b16310ca50#readContract)
|
418
|
+
Source code - https://github.com/seer-pm/demo/blob/main/contracts/src/interaction/1155-to-20/Wrapped1155Factory.sol#L224
|
419
|
+
This contract inherits from ERC20 and contains additional properties, such as multiToken (conditional Token contract implementation)
|
420
|
+
and tokenId (token identifier). Goal is to wrap individual tokens into a standalone ERC20 token.
|
421
|
+
"""
|
422
|
+
|
423
|
+
abi: ABI = abi_field_validator(
|
424
|
+
os.path.join(
|
425
|
+
os.path.dirname(os.path.realpath(__file__)), "../abis/erc1155.abi.json"
|
426
|
+
)
|
427
|
+
)
|
428
|
+
|
429
|
+
|
416
430
|
class OwnableContract(ContractBaseClass):
|
417
431
|
abi: ABI = abi_field_validator(
|
418
432
|
os.path.join(
|
@@ -533,6 +547,12 @@ class ContractDepositableWrapperERC20OnGnosisChain(
|
|
533
547
|
"""
|
534
548
|
|
535
549
|
|
550
|
+
class ContractWrapped1155OnGnosisChain(
|
551
|
+
ContractWrapped1155BaseClass, ContractERC20OnGnosisChain
|
552
|
+
):
|
553
|
+
pass
|
554
|
+
|
555
|
+
|
536
556
|
class ContractERC4626OnGnosisChain(
|
537
557
|
ContractERC4626BaseClass, ContractERC20OnGnosisChain
|
538
558
|
):
|
@@ -626,6 +646,11 @@ def contract_implements_function(
|
|
626
646
|
function_name=function_name,
|
627
647
|
web3=web3,
|
628
648
|
function_arg_types=function_arg_types,
|
649
|
+
) or seer_minimal_proxy_implements_function(
|
650
|
+
contract_address=contract_address,
|
651
|
+
function_name=function_name,
|
652
|
+
web3=web3,
|
653
|
+
function_arg_types=function_arg_types,
|
629
654
|
)
|
630
655
|
|
631
656
|
return implements
|
@@ -654,6 +679,31 @@ def minimal_proxy_implements_function(
|
|
654
679
|
return False
|
655
680
|
|
656
681
|
|
682
|
+
def seer_minimal_proxy_implements_function(
|
683
|
+
contract_address: ChecksumAddress,
|
684
|
+
function_name: str,
|
685
|
+
web3: Web3,
|
686
|
+
function_arg_types: list[str] | None = None,
|
687
|
+
) -> bool:
|
688
|
+
try:
|
689
|
+
# Read address between specific indices to find logic contract
|
690
|
+
bytecode = web3.eth.get_code(contract_address)
|
691
|
+
logic_contract_address = bytecode[11:31]
|
692
|
+
if not Web3.is_address(logic_contract_address):
|
693
|
+
return False
|
694
|
+
|
695
|
+
return contract_implements_function(
|
696
|
+
Web3.to_checksum_address(logic_contract_address),
|
697
|
+
function_name=function_name,
|
698
|
+
web3=web3,
|
699
|
+
function_arg_types=function_arg_types,
|
700
|
+
look_for_proxy_contract=False,
|
701
|
+
)
|
702
|
+
except DecodingError:
|
703
|
+
logger.info("Error decoding contract address on seer minimal proxy")
|
704
|
+
return False
|
705
|
+
|
706
|
+
|
657
707
|
def init_collateral_token_contract(
|
658
708
|
address: ChecksumAddress, web3: Web3 | None
|
659
709
|
) -> ContractERC20BaseClass:
|
@@ -673,6 +723,13 @@ def init_collateral_token_contract(
|
|
673
723
|
):
|
674
724
|
return ContractDepositableWrapperERC20BaseClass(address=address)
|
675
725
|
|
726
|
+
elif contract_implements_function(
|
727
|
+
address,
|
728
|
+
"multiToken",
|
729
|
+
web3=web3,
|
730
|
+
):
|
731
|
+
return ContractWrapped1155BaseClass(address=address)
|
732
|
+
|
676
733
|
elif contract_implements_function(
|
677
734
|
address,
|
678
735
|
"balanceOf",
|
@@ -694,6 +751,8 @@ def to_gnosis_chain_contract(
|
|
694
751
|
return ContractERC4626OnGnosisChain(address=contract.address)
|
695
752
|
elif isinstance(contract, ContractDepositableWrapperERC20BaseClass):
|
696
753
|
return ContractDepositableWrapperERC20OnGnosisChain(address=contract.address)
|
754
|
+
elif isinstance(contract, ContractWrapped1155BaseClass):
|
755
|
+
return ContractWrapped1155OnGnosisChain(address=contract.address)
|
697
756
|
elif isinstance(contract, ContractERC20BaseClass):
|
698
757
|
return ContractERC20OnGnosisChain(address=contract.address)
|
699
758
|
else:
|
@@ -167,10 +167,13 @@ def handle_allowance(
|
|
167
167
|
api_keys: APIKeys,
|
168
168
|
sell_token: ChecksumAddress,
|
169
169
|
amount_wei: Wei,
|
170
|
+
for_address: ChecksumAddress | None = None,
|
170
171
|
web3: Web3 | None = None,
|
171
172
|
) -> None:
|
172
173
|
# Approve the CoW Swap Vault Relayer to get the sell token only if allowance not sufficient.
|
173
|
-
for_address = Web3.to_checksum_address(
|
174
|
+
for_address = for_address or Web3.to_checksum_address(
|
175
|
+
CowContractAddress.VAULT_RELAYER.value
|
176
|
+
)
|
174
177
|
current_allowance = ContractERC20OnGnosisChain(address=sell_token).allowance(
|
175
178
|
owner=api_keys.bet_from_address,
|
176
179
|
for_address=for_address,
|
@@ -60,6 +60,15 @@ class HexBytes(HexBytesBase, BaseHex):
|
|
60
60
|
value = hex_str[2:] if hex_str.startswith("0x") else hex_str
|
61
61
|
return super().fromhex(value)
|
62
62
|
|
63
|
+
def hex(
|
64
|
+
self,
|
65
|
+
sep: t.Union[str, bytes] | None = None,
|
66
|
+
bytes_per_sep: t.SupportsIndex = 1,
|
67
|
+
) -> str:
|
68
|
+
"""We enforce a 0x prefix."""
|
69
|
+
x = super().hex(sep, bytes_per_sep) # type: ignore[arg-type]
|
70
|
+
return x if x.startswith("0x") else "0x" + x
|
71
|
+
|
63
72
|
@classmethod
|
64
73
|
def __eth_pydantic_validate__(
|
65
74
|
cls, value: t.Any, info: ValidationInfo | None = None
|
@@ -1,15 +1,17 @@
|
|
1
|
+
from datetime import timedelta
|
2
|
+
|
1
3
|
import hishel
|
2
4
|
import httpx
|
3
5
|
|
4
6
|
from prediction_market_agent_tooling.tools.singleton import SingletonMeta
|
5
7
|
|
6
|
-
|
8
|
+
ONE_DAY = timedelta(days=1)
|
7
9
|
|
8
10
|
|
9
11
|
class HttpxCachedClient(metaclass=SingletonMeta):
|
10
|
-
def __init__(self, ttl:
|
12
|
+
def __init__(self, ttl: timedelta = ONE_DAY) -> None:
|
11
13
|
storage = hishel.FileStorage(
|
12
|
-
ttl=ttl,
|
14
|
+
ttl=ttl.total_seconds(),
|
13
15
|
check_ttl_every=60,
|
14
16
|
)
|
15
17
|
controller = hishel.Controller(force_cache=True)
|
@@ -68,6 +68,7 @@ def get_traces_for_agent(
|
|
68
68
|
client: Langfuse,
|
69
69
|
to_timestamp: DatetimeUTC | None = None,
|
70
70
|
tags: str | list[str] | None = None,
|
71
|
+
limit: int | None = None,
|
71
72
|
) -> list[TraceWithDetails]:
|
72
73
|
"""
|
73
74
|
Fetch agent traces using pagination
|
@@ -98,6 +99,9 @@ def get_traces_for_agent(
|
|
98
99
|
if has_output:
|
99
100
|
agent_traces = [t for t in agent_traces if t.output is not None]
|
100
101
|
all_agent_traces.extend(agent_traces)
|
102
|
+
if limit is not None and len(all_agent_traces) >= limit:
|
103
|
+
all_agent_traces = all_agent_traces[:limit]
|
104
|
+
break
|
101
105
|
return all_agent_traces
|
102
106
|
|
103
107
|
|
@@ -33,7 +33,7 @@ Output only the rephrased question.
|
|
33
33
|
@tenacity.retry(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1))
|
34
34
|
@observe()
|
35
35
|
@db_cache
|
36
|
-
def
|
36
|
+
def rephrase_question_to_unconditional(
|
37
37
|
question: str,
|
38
38
|
parent_question: str,
|
39
39
|
needed_parent_outcome: str,
|
@@ -8,16 +8,21 @@ class SingletonMeta(type, t.Generic[_T]):
|
|
8
8
|
The Singleton class can be implemented in different ways in Python. Some
|
9
9
|
possible methods include: base class, decorator, metaclass. We will use the
|
10
10
|
metaclass because it is best suited for this purpose.
|
11
|
+
|
12
|
+
This version creates a unique instance for each unique set of __init__ arguments.
|
11
13
|
"""
|
12
14
|
|
13
|
-
_instances: dict[
|
15
|
+
_instances: dict[
|
16
|
+
tuple[t.Any, tuple[t.Any, ...], tuple[tuple[str, t.Any], ...]], _T
|
17
|
+
] = {}
|
14
18
|
|
15
19
|
def __call__(self, *args: t.Any, **kwargs: t.Any) -> _T:
|
16
20
|
"""
|
17
|
-
|
18
|
-
the returned instance.
|
21
|
+
Different __init__ arguments will result in different instances.
|
19
22
|
"""
|
20
|
-
|
23
|
+
# Create a key based on the class, args, and kwargs (sorted for consistency)
|
24
|
+
key = (self, args, tuple(sorted(kwargs.items())))
|
25
|
+
if key not in self._instances:
|
21
26
|
instance = super().__call__(*args, **kwargs)
|
22
|
-
self._instances[
|
23
|
-
return self._instances[
|
27
|
+
self._instances[key] = instance
|
28
|
+
return self._instances[key]
|
@@ -3,14 +3,22 @@ from web3 import Web3
|
|
3
3
|
from prediction_market_agent_tooling.config import APIKeys
|
4
4
|
from prediction_market_agent_tooling.gtypes import USD, Wei
|
5
5
|
from prediction_market_agent_tooling.loggers import logger
|
6
|
+
from prediction_market_agent_tooling.markets.seer.seer_contracts import GnosisRouter
|
7
|
+
from prediction_market_agent_tooling.markets.seer.seer_subgraph_handler import (
|
8
|
+
SeerSubgraphHandler,
|
9
|
+
)
|
6
10
|
from prediction_market_agent_tooling.tools.contract import (
|
7
11
|
ContractDepositableWrapperERC20BaseClass,
|
8
12
|
ContractERC20BaseClass,
|
9
13
|
ContractERC20OnGnosisChain,
|
10
14
|
ContractERC4626BaseClass,
|
15
|
+
ContractWrapped1155BaseClass,
|
16
|
+
init_collateral_token_contract,
|
17
|
+
to_gnosis_chain_contract,
|
11
18
|
)
|
12
19
|
from prediction_market_agent_tooling.tools.cow.cow_order import (
|
13
20
|
get_sell_token_amount,
|
21
|
+
handle_allowance,
|
14
22
|
swap_tokens_waiting,
|
15
23
|
)
|
16
24
|
from prediction_market_agent_tooling.tools.tokens.main_token import KEEPING_ERC20_TOKEN
|
@@ -49,6 +57,9 @@ def auto_deposit_collateral_token(
|
|
49
57
|
collateral_token_contract, collateral_amount_wei, api_keys, web3
|
50
58
|
)
|
51
59
|
|
60
|
+
elif isinstance(collateral_token_contract, ContractWrapped1155BaseClass):
|
61
|
+
mint_full_set(collateral_token_contract, collateral_amount_wei, api_keys, web3)
|
62
|
+
|
52
63
|
elif isinstance(collateral_token_contract, ContractERC20BaseClass):
|
53
64
|
auto_deposit_erc20(
|
54
65
|
collateral_token_contract, collateral_amount_wei, api_keys, web3
|
@@ -170,3 +181,49 @@ def auto_deposit_erc20(
|
|
170
181
|
web3=web3,
|
171
182
|
slippage_tolerance=slippage_tolerance,
|
172
183
|
)
|
184
|
+
|
185
|
+
|
186
|
+
def mint_full_set(
|
187
|
+
collateral_token_contract: ContractERC20BaseClass,
|
188
|
+
collateral_amount_wei: Wei,
|
189
|
+
api_keys: APIKeys,
|
190
|
+
web3: Web3 | None,
|
191
|
+
) -> None:
|
192
|
+
router = GnosisRouter()
|
193
|
+
# We need to fetch the parent's market collateral token, to split it and get the collateral token
|
194
|
+
# of the child market.
|
195
|
+
seer_subgraph_handler = SeerSubgraphHandler()
|
196
|
+
market = seer_subgraph_handler.get_market_by_wrapped_token(
|
197
|
+
token=collateral_token_contract.address
|
198
|
+
)
|
199
|
+
market_collateral_token = Web3.to_checksum_address(market.collateral_token)
|
200
|
+
|
201
|
+
balance_market_collateral = ContractERC20OnGnosisChain(
|
202
|
+
address=market_collateral_token
|
203
|
+
).balanceOf(for_address=api_keys.bet_from_address, web3=web3)
|
204
|
+
if balance_market_collateral < collateral_amount_wei:
|
205
|
+
logger.debug(
|
206
|
+
f"Not enough collateral token in the market. Expected {collateral_amount_wei} but got {balance_market_collateral}. Auto-depositing market collateral."
|
207
|
+
)
|
208
|
+
market_collateral_token_contract = to_gnosis_chain_contract(
|
209
|
+
init_collateral_token_contract(market_collateral_token, web3=web3)
|
210
|
+
)
|
211
|
+
auto_deposit_collateral_token(
|
212
|
+
market_collateral_token_contract, collateral_amount_wei, api_keys, web3
|
213
|
+
)
|
214
|
+
|
215
|
+
handle_allowance(
|
216
|
+
api_keys=api_keys,
|
217
|
+
sell_token=market_collateral_token,
|
218
|
+
amount_wei=collateral_amount_wei,
|
219
|
+
for_address=router.address,
|
220
|
+
web3=web3,
|
221
|
+
)
|
222
|
+
|
223
|
+
router.split_position(
|
224
|
+
api_keys=api_keys,
|
225
|
+
collateral_token=market_collateral_token,
|
226
|
+
market_id=Web3.to_checksum_address(market.id),
|
227
|
+
amount=collateral_amount_wei,
|
228
|
+
web3=web3,
|
229
|
+
)
|