polymarket-apis 0.3.4__py3-none-any.whl → 0.3.6__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.
Potentially problematic release.
This version of polymarket-apis might be problematic. Click here for more details.
- polymarket_apis/__init__.py +1 -1
- polymarket_apis/clients/clob_client.py +42 -30
- polymarket_apis/clients/data_client.py +34 -0
- polymarket_apis/clients/web3_client.py +104 -113
- polymarket_apis/types/common.py +5 -3
- polymarket_apis/types/data_types.py +33 -1
- polymarket_apis/utilities/web3/helpers.py +4 -4
- {polymarket_apis-0.3.4.dist-info → polymarket_apis-0.3.6.dist-info}/METADATA +5 -2
- {polymarket_apis-0.3.4.dist-info → polymarket_apis-0.3.6.dist-info}/RECORD +10 -10
- {polymarket_apis-0.3.4.dist-info → polymarket_apis-0.3.6.dist-info}/WHEEL +0 -0
polymarket_apis/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
from datetime import UTC, datetime
|
|
3
|
+
from datetime import UTC, datetime, timedelta
|
|
4
4
|
from typing import Literal, Optional
|
|
5
5
|
from urllib.parse import urljoin
|
|
6
6
|
|
|
@@ -93,13 +93,13 @@ class PolymarketClobClient:
|
|
|
93
93
|
def __init__(
|
|
94
94
|
self,
|
|
95
95
|
private_key: str,
|
|
96
|
-
|
|
96
|
+
address: EthAddress,
|
|
97
97
|
creds: Optional[ApiCreds] = None,
|
|
98
98
|
chain_id: Literal[137, 80002] = POLYGON,
|
|
99
99
|
signature_type: Literal[0, 1, 2] = 1,
|
|
100
100
|
# 0 - EOA wallet, 1 - Proxy wallet, 2 - Gnosis Safe wallet
|
|
101
101
|
):
|
|
102
|
-
self.
|
|
102
|
+
self.address = address
|
|
103
103
|
self.client = httpx.Client(http2=True, timeout=30.0)
|
|
104
104
|
self.async_client = httpx.AsyncClient(http2=True, timeout=30.0)
|
|
105
105
|
self.base_url: str = "https://clob.polymarket.com"
|
|
@@ -107,7 +107,7 @@ class PolymarketClobClient:
|
|
|
107
107
|
self.builder = OrderBuilder(
|
|
108
108
|
signer=self.signer,
|
|
109
109
|
sig_type=signature_type,
|
|
110
|
-
funder=
|
|
110
|
+
funder=address,
|
|
111
111
|
)
|
|
112
112
|
self.creds = creds if creds else self.create_or_derive_api_creds()
|
|
113
113
|
|
|
@@ -349,12 +349,21 @@ class PolymarketClobClient:
|
|
|
349
349
|
def get_recent_history(
|
|
350
350
|
self,
|
|
351
351
|
token_id: str,
|
|
352
|
-
interval:
|
|
352
|
+
interval: Literal["1h", "6h", "1d", "1w", "1m", "max"] = "1d",
|
|
353
353
|
fidelity: int = 1, # resolution in minutes
|
|
354
354
|
) -> PriceHistory:
|
|
355
|
-
"""Get the recent price history of a token (up to now) - 1h, 6h, 1d."""
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
"""Get the recent price history of a token (up to now) - 1h, 6h, 1d, 1w, 1m."""
|
|
356
|
+
min_fidelities: dict[str, int] = {
|
|
357
|
+
"1h": 1,
|
|
358
|
+
"6h": 1,
|
|
359
|
+
"1d": 1,
|
|
360
|
+
"1w": 5,
|
|
361
|
+
"1m": 10,
|
|
362
|
+
"max": 2,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if fidelity < min_fidelities.get(interval):
|
|
366
|
+
msg = f"invalid filters: minimum 'fidelity' for '{interval}' range is {min_fidelities.get(interval)}"
|
|
358
367
|
raise ValueError(msg)
|
|
359
368
|
|
|
360
369
|
params = {
|
|
@@ -371,38 +380,41 @@ class PolymarketClobClient:
|
|
|
371
380
|
token_id: str,
|
|
372
381
|
start_time: Optional[datetime] = None,
|
|
373
382
|
end_time: Optional[datetime] = None,
|
|
374
|
-
interval: Literal["max", "1m", "1w"] = "max",
|
|
375
383
|
fidelity: int = 2, # resolution in minutes
|
|
376
384
|
) -> PriceHistory:
|
|
377
|
-
"""Get the price history of a token between selected
|
|
378
|
-
min_fidelities: dict[str, int] = {"1m": 10, "1w": 5, "max": 2}
|
|
379
|
-
|
|
380
|
-
if fidelity is None or fidelity < min_fidelities[interval]:
|
|
381
|
-
msg = f"invalid filters: minimum 'fidelity' for '{interval}' range is {min_fidelities[interval]}"
|
|
382
|
-
raise ValueError(msg)
|
|
383
|
-
|
|
385
|
+
"""Get the price history of a token between a selected date range of max 15 days or from start_time to now."""
|
|
384
386
|
if start_time is None and end_time is None:
|
|
385
|
-
msg = "At least
|
|
387
|
+
msg = "At least 'start_time' or ('start_time' and 'end_time') must be provided"
|
|
386
388
|
raise ValueError(msg)
|
|
387
389
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
start_time
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
if (
|
|
391
|
+
start_time
|
|
392
|
+
and end_time
|
|
393
|
+
and start_time + timedelta(days=15, seconds=1) < end_time
|
|
394
|
+
):
|
|
395
|
+
msg = "'start_time' - 'end_time' range cannot exceed 15 days. Remove 'end_time' to get prices up to now or set a shorter range."
|
|
396
|
+
raise ValueError(msg)
|
|
394
397
|
|
|
395
398
|
params = {
|
|
396
399
|
"market": token_id,
|
|
397
|
-
"startTs": int(start_time.timestamp()),
|
|
398
|
-
"endTs": int(end_time.timestamp()),
|
|
399
|
-
"interval": interval,
|
|
400
400
|
"fidelity": fidelity,
|
|
401
401
|
}
|
|
402
|
+
if start_time:
|
|
403
|
+
params["startTs"] = int(start_time.timestamp())
|
|
404
|
+
if end_time:
|
|
405
|
+
params["endTs"] = int(end_time.timestamp())
|
|
406
|
+
|
|
402
407
|
response = self.client.get(self._build_url("/prices-history"), params=params)
|
|
403
408
|
response.raise_for_status()
|
|
404
409
|
return PriceHistory(**response.json(), token_id=token_id)
|
|
405
410
|
|
|
411
|
+
def get_all_history(self, token_id: str) -> PriceHistory:
|
|
412
|
+
"""Get the full price history of a token."""
|
|
413
|
+
return self.get_history(
|
|
414
|
+
token_id=token_id,
|
|
415
|
+
start_time=datetime(2020, 1, 1, tzinfo=UTC),
|
|
416
|
+
)
|
|
417
|
+
|
|
406
418
|
def get_orders(
|
|
407
419
|
self,
|
|
408
420
|
order_id: Optional[str] = None,
|
|
@@ -732,7 +744,7 @@ class PolymarketClobClient:
|
|
|
732
744
|
trade_id: Optional[str] = None,
|
|
733
745
|
before: Optional[datetime] = None,
|
|
734
746
|
after: Optional[datetime] = None,
|
|
735
|
-
|
|
747
|
+
address: Optional[EthAddress] = None,
|
|
736
748
|
next_cursor="MA==",
|
|
737
749
|
) -> list[PolygonTrade]:
|
|
738
750
|
"""Fetches the trade history for a user."""
|
|
@@ -747,8 +759,8 @@ class PolymarketClobClient:
|
|
|
747
759
|
params["before"] = int(before.replace(microsecond=0).timestamp())
|
|
748
760
|
if after:
|
|
749
761
|
params["after"] = int(after.replace(microsecond=0).timestamp())
|
|
750
|
-
if
|
|
751
|
-
params["maker_address"] =
|
|
762
|
+
if address:
|
|
763
|
+
params["maker_address"] = address
|
|
752
764
|
|
|
753
765
|
request_args = RequestArgs(method="GET", request_path=TRADES)
|
|
754
766
|
headers = create_level_2_headers(self.signer, self.creds, request_args)
|
|
@@ -788,7 +800,7 @@ class PolymarketClobClient:
|
|
|
788
800
|
return DailyEarnedReward(
|
|
789
801
|
date=date,
|
|
790
802
|
asset_address="0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
|
|
791
|
-
maker_address=self.
|
|
803
|
+
maker_address=self.address,
|
|
792
804
|
earnings=0.0,
|
|
793
805
|
asset_rate=0.0,
|
|
794
806
|
)
|
|
@@ -4,10 +4,12 @@ from urllib.parse import urljoin
|
|
|
4
4
|
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
|
+
from ..clients.graphql_client import PolymarketGraphQLClient
|
|
7
8
|
from ..types.common import EthAddress, TimeseriesPoint
|
|
8
9
|
from ..types.data_types import (
|
|
9
10
|
Activity,
|
|
10
11
|
EventLiveVolume,
|
|
12
|
+
GQLPosition,
|
|
11
13
|
HolderResponse,
|
|
12
14
|
MarketValue,
|
|
13
15
|
Position,
|
|
@@ -22,6 +24,9 @@ class PolymarketDataClient:
|
|
|
22
24
|
def __init__(self, base_url: str = "https://data-api.polymarket.com"):
|
|
23
25
|
self.base_url = base_url
|
|
24
26
|
self.client = httpx.Client(http2=True, timeout=30.0)
|
|
27
|
+
self.gql_positions_client = PolymarketGraphQLClient(
|
|
28
|
+
endpoint_name="positions_subgraph"
|
|
29
|
+
)
|
|
25
30
|
|
|
26
31
|
def _build_url(self, endpoint: str) -> str:
|
|
27
32
|
return urljoin(self.base_url, endpoint)
|
|
@@ -31,6 +36,35 @@ class PolymarketDataClient:
|
|
|
31
36
|
response.raise_for_status()
|
|
32
37
|
return response.json()["data"]
|
|
33
38
|
|
|
39
|
+
def get_all_positions(
|
|
40
|
+
self,
|
|
41
|
+
user: EthAddress,
|
|
42
|
+
size_threshold: float = 0.0,
|
|
43
|
+
):
|
|
44
|
+
# data-api /positions endpoint does not support fetching all positions without filters
|
|
45
|
+
# a workaround is to use the GraphQL positions subgraph directly
|
|
46
|
+
query = f"""query {{
|
|
47
|
+
userBalances(where: {{
|
|
48
|
+
user: "{user.lower()}",
|
|
49
|
+
balance_gt: "{int(size_threshold * 10**6)}"
|
|
50
|
+
}}) {{
|
|
51
|
+
user
|
|
52
|
+
asset {{
|
|
53
|
+
id
|
|
54
|
+
condition {{
|
|
55
|
+
id
|
|
56
|
+
}}
|
|
57
|
+
complement
|
|
58
|
+
outcomeIndex
|
|
59
|
+
}}
|
|
60
|
+
balance
|
|
61
|
+
}}
|
|
62
|
+
}}
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
response = self.gql_positions_client.query(query)
|
|
66
|
+
return [GQLPosition(**pos) for pos in response["userBalances"]]
|
|
67
|
+
|
|
34
68
|
def get_positions(
|
|
35
69
|
self,
|
|
36
70
|
user: EthAddress,
|
|
@@ -32,7 +32,7 @@ class PolymarketWeb3Client:
|
|
|
32
32
|
def __init__(
|
|
33
33
|
self,
|
|
34
34
|
private_key: str,
|
|
35
|
-
signature_type: Literal[1, 2] = 1,
|
|
35
|
+
signature_type: Literal[0, 1, 2] = 1,
|
|
36
36
|
chain_id: Literal[137, 80002] = POLYGON,
|
|
37
37
|
):
|
|
38
38
|
self.w3 = Web3(Web3.HTTPProvider("https://polygon-rpc.com"))
|
|
@@ -90,15 +90,14 @@ class PolymarketWeb3Client:
|
|
|
90
90
|
)
|
|
91
91
|
|
|
92
92
|
match self.signature_type:
|
|
93
|
+
case 0:
|
|
94
|
+
self.address = self.account.address
|
|
93
95
|
case 1:
|
|
94
|
-
self.
|
|
96
|
+
self.address = self.get_poly_proxy_address()
|
|
95
97
|
case 2:
|
|
96
|
-
self.
|
|
98
|
+
self.address = self.get_safe_proxy_address()
|
|
97
99
|
self.safe_abi = _load_abi("Safe")
|
|
98
|
-
self.safe = self.contract(self.
|
|
99
|
-
case _:
|
|
100
|
-
msg = "Invalid signature_type: choose 1 for Magic/Email wallet or 2 for Safe/Gnosis wallet"
|
|
101
|
-
raise ValueError(msg)
|
|
100
|
+
self.safe = self.contract(self.address, self.safe_abi)
|
|
102
101
|
|
|
103
102
|
def _encode_split(self, condition_id: Keccak256, amount: int) -> str:
|
|
104
103
|
return self.conditional_tokens.encode_abi(
|
|
@@ -159,23 +158,16 @@ class PolymarketWeb3Client:
|
|
|
159
158
|
Explicitly passing the proxy address is faster due to only one contract function call.
|
|
160
159
|
"""
|
|
161
160
|
if address is None:
|
|
162
|
-
address = self.
|
|
161
|
+
address = self.address
|
|
163
162
|
balance_res = self.usdc.functions.balanceOf(address).call()
|
|
164
163
|
return float(balance_res / 1e6)
|
|
165
164
|
|
|
166
165
|
def get_token_balance(
|
|
167
|
-
self, token_id: str, address: EthAddress
|
|
166
|
+
self, token_id: str, address: Optional[EthAddress] = None
|
|
168
167
|
) -> float:
|
|
169
168
|
"""Get the token balance of the given address."""
|
|
170
169
|
if not address:
|
|
171
|
-
|
|
172
|
-
case 1:
|
|
173
|
-
address = self.get_poly_proxy_address(self.account.address)
|
|
174
|
-
case 2:
|
|
175
|
-
address = self.get_safe_proxy_address(self.account.address)
|
|
176
|
-
case _:
|
|
177
|
-
msg = "Invalid signature_type: choose 1 for Magic/Email wallet or 2 for Safe/Gnosis wallet"
|
|
178
|
-
raise ValueError(msg)
|
|
170
|
+
address = self.address
|
|
179
171
|
balance_res = self.conditional_tokens.functions.balanceOf(
|
|
180
172
|
address, int(token_id)
|
|
181
173
|
).call()
|
|
@@ -214,7 +206,7 @@ class PolymarketWeb3Client:
|
|
|
214
206
|
)
|
|
215
207
|
|
|
216
208
|
def split_position(
|
|
217
|
-
self, condition_id: Keccak256, amount:
|
|
209
|
+
self, condition_id: Keccak256, amount: float, neg_risk: bool = True
|
|
218
210
|
):
|
|
219
211
|
"""Splits usdc into two complementary positions of equal size."""
|
|
220
212
|
amount = int(amount * 1e6)
|
|
@@ -224,40 +216,47 @@ class PolymarketWeb3Client:
|
|
|
224
216
|
if neg_risk
|
|
225
217
|
else self.conditional_tokens_address
|
|
226
218
|
)
|
|
219
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
220
|
+
transaction = {
|
|
221
|
+
"nonce": nonce,
|
|
222
|
+
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
223
|
+
"gas": 1000000,
|
|
224
|
+
"from": self.account.address,
|
|
225
|
+
}
|
|
227
226
|
|
|
228
227
|
match self.signature_type:
|
|
228
|
+
case 0:
|
|
229
|
+
contract = (
|
|
230
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
231
|
+
)
|
|
232
|
+
txn_data = contract.functions.splitPosition(
|
|
233
|
+
self.usdc_address,
|
|
234
|
+
HASH_ZERO,
|
|
235
|
+
condition_id,
|
|
236
|
+
[1, 2],
|
|
237
|
+
amount,
|
|
238
|
+
).build_transaction(transaction=transaction)
|
|
229
239
|
case 1:
|
|
230
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
231
240
|
proxy_txn = {
|
|
232
241
|
"typeCode": 1,
|
|
233
242
|
"to": to,
|
|
234
243
|
"value": 0,
|
|
235
244
|
"data": data,
|
|
236
245
|
}
|
|
237
|
-
|
|
238
246
|
txn_data = self.proxy_factory.functions.proxy(
|
|
239
247
|
[proxy_txn]
|
|
240
|
-
).build_transaction(
|
|
241
|
-
{
|
|
242
|
-
"nonce": nonce,
|
|
243
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
244
|
-
"gas": 1000000,
|
|
245
|
-
"from": self.account.address,
|
|
246
|
-
}
|
|
247
|
-
)
|
|
248
|
+
).build_transaction(transaction=transaction)
|
|
248
249
|
case 2:
|
|
249
|
-
|
|
250
|
+
safe_nonce = self.safe.functions.nonce().call()
|
|
250
251
|
safe_txn = {
|
|
251
252
|
"to": to,
|
|
252
253
|
"data": data,
|
|
253
254
|
"operation": 0, # 1 for delegatecall, 0 for call
|
|
254
255
|
"value": 0,
|
|
255
256
|
}
|
|
256
|
-
|
|
257
257
|
packed_sig = sign_safe_transaction(
|
|
258
|
-
self.account, self.safe, safe_txn,
|
|
258
|
+
self.account, self.safe, safe_txn, safe_nonce
|
|
259
259
|
)
|
|
260
|
-
|
|
261
260
|
txn_data = self.safe.functions.execTransaction(
|
|
262
261
|
safe_txn["to"],
|
|
263
262
|
safe_txn["value"],
|
|
@@ -269,16 +268,7 @@ class PolymarketWeb3Client:
|
|
|
269
268
|
ADDRESS_ZERO, # gasToken
|
|
270
269
|
ADDRESS_ZERO, # refundReceiver
|
|
271
270
|
packed_sig,
|
|
272
|
-
).build_transaction(
|
|
273
|
-
{
|
|
274
|
-
"nonce": self.w3.eth.get_transaction_count(
|
|
275
|
-
self.account.address
|
|
276
|
-
),
|
|
277
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
278
|
-
"gas": 1000000,
|
|
279
|
-
"from": self.account.address,
|
|
280
|
-
}
|
|
281
|
-
)
|
|
271
|
+
).build_transaction(transaction=transaction)
|
|
282
272
|
|
|
283
273
|
# Sign and send transaction
|
|
284
274
|
signed_txn = self.account.sign_transaction(txn_data)
|
|
@@ -293,7 +283,7 @@ class PolymarketWeb3Client:
|
|
|
293
283
|
print("Done!")
|
|
294
284
|
|
|
295
285
|
def merge_position(
|
|
296
|
-
self, condition_id: Keccak256, amount:
|
|
286
|
+
self, condition_id: Keccak256, amount: float, neg_risk: bool = True
|
|
297
287
|
):
|
|
298
288
|
"""Merges two complementary positions into usdc."""
|
|
299
289
|
amount = int(amount * 1e6)
|
|
@@ -303,10 +293,27 @@ class PolymarketWeb3Client:
|
|
|
303
293
|
if neg_risk
|
|
304
294
|
else self.conditional_tokens_address
|
|
305
295
|
)
|
|
296
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
297
|
+
transaction = {
|
|
298
|
+
"nonce": nonce,
|
|
299
|
+
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
300
|
+
"gas": 1000000,
|
|
301
|
+
"from": self.account.address,
|
|
302
|
+
}
|
|
306
303
|
|
|
307
304
|
match self.signature_type:
|
|
305
|
+
case 0:
|
|
306
|
+
contract = (
|
|
307
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
308
|
+
)
|
|
309
|
+
txn_data = contract.functions.mergePositions(
|
|
310
|
+
self.usdc_address,
|
|
311
|
+
HASH_ZERO,
|
|
312
|
+
condition_id,
|
|
313
|
+
[1, 2],
|
|
314
|
+
amount,
|
|
315
|
+
).build_transaction(transaction=transaction)
|
|
308
316
|
case 1:
|
|
309
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
310
317
|
proxy_txn = {
|
|
311
318
|
"typeCode": 1,
|
|
312
319
|
"to": to,
|
|
@@ -316,25 +323,17 @@ class PolymarketWeb3Client:
|
|
|
316
323
|
|
|
317
324
|
txn_data = self.proxy_factory.functions.proxy(
|
|
318
325
|
[proxy_txn]
|
|
319
|
-
).build_transaction(
|
|
320
|
-
{
|
|
321
|
-
"nonce": nonce,
|
|
322
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
323
|
-
"gas": 1000000,
|
|
324
|
-
"from": self.account.address,
|
|
325
|
-
}
|
|
326
|
-
)
|
|
326
|
+
).build_transaction(transaction=transaction)
|
|
327
327
|
case 2:
|
|
328
|
-
|
|
328
|
+
safe_nonce = self.safe.functions.nonce().call()
|
|
329
329
|
safe_txn = {
|
|
330
330
|
"to": to,
|
|
331
331
|
"data": data,
|
|
332
332
|
"operation": 0, # 1 for delegatecall, 0 for call
|
|
333
333
|
"value": 0,
|
|
334
334
|
}
|
|
335
|
-
|
|
336
335
|
packed_sig = sign_safe_transaction(
|
|
337
|
-
self.account, self.safe, safe_txn,
|
|
336
|
+
self.account, self.safe, safe_txn, safe_nonce
|
|
338
337
|
)
|
|
339
338
|
txn_data = self.safe.functions.execTransaction(
|
|
340
339
|
safe_txn["to"],
|
|
@@ -347,16 +346,7 @@ class PolymarketWeb3Client:
|
|
|
347
346
|
ADDRESS_ZERO, # gasToken
|
|
348
347
|
ADDRESS_ZERO, # refundReceiver
|
|
349
348
|
packed_sig,
|
|
350
|
-
).build_transaction(
|
|
351
|
-
{
|
|
352
|
-
"nonce": self.w3.eth.get_transaction_count(
|
|
353
|
-
self.account.address
|
|
354
|
-
),
|
|
355
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
356
|
-
"gas": 1000000,
|
|
357
|
-
"from": self.account.address,
|
|
358
|
-
}
|
|
359
|
-
)
|
|
349
|
+
).build_transaction(transaction=transaction)
|
|
360
350
|
|
|
361
351
|
# Sign and send transaction
|
|
362
352
|
signed_txn = self.account.sign_transaction(txn_data)
|
|
@@ -380,9 +370,9 @@ class PolymarketWeb3Client:
|
|
|
380
370
|
where x is the number of shares of the first outcome
|
|
381
371
|
y is the number of shares of the second outcome.
|
|
382
372
|
"""
|
|
383
|
-
|
|
373
|
+
int_amounts = [int(amount * 1e6) for amount in amounts]
|
|
384
374
|
data = (
|
|
385
|
-
self._encode_redeem_neg_risk(condition_id,
|
|
375
|
+
self._encode_redeem_neg_risk(condition_id, int_amounts)
|
|
386
376
|
if neg_risk
|
|
387
377
|
else self._encode_redeem(condition_id)
|
|
388
378
|
)
|
|
@@ -391,38 +381,50 @@ class PolymarketWeb3Client:
|
|
|
391
381
|
if neg_risk
|
|
392
382
|
else self.conditional_tokens_address
|
|
393
383
|
)
|
|
384
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
385
|
+
transaction = {
|
|
386
|
+
"nonce": nonce,
|
|
387
|
+
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
388
|
+
"gas": 1000000,
|
|
389
|
+
"from": self.account.address,
|
|
390
|
+
}
|
|
394
391
|
|
|
395
392
|
match self.signature_type:
|
|
393
|
+
case 0:
|
|
394
|
+
contract = (
|
|
395
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
396
|
+
)
|
|
397
|
+
if neg_risk:
|
|
398
|
+
txn_data = contract.functions.redeemPositions(
|
|
399
|
+
condition_id, int_amounts
|
|
400
|
+
).build_transaction(transaction=transaction)
|
|
401
|
+
else:
|
|
402
|
+
txn_data = contract.functions.redeemPositions(
|
|
403
|
+
self.usdc_address,
|
|
404
|
+
HASH_ZERO,
|
|
405
|
+
condition_id,
|
|
406
|
+
[1, 2],
|
|
407
|
+
).build_transaction(transaction=transaction)
|
|
396
408
|
case 1:
|
|
397
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
398
409
|
proxy_txn = {
|
|
399
410
|
"typeCode": 1,
|
|
400
411
|
"to": to,
|
|
401
412
|
"value": 0,
|
|
402
413
|
"data": data,
|
|
403
414
|
}
|
|
404
|
-
|
|
405
415
|
txn_data = self.proxy_factory.functions.proxy(
|
|
406
416
|
[proxy_txn]
|
|
407
|
-
).build_transaction(
|
|
408
|
-
{
|
|
409
|
-
"nonce": nonce,
|
|
410
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
411
|
-
"gas": 1000000,
|
|
412
|
-
"from": self.account.address,
|
|
413
|
-
}
|
|
414
|
-
)
|
|
417
|
+
).build_transaction(transaction=transaction)
|
|
415
418
|
case 2:
|
|
416
|
-
|
|
419
|
+
safe_nonce = self.safe.functions.nonce().call()
|
|
417
420
|
safe_txn = {
|
|
418
421
|
"to": to,
|
|
419
422
|
"data": data,
|
|
420
423
|
"operation": 0, # 1 for delegatecall, 0 for call
|
|
421
424
|
"value": 0,
|
|
422
425
|
}
|
|
423
|
-
|
|
424
426
|
packed_sig = sign_safe_transaction(
|
|
425
|
-
self.account, self.safe, safe_txn,
|
|
427
|
+
self.account, self.safe, safe_txn, safe_nonce
|
|
426
428
|
)
|
|
427
429
|
txn_data = self.safe.functions.execTransaction(
|
|
428
430
|
safe_txn["to"],
|
|
@@ -435,16 +437,7 @@ class PolymarketWeb3Client:
|
|
|
435
437
|
ADDRESS_ZERO, # gasToken
|
|
436
438
|
ADDRESS_ZERO, # refundReceiver
|
|
437
439
|
packed_sig,
|
|
438
|
-
).build_transaction(
|
|
439
|
-
{
|
|
440
|
-
"nonce": self.w3.eth.get_transaction_count(
|
|
441
|
-
self.account.address
|
|
442
|
-
),
|
|
443
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
444
|
-
"gas": 1000000,
|
|
445
|
-
"from": self.account.address,
|
|
446
|
-
}
|
|
447
|
-
)
|
|
440
|
+
).build_transaction(transaction=transaction)
|
|
448
441
|
|
|
449
442
|
# Sign and send transaction
|
|
450
443
|
signed_txn = self.account.sign_transaction(txn_data)
|
|
@@ -459,18 +452,32 @@ class PolymarketWeb3Client:
|
|
|
459
452
|
print("Done!")
|
|
460
453
|
|
|
461
454
|
def convert_positions(
|
|
462
|
-
self,
|
|
455
|
+
self,
|
|
456
|
+
question_ids: list[Keccak256],
|
|
457
|
+
amount: float,
|
|
463
458
|
):
|
|
464
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
465
459
|
amount = int(amount * 1e6)
|
|
460
|
+
neg_risk_market_id = question_ids[0][:-2] + "00"
|
|
466
461
|
data = self._encode_convert(
|
|
467
462
|
neg_risk_market_id, get_index_set(question_ids), amount
|
|
468
463
|
)
|
|
469
464
|
to = self.neg_risk_adapter_address
|
|
465
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
466
|
+
transaction = {
|
|
467
|
+
"nonce": nonce,
|
|
468
|
+
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
469
|
+
"gas": 1000000,
|
|
470
|
+
"from": self.account.address,
|
|
471
|
+
}
|
|
470
472
|
|
|
471
473
|
match self.signature_type:
|
|
474
|
+
case 0:
|
|
475
|
+
txn_data = self.neg_risk_adapter.functions.convertPositions(
|
|
476
|
+
neg_risk_market_id,
|
|
477
|
+
get_index_set(question_ids),
|
|
478
|
+
amount,
|
|
479
|
+
).build_transaction(transaction=transaction)
|
|
472
480
|
case 1:
|
|
473
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
474
481
|
proxy_txn = {
|
|
475
482
|
"typeCode": 1,
|
|
476
483
|
"to": to,
|
|
@@ -480,16 +487,9 @@ class PolymarketWeb3Client:
|
|
|
480
487
|
|
|
481
488
|
txn_data = self.proxy_factory.functions.proxy(
|
|
482
489
|
[proxy_txn]
|
|
483
|
-
).build_transaction(
|
|
484
|
-
{
|
|
485
|
-
"nonce": nonce,
|
|
486
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
487
|
-
"gas": 1000000,
|
|
488
|
-
"from": self.account.address,
|
|
489
|
-
}
|
|
490
|
-
)
|
|
490
|
+
).build_transaction(transaction=transaction)
|
|
491
491
|
case 2:
|
|
492
|
-
|
|
492
|
+
safe_nonce = self.safe.functions.nonce().call()
|
|
493
493
|
safe_txn = {
|
|
494
494
|
"to": to,
|
|
495
495
|
"data": data,
|
|
@@ -498,7 +498,7 @@ class PolymarketWeb3Client:
|
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
packed_sig = sign_safe_transaction(
|
|
501
|
-
self.account, self.safe, safe_txn,
|
|
501
|
+
self.account, self.safe, safe_txn, safe_nonce
|
|
502
502
|
)
|
|
503
503
|
txn_data = self.safe.functions.execTransaction(
|
|
504
504
|
safe_txn["to"],
|
|
@@ -511,16 +511,7 @@ class PolymarketWeb3Client:
|
|
|
511
511
|
ADDRESS_ZERO, # gasToken
|
|
512
512
|
ADDRESS_ZERO, # refundReceiver
|
|
513
513
|
packed_sig,
|
|
514
|
-
).build_transaction(
|
|
515
|
-
{
|
|
516
|
-
"nonce": self.w3.eth.get_transaction_count(
|
|
517
|
-
self.account.address
|
|
518
|
-
),
|
|
519
|
-
"gasPrice": int(1.05 * self.w3.eth.gas_price),
|
|
520
|
-
"gas": 1000000,
|
|
521
|
-
"from": self.account.address,
|
|
522
|
-
}
|
|
523
|
-
)
|
|
514
|
+
).build_transaction(transaction=transaction)
|
|
524
515
|
|
|
525
516
|
# Sign and send transaction
|
|
526
517
|
signed_txn = self.account.sign_transaction(txn_data)
|
polymarket_apis/types/common.py
CHANGED
|
@@ -3,7 +3,7 @@ from datetime import UTC, datetime
|
|
|
3
3
|
from typing import Annotated
|
|
4
4
|
|
|
5
5
|
from dateutil import parser
|
|
6
|
-
from pydantic import AfterValidator, BaseModel, BeforeValidator, Field
|
|
6
|
+
from pydantic import AfterValidator, BaseModel, BeforeValidator, ConfigDict, Field
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def validate_keccak256(v: str) -> str:
|
|
@@ -30,5 +30,7 @@ EmptyString = Annotated[str, Field(pattern=r"^$", description="An empty string")
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class TimeseriesPoint(BaseModel):
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
34
|
+
|
|
35
|
+
value: float = Field(alias="p")
|
|
36
|
+
timestamp: datetime = Field(alias="t")
|
|
@@ -1,11 +1,43 @@
|
|
|
1
1
|
from datetime import UTC, datetime
|
|
2
2
|
from typing import Literal, Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel, Field, field_validator
|
|
4
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
5
5
|
|
|
6
6
|
from .common import EmptyString, EthAddress, Keccak256
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
class GQLPosition(BaseModel):
|
|
10
|
+
user: EthAddress
|
|
11
|
+
token_id: str
|
|
12
|
+
complementary_token_id: str
|
|
13
|
+
condition_id: Keccak256
|
|
14
|
+
outcome_index: int
|
|
15
|
+
balance: float
|
|
16
|
+
|
|
17
|
+
@model_validator(mode="before")
|
|
18
|
+
def _flatten(cls, values):
|
|
19
|
+
asset = values.get("asset")
|
|
20
|
+
if isinstance(asset, dict):
|
|
21
|
+
if "id" in asset:
|
|
22
|
+
values.setdefault("token_id", asset["id"])
|
|
23
|
+
if "complement" in asset:
|
|
24
|
+
values.setdefault("complementary_token_id", asset["complement"])
|
|
25
|
+
condition = asset.get("condition")
|
|
26
|
+
if isinstance(condition, dict) and "id" in condition:
|
|
27
|
+
values.setdefault("condition_id", condition["id"])
|
|
28
|
+
if "outcomeIndex" in asset:
|
|
29
|
+
values.setdefault("outcome_index", asset["outcomeIndex"])
|
|
30
|
+
values.pop("asset", None)
|
|
31
|
+
return values
|
|
32
|
+
|
|
33
|
+
@field_validator("balance", mode="before")
|
|
34
|
+
@classmethod
|
|
35
|
+
def _parse_balance(cls, value):
|
|
36
|
+
if isinstance(value, str):
|
|
37
|
+
value = int(value)
|
|
38
|
+
return value / 10**6
|
|
39
|
+
|
|
40
|
+
|
|
9
41
|
class Position(BaseModel):
|
|
10
42
|
# User identification
|
|
11
43
|
proxy_wallet: EthAddress = Field(alias="proxyWallet")
|
|
@@ -28,7 +28,7 @@ def _pack_primitive(typ: str, val: Any) -> bytes:
|
|
|
28
28
|
raw = val
|
|
29
29
|
|
|
30
30
|
if typ == "string":
|
|
31
|
-
if not isinstance(raw,
|
|
31
|
+
if not isinstance(raw, bytes | str):
|
|
32
32
|
msg = "string value must be str or bytes"
|
|
33
33
|
raise TypeError(msg)
|
|
34
34
|
return raw.encode() if isinstance(raw, str) else raw
|
|
@@ -47,7 +47,7 @@ def _pack_primitive(typ: str, val: Any) -> bytes:
|
|
|
47
47
|
b = raw.to_bytes(n, "big")
|
|
48
48
|
elif isinstance(raw, str) and raw.startswith("0x"):
|
|
49
49
|
b = bytes.fromhex(raw[2:])
|
|
50
|
-
elif isinstance(raw,
|
|
50
|
+
elif isinstance(raw, bytes | bytearray):
|
|
51
51
|
b = bytes(raw)
|
|
52
52
|
else:
|
|
53
53
|
msg = f"unsupported value for {typ}"
|
|
@@ -63,7 +63,7 @@ def _pack_primitive(typ: str, val: Any) -> bytes:
|
|
|
63
63
|
addr = raw[2:]
|
|
64
64
|
elif isinstance(raw, str):
|
|
65
65
|
addr = raw
|
|
66
|
-
elif isinstance(raw,
|
|
66
|
+
elif isinstance(raw, bytes | bytearray):
|
|
67
67
|
return bytes(raw[-20:])
|
|
68
68
|
else:
|
|
69
69
|
msg = "address must be hex string or bytes"
|
|
@@ -75,7 +75,7 @@ def _pack_primitive(typ: str, val: Any) -> bytes:
|
|
|
75
75
|
if m:
|
|
76
76
|
bits = int(m.group(1)) if m.group(1) else 256
|
|
77
77
|
size = bits // 8
|
|
78
|
-
if isinstance(raw,
|
|
78
|
+
if isinstance(raw, bytes | bytearray):
|
|
79
79
|
intval = int.from_bytes(raw, "big")
|
|
80
80
|
elif isinstance(raw, str) and raw.startswith("0x"):
|
|
81
81
|
intval = int(raw, 16)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polymarket-apis
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Unified Polymarket APIs with Pydantic data validation - Clob, Gamma, Data, Web3, Websockets, GraphQL clients.
|
|
5
|
+
Project-URL: repository, https://github.com/qualiaenjoyer/polymarket-apis
|
|
5
6
|
Author-email: Razvan Gheorghe <razvan@gheorghe.me>
|
|
6
7
|
Requires-Python: >=3.12
|
|
7
8
|
Requires-Dist: gql[httpx]>=4.0.0
|
|
@@ -88,7 +89,9 @@ flowchart LR
|
|
|
88
89
|
- check if a **Market** offers rewards by `condition_id` - **get_market_rewards()**
|
|
89
90
|
- get all active markets that offer rewards sorted by different metrics and ordered, filtered by a query, show your favourites from the web app - **get_reward_markets()** (*naming would do with some work*)
|
|
90
91
|
- #### Miscellaneous
|
|
91
|
-
- get price history
|
|
92
|
+
- get recent price history by `token_id` in the last 1h, 6h, 1d, 1w, 1m
|
|
93
|
+
- get price history by `token_id` in start/end interval
|
|
94
|
+
- get all price history by `token_id` in 2 min increments
|
|
92
95
|
- get **ClobMarket** by `condition_id`
|
|
93
96
|
- get all **ClobMarkets**
|
|
94
97
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
polymarket_apis/__init__.py,sha256=
|
|
1
|
+
polymarket_apis/__init__.py,sha256=qovYOBPNsQVc792R8UIVlGIrU-p-2DKclTosLQZbhT4,1126
|
|
2
2
|
polymarket_apis/clients/__init__.py,sha256=ruMvFEA4HNkbWEYqbnrCuYXR4PUwkV1XWG0w63we-LA,759
|
|
3
|
-
polymarket_apis/clients/clob_client.py,sha256=
|
|
4
|
-
polymarket_apis/clients/data_client.py,sha256=
|
|
3
|
+
polymarket_apis/clients/clob_client.py,sha256=gt3EqqMYOYB0_hI8HByp0flfKRsUkzL9gpQZJxX_r5k,32359
|
|
4
|
+
polymarket_apis/clients/data_client.py,sha256=xHZcjZMKpzPD8kipPc1qN08q7jWPqN3Py1hD28MQ2EQ,13151
|
|
5
5
|
polymarket_apis/clients/gamma_client.py,sha256=tBR_z03c3wh1_sxFbjNx54DcrFEjFB0TmJiOYNUc_pk,27632
|
|
6
6
|
polymarket_apis/clients/graphql_client.py,sha256=KgjxbXNWEXp82ZEz464in5mCn1PydnZqWq-g11xu9YU,1839
|
|
7
|
-
polymarket_apis/clients/web3_client.py,sha256=
|
|
7
|
+
polymarket_apis/clients/web3_client.py,sha256=KR48E9rVKfwqo6BCtGKQZ6VYyeRTiKPKMlI3sv0gjpk,19843
|
|
8
8
|
polymarket_apis/clients/websockets_client.py,sha256=gaGcsjqp1ZRyxrL6EWvZ9XaTv7koTPDzlcShj0u0B2A,7737
|
|
9
9
|
polymarket_apis/types/__init__.py,sha256=cyrAPNX0ty0wkMwZqB0lNA7n3JoOJCSeTpclmamMh9k,3258
|
|
10
10
|
polymarket_apis/types/clob_types.py,sha256=PmD_afx7zowX0cFJ39PQQ4jywGtwNjLfoke1H7iZZnI,11744
|
|
11
|
-
polymarket_apis/types/common.py,sha256=
|
|
12
|
-
polymarket_apis/types/data_types.py,sha256=
|
|
11
|
+
polymarket_apis/types/common.py,sha256=zFo10eBj4_3alOhYSilCm6Dh5ZkzhrdR9nuRSDQ_od8,1107
|
|
12
|
+
polymarket_apis/types/data_types.py,sha256=vBElvARw8LkH2xXwJkVErpQo0FkXJA3b44hKD5clSOI,5729
|
|
13
13
|
polymarket_apis/types/gamma_types.py,sha256=MoIUI6csqEp_vij7mkFi2IlIA99c5UNzI3zODSONWzQ,30641
|
|
14
14
|
polymarket_apis/types/websockets_types.py,sha256=XGTpfix2rFFQg-97ZkPagS_tHJJpSnB5mCMf_sT2MOI,11180
|
|
15
15
|
polymarket_apis/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -27,7 +27,7 @@ polymarket_apis/utilities/signing/hmac.py,sha256=1VPfO2yT8nyStk6U4AQeyTzQTt5-69P
|
|
|
27
27
|
polymarket_apis/utilities/signing/model.py,sha256=kVduuJGth7WSCUDCVVydCgPd4yEVI85gEmMxohXsvp0,191
|
|
28
28
|
polymarket_apis/utilities/signing/signer.py,sha256=7-nFALFrz0qg4F4lZRyHI41S7IWxr7t0WMLOHqHzJcg,732
|
|
29
29
|
polymarket_apis/utilities/web3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
polymarket_apis/utilities/web3/helpers.py,sha256=
|
|
30
|
+
polymarket_apis/utilities/web3/helpers.py,sha256=qzQA-n9rHbuFk57olvBa8P_T6IzrUsaQ2Vv0887EX4o,4694
|
|
31
31
|
polymarket_apis/utilities/web3/abis/CTFExchange.json,sha256=zt8fZnUaOrD8Vh5njM0EEUpeITWhuu0SZrIZigWxgV8,38499
|
|
32
32
|
polymarket_apis/utilities/web3/abis/ConditionalTokens.json,sha256=3TUcX7He74VMkoL1kxbDbtULZ70VY_EBe01pfByprsk,12584
|
|
33
33
|
polymarket_apis/utilities/web3/abis/NegRiskAdapter.json,sha256=HABIoRF1s1NgctpRTdaaNDqzODzgdZLE-s2E6ef4nAY,18867
|
|
@@ -38,6 +38,6 @@ polymarket_apis/utilities/web3/abis/SafeProxyFactory.json,sha256=bdr2WdYCRClXLTT
|
|
|
38
38
|
polymarket_apis/utilities/web3/abis/UChildERC20Proxy.json,sha256=ZyQC38U0uxInlmnW2VXDVD3TJfTIRmSNMkTxQsaG7oA,27396
|
|
39
39
|
polymarket_apis/utilities/web3/abis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
40
|
polymarket_apis/utilities/web3/abis/custom_contract_errors.py,sha256=GjCVn2b6iRheT7s-kc8Po9uwH9LfaHA1yRpJyjXRcxs,1172
|
|
41
|
-
polymarket_apis-0.3.
|
|
42
|
-
polymarket_apis-0.3.
|
|
43
|
-
polymarket_apis-0.3.
|
|
41
|
+
polymarket_apis-0.3.6.dist-info/METADATA,sha256=AToVPj7UlKers6vyyeIFBjgNvdridWvI7Y82_T8fHzg,12479
|
|
42
|
+
polymarket_apis-0.3.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
43
|
+
polymarket_apis-0.3.6.dist-info/RECORD,,
|
|
File without changes
|