prediction-market-agent-tooling 0.17.0__tar.gz → 0.19.0__tar.gz
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-0.17.0 → prediction_market_agent_tooling-0.19.0}/PKG-INFO +2 -1
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/config.py +32 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/agent.py +33 -4
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/markets.py +43 -1
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/omen.py +32 -16
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/omen_contracts.py +44 -32
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/omen_resolving.py +14 -4
- prediction_market_agent_tooling-0.19.0/prediction_market_agent_tooling/monitor/langfuse/langfuse_wrapper.py +26 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/monitor.py +9 -5
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/balances.py +8 -4
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/contract.py +17 -0
- prediction_market_agent_tooling-0.19.0/prediction_market_agent_tooling/tools/gnosis_rpc.py +6 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/web3_utils.py +64 -14
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/pyproject.toml +2 -1
- prediction_market_agent_tooling-0.17.0/prediction_market_agent_tooling/tools/gnosis_rpc.py +0 -24
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/README.md +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/erc20.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_dxdao.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_fpmm.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_fpmm_factory.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_kleros.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_oracle.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/omen_realitio.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/abis/wxdai.abi.json +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/benchmark/__init__.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/benchmark/agents.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/benchmark/benchmark.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/benchmark/utils.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/agent_example.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/constants.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/gcp/deploy.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/gcp/kubernetes_models.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/deploy/gcp/utils.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/gtypes.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/agent_market.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/categorize.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/data_models.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/manifold/__init__.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/manifold/api.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/manifold/data_models.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/manifold/manifold.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/manifold/utils.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/__init__.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/data_models.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/polymarket/api.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/polymarket/data_models.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/polymarket/data_models_web.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/polymarket/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/markets/polymarket/utils.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/markets/manifold.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/markets/omen.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/markets/polymarket.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/monitor_app.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/monitor/monitor_settings.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/py.typed +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/betting_strategies/kelly_criterion.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/betting_strategies/market_moving.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/betting_strategies/minimum_bet_to_win.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/betting_strategies/stretch_bet_between.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/cache.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/costs.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/google.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/hexbytes_custom.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/is_predictable.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/parallelism.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/safe.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/singleton.py +0 -0
- {prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/prediction_market_agent_tooling/tools/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: prediction-market-agent-tooling
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.19.0
|
4
4
|
Summary: Tools to benchmark, deploy and monitor prediction market agents.
|
5
5
|
Author: Gnosis
|
6
6
|
Requires-Python: >=3.10,<3.12
|
@@ -22,6 +22,7 @@ Requires-Dist: isort (>=5.13.2,<6.0.0)
|
|
22
22
|
Requires-Dist: langchain (>=0.1.9,<0.2.0) ; extra == "langchain"
|
23
23
|
Requires-Dist: langchain-community (>=0.0.19)
|
24
24
|
Requires-Dist: langchain-openai (>=0.0.5,<0.0.6) ; extra == "langchain"
|
25
|
+
Requires-Dist: langfuse (>=2.27.1,<3.0.0)
|
25
26
|
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
26
27
|
Requires-Dist: mech-client (>=0.2.13,<0.3.0)
|
27
28
|
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import typing as t
|
2
2
|
|
3
|
+
from gnosis.eth import EthereumClient
|
4
|
+
from gnosis.safe import Safe
|
3
5
|
from pydantic import BaseModel
|
4
6
|
from pydantic.types import SecretStr
|
5
7
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
@@ -30,6 +32,10 @@ class APIKeys(BaseSettings):
|
|
30
32
|
GOOGLE_SEARCH_API_KEY: t.Optional[SecretStr] = None
|
31
33
|
GOOGLE_SEARCH_ENGINE_ID: t.Optional[SecretStr] = None
|
32
34
|
|
35
|
+
LANGFUSE_SECRET_KEY: t.Optional[SecretStr] = None
|
36
|
+
LANGFUSE_PUBLIC_KEY: t.Optional[SecretStr] = None
|
37
|
+
LANGFUSE_HOST: t.Optional[str] = None
|
38
|
+
|
33
39
|
ENABLE_CACHE: bool = True
|
34
40
|
CACHE_DIR: str = "./.cache"
|
35
41
|
|
@@ -72,6 +78,24 @@ class APIKeys(BaseSettings):
|
|
72
78
|
"GOOGLE_SEARCH_ENGINE_ID missing in the environment.",
|
73
79
|
)
|
74
80
|
|
81
|
+
@property
|
82
|
+
def langfuse_secret_key(self) -> SecretStr:
|
83
|
+
return check_not_none(
|
84
|
+
self.LANGFUSE_SECRET_KEY, "LANGFUSE_SECRET_KEY missing in the environment."
|
85
|
+
)
|
86
|
+
|
87
|
+
@property
|
88
|
+
def langfuse_public_key(self) -> SecretStr:
|
89
|
+
return check_not_none(
|
90
|
+
self.LANGFUSE_PUBLIC_KEY, "LANGFUSE_PUBLIC_KEY missing in the environment."
|
91
|
+
)
|
92
|
+
|
93
|
+
@property
|
94
|
+
def langfuse_host(self) -> str:
|
95
|
+
return check_not_none(
|
96
|
+
self.LANGFUSE_HOST, "LANGFUSE_HOST missing in the environment."
|
97
|
+
)
|
98
|
+
|
75
99
|
def model_dump_public(self) -> dict[str, t.Any]:
|
76
100
|
return {
|
77
101
|
k: v
|
@@ -110,3 +134,11 @@ class PrivateCredentials(BaseModel):
|
|
110
134
|
private_key=api_keys.bet_from_private_key,
|
111
135
|
safe_address=api_keys.SAFE_ADDRESS,
|
112
136
|
)
|
137
|
+
|
138
|
+
def check_if_is_safe_owner(self, ethereum_client: EthereumClient) -> bool:
|
139
|
+
if not self.safe_address:
|
140
|
+
raise ValueError("Cannot check ownership if safe_address is not defined.")
|
141
|
+
|
142
|
+
s = Safe(self.safe_address, ethereum_client) # type: ignore[abstract]
|
143
|
+
public_key_from_signer = private_key_to_public_key(self.private_key)
|
144
|
+
return s.retrieve_is_owner(public_key_from_signer)
|
@@ -3,7 +3,7 @@ import os
|
|
3
3
|
import tempfile
|
4
4
|
import time
|
5
5
|
import typing as t
|
6
|
-
from datetime import datetime
|
6
|
+
from datetime import datetime, timedelta
|
7
7
|
|
8
8
|
from loguru import logger
|
9
9
|
from pydantic import BaseModel, BeforeValidator
|
@@ -30,13 +30,20 @@ from prediction_market_agent_tooling.markets.agent_market import (
|
|
30
30
|
SortBy,
|
31
31
|
)
|
32
32
|
from prediction_market_agent_tooling.markets.data_models import BetAmount
|
33
|
-
from prediction_market_agent_tooling.markets.markets import
|
33
|
+
from prediction_market_agent_tooling.markets.markets import (
|
34
|
+
MarketType,
|
35
|
+
have_bet_on_market_since,
|
36
|
+
)
|
34
37
|
from prediction_market_agent_tooling.markets.omen.omen import (
|
35
38
|
redeem_from_all_user_positions,
|
36
39
|
)
|
40
|
+
from prediction_market_agent_tooling.monitor.langfuse.langfuse_wrapper import (
|
41
|
+
LangfuseWrapper,
|
42
|
+
)
|
37
43
|
from prediction_market_agent_tooling.monitor.monitor_app import (
|
38
44
|
MARKET_TYPE_TO_DEPLOYED_AGENT,
|
39
45
|
)
|
46
|
+
from prediction_market_agent_tooling.tools.is_predictable import is_predictable_binary
|
40
47
|
from prediction_market_agent_tooling.tools.utils import DatetimeWithTimezone, utcnow
|
41
48
|
|
42
49
|
MAX_AVAILABLE_MARKETS = 20
|
@@ -77,7 +84,10 @@ class Answer(BaseModel):
|
|
77
84
|
|
78
85
|
|
79
86
|
class DeployableAgent:
|
87
|
+
bet_on_n_markets_per_run: int = 1
|
88
|
+
|
80
89
|
def __init__(self) -> None:
|
90
|
+
self.langfuse_wrapper = LangfuseWrapper(agent_name=self.__class__.__name__)
|
81
91
|
self.load()
|
82
92
|
|
83
93
|
def __init_subclass__(cls, **kwargs: t.Any) -> None:
|
@@ -89,11 +99,30 @@ class DeployableAgent:
|
|
89
99
|
def load(self) -> None:
|
90
100
|
pass
|
91
101
|
|
102
|
+
def have_bet_on_market_since(self, market: AgentMarket, since: timedelta) -> bool:
|
103
|
+
return have_bet_on_market_since(keys=APIKeys(), market=market, since=since)
|
104
|
+
|
92
105
|
def pick_markets(self, markets: t.Sequence[AgentMarket]) -> t.Sequence[AgentMarket]:
|
93
106
|
"""
|
94
|
-
|
107
|
+
Subclasses can implement their own logic instead of this one, or on top of this one.
|
108
|
+
By default, it picks only the first {n_markets_per_run} markets where user didn't bet recently and it's a reasonable question.
|
95
109
|
"""
|
96
|
-
|
110
|
+
picked: list[AgentMarket] = []
|
111
|
+
|
112
|
+
for market in markets:
|
113
|
+
if len(picked) >= self.bet_on_n_markets_per_run:
|
114
|
+
break
|
115
|
+
|
116
|
+
if self.have_bet_on_market_since(market, since=timedelta(hours=24)):
|
117
|
+
continue
|
118
|
+
|
119
|
+
# Do as a last check, as it uses paid OpenAI API.
|
120
|
+
if not is_predictable_binary(market.question):
|
121
|
+
continue
|
122
|
+
|
123
|
+
picked.append(market)
|
124
|
+
|
125
|
+
return picked
|
97
126
|
|
98
127
|
def answer_binary_market(self, market: AgentMarket) -> Answer | None:
|
99
128
|
"""
|
@@ -1,19 +1,29 @@
|
|
1
1
|
import typing as t
|
2
|
-
from datetime import datetime
|
2
|
+
from datetime import datetime, timedelta
|
3
3
|
from enum import Enum
|
4
4
|
|
5
|
+
from prediction_market_agent_tooling.config import APIKeys, PrivateCredentials
|
5
6
|
from prediction_market_agent_tooling.markets.agent_market import (
|
6
7
|
AgentMarket,
|
7
8
|
FilterBy,
|
8
9
|
SortBy,
|
9
10
|
)
|
11
|
+
from prediction_market_agent_tooling.markets.manifold.api import (
|
12
|
+
get_authenticated_user,
|
13
|
+
get_manifold_bets,
|
14
|
+
get_manifold_market,
|
15
|
+
)
|
10
16
|
from prediction_market_agent_tooling.markets.manifold.manifold import (
|
11
17
|
ManifoldAgentMarket,
|
12
18
|
)
|
13
19
|
from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket
|
20
|
+
from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
|
21
|
+
OmenSubgraphHandler,
|
22
|
+
)
|
14
23
|
from prediction_market_agent_tooling.markets.polymarket.polymarket import (
|
15
24
|
PolymarketAgentMarket,
|
16
25
|
)
|
26
|
+
from prediction_market_agent_tooling.tools.utils import should_not_happen, utcnow
|
17
27
|
|
18
28
|
|
19
29
|
class MarketType(str, Enum):
|
@@ -52,3 +62,35 @@ def get_binary_markets(
|
|
52
62
|
excluded_questions=excluded_questions,
|
53
63
|
)
|
54
64
|
return markets
|
65
|
+
|
66
|
+
|
67
|
+
def have_bet_on_market_since(
|
68
|
+
keys: APIKeys, market: AgentMarket, since: timedelta
|
69
|
+
) -> bool:
|
70
|
+
start_time = utcnow() - since
|
71
|
+
credentials = PrivateCredentials.from_api_keys(keys)
|
72
|
+
recently_betted_questions = (
|
73
|
+
set(
|
74
|
+
get_manifold_market(b.contractId).question
|
75
|
+
for b in get_manifold_bets(
|
76
|
+
user_id=get_authenticated_user(
|
77
|
+
keys.manifold_api_key.get_secret_value()
|
78
|
+
).id,
|
79
|
+
start_time=start_time,
|
80
|
+
end_time=None,
|
81
|
+
)
|
82
|
+
)
|
83
|
+
if isinstance(market, ManifoldAgentMarket)
|
84
|
+
else (
|
85
|
+
set(
|
86
|
+
b.title
|
87
|
+
for b in OmenSubgraphHandler().get_bets(
|
88
|
+
better_address=credentials.public_key,
|
89
|
+
start_time=start_time,
|
90
|
+
)
|
91
|
+
)
|
92
|
+
if isinstance(market, OmenAgentMarket)
|
93
|
+
else should_not_happen(f"Uknown market: {market}")
|
94
|
+
)
|
95
|
+
)
|
96
|
+
return market.question in recently_betted_questions
|
@@ -432,6 +432,7 @@ def omen_sell_outcome_tx(
|
|
432
432
|
market: OmenAgentMarket,
|
433
433
|
outcome: str,
|
434
434
|
auto_withdraw: bool,
|
435
|
+
web3: Web3 | None = None,
|
435
436
|
) -> None:
|
436
437
|
"""
|
437
438
|
Sells the given xDai value of shares corresponding to the given outcome in
|
@@ -447,7 +448,10 @@ def omen_sell_outcome_tx(
|
|
447
448
|
collateral_token = OmenCollateralTokenContract()
|
448
449
|
|
449
450
|
# Verify, that markets uses conditional tokens that we expect.
|
450
|
-
if
|
451
|
+
if (
|
452
|
+
market_contract.conditionalTokens(web3=web3)
|
453
|
+
!= conditional_token_contract.address
|
454
|
+
):
|
451
455
|
raise ValueError(
|
452
456
|
f"Market {market.id} uses conditional token that we didn't expect, {market_contract.conditionalTokens()} != {conditional_token_contract.address=}"
|
453
457
|
)
|
@@ -457,7 +461,7 @@ def omen_sell_outcome_tx(
|
|
457
461
|
|
458
462
|
# Calculate the amount of shares we will sell for the given selling amount of xdai.
|
459
463
|
max_outcome_tokens_to_sell = market_contract.calcSellAmount(
|
460
|
-
amount_wei, outcome_index
|
464
|
+
amount_wei, outcome_index, web3=web3
|
461
465
|
)
|
462
466
|
# Allow 1% slippage.
|
463
467
|
max_outcome_tokens_to_sell = add_fraction(max_outcome_tokens_to_sell, 0.01)
|
@@ -467,6 +471,7 @@ def omen_sell_outcome_tx(
|
|
467
471
|
private_credentials=private_credentials,
|
468
472
|
for_address=market_contract.address,
|
469
473
|
approve=True,
|
474
|
+
web3=web3,
|
470
475
|
)
|
471
476
|
# Sell the shares.
|
472
477
|
market_contract.sell(
|
@@ -474,12 +479,12 @@ def omen_sell_outcome_tx(
|
|
474
479
|
amount_wei,
|
475
480
|
outcome_index,
|
476
481
|
max_outcome_tokens_to_sell,
|
482
|
+
web3=web3,
|
477
483
|
)
|
478
484
|
if auto_withdraw:
|
479
485
|
# Optionally, withdraw from the collateral token back to the `from_address` wallet.
|
480
486
|
collateral_token.withdraw(
|
481
|
-
private_credentials=private_credentials,
|
482
|
-
amount_wei=amount_wei,
|
487
|
+
private_credentials=private_credentials, amount_wei=amount_wei, web3=web3
|
483
488
|
)
|
484
489
|
|
485
490
|
|
@@ -489,6 +494,7 @@ def binary_omen_sell_outcome_tx(
|
|
489
494
|
market: OmenAgentMarket,
|
490
495
|
binary_outcome: bool,
|
491
496
|
auto_withdraw: bool,
|
497
|
+
web3: Web3 | None = None,
|
492
498
|
) -> None:
|
493
499
|
omen_sell_outcome_tx(
|
494
500
|
private_credentials=private_credentials,
|
@@ -496,6 +502,7 @@ def binary_omen_sell_outcome_tx(
|
|
496
502
|
market=market,
|
497
503
|
outcome=OMEN_TRUE_OUTCOME if binary_outcome else OMEN_FALSE_OUTCOME,
|
498
504
|
auto_withdraw=auto_withdraw,
|
505
|
+
web3=web3,
|
499
506
|
)
|
500
507
|
|
501
508
|
|
@@ -509,6 +516,7 @@ def omen_create_market_tx(
|
|
509
516
|
outcomes: list[str],
|
510
517
|
auto_deposit: bool,
|
511
518
|
fee: float = OMEN_DEFAULT_MARKET_FEE,
|
519
|
+
web3: Web3 | None = None,
|
512
520
|
) -> ChecksumAddress:
|
513
521
|
"""
|
514
522
|
Based on omen-exchange TypeScript code: https://github.com/protofire/omen-exchange/blob/b0b9a3e71b415d6becf21fe428e1c4fc0dad2e80/app/src/services/cpk/cpk.ts#L308
|
@@ -539,19 +547,22 @@ def omen_create_market_tx(
|
|
539
547
|
private_credentials=private_credentials,
|
540
548
|
for_address=factory_contract.address,
|
541
549
|
amount_wei=initial_funds_wei,
|
550
|
+
web3=web3,
|
542
551
|
)
|
543
552
|
|
544
553
|
# Deposit xDai to the collateral token,
|
545
554
|
# this can be skipped, if we know we already have enough collateral tokens.
|
546
555
|
collateral_token_balance = collateral_token_contract.balanceOf(
|
547
|
-
for_address=from_address,
|
556
|
+
for_address=from_address, web3=web3
|
548
557
|
)
|
549
558
|
if (
|
550
559
|
auto_deposit
|
551
560
|
and initial_funds_wei > 0
|
552
561
|
and collateral_token_balance < initial_funds_wei
|
553
562
|
):
|
554
|
-
collateral_token_contract.deposit(
|
563
|
+
collateral_token_contract.deposit(
|
564
|
+
private_credentials, initial_funds_wei, web3=web3
|
565
|
+
)
|
555
566
|
|
556
567
|
# Create the question on Realitio.
|
557
568
|
question_id = realitio_contract.askQuestion(
|
@@ -562,6 +573,7 @@ def omen_create_market_tx(
|
|
562
573
|
language=language,
|
563
574
|
arbitrator=Arbitrator.KLEROS,
|
564
575
|
opening=closing_time, # The question is opened at the closing time of the market.
|
576
|
+
web3=web3,
|
565
577
|
)
|
566
578
|
|
567
579
|
# Construct the condition id.
|
@@ -569,13 +581,15 @@ def omen_create_market_tx(
|
|
569
581
|
question_id=question_id,
|
570
582
|
oracle_address=oracle_contract.address,
|
571
583
|
outcomes_slot_count=len(outcomes),
|
584
|
+
web3=web3,
|
572
585
|
)
|
573
|
-
if not conditional_token_contract.does_condition_exists(condition_id):
|
586
|
+
if not conditional_token_contract.does_condition_exists(condition_id, web3=web3):
|
574
587
|
conditional_token_contract.prepareCondition(
|
575
588
|
private_credentials=private_credentials,
|
576
589
|
question_id=question_id,
|
577
590
|
oracle_address=oracle_contract.address,
|
578
591
|
outcomes_slot_count=len(outcomes),
|
592
|
+
web3=web3,
|
579
593
|
)
|
580
594
|
|
581
595
|
# Create the market.
|
@@ -584,6 +598,7 @@ def omen_create_market_tx(
|
|
584
598
|
condition_id=condition_id,
|
585
599
|
fee=fee,
|
586
600
|
initial_funds_wei=initial_funds_wei,
|
601
|
+
web3=web3,
|
587
602
|
)
|
588
603
|
|
589
604
|
# Note: In the Omen's Typescript code, there is futher a creation of `stakingRewardsFactoryAddress`,
|
@@ -602,6 +617,7 @@ def omen_fund_market_tx(
|
|
602
617
|
market: OmenAgentMarket,
|
603
618
|
funds: Wei,
|
604
619
|
auto_deposit: bool,
|
620
|
+
web3: Web3 | None = None,
|
605
621
|
) -> None:
|
606
622
|
from_address = private_credentials.public_key
|
607
623
|
market_contract = market.get_contract()
|
@@ -611,20 +627,19 @@ def omen_fund_market_tx(
|
|
611
627
|
# this can be skipped, if we know we already have enough collateral tokens.
|
612
628
|
if (
|
613
629
|
auto_deposit
|
614
|
-
and collateral_token_contract.balanceOf(
|
615
|
-
for_address=from_address,
|
616
|
-
)
|
630
|
+
and collateral_token_contract.balanceOf(for_address=from_address, web3=web3)
|
617
631
|
< funds
|
618
632
|
):
|
619
|
-
collateral_token_contract.deposit(private_credentials, funds)
|
633
|
+
collateral_token_contract.deposit(private_credentials, funds, web3=web3)
|
620
634
|
|
621
635
|
collateral_token_contract.approve(
|
622
636
|
private_credentials=private_credentials,
|
623
637
|
for_address=market_contract.address,
|
624
638
|
amount_wei=funds,
|
639
|
+
web3=web3,
|
625
640
|
)
|
626
641
|
|
627
|
-
market_contract.addFunding(private_credentials, funds)
|
642
|
+
market_contract.addFunding(private_credentials, funds, web3=web3)
|
628
643
|
|
629
644
|
|
630
645
|
def build_parent_collection_id() -> HexStr:
|
@@ -727,7 +742,7 @@ def omen_remove_fund_market_tx(
|
|
727
742
|
"""
|
728
743
|
from_address = private_credentials.public_key
|
729
744
|
market_contract = market.get_contract()
|
730
|
-
original_balances = get_balances(from_address)
|
745
|
+
original_balances = get_balances(from_address, web3=web3)
|
731
746
|
|
732
747
|
total_shares = market_contract.balanceOf(from_address, web3=web3)
|
733
748
|
if total_shares == 0:
|
@@ -763,7 +778,8 @@ def omen_remove_fund_market_tx(
|
|
763
778
|
amount=amount_to_merge,
|
764
779
|
web3=web3,
|
765
780
|
)
|
766
|
-
|
781
|
+
|
782
|
+
new_balances = get_balances(from_address, web3)
|
767
783
|
|
768
784
|
logger.debug(f"Result from merge positions {result}")
|
769
785
|
logger.info(
|
@@ -800,7 +816,7 @@ def redeem_from_all_user_positions(
|
|
800
816
|
f"[{index+1} / {len(user_positions)}] Processing redeem from {user_position.id=}."
|
801
817
|
)
|
802
818
|
|
803
|
-
original_balances = get_balances(public_key)
|
819
|
+
original_balances = get_balances(public_key, web3)
|
804
820
|
conditional_token_contract.redeemPositions(
|
805
821
|
private_credentials=private_credentials,
|
806
822
|
collateral_token_address=user_position.position.collateral_token_contract_address_checksummed,
|
@@ -809,7 +825,7 @@ def redeem_from_all_user_positions(
|
|
809
825
|
index_sets=user_position.position.indexSets,
|
810
826
|
web3=web3,
|
811
827
|
)
|
812
|
-
new_balances = get_balances(public_key)
|
828
|
+
new_balances = get_balances(public_key, web3)
|
813
829
|
|
814
830
|
logger.info(
|
815
831
|
f"Redeemed {new_balances.wxdai - original_balances.wxdai} wxDai from position {user_position.id=}."
|
@@ -56,6 +56,7 @@ class OmenOracleContract(ContractOnGnosisChain):
|
|
56
56
|
template_id: int,
|
57
57
|
question_raw: str,
|
58
58
|
n_outcomes: int,
|
59
|
+
web3: Web3 | None = None,
|
59
60
|
) -> TxReceipt:
|
60
61
|
return self.send(
|
61
62
|
private_credentials=private_credentials,
|
@@ -66,6 +67,7 @@ class OmenOracleContract(ContractOnGnosisChain):
|
|
66
67
|
question=question_raw,
|
67
68
|
numOutcomes=n_outcomes,
|
68
69
|
),
|
70
|
+
web3=web3,
|
69
71
|
)
|
70
72
|
|
71
73
|
|
@@ -86,11 +88,13 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
86
88
|
question_id: HexBytes,
|
87
89
|
oracle_address: ChecksumAddress,
|
88
90
|
outcomes_slot_count: int,
|
91
|
+
web3: Web3 | None = None,
|
89
92
|
) -> HexBytes:
|
90
93
|
id_ = HexBytes(
|
91
94
|
self.call(
|
92
95
|
"getConditionId",
|
93
96
|
[oracle_address, question_id, outcomes_slot_count],
|
97
|
+
web3=web3,
|
94
98
|
)
|
95
99
|
)
|
96
100
|
return id_
|
@@ -177,34 +181,29 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
177
181
|
)
|
178
182
|
|
179
183
|
def getOutcomeSlotCount(
|
180
|
-
self,
|
181
|
-
condition_id: HexBytes,
|
184
|
+
self, condition_id: HexBytes, web3: Web3 | None = None
|
182
185
|
) -> int:
|
183
|
-
count: int = self.call(
|
184
|
-
"getOutcomeSlotCount",
|
185
|
-
[condition_id],
|
186
|
-
)
|
186
|
+
count: int = self.call("getOutcomeSlotCount", [condition_id], web3=web3)
|
187
187
|
return count
|
188
188
|
|
189
189
|
def does_condition_exists(
|
190
|
-
self,
|
191
|
-
condition_id: HexBytes,
|
190
|
+
self, condition_id: HexBytes, web3: Web3 | None = None
|
192
191
|
) -> bool:
|
193
|
-
return self.getOutcomeSlotCount(condition_id) > 0
|
192
|
+
return self.getOutcomeSlotCount(condition_id, web3=web3) > 0
|
194
193
|
|
195
194
|
def is_condition_resolved(
|
196
|
-
self,
|
197
|
-
condition_id: HexBytes,
|
195
|
+
self, condition_id: HexBytes, web3: Web3 | None = None
|
198
196
|
) -> bool:
|
199
197
|
# from ConditionalTokens.redeemPositions:
|
200
198
|
# uint den = payoutDenominator[conditionId]; require(den > 0, "result for condition not received yet");
|
201
|
-
payout_for_condition = self.payoutDenominator(condition_id)
|
199
|
+
payout_for_condition = self.payoutDenominator(condition_id, web3=web3)
|
202
200
|
return payout_for_condition > 0
|
203
201
|
|
204
|
-
def payoutDenominator(
|
202
|
+
def payoutDenominator(
|
203
|
+
self, condition_id: HexBytes, web3: Web3 | None = None
|
204
|
+
) -> int:
|
205
205
|
payoutForCondition: int = self.call(
|
206
|
-
"payoutDenominator",
|
207
|
-
[condition_id],
|
206
|
+
"payoutDenominator", [condition_id], web3=web3
|
208
207
|
)
|
209
208
|
return payoutForCondition
|
210
209
|
|
@@ -214,6 +213,7 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
214
213
|
for_address: ChecksumAddress,
|
215
214
|
approve: bool,
|
216
215
|
tx_params: t.Optional[TxParams] = None,
|
216
|
+
web3: Web3 | None = None,
|
217
217
|
) -> TxReceipt:
|
218
218
|
return self.send(
|
219
219
|
private_credentials=private_credentials,
|
@@ -223,6 +223,7 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
223
223
|
approve,
|
224
224
|
],
|
225
225
|
tx_params=tx_params,
|
226
|
+
web3=web3,
|
226
227
|
)
|
227
228
|
|
228
229
|
def prepareCondition(
|
@@ -232,6 +233,7 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
232
233
|
question_id: HexBytes,
|
233
234
|
outcomes_slot_count: int,
|
234
235
|
tx_params: t.Optional[TxParams] = None,
|
236
|
+
web3: Web3 | None = None,
|
235
237
|
) -> TxReceipt:
|
236
238
|
return self.send(
|
237
239
|
private_credentials=private_credentials,
|
@@ -242,6 +244,7 @@ class OmenConditionalTokenContract(ContractOnGnosisChain):
|
|
242
244
|
outcomes_slot_count,
|
243
245
|
],
|
244
246
|
tx_params=tx_params,
|
247
|
+
web3=web3,
|
245
248
|
)
|
246
249
|
|
247
250
|
|
@@ -272,25 +275,18 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
272
275
|
return calculated_shares
|
273
276
|
|
274
277
|
def calcSellAmount(
|
275
|
-
self,
|
276
|
-
return_amount: Wei,
|
277
|
-
outcome_index: int,
|
278
|
+
self, return_amount: Wei, outcome_index: int, web3: Web3 | None = None
|
278
279
|
) -> OmenOutcomeToken:
|
279
280
|
"""
|
280
281
|
Returns amount of shares we will sell for the requested wei.
|
281
282
|
"""
|
282
283
|
calculated_shares: OmenOutcomeToken = self.call(
|
283
|
-
"calcSellAmount",
|
284
|
-
[return_amount, outcome_index],
|
284
|
+
"calcSellAmount", [return_amount, outcome_index], web3=web3
|
285
285
|
)
|
286
286
|
return calculated_shares
|
287
287
|
|
288
|
-
def conditionalTokens(
|
289
|
-
self,
|
290
|
-
) -> HexAddress:
|
291
|
-
address: HexAddress = self.call(
|
292
|
-
"conditionalTokens",
|
293
|
-
)
|
288
|
+
def conditionalTokens(self, web3: Web3 | None = None) -> HexAddress:
|
289
|
+
address: HexAddress = self.call("conditionalTokens", web3=web3)
|
294
290
|
return address
|
295
291
|
|
296
292
|
def buy(
|
@@ -321,6 +317,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
321
317
|
outcome_index: int,
|
322
318
|
max_outcome_tokens_to_sell: OmenOutcomeToken,
|
323
319
|
tx_params: t.Optional[TxParams] = None,
|
320
|
+
web3: Web3 | None = None,
|
324
321
|
) -> TxReceipt:
|
325
322
|
return self.send(
|
326
323
|
private_credentials=private_credentials,
|
@@ -331,6 +328,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
331
328
|
max_outcome_tokens_to_sell,
|
332
329
|
],
|
333
330
|
tx_params=tx_params,
|
331
|
+
web3=web3,
|
334
332
|
)
|
335
333
|
|
336
334
|
def addFunding(
|
@@ -338,6 +336,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
338
336
|
private_credentials: PrivateCredentials,
|
339
337
|
add_funding: Wei,
|
340
338
|
tx_params: t.Optional[TxParams] = None,
|
339
|
+
web3: Web3 | None = None,
|
341
340
|
) -> TxReceipt:
|
342
341
|
"""
|
343
342
|
Funding is added in Weis (xDai) and then converted to shares.
|
@@ -349,6 +348,7 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
349
348
|
function_name="addFunding",
|
350
349
|
function_params=[add_funding, distribution_hint],
|
351
350
|
tx_params=tx_params,
|
351
|
+
web3=web3,
|
352
352
|
)
|
353
353
|
|
354
354
|
def removeFunding(
|
@@ -369,9 +369,9 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
|
|
369
369
|
web3=web3,
|
370
370
|
)
|
371
371
|
|
372
|
-
def totalSupply(self) -> Wei:
|
372
|
+
def totalSupply(self, web3: Web3 | None = None) -> Wei:
|
373
373
|
# This is the liquidity you seen on the Omen website (but in Wei).
|
374
|
-
total_supply: Wei = self.call("totalSupply")
|
374
|
+
total_supply: Wei = self.call("totalSupply", web3=web3)
|
375
375
|
return total_supply
|
376
376
|
|
377
377
|
|
@@ -412,6 +412,7 @@ class OmenFixedProductMarketMakerFactoryContract(ContractOnGnosisChain):
|
|
412
412
|
initial_funds_wei: Wei,
|
413
413
|
fee: float = OMEN_DEFAULT_MARKET_FEE,
|
414
414
|
tx_params: t.Optional[TxParams] = None,
|
415
|
+
web3: Web3 | None = None,
|
415
416
|
) -> TxReceipt:
|
416
417
|
fee_wei = xdai_to_wei(
|
417
418
|
xdai_type(fee)
|
@@ -431,6 +432,7 @@ class OmenFixedProductMarketMakerFactoryContract(ContractOnGnosisChain):
|
|
431
432
|
distributionHint=[],
|
432
433
|
),
|
433
434
|
tx_params=tx_params,
|
435
|
+
web3=web3,
|
434
436
|
)
|
435
437
|
|
436
438
|
|
@@ -498,6 +500,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
498
500
|
opening: datetime,
|
499
501
|
nonce: int | None = None,
|
500
502
|
tx_params: t.Optional[TxParams] = None,
|
503
|
+
web3: Web3 | None = None,
|
501
504
|
) -> HexBytes:
|
502
505
|
"""
|
503
506
|
After the question is created, you can find it at https://reality.eth.link/app/#!/creator/{from_address}.
|
@@ -528,6 +531,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
528
531
|
), # Two equal questions need to have different nonces.
|
529
532
|
),
|
530
533
|
tx_params=tx_params,
|
534
|
+
web3=web3,
|
531
535
|
)
|
532
536
|
question_id = HexBytes(
|
533
537
|
receipt_tx["logs"][0]["topics"][1]
|
@@ -542,6 +546,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
542
546
|
outcomes: list[str],
|
543
547
|
bond: Wei,
|
544
548
|
max_previous: Wei | None = None,
|
549
|
+
web3: Web3 | None = None,
|
545
550
|
) -> TxReceipt:
|
546
551
|
if max_previous is None:
|
547
552
|
# If not provided, defaults to 0, which means no checking,
|
@@ -563,6 +568,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
563
568
|
max_previous=max_previous,
|
564
569
|
),
|
565
570
|
amount_wei=bond,
|
571
|
+
web3=web3,
|
566
572
|
)
|
567
573
|
|
568
574
|
def claimWinnings(
|
@@ -574,6 +580,7 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
574
580
|
bonds: list[Wei],
|
575
581
|
answers: list[HexBytes],
|
576
582
|
tx_params: t.Optional[TxParams] = None,
|
583
|
+
web3: Web3 | None = None,
|
577
584
|
) -> TxReceipt:
|
578
585
|
return self.send(
|
579
586
|
private_credentials=private_credentials,
|
@@ -586,17 +593,22 @@ class OmenRealitioContract(ContractOnGnosisChain):
|
|
586
593
|
answers=answers,
|
587
594
|
),
|
588
595
|
tx_params=tx_params,
|
596
|
+
web3=web3,
|
589
597
|
)
|
590
598
|
|
591
|
-
def balanceOf(
|
592
|
-
|
599
|
+
def balanceOf(
|
600
|
+
self,
|
601
|
+
from_address: ChecksumAddress,
|
602
|
+
web3: Web3 | None = None,
|
603
|
+
) -> Wei:
|
604
|
+
balance = wei_type(self.call("balanceOf", [from_address], web3=web3))
|
593
605
|
return balance
|
594
606
|
|
595
607
|
def withdraw(
|
596
608
|
self,
|
597
609
|
private_credentials: PrivateCredentials,
|
610
|
+
web3: Web3 | None = None,
|
598
611
|
) -> TxReceipt:
|
599
612
|
return self.send(
|
600
|
-
private_credentials=private_credentials,
|
601
|
-
function_name="withdraw",
|
613
|
+
private_credentials=private_credentials, function_name="withdraw", web3=web3
|
602
614
|
)
|
@@ -38,6 +38,7 @@ def claim_bonds_on_realitio_questions(
|
|
38
38
|
private_credentials: PrivateCredentials,
|
39
39
|
questions: list[RealityQuestion],
|
40
40
|
auto_withdraw: bool,
|
41
|
+
web3: Web3 | None = None,
|
41
42
|
) -> list[HexBytes]:
|
42
43
|
claimed_questions: list[HexBytes] = []
|
43
44
|
|
@@ -46,7 +47,7 @@ def claim_bonds_on_realitio_questions(
|
|
46
47
|
f"[{idx+1} / {len(questions)}] Claiming bond for {question.questionId=} {question.url=}"
|
47
48
|
)
|
48
49
|
claim_bonds_on_realitio_question(
|
49
|
-
private_credentials, question, auto_withdraw=auto_withdraw
|
50
|
+
private_credentials, question, auto_withdraw=auto_withdraw, web3=web3
|
50
51
|
)
|
51
52
|
claimed_questions.append(question.questionId)
|
52
53
|
|
@@ -57,6 +58,7 @@ def claim_bonds_on_realitio_question(
|
|
57
58
|
private_credentials: PrivateCredentials,
|
58
59
|
question: RealityQuestion,
|
59
60
|
auto_withdraw: bool,
|
61
|
+
web3: Web3 | None = None,
|
60
62
|
) -> None:
|
61
63
|
public_key = private_credentials.public_key
|
62
64
|
realitio_contract = OmenRealitioContract()
|
@@ -109,18 +111,20 @@ def claim_bonds_on_realitio_question(
|
|
109
111
|
addresses=addresses,
|
110
112
|
bonds=bonds,
|
111
113
|
answers=answers,
|
114
|
+
web3=web3,
|
112
115
|
)
|
113
116
|
|
114
|
-
current_balance = realitio_contract.balanceOf(public_key)
|
117
|
+
current_balance = realitio_contract.balanceOf(public_key, web3=web3)
|
115
118
|
# Keeping balance on Realitio is not useful, so it's recommended to just withdraw it.
|
116
119
|
if current_balance > 0 and auto_withdraw:
|
117
120
|
logger.info(f"Withdrawing remaining balance {current_balance=}")
|
118
|
-
realitio_contract.withdraw(private_credentials)
|
121
|
+
realitio_contract.withdraw(private_credentials, web3=web3)
|
119
122
|
|
120
123
|
|
121
124
|
def finalize_markets(
|
122
125
|
private_credentials: PrivateCredentials,
|
123
126
|
markets_with_resolutions: list[tuple[OmenMarket, Resolution | None]],
|
127
|
+
web3: Web3 | None = None,
|
124
128
|
) -> list[HexAddress]:
|
125
129
|
finalized_markets: list[HexAddress] = []
|
126
130
|
|
@@ -139,6 +143,7 @@ def finalize_markets(
|
|
139
143
|
market,
|
140
144
|
resolution,
|
141
145
|
OMEN_DEFAULT_REALITIO_BOND_VALUE,
|
146
|
+
web3=web3,
|
142
147
|
)
|
143
148
|
finalized_markets.append(market.id)
|
144
149
|
logger.info(f"Finalized {market.url=}")
|
@@ -152,6 +157,7 @@ def finalize_markets(
|
|
152
157
|
def resolve_markets(
|
153
158
|
private_credentials: PrivateCredentials,
|
154
159
|
markets: list[OmenMarket],
|
160
|
+
web3: Web3 | None = None,
|
155
161
|
) -> list[HexAddress]:
|
156
162
|
resolved_markets: list[HexAddress] = []
|
157
163
|
|
@@ -159,7 +165,7 @@ def resolve_markets(
|
|
159
165
|
logger.info(
|
160
166
|
f"[{idx+1} / {len(markets)}] Resolving {market.url=} {market.question_title=}"
|
161
167
|
)
|
162
|
-
omen_resolve_market_tx(private_credentials, market)
|
168
|
+
omen_resolve_market_tx(private_credentials, market, web3=web3)
|
163
169
|
resolved_markets.append(market.id)
|
164
170
|
|
165
171
|
return resolved_markets
|
@@ -170,6 +176,7 @@ def omen_submit_answer_market_tx(
|
|
170
176
|
market: OmenMarket,
|
171
177
|
resolution: Resolution,
|
172
178
|
bond: xDai,
|
179
|
+
web3: Web3 | None = None,
|
173
180
|
) -> None:
|
174
181
|
"""
|
175
182
|
After the answer is submitted, there is 24h waiting period where the answer can be challenged by others.
|
@@ -182,12 +189,14 @@ def omen_submit_answer_market_tx(
|
|
182
189
|
answer=resolution.value,
|
183
190
|
outcomes=market.question.outcomes,
|
184
191
|
bond=xdai_to_wei(bond),
|
192
|
+
web3=web3,
|
185
193
|
)
|
186
194
|
|
187
195
|
|
188
196
|
def omen_resolve_market_tx(
|
189
197
|
private_credentials: PrivateCredentials,
|
190
198
|
market: OmenMarket,
|
199
|
+
web3: Web3 | None = None,
|
191
200
|
) -> None:
|
192
201
|
"""
|
193
202
|
Market can be resolved 24h after last answer was submitted via `omen_submit_answer_market_tx`.
|
@@ -199,6 +208,7 @@ def omen_resolve_market_tx(
|
|
199
208
|
template_id=market.question.templateId,
|
200
209
|
question_raw=market.question.question_raw,
|
201
210
|
n_outcomes=market.question.n_outcomes,
|
211
|
+
web3=web3,
|
202
212
|
)
|
203
213
|
|
204
214
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from functools import cached_property
|
2
|
+
|
3
|
+
from langfuse.callback import CallbackHandler
|
4
|
+
from pydantic import BaseModel, computed_field
|
5
|
+
|
6
|
+
from prediction_market_agent_tooling.config import APIKeys
|
7
|
+
from prediction_market_agent_tooling.tools.utils import utcnow
|
8
|
+
|
9
|
+
|
10
|
+
class LangfuseWrapper(BaseModel):
|
11
|
+
agent_name: str
|
12
|
+
|
13
|
+
@computed_field # type: ignore[misc] # Mypy issue: https://github.com/python/mypy/issues/14461
|
14
|
+
@cached_property
|
15
|
+
def session_id(self) -> str:
|
16
|
+
return f"{self.agent_name} - {utcnow()}"
|
17
|
+
|
18
|
+
def get_langfuse_handler(self) -> CallbackHandler:
|
19
|
+
keys = APIKeys()
|
20
|
+
langfuse_handler = CallbackHandler(
|
21
|
+
secret_key=keys.langfuse_secret_key.get_secret_value(),
|
22
|
+
public_key=keys.langfuse_public_key.get_secret_value(),
|
23
|
+
host=keys.langfuse_host,
|
24
|
+
session_id=self.session_id,
|
25
|
+
)
|
26
|
+
return langfuse_handler
|
@@ -258,27 +258,31 @@ def monitor_brier_score(resolved_markets: t.Sequence[AgentMarket]) -> None:
|
|
258
258
|
- the overall brier score
|
259
259
|
- the brier score for the last 30 markets
|
260
260
|
"""
|
261
|
-
st.subheader("Brier Score (0-
|
261
|
+
st.subheader("Brier Score (0-2, lower is better)")
|
262
262
|
|
263
263
|
# We need to use `get_last_trade_p_yes` instead of `current_p_yes` because, for resolved markets, the probabilities can be fixed to 0 and 1 (for example, on Omen).
|
264
264
|
# And for the brier score, we need the true market prediction, not its resolution after the outcome is known.
|
265
265
|
# If no trades were made, take it as 0.5 because the platform didn't provide any valuable information.
|
266
|
-
|
266
|
+
created_time_and_squared_errors_summed_across_outcomes = par_map(
|
267
267
|
list(resolved_markets),
|
268
268
|
lambda m: (
|
269
269
|
m.created_time,
|
270
270
|
(
|
271
271
|
(p_yes - m.boolean_outcome) ** 2
|
272
|
+
+ ((1 - p_yes) - (1 - m.boolean_outcome)) ** 2
|
272
273
|
if (p_yes := m.get_last_trade_p_yes()) is not None
|
273
274
|
else None
|
274
275
|
),
|
275
276
|
),
|
276
277
|
)
|
277
|
-
|
278
|
-
x
|
278
|
+
created_time_and_squared_errors_summed_across_outcomes_with_trades = [
|
279
|
+
x
|
280
|
+
for x in created_time_and_squared_errors_summed_across_outcomes
|
281
|
+
if x[1] is not None
|
279
282
|
]
|
280
283
|
df = pd.DataFrame(
|
281
|
-
|
284
|
+
created_time_and_squared_errors_summed_across_outcomes_with_trades,
|
285
|
+
columns=["Date", "Squared Error"],
|
282
286
|
).sort_values(by="Date")
|
283
287
|
|
284
288
|
# Compute rolling mean squared error for last 30 markets
|
@@ -1,10 +1,11 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
|
+
from web3 import Web3
|
3
|
+
from web3.types import Wei
|
2
4
|
|
3
5
|
from prediction_market_agent_tooling.gtypes import ChecksumAddress, xDai
|
4
6
|
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
|
5
7
|
WrappedxDaiContract,
|
6
8
|
)
|
7
|
-
from prediction_market_agent_tooling.tools.gnosis_rpc import get_balance
|
8
9
|
from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
|
9
10
|
|
10
11
|
|
@@ -13,7 +14,10 @@ class Balances(BaseModel):
|
|
13
14
|
wxdai: xDai
|
14
15
|
|
15
16
|
|
16
|
-
def get_balances(address: ChecksumAddress) -> Balances:
|
17
|
-
|
18
|
-
|
17
|
+
def get_balances(address: ChecksumAddress, web3: Web3 | None) -> Balances:
|
18
|
+
if not web3:
|
19
|
+
web3 = WrappedxDaiContract().get_web3()
|
20
|
+
xdai_balance = Wei(web3.eth.get_balance(address))
|
21
|
+
xdai = wei_to_xdai(xdai_balance)
|
22
|
+
wxdai = wei_to_xdai(WrappedxDaiContract().balanceOf(address, web3=web3))
|
19
23
|
return Balances(xdai=xdai, wxdai=wxdai)
|
@@ -190,6 +190,23 @@ class ContractERC20BaseClass(ContractBaseClass):
|
|
190
190
|
web3=web3,
|
191
191
|
)
|
192
192
|
|
193
|
+
def transferFrom(
|
194
|
+
self,
|
195
|
+
private_credentials: PrivateCredentials,
|
196
|
+
sender: ChecksumAddress,
|
197
|
+
recipient: ChecksumAddress,
|
198
|
+
amount_wei: Wei,
|
199
|
+
tx_params: t.Optional[TxParams] = None,
|
200
|
+
web3: Web3 | None = None,
|
201
|
+
) -> TxReceipt:
|
202
|
+
return self.send(
|
203
|
+
private_credentials=private_credentials,
|
204
|
+
function_name="transferFrom",
|
205
|
+
function_params=[sender, recipient, amount_wei],
|
206
|
+
tx_params=tx_params,
|
207
|
+
web3=web3,
|
208
|
+
)
|
209
|
+
|
193
210
|
def withdraw(
|
194
211
|
self,
|
195
212
|
private_credentials: PrivateCredentials,
|
@@ -116,8 +116,20 @@ def prepare_tx(
|
|
116
116
|
function_params: Optional[list[Any] | dict[str, Any]] = None,
|
117
117
|
tx_params: Optional[TxParams] = None,
|
118
118
|
) -> TxParams:
|
119
|
+
tx_params_new = _prepare_tx_params(web3, from_address, tx_params)
|
119
120
|
contract = web3.eth.contract(address=contract_address, abi=contract_abi)
|
120
121
|
|
122
|
+
# Build the transaction.
|
123
|
+
function_call = contract.functions[function_name](*parse_function_params(function_params)) # type: ignore # TODO: Fix Mypy, as this works just OK.
|
124
|
+
tx_params_new = function_call.build_transaction(tx_params_new)
|
125
|
+
return tx_params_new
|
126
|
+
|
127
|
+
|
128
|
+
def _prepare_tx_params(
|
129
|
+
web3: Web3,
|
130
|
+
from_address: ChecksumAddress | None,
|
131
|
+
tx_params: Optional[TxParams] = None,
|
132
|
+
) -> TxParams:
|
121
133
|
# Fill in required defaults, if not provided.
|
122
134
|
tx_params_new = TxParams()
|
123
135
|
if tx_params:
|
@@ -135,11 +147,6 @@ def prepare_tx(
|
|
135
147
|
from_checksummed = Web3.to_checksum_address(tx_params_new["from"])
|
136
148
|
tx_params_new["nonce"] = web3.eth.get_transaction_count(from_checksummed)
|
137
149
|
|
138
|
-
# Build the transaction.
|
139
|
-
function_call = contract.functions[function_name](*parse_function_params(function_params)) # type: ignore # TODO: Fix Mypy, as this works just OK.
|
140
|
-
tx_params_new = function_call.build_transaction(tx_params_new)
|
141
|
-
gas = web3.eth.estimate_gas(tx_params_new)
|
142
|
-
tx_params_new["gas"] = gas
|
143
150
|
return tx_params_new
|
144
151
|
|
145
152
|
|
@@ -176,16 +183,10 @@ def send_function_on_contract_tx(
|
|
176
183
|
function_params=function_params,
|
177
184
|
tx_params=tx_params,
|
178
185
|
)
|
179
|
-
|
180
|
-
|
181
|
-
tx_params,
|
186
|
+
|
187
|
+
receipt_tx = sign_send_and_get_receipt_tx(
|
188
|
+
web3, tx_params, from_private_key, timeout
|
182
189
|
)
|
183
|
-
# Send the signed transaction.
|
184
|
-
send_tx = web3.eth.send_raw_transaction(signed_tx.rawTransaction)
|
185
|
-
# And wait for the receipt.
|
186
|
-
receipt_tx = web3.eth.wait_for_transaction_receipt(send_tx, timeout=timeout)
|
187
|
-
# Verify it didn't fail.
|
188
|
-
check_tx_receipt(receipt_tx)
|
189
190
|
return receipt_tx
|
190
191
|
|
191
192
|
|
@@ -237,3 +238,52 @@ def send_function_on_contract_tx_using_safe(
|
|
237
238
|
receipt_tx = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout)
|
238
239
|
check_tx_receipt(receipt_tx)
|
239
240
|
return receipt_tx
|
241
|
+
|
242
|
+
|
243
|
+
def sign_send_and_get_receipt_tx(
|
244
|
+
web3: Web3,
|
245
|
+
tx_params_new: TxParams,
|
246
|
+
from_private_key: PrivateKey,
|
247
|
+
timeout: int = 180,
|
248
|
+
) -> TxReceipt:
|
249
|
+
# Sign with the private key.
|
250
|
+
signed_tx = web3.eth.account.sign_transaction(
|
251
|
+
tx_params_new, private_key=from_private_key.get_secret_value()
|
252
|
+
)
|
253
|
+
# Send the signed transaction.
|
254
|
+
send_tx = web3.eth.send_raw_transaction(signed_tx.rawTransaction)
|
255
|
+
# And wait for the receipt.
|
256
|
+
receipt_tx = web3.eth.wait_for_transaction_receipt(send_tx, timeout=timeout)
|
257
|
+
# Verify it didn't fail.
|
258
|
+
check_tx_receipt(receipt_tx)
|
259
|
+
return receipt_tx
|
260
|
+
|
261
|
+
|
262
|
+
def send_xdai_to(
|
263
|
+
web3: Web3,
|
264
|
+
from_private_key: PrivateKey,
|
265
|
+
to_address: ChecksumAddress,
|
266
|
+
value: Wei,
|
267
|
+
tx_params: Optional[TxParams] = None,
|
268
|
+
timeout: int = 180,
|
269
|
+
) -> TxReceipt:
|
270
|
+
from_address = private_key_to_public_key(from_private_key)
|
271
|
+
|
272
|
+
tx_params_new: TxParams = {"value": value, "to": to_address}
|
273
|
+
if tx_params:
|
274
|
+
tx_params_new.update(tx_params)
|
275
|
+
tx_params_new = _prepare_tx_params(web3, from_address, tx_params_new)
|
276
|
+
|
277
|
+
# We need gas and gasPrice here (and not elsewhere) because we are not calling
|
278
|
+
# contract.functions.myFunction().build_transaction, which autofills some params
|
279
|
+
# with defaults, incl. gas and gasPrice.
|
280
|
+
gas = web3.eth.estimate_gas(tx_params_new)
|
281
|
+
tx_params_new["gas"] = int(
|
282
|
+
gas * 1.5
|
283
|
+
) # We conservatively overestimate gas here, knowing it will be returned if unused
|
284
|
+
tx_params_new["gasPrice"] = web3.eth.gas_price
|
285
|
+
|
286
|
+
receipt_tx = sign_send_and_get_receipt_tx(
|
287
|
+
web3, tx_params_new, from_private_key, timeout
|
288
|
+
)
|
289
|
+
return receipt_tx
|
{prediction_market_agent_tooling-0.17.0 → prediction_market_agent_tooling-0.19.0}/pyproject.toml
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "prediction-market-agent-tooling"
|
3
|
-
version = "0.
|
3
|
+
version = "0.19.0"
|
4
4
|
description = "Tools to benchmark, deploy and monitor prediction market agents."
|
5
5
|
authors = ["Gnosis"]
|
6
6
|
readme = "README.md"
|
@@ -41,6 +41,7 @@ eth-account = "^0.8.0"
|
|
41
41
|
prompt-toolkit = "^3.0.43"
|
42
42
|
safe-cli = "^1.0.0"
|
43
43
|
mech-client = "^0.2.13"
|
44
|
+
langfuse = "^2.27.1"
|
44
45
|
|
45
46
|
[tool.poetry.extras]
|
46
47
|
langchain = ["langchain", "langchain-openai"]
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
|
3
|
-
import requests
|
4
|
-
|
5
|
-
from prediction_market_agent_tooling.gtypes import ChainID, HexAddress, HexBytes, Wei
|
6
|
-
|
7
|
-
GNOSIS_NETWORK_ID = ChainID(100) # xDai network.
|
8
|
-
GNOSIS_RPC_URL = os.getenv("GNOSIS_RPC_URL", "https://gnosis-rpc.publicnode.com")
|
9
|
-
|
10
|
-
|
11
|
-
def get_balance(address: HexAddress) -> Wei:
|
12
|
-
response = requests.post(
|
13
|
-
GNOSIS_RPC_URL,
|
14
|
-
json={
|
15
|
-
"jsonrpc": "2.0",
|
16
|
-
"method": "eth_getBalance",
|
17
|
-
"params": [address, "latest"],
|
18
|
-
"id": 1,
|
19
|
-
},
|
20
|
-
headers={"content-type": "application/json"},
|
21
|
-
).json()
|
22
|
-
balance_bytes = HexBytes(response["result"])
|
23
|
-
balance = Wei(balance_bytes.as_int())
|
24
|
-
return balance
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|