prediction-market-agent-tooling 0.43.1__py3-none-any.whl → 0.43.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/depositablewrapper_erc20.abi.json +279 -0
- prediction_market_agent_tooling/abis/erc20.abi.json +221 -314
- prediction_market_agent_tooling/abis/erc4626.abi.json +623 -0
- prediction_market_agent_tooling/abis/proxy.abi.json +24 -0
- prediction_market_agent_tooling/deploy/agent.py +10 -2
- prediction_market_agent_tooling/deploy/agent_example.py +4 -1
- prediction_market_agent_tooling/gtypes.py +1 -1
- prediction_market_agent_tooling/markets/agent_market.py +2 -1
- prediction_market_agent_tooling/markets/manifold/manifold.py +3 -2
- prediction_market_agent_tooling/markets/omen/data_models.py +2 -1
- prediction_market_agent_tooling/markets/omen/omen.py +73 -38
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +43 -12
- prediction_market_agent_tooling/markets/omen/omen_resolving.py +17 -6
- prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +9 -0
- prediction_market_agent_tooling/markets/polymarket/data_models_web.py +83 -83
- prediction_market_agent_tooling/markets/polymarket/polymarket.py +2 -1
- prediction_market_agent_tooling/tools/contract.py +303 -11
- prediction_market_agent_tooling/tools/is_predictable.py +4 -2
- {prediction_market_agent_tooling-0.43.1.dist-info → prediction_market_agent_tooling-0.43.4.dist-info}/METADATA +1 -1
- {prediction_market_agent_tooling-0.43.1.dist-info → prediction_market_agent_tooling-0.43.4.dist-info}/RECORD +23 -21
- prediction_market_agent_tooling/abis/wxdai.abi.json +0 -279
- {prediction_market_agent_tooling-0.43.1.dist-info → prediction_market_agent_tooling-0.43.4.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.43.1.dist-info → prediction_market_agent_tooling-0.43.4.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.43.1.dist-info → prediction_market_agent_tooling-0.43.4.dist-info}/entry_points.txt +0 -0
@@ -34,7 +34,7 @@ class Event(BaseModel):
|
|
34
34
|
slug: str
|
35
35
|
ticker: str
|
36
36
|
title: str
|
37
|
-
series:
|
37
|
+
series: t.Any | None = None
|
38
38
|
|
39
39
|
|
40
40
|
class Event1(BaseModel):
|
@@ -46,7 +46,7 @@ class Market1(BaseModel):
|
|
46
46
|
slug: str
|
47
47
|
question: str
|
48
48
|
image: str
|
49
|
-
volume: USDC | None
|
49
|
+
volume: USDC | None = None
|
50
50
|
outcomes: list[str]
|
51
51
|
outcomePrices: list[USDC]
|
52
52
|
active: bool
|
@@ -54,7 +54,7 @@ class Market1(BaseModel):
|
|
54
54
|
closed: bool
|
55
55
|
orderPriceMinTickSize: USDC
|
56
56
|
clobTokenIds: str
|
57
|
-
events: list[Event1]
|
57
|
+
events: list[Event1] | None = None
|
58
58
|
|
59
59
|
|
60
60
|
class ResolutionData(BaseModel):
|
@@ -77,98 +77,98 @@ class Market(BaseModel):
|
|
77
77
|
question: str
|
78
78
|
conditionId: str
|
79
79
|
slug: str
|
80
|
-
twitterCardImage: t.Any | None
|
80
|
+
twitterCardImage: t.Any | None = None
|
81
81
|
resolutionSource: str
|
82
82
|
endDate: datetime
|
83
|
-
category: t.Any | None
|
84
|
-
ammType: t.Any | None
|
83
|
+
category: t.Any | None = None
|
84
|
+
ammType: t.Any | None = None
|
85
85
|
description: str
|
86
|
-
liquidity: str | None
|
86
|
+
liquidity: str | None = None
|
87
87
|
startDate: datetime
|
88
88
|
createdAt: datetime
|
89
|
-
xAxisValue: t.Any | None
|
90
|
-
yAxisValue: t.Any | None
|
91
|
-
denominationToken: t.Any | None
|
92
|
-
fee: str | None
|
93
|
-
lowerBound: t.Any | None
|
94
|
-
upperBound: t.Any | None
|
89
|
+
xAxisValue: t.Any | None = None
|
90
|
+
yAxisValue: t.Any | None = None
|
91
|
+
denominationToken: t.Any | None = None
|
92
|
+
fee: str | None = None
|
93
|
+
lowerBound: t.Any | None = None
|
94
|
+
upperBound: t.Any | None = None
|
95
95
|
outcomes: list[str]
|
96
96
|
image: str
|
97
97
|
icon: str
|
98
|
-
imageOptimized: t.Any | None
|
99
|
-
iconOptimized: t.Any | None
|
98
|
+
imageOptimized: t.Any | None = None
|
99
|
+
iconOptimized: t.Any | None = None
|
100
100
|
outcomePrices: list[USDC]
|
101
|
-
volume: USDC | None
|
101
|
+
volume: USDC | None = None
|
102
102
|
active: bool
|
103
|
-
marketType: str | None
|
104
|
-
formatType: t.Any | None
|
105
|
-
lowerBoundDate: t.Any | None
|
106
|
-
upperBoundDate: t.Any | None
|
103
|
+
marketType: str | None = None
|
104
|
+
formatType: t.Any | None = None
|
105
|
+
lowerBoundDate: t.Any | None = None
|
106
|
+
upperBoundDate: t.Any | None = None
|
107
107
|
closed: bool
|
108
108
|
marketMakerAddress: HexAddress
|
109
|
-
closedTime: datetime | None
|
110
|
-
wideFormat: bool | None
|
109
|
+
closedTime: datetime | None = None
|
110
|
+
wideFormat: bool | None = None
|
111
111
|
new: bool
|
112
|
-
sentDiscord: t.Any | None
|
113
|
-
mailchimpTag: t.Any | None
|
112
|
+
sentDiscord: t.Any | None = None
|
113
|
+
mailchimpTag: t.Any | None = None
|
114
114
|
featured: bool
|
115
115
|
submitted_by: str
|
116
|
-
subcategory: t.Any | None
|
117
|
-
categoryMailchimpTag: t.Any | None
|
116
|
+
subcategory: t.Any | None = None
|
117
|
+
categoryMailchimpTag: t.Any | None = None
|
118
118
|
archived: bool
|
119
119
|
resolvedBy: str
|
120
120
|
restricted: bool
|
121
121
|
groupItemTitle: str
|
122
122
|
groupItemThreshold: str
|
123
123
|
questionID: str
|
124
|
-
umaEndDate: t.Any | None
|
124
|
+
umaEndDate: t.Any | None = None
|
125
125
|
enableOrderBook: bool
|
126
126
|
orderPriceMinTickSize: float
|
127
127
|
orderMinSize: int
|
128
|
-
umaResolutionStatus: t.Any | None
|
129
|
-
curationOrder: t.Any | None
|
130
|
-
volumeNum: USDC | None
|
131
|
-
liquidityNum: float | None
|
132
|
-
endDateIso: datetime | None
|
133
|
-
startDateIso: datetime | None
|
134
|
-
umaEndDateIso: datetime | None
|
135
|
-
commentsEnabled: bool | None
|
136
|
-
disqusThread: t.Any | None
|
137
|
-
gameStartTime: datetime | None
|
138
|
-
secondsDelay: int | None
|
128
|
+
umaResolutionStatus: t.Any | None = None
|
129
|
+
curationOrder: t.Any | None = None
|
130
|
+
volumeNum: USDC | None = None
|
131
|
+
liquidityNum: float | None = None
|
132
|
+
endDateIso: datetime | None = None
|
133
|
+
startDateIso: datetime | None = None
|
134
|
+
umaEndDateIso: datetime | None = None
|
135
|
+
commentsEnabled: bool | None = None
|
136
|
+
disqusThread: t.Any | None = None
|
137
|
+
gameStartTime: datetime | None = None
|
138
|
+
secondsDelay: int | None = None
|
139
139
|
clobTokenIds: list[str]
|
140
|
-
liquidityAmm: float | None
|
141
|
-
liquidityClob: float | None
|
142
|
-
makerBaseFee: int | None
|
143
|
-
takerBaseFee: int | None
|
144
|
-
negRisk: t.Any | None
|
145
|
-
negRiskRequestID: t.Any | None
|
146
|
-
negRiskMarketID: t.Any | None
|
140
|
+
liquidityAmm: float | None = None
|
141
|
+
liquidityClob: float | None = None
|
142
|
+
makerBaseFee: int | None = None
|
143
|
+
takerBaseFee: int | None = None
|
144
|
+
negRisk: t.Any | None = None
|
145
|
+
negRiskRequestID: t.Any | None = None
|
146
|
+
negRiskMarketID: t.Any | None = None
|
147
147
|
events: list[Event]
|
148
148
|
markets: list[Market1]
|
149
|
-
lower_bound_date: t.Any | None
|
150
|
-
upper_bound_date: t.Any | None
|
151
|
-
market_type: str | None
|
149
|
+
lower_bound_date: t.Any | None = None
|
150
|
+
upper_bound_date: t.Any | None = None
|
151
|
+
market_type: str | None = None
|
152
152
|
resolution_source: str
|
153
153
|
end_date: str
|
154
|
-
amm_type: t.Any | None
|
155
|
-
x_axis_value: t.Any | None
|
156
|
-
y_axis_value: t.Any | None
|
157
|
-
denomination_token: t.Any | None
|
154
|
+
amm_type: t.Any | None = None
|
155
|
+
x_axis_value: t.Any | None = None
|
156
|
+
y_axis_value: t.Any | None = None
|
157
|
+
denomination_token: t.Any | None = None
|
158
158
|
resolved_by: str
|
159
|
-
upper_bound: t.Any | None
|
160
|
-
lower_bound: t.Any | None
|
159
|
+
upper_bound: t.Any | None = None
|
160
|
+
lower_bound: t.Any | None = None
|
161
161
|
created_at: str
|
162
|
-
updated_at: t.Any | None
|
163
|
-
closed_time: t.Any | None
|
164
|
-
wide_format: bool | None
|
165
|
-
volume_num: USDC | None
|
166
|
-
liquidity_num: USDC | None
|
162
|
+
updated_at: t.Any | None = None
|
163
|
+
closed_time: t.Any | None = None
|
164
|
+
wide_format: bool | None = None
|
165
|
+
volume_num: USDC | None = None
|
166
|
+
liquidity_num: USDC | None = None
|
167
167
|
image_raw: str
|
168
168
|
resolutionData: ResolutionData
|
169
169
|
|
170
170
|
@field_validator("closedTime", mode="before")
|
171
|
-
def field_validator_closedTime(cls, v: str | None) -> str | None:
|
171
|
+
def field_validator_closedTime(cls, v: str | None = None) -> str | None:
|
172
172
|
return v.replace("+00", "") if v else None
|
173
173
|
|
174
174
|
@property
|
@@ -232,7 +232,7 @@ class PolymarketFullMarket(BaseModel):
|
|
232
232
|
ticker: str
|
233
233
|
slug: str
|
234
234
|
title: str
|
235
|
-
subtitle: t.Any | None
|
235
|
+
subtitle: t.Any | None = None
|
236
236
|
description: str
|
237
237
|
commentCount: int
|
238
238
|
resolutionSource: str
|
@@ -240,35 +240,35 @@ class PolymarketFullMarket(BaseModel):
|
|
240
240
|
endDate: datetime
|
241
241
|
image: str
|
242
242
|
icon: str
|
243
|
-
featuredImage: str | None
|
243
|
+
featuredImage: str | None = None
|
244
244
|
active: bool
|
245
245
|
closed: bool
|
246
246
|
archived: bool
|
247
247
|
new: bool
|
248
248
|
featured: bool
|
249
249
|
restricted: bool
|
250
|
-
liquidity: USDC | None
|
251
|
-
volume: USDC | None
|
252
|
-
volume24hr: USDC | None
|
253
|
-
competitive: float | None
|
254
|
-
openInterest: int | None
|
255
|
-
sortBy: str | None
|
250
|
+
liquidity: USDC | None = None
|
251
|
+
volume: USDC | None = None
|
252
|
+
volume24hr: USDC | None = None
|
253
|
+
competitive: float | None = None
|
254
|
+
openInterest: int | None = None
|
255
|
+
sortBy: str | None = None
|
256
256
|
createdAt: datetime
|
257
|
-
commentsEnabled: bool | None
|
258
|
-
disqusThread: t.Any | None
|
257
|
+
commentsEnabled: bool | None = None
|
258
|
+
disqusThread: t.Any | None = None
|
259
259
|
updatedAt: datetime
|
260
260
|
enableOrderBook: bool
|
261
|
-
liquidityAmm: float | None
|
262
|
-
liquidityClob: float | None
|
263
|
-
imageOptimized: ImageOptimized | None
|
264
|
-
iconOptimized: IconOptimized | None
|
265
|
-
featuredImageOptimized: str | None
|
266
|
-
negRisk: t.Any | None
|
267
|
-
negRiskMarketID: t.Any | None
|
268
|
-
negRiskFeeBips: t.Any | None
|
261
|
+
liquidityAmm: float | None = None
|
262
|
+
liquidityClob: float | None = None
|
263
|
+
imageOptimized: ImageOptimized | None = None
|
264
|
+
iconOptimized: IconOptimized | None = None
|
265
|
+
featuredImageOptimized: str | None = None
|
266
|
+
negRisk: t.Any | None = None
|
267
|
+
negRiskMarketID: t.Any | None = None
|
268
|
+
negRiskFeeBips: t.Any | None = None
|
269
269
|
markets: list[Market]
|
270
270
|
categories: list[Category] | None = None
|
271
|
-
series: t.Any | None
|
271
|
+
series: t.Any | None = None
|
272
272
|
image_raw: str
|
273
273
|
|
274
274
|
@property
|
@@ -355,12 +355,12 @@ class State(BaseModel):
|
|
355
355
|
) # It's none if you go to the website and it says "Oops...we didn't forecast this".
|
356
356
|
dataUpdateCount: int
|
357
357
|
dataUpdatedAt: int
|
358
|
-
error: t.Any | None
|
358
|
+
error: t.Any | None = None
|
359
359
|
errorUpdateCount: int
|
360
360
|
errorUpdatedAt: int
|
361
361
|
fetchFailureCount: int
|
362
|
-
fetchFailureReason: t.Any | None
|
363
|
-
fetchMeta: t.Any | None
|
362
|
+
fetchFailureReason: t.Any | None = None
|
363
|
+
fetchMeta: t.Any | None = None
|
364
364
|
isInvalidated: bool
|
365
365
|
status: str
|
366
366
|
fetchStatus: str
|
@@ -381,7 +381,7 @@ class PageProps(BaseModel):
|
|
381
381
|
key: str
|
382
382
|
dehydratedState: DehydratedState
|
383
383
|
eslug: str
|
384
|
-
mslug: str | None
|
384
|
+
mslug: str | None = None
|
385
385
|
isSingleMarket: bool
|
386
386
|
|
387
387
|
|
@@ -41,7 +41,8 @@ class PolymarketAgentMarket(AgentMarket):
|
|
41
41
|
volume=None,
|
42
42
|
)
|
43
43
|
|
44
|
-
|
44
|
+
@classmethod
|
45
|
+
def get_tiny_bet_amount(cls) -> BetAmount:
|
45
46
|
raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
|
46
47
|
|
47
48
|
def place_bet(self, outcome: bool, amount: BetAmount) -> None:
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import json
|
2
|
+
import os
|
2
3
|
import time
|
3
4
|
import typing as t
|
4
5
|
from contextlib import contextmanager
|
@@ -151,11 +152,33 @@ class ContractBaseClass(BaseModel):
|
|
151
152
|
return Web3(Web3.HTTPProvider(cls.CHAIN_RPC_URL))
|
152
153
|
|
153
154
|
|
155
|
+
class ContractProxyBaseClass(ContractBaseClass):
|
156
|
+
"""
|
157
|
+
Contract base class for proxy contracts.
|
158
|
+
"""
|
159
|
+
|
160
|
+
abi: ABI = abi_field_validator(
|
161
|
+
os.path.join(
|
162
|
+
os.path.dirname(os.path.realpath(__file__)), "../abis/proxy.abi.json"
|
163
|
+
)
|
164
|
+
)
|
165
|
+
|
166
|
+
def implementation(self, web3: Web3 | None = None) -> ChecksumAddress:
|
167
|
+
address = self.call("implementation", web3=web3)
|
168
|
+
return Web3.to_checksum_address(address)
|
169
|
+
|
170
|
+
|
154
171
|
class ContractERC20BaseClass(ContractBaseClass):
|
155
172
|
"""
|
156
173
|
Contract base class extended by ERC-20 standard methods.
|
157
174
|
"""
|
158
175
|
|
176
|
+
abi: ABI = abi_field_validator(
|
177
|
+
os.path.join(
|
178
|
+
os.path.dirname(os.path.realpath(__file__)), "../abis/erc20.abi.json"
|
179
|
+
)
|
180
|
+
)
|
181
|
+
|
159
182
|
def approve(
|
160
183
|
self,
|
161
184
|
api_keys: APIKeys,
|
@@ -175,6 +198,41 @@ class ContractERC20BaseClass(ContractBaseClass):
|
|
175
198
|
web3=web3,
|
176
199
|
)
|
177
200
|
|
201
|
+
def transferFrom(
|
202
|
+
self,
|
203
|
+
api_keys: APIKeys,
|
204
|
+
sender: ChecksumAddress,
|
205
|
+
recipient: ChecksumAddress,
|
206
|
+
amount_wei: Wei,
|
207
|
+
tx_params: t.Optional[TxParams] = None,
|
208
|
+
web3: Web3 | None = None,
|
209
|
+
) -> TxReceipt:
|
210
|
+
return self.send(
|
211
|
+
api_keys=api_keys,
|
212
|
+
function_name="transferFrom",
|
213
|
+
function_params=[sender, recipient, amount_wei],
|
214
|
+
tx_params=tx_params,
|
215
|
+
web3=web3,
|
216
|
+
)
|
217
|
+
|
218
|
+
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
|
219
|
+
balance: Wei = self.call("balanceOf", [for_address], web3=web3)
|
220
|
+
return balance
|
221
|
+
|
222
|
+
|
223
|
+
class ContractDepositableWrapperERC20BaseClass(ContractERC20BaseClass):
|
224
|
+
"""
|
225
|
+
ERC-20 standard base class extended for wrapper tokens.
|
226
|
+
Altough this is not a standard, it's seems to be a common pattern for wrapped tokens (at least it checks out for wxDai and wETH).
|
227
|
+
"""
|
228
|
+
|
229
|
+
abi: ABI = abi_field_validator(
|
230
|
+
os.path.join(
|
231
|
+
os.path.dirname(os.path.realpath(__file__)),
|
232
|
+
"../abis/depositablewrapper_erc20.abi.json",
|
233
|
+
)
|
234
|
+
)
|
235
|
+
|
178
236
|
def deposit(
|
179
237
|
self,
|
180
238
|
api_keys: APIKeys,
|
@@ -190,41 +248,93 @@ class ContractERC20BaseClass(ContractBaseClass):
|
|
190
248
|
web3=web3,
|
191
249
|
)
|
192
250
|
|
193
|
-
def
|
251
|
+
def withdraw(
|
194
252
|
self,
|
195
253
|
api_keys: APIKeys,
|
196
|
-
sender: ChecksumAddress,
|
197
|
-
recipient: ChecksumAddress,
|
198
254
|
amount_wei: Wei,
|
199
255
|
tx_params: t.Optional[TxParams] = None,
|
200
256
|
web3: Web3 | None = None,
|
201
257
|
) -> TxReceipt:
|
202
258
|
return self.send(
|
203
259
|
api_keys=api_keys,
|
204
|
-
function_name="
|
205
|
-
function_params=[
|
260
|
+
function_name="withdraw",
|
261
|
+
function_params=[amount_wei],
|
206
262
|
tx_params=tx_params,
|
207
263
|
web3=web3,
|
208
264
|
)
|
209
265
|
|
210
|
-
|
266
|
+
|
267
|
+
class ContractERC4626BaseClass(ContractERC20BaseClass):
|
268
|
+
"""
|
269
|
+
Class for ERC-4626, which is a superset for ERC-20.
|
270
|
+
"""
|
271
|
+
|
272
|
+
abi: ABI = abi_field_validator(
|
273
|
+
os.path.join(
|
274
|
+
os.path.dirname(os.path.realpath(__file__)), "../abis/erc4626.abi.json"
|
275
|
+
)
|
276
|
+
)
|
277
|
+
|
278
|
+
def asset(self, web3: Web3 | None = None) -> ChecksumAddress:
|
279
|
+
address = self.call("asset", web3=web3)
|
280
|
+
return Web3.to_checksum_address(address)
|
281
|
+
|
282
|
+
def deposit(
|
211
283
|
self,
|
212
284
|
api_keys: APIKeys,
|
213
285
|
amount_wei: Wei,
|
286
|
+
receiver: ChecksumAddress,
|
214
287
|
tx_params: t.Optional[TxParams] = None,
|
215
288
|
web3: Web3 | None = None,
|
216
289
|
) -> TxReceipt:
|
217
290
|
return self.send(
|
218
291
|
api_keys=api_keys,
|
219
|
-
function_name="
|
220
|
-
function_params=[amount_wei],
|
292
|
+
function_name="deposit",
|
293
|
+
function_params=[amount_wei, receiver],
|
221
294
|
tx_params=tx_params,
|
222
295
|
web3=web3,
|
223
296
|
)
|
224
297
|
|
225
|
-
def
|
226
|
-
|
227
|
-
return
|
298
|
+
def convertToShares(self, assets: Wei, web3: Web3 | None = None) -> Wei:
|
299
|
+
shares: Wei = self.call("convertToShares", [assets], web3=web3)
|
300
|
+
return shares
|
301
|
+
|
302
|
+
def convertToAssets(self, shares: Wei, web3: Web3 | None = None) -> Wei:
|
303
|
+
assets: Wei = self.call("convertToAssets", [shares], web3=web3)
|
304
|
+
return assets
|
305
|
+
|
306
|
+
def get_asset_token_contract(
|
307
|
+
self, web3: Web3 | None = None
|
308
|
+
) -> ContractERC20BaseClass | ContractDepositableWrapperERC20BaseClass:
|
309
|
+
web3 = web3 or self.get_web3()
|
310
|
+
contract = init_erc4626_or_wrappererc20_or_erc20_contract(
|
311
|
+
self.asset(), web3=web3
|
312
|
+
)
|
313
|
+
assert not isinstance(
|
314
|
+
contract, ContractERC4626OnGnosisChain
|
315
|
+
), "Asset token should be either Depositable Wrapper ERC-20 or ERC-20." # Shrinking down possible types.
|
316
|
+
return contract
|
317
|
+
|
318
|
+
def get_asset_token_balance(
|
319
|
+
self, for_address: ChecksumAddress, web3: Web3 | None = None
|
320
|
+
) -> Wei:
|
321
|
+
asset_token_contract = self.get_asset_token_contract(web3=web3)
|
322
|
+
return asset_token_contract.balanceOf(for_address, web3=web3)
|
323
|
+
|
324
|
+
def deposit_asset_token(
|
325
|
+
self, asset_value: Wei, api_keys: APIKeys, web3: Web3 | None = None
|
326
|
+
) -> TxReceipt:
|
327
|
+
for_address = api_keys.bet_from_address
|
328
|
+
web3 = web3 or self.get_web3()
|
329
|
+
|
330
|
+
asset_token_contract = self.get_asset_token_contract(web3=web3)
|
331
|
+
# Approve vault to withdraw the erc-20 token from the user.
|
332
|
+
asset_token_contract.approve(api_keys, self.address, asset_value, web3=web3)
|
333
|
+
|
334
|
+
# Deposit asset token (erc20) and we will receive shares in this vault.
|
335
|
+
receipt = self.deposit(api_keys, asset_value, for_address, web3=web3)
|
336
|
+
|
337
|
+
return receipt
|
228
338
|
|
229
339
|
|
230
340
|
class ContractOnGnosisChain(ContractBaseClass):
|
@@ -236,7 +346,189 @@ class ContractOnGnosisChain(ContractBaseClass):
|
|
236
346
|
CHAIN_RPC_URL = GNOSIS_RPC_URL
|
237
347
|
|
238
348
|
|
349
|
+
class ContractProxyOnGnosisChain(ContractProxyBaseClass, ContractOnGnosisChain):
|
350
|
+
"""
|
351
|
+
Proxy contract base class with Gnosis Chain configuration.
|
352
|
+
"""
|
353
|
+
|
354
|
+
|
239
355
|
class ContractERC20OnGnosisChain(ContractERC20BaseClass, ContractOnGnosisChain):
|
240
356
|
"""
|
241
357
|
ERC-20 standard base class with Gnosis Chain configuration.
|
242
358
|
"""
|
359
|
+
|
360
|
+
|
361
|
+
class ContractDepositableWrapperERC20OnGnosisChain(
|
362
|
+
ContractDepositableWrapperERC20BaseClass, ContractOnGnosisChain
|
363
|
+
):
|
364
|
+
"""
|
365
|
+
Depositable Wrapper ERC-20 standard base class with Gnosis Chain configuration.
|
366
|
+
"""
|
367
|
+
|
368
|
+
|
369
|
+
class ContractERC4626OnGnosisChain(ContractERC4626BaseClass, ContractOnGnosisChain):
|
370
|
+
"""
|
371
|
+
ERC-4626 standard base class with Gnosis Chain configuration.
|
372
|
+
"""
|
373
|
+
|
374
|
+
|
375
|
+
def contract_implements_function(
|
376
|
+
contract_address: ChecksumAddress,
|
377
|
+
function_name: str,
|
378
|
+
web3: Web3,
|
379
|
+
function_arg_types: list[str] | None = None,
|
380
|
+
look_for_proxy_contract: bool = True,
|
381
|
+
) -> bool:
|
382
|
+
function_signature = f"{function_name}({','.join(function_arg_types or [])})"
|
383
|
+
function_hash = web3.keccak(text=function_signature)[0:4].hex()[2:]
|
384
|
+
contract_code = web3.eth.get_code(contract_address).hex()
|
385
|
+
implements = function_hash in contract_code
|
386
|
+
if (
|
387
|
+
not implements
|
388
|
+
and look_for_proxy_contract
|
389
|
+
and contract_implements_function(
|
390
|
+
contract_address, "implementation", web3, look_for_proxy_contract=False
|
391
|
+
)
|
392
|
+
):
|
393
|
+
implementation_address = ContractProxyOnGnosisChain(
|
394
|
+
address=contract_address
|
395
|
+
).implementation()
|
396
|
+
implements = contract_implements_function(
|
397
|
+
implementation_address,
|
398
|
+
function_name=function_name,
|
399
|
+
web3=web3,
|
400
|
+
function_arg_types=function_arg_types,
|
401
|
+
look_for_proxy_contract=False,
|
402
|
+
)
|
403
|
+
return implements
|
404
|
+
|
405
|
+
|
406
|
+
def init_erc4626_or_wrappererc20_or_erc20_contract(
|
407
|
+
address: ChecksumAddress,
|
408
|
+
web3: Web3,
|
409
|
+
) -> (
|
410
|
+
ContractERC20BaseClass
|
411
|
+
| ContractERC4626BaseClass
|
412
|
+
| ContractDepositableWrapperERC20BaseClass
|
413
|
+
):
|
414
|
+
"""
|
415
|
+
Checks if the given contract is Depositable ERC-20, ERC-20 or ERC-4626 and returns the appropriate class instance.
|
416
|
+
Throws an error if the contract is neither of them.
|
417
|
+
"""
|
418
|
+
if contract_implements_function(address, "asset", web3=web3):
|
419
|
+
return ContractERC4626BaseClass(address=address)
|
420
|
+
|
421
|
+
elif contract_implements_function(
|
422
|
+
address,
|
423
|
+
"deposit",
|
424
|
+
web3=web3,
|
425
|
+
):
|
426
|
+
return ContractDepositableWrapperERC20BaseClass(address=address)
|
427
|
+
|
428
|
+
elif contract_implements_function(
|
429
|
+
address,
|
430
|
+
"balanceOf",
|
431
|
+
web3=web3,
|
432
|
+
function_arg_types=["address"],
|
433
|
+
):
|
434
|
+
return ContractERC20BaseClass(address=address)
|
435
|
+
|
436
|
+
else:
|
437
|
+
raise ValueError(
|
438
|
+
f"Contract at {address} on Gnosis Chain is neither WrapperERC-20, ERC-20 nor ERC-4626."
|
439
|
+
)
|
440
|
+
|
441
|
+
|
442
|
+
def auto_deposit_collateral_token(
|
443
|
+
collateral_token_contract: (
|
444
|
+
ContractERC20BaseClass
|
445
|
+
| ContractERC4626BaseClass
|
446
|
+
| ContractDepositableWrapperERC20BaseClass
|
447
|
+
),
|
448
|
+
amount_wei: Wei,
|
449
|
+
api_keys: APIKeys,
|
450
|
+
web3: Web3 | None,
|
451
|
+
) -> None:
|
452
|
+
for_address = api_keys.bet_from_address
|
453
|
+
# This might be in shares, if it's an erc-4626 token.
|
454
|
+
collateral_token_balance = collateral_token_contract.balanceOf(
|
455
|
+
for_address=for_address, web3=web3
|
456
|
+
)
|
457
|
+
|
458
|
+
if isinstance(collateral_token_contract, ContractERC4626BaseClass):
|
459
|
+
# In the more complex case, we need to deposit into the saving token, out of the erc-20 token.
|
460
|
+
# We need to compare with shares, because if erc-4626 is used, the liquidity in market will be in shares as well.
|
461
|
+
if collateral_token_balance < collateral_token_contract.convertToShares(
|
462
|
+
amount_wei
|
463
|
+
):
|
464
|
+
asset_token_contract = collateral_token_contract.get_asset_token_contract(
|
465
|
+
web3=web3
|
466
|
+
)
|
467
|
+
|
468
|
+
# If the asset token is Depositable Wrapper ERC-20, we can deposit it, in case we don't have enough.
|
469
|
+
if (
|
470
|
+
collateral_token_contract.get_asset_token_balance(for_address, web3)
|
471
|
+
< amount_wei
|
472
|
+
):
|
473
|
+
if isinstance(
|
474
|
+
asset_token_contract, ContractDepositableWrapperERC20BaseClass
|
475
|
+
):
|
476
|
+
asset_token_contract.deposit(api_keys, amount_wei, web3=web3)
|
477
|
+
else:
|
478
|
+
raise ValueError(
|
479
|
+
f"Not enough of the asset token, but it's not a depositable wrapper token that we can deposit automatically."
|
480
|
+
)
|
481
|
+
|
482
|
+
collateral_token_contract.deposit_asset_token(amount_wei, api_keys, web3)
|
483
|
+
|
484
|
+
elif isinstance(
|
485
|
+
collateral_token_contract, ContractDepositableWrapperERC20BaseClass
|
486
|
+
):
|
487
|
+
# If the collateral token is Depositable Wrapper ERC-20, it's a simple case where we can just deposit it, if needed.
|
488
|
+
if collateral_token_balance < amount_wei:
|
489
|
+
collateral_token_contract.deposit(api_keys, amount_wei, web3=web3)
|
490
|
+
|
491
|
+
elif isinstance(collateral_token_contract, ContractERC20BaseClass):
|
492
|
+
if collateral_token_balance < amount_wei:
|
493
|
+
raise ValueError(
|
494
|
+
f"Not enough of the collateral token, but it's not a wrapper token that we can deposit automatically."
|
495
|
+
)
|
496
|
+
|
497
|
+
else:
|
498
|
+
raise RuntimeError("Bug in our logic! :(")
|
499
|
+
|
500
|
+
|
501
|
+
def asset_or_shares(
|
502
|
+
collateral_token_contract: (
|
503
|
+
ContractERC20BaseClass
|
504
|
+
| ContractERC4626BaseClass
|
505
|
+
| ContractDepositableWrapperERC20BaseClass
|
506
|
+
),
|
507
|
+
amount_wei: Wei,
|
508
|
+
) -> Wei:
|
509
|
+
return (
|
510
|
+
collateral_token_contract.convertToShares(amount_wei)
|
511
|
+
if isinstance(collateral_token_contract, ContractERC4626BaseClass)
|
512
|
+
else amount_wei
|
513
|
+
)
|
514
|
+
|
515
|
+
|
516
|
+
def to_gnosis_chain_contract(
|
517
|
+
contract: (
|
518
|
+
ContractDepositableWrapperERC20BaseClass
|
519
|
+
| ContractERC4626BaseClass
|
520
|
+
| ContractERC20BaseClass
|
521
|
+
),
|
522
|
+
) -> (
|
523
|
+
ContractDepositableWrapperERC20OnGnosisChain
|
524
|
+
| ContractERC4626OnGnosisChain
|
525
|
+
| ContractERC20OnGnosisChain
|
526
|
+
):
|
527
|
+
if isinstance(contract, ContractERC4626BaseClass):
|
528
|
+
return ContractERC4626OnGnosisChain(address=contract.address)
|
529
|
+
elif isinstance(contract, ContractDepositableWrapperERC20BaseClass):
|
530
|
+
return ContractDepositableWrapperERC20OnGnosisChain(address=contract.address)
|
531
|
+
elif isinstance(contract, ContractERC20BaseClass):
|
532
|
+
return ContractERC20OnGnosisChain(address=contract.address)
|
533
|
+
else:
|
534
|
+
raise ValueError("Unsupported contract type")
|
@@ -76,6 +76,7 @@ def is_predictable_binary(
|
|
76
76
|
question: str,
|
77
77
|
engine: str = "gpt-4-1106-preview",
|
78
78
|
prompt_template: str = QUESTION_IS_PREDICTABLE_BINARY_PROMPT,
|
79
|
+
max_tokens: int = 1024,
|
79
80
|
) -> bool:
|
80
81
|
"""
|
81
82
|
Evaluate if the question is actually answerable.
|
@@ -95,7 +96,7 @@ def is_predictable_binary(
|
|
95
96
|
|
96
97
|
prompt = ChatPromptTemplate.from_template(template=prompt_template)
|
97
98
|
messages = prompt.format_messages(question=question)
|
98
|
-
completion = str(llm(messages, max_tokens=
|
99
|
+
completion = str(llm(messages, max_tokens=max_tokens).content)
|
99
100
|
|
100
101
|
return parse_decision_yes_no_completion(question, completion)
|
101
102
|
|
@@ -106,6 +107,7 @@ def is_predictable_without_description(
|
|
106
107
|
description: str,
|
107
108
|
engine: str = "gpt-4-1106-preview",
|
108
109
|
prompt_template: str = QUESTION_IS_PREDICTABLE_WITHOUT_DESCRIPTION_PROMPT,
|
110
|
+
max_tokens: int = 1024,
|
109
111
|
) -> bool:
|
110
112
|
"""
|
111
113
|
Evaluate if the question is fully self-contained.
|
@@ -130,7 +132,7 @@ def is_predictable_without_description(
|
|
130
132
|
question=question,
|
131
133
|
description=description,
|
132
134
|
)
|
133
|
-
completion = str(llm(messages, max_tokens=
|
135
|
+
completion = str(llm(messages, max_tokens=max_tokens).content)
|
134
136
|
|
135
137
|
return parse_decision_yes_no_completion(question, completion)
|
136
138
|
|