poly-web3 1.0.0__tar.gz → 1.0.2__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.
- {poly_web3-1.0.0 → poly_web3-1.0.2}/PKG-INFO +164 -69
- {poly_web3-1.0.0 → poly_web3-1.0.2}/README.md +162 -67
- {poly_web3-1.0.0 → poly_web3-1.0.2}/examples/example_redeem.py +7 -7
- poly_web3-1.0.2/examples/example_split_merge.py +56 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/const.py +70 -6
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/web3_service/base.py +113 -4
- poly_web3-1.0.2/poly_web3/web3_service/eoa_service.py +40 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/web3_service/proxy_service.py +15 -11
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/web3_service/safe_service.py +7 -2
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3.egg-info/PKG-INFO +164 -69
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3.egg-info/SOURCES.txt +1 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3.egg-info/requires.txt +1 -1
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3.egg-info/top_level.txt +1 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/pyproject.toml +2 -2
- poly_web3-1.0.0/poly_web3/web3_service/eoa_service.py +0 -19
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/__init__.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/log.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/schema.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/signature/__init__.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/signature/build.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/signature/hash_message.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/signature/secp256k1.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3/web3_service/__init__.py +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/poly_web3.egg-info/dependency_links.txt +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/setup.cfg +0 -0
- {poly_web3-1.0.0 → poly_web3-1.0.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: poly-web3
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Polymarket Proxy wallet redeem SDK - Execute redeem operations on Polymarket using proxy wallets
|
|
5
5
|
Home-page: https://github.com/tosmart01/poly-web3
|
|
6
6
|
Author: PinBar
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.11
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
Requires-Dist: py-clob-client>=0.25.0
|
|
21
21
|
Requires-Dist: py-builder-relayer-client>=0.0.1
|
|
22
|
-
Requires-Dist: web3
|
|
22
|
+
Requires-Dist: web3<8,>=7.0.0
|
|
23
23
|
Requires-Dist: eth-utils==5.3.1
|
|
24
24
|
Requires-Dist: setuptools>=80.9.0
|
|
25
25
|
Dynamic: home-page
|
|
@@ -27,16 +27,61 @@ Dynamic: requires-python
|
|
|
27
27
|
|
|
28
28
|
# poly-web3
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
Python SDK for redeeming and splitting/merging Polymarket positions via Proxy/Safe wallets (gas-free).
|
|
31
35
|
|
|
32
36
|
[English](README.md) | [中文](README.zh.md)
|
|
33
37
|
|
|
38
|
+
```bash
|
|
39
|
+
Python >= 3.11
|
|
40
|
+
pip install poly-web3
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from poly_web3 import PolyWeb3Service
|
|
45
|
+
|
|
46
|
+
service = PolyWeb3Service(
|
|
47
|
+
clob_client=client,
|
|
48
|
+
relayer_client=relayer_client,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Redeem all redeemable positions for the current account.
|
|
52
|
+
service.redeem_all(batch_size=10)
|
|
53
|
+
|
|
54
|
+
# Split/Merge for binary markets (amount in human USDC units).
|
|
55
|
+
service.split("0x...", 10)
|
|
56
|
+
service.merge("0x...", 10)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
[See the full example](#quick-start)
|
|
60
|
+
|
|
61
|
+
## Redeem Behavior Notes
|
|
62
|
+
|
|
63
|
+
- Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
|
|
64
|
+
- `redeem_all` returns an empty list if there are no redeemable positions. If the returned list contains `None`, the redeem failed and should be retried.
|
|
65
|
+
|
|
66
|
+
## Split/Merge Notes
|
|
67
|
+
|
|
68
|
+
- `split`/`merge` are designed for binary markets (Yes/No) and use the default partition internally.
|
|
69
|
+
- `amount` is in human units (USDC), and is converted to base units internally.
|
|
70
|
+
|
|
71
|
+
## FAQ
|
|
72
|
+
|
|
73
|
+
1. **UI shows redeemable, but `redeem_all` returns `[]`**: The official Positions API can be delayed by 1–3 minutes. Wait a bit and retry.
|
|
74
|
+
2. **RPC error during redeem**: Switch RPC endpoints by setting `rpc_url` when instantiating `PolyWeb3Service`.
|
|
75
|
+
3. **Redeem stuck in `execute`**: The official relayer may be congested. Stop redeeming for 1 hour to avoid nonce looping from repeated submissions.
|
|
76
|
+
4. **Relayer client returns 403**: You need to apply for Builder API access and use a valid key. Reference: Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
77
|
+
5. **Relayer daily limit**: The official relayer typically limits to 100 requests per day. Prefer batch redeem (`batch_size`) to reduce the number of requests and avoid hitting the limit.
|
|
78
|
+
|
|
34
79
|
## About the Project
|
|
35
80
|
|
|
36
|
-
This project is a Python rewrite of Polymarket's official TypeScript implementation of `builder-relayer-client`, designed to provide Python developers with a convenient tool for executing
|
|
81
|
+
This project is a Python rewrite of Polymarket's official TypeScript implementation of `builder-relayer-client`, designed to provide Python developers with a convenient tool for executing Proxy and Safe wallet redeem operations on Polymarket.
|
|
37
82
|
|
|
38
83
|
**Important Notes:**
|
|
39
|
-
- This project
|
|
84
|
+
- This project implements official CTF redeem plus binary split/merge operations
|
|
40
85
|
- Other features (such as trading, order placement, etc.) are not within the scope of this project
|
|
41
86
|
|
|
42
87
|
**Some Polymarket-related redeem or write operations implemented in this project depend on access granted through Polymarket's Builder program. To perform real redeem operations against Polymarket, you must apply for and obtain a Builder key/credentials via Polymarket's official Builder application process. After approval you will receive the credentials required to use the Builder API—only then will the redeem flows in this repository work against the live service. For local development or automated tests, use mocks or testnet setups instead of real keys to avoid exposing production credentials.**
|
|
@@ -45,19 +90,11 @@ Reference:
|
|
|
45
90
|
- Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
46
91
|
|
|
47
92
|
**Current Status:**
|
|
48
|
-
- ✅ **Proxy Wallet** - Fully supported for redeem
|
|
49
|
-
-
|
|
93
|
+
- ✅ **Proxy Wallet** - Fully supported for redeem/split/merge
|
|
94
|
+
- ✅ **Safe Wallet** - Fully supported for redeem/split/merge
|
|
50
95
|
- 🚧 **EOA Wallet** - Under development
|
|
51
96
|
|
|
52
|
-
We welcome community contributions! If you'd like to help implement
|
|
53
|
-
|
|
54
|
-
## Features
|
|
55
|
-
|
|
56
|
-
- ✅ Support for Polymarket Proxy wallet redeem operations (currently only Proxy wallet is supported)
|
|
57
|
-
- ✅ Check if conditions are resolved
|
|
58
|
-
- ✅ Get redeemable indexes and balances
|
|
59
|
-
- ✅ Support for standard CTF redeem and negative risk (neg_risk) redeem
|
|
60
|
-
- ✅ Automatic transaction execution through Relayer service
|
|
97
|
+
We welcome community contributions! If you'd like to help implement EOA wallet redeem functionality, or have other improvement suggestions, please feel free to submit a Pull Request.
|
|
61
98
|
|
|
62
99
|
## Installation
|
|
63
100
|
|
|
@@ -79,7 +116,7 @@ uv add poly-web3
|
|
|
79
116
|
|
|
80
117
|
- `py-clob-client >= 0.25.0` - Polymarket CLOB client
|
|
81
118
|
- `py-builder-relayer-client >= 0.0.1` - Builder Relayer client
|
|
82
|
-
- `web3
|
|
119
|
+
- `web3 >= 7.0.0` - Web3.py library
|
|
83
120
|
- `eth-utils == 5.3.1` - Ethereum utilities library
|
|
84
121
|
|
|
85
122
|
## Quick Start
|
|
@@ -104,7 +141,7 @@ client = ClobClient(
|
|
|
104
141
|
host,
|
|
105
142
|
key=os.getenv("POLY_API_KEY"),
|
|
106
143
|
chain_id=chain_id,
|
|
107
|
-
signature_type=1, # Proxy wallet type
|
|
144
|
+
signature_type=1, # Proxy wallet type (signature_type=2 for Safe)
|
|
108
145
|
funder=os.getenv("POLYMARKET_PROXY_ADDRESS"),
|
|
109
146
|
)
|
|
110
147
|
|
|
@@ -132,35 +169,39 @@ service = PolyWeb3Service(
|
|
|
132
169
|
)
|
|
133
170
|
|
|
134
171
|
|
|
172
|
+
# Redeem all positions that are currently redeemable
|
|
173
|
+
redeem_all_result = service.redeem_all(batch_size=10)
|
|
174
|
+
print(f"Redeem all result: {redeem_all_result}")
|
|
175
|
+
# If redeem_all_result contains None, refer to README FAQ and retry.
|
|
176
|
+
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
177
|
+
print("Redeem failed for some items; please retry.")
|
|
178
|
+
|
|
135
179
|
# Execute redeem operation (batch)
|
|
136
180
|
condition_ids = [
|
|
137
181
|
"0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
|
|
138
182
|
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
139
183
|
]
|
|
140
|
-
redeem_batch_result = service.redeem(condition_ids, batch_size=
|
|
184
|
+
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
|
|
141
185
|
print(f"Redeem batch result: {redeem_batch_result}")
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
redeem_all_result = service.redeem_all(batch_size=20)
|
|
145
|
-
print(f"Redeem all result: {redeem_all_result}")
|
|
186
|
+
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
187
|
+
print("Redeem failed for some items; please retry.")
|
|
146
188
|
```
|
|
147
189
|
|
|
148
|
-
###
|
|
149
|
-
|
|
150
|
-
Before executing redeem, you can optionally check the condition status and query redeemable balances:
|
|
190
|
+
### Basic Usage - Split/Merge (Binary Markets)
|
|
151
191
|
|
|
152
192
|
```python
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# Get redeemable indexes and balances
|
|
158
|
-
redeem_balance = service.get_redeemable_index_and_balance(
|
|
159
|
-
condition_id, owner=client.builder.funder
|
|
193
|
+
# amount is in human units (USDC)
|
|
194
|
+
split_result = service.split(
|
|
195
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
196
|
+
1.5,
|
|
160
197
|
)
|
|
198
|
+
print(f"Split result: {split_result}")
|
|
161
199
|
|
|
162
|
-
|
|
163
|
-
|
|
200
|
+
merge_result = service.merge(
|
|
201
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
202
|
+
1.5,
|
|
203
|
+
)
|
|
204
|
+
print(f"Merge result: {merge_result}")
|
|
164
205
|
```
|
|
165
206
|
|
|
166
207
|
## API Documentation
|
|
@@ -171,6 +212,74 @@ The main service class that automatically selects the appropriate service implem
|
|
|
171
212
|
|
|
172
213
|
#### Methods
|
|
173
214
|
|
|
215
|
+
##### `redeem(condition_ids: list[str], batch_size: int = 20)`
|
|
216
|
+
|
|
217
|
+
Execute redeem operation.
|
|
218
|
+
|
|
219
|
+
**Parameters:**
|
|
220
|
+
- `condition_ids` (list[str]): List of condition IDs
|
|
221
|
+
- `batch_size` (int): Batch size for redeem requests
|
|
222
|
+
|
|
223
|
+
**Returns:**
|
|
224
|
+
- `dict | list[dict]`: Transaction result(s) containing transaction status and related information
|
|
225
|
+
|
|
226
|
+
**Examples:**
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
# Batch redeem
|
|
230
|
+
result = service.redeem(["0x...", "0x..."], batch_size=10)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
##### `redeem_all(batch_size: int = 20) -> list[dict]`
|
|
234
|
+
|
|
235
|
+
Redeem all positions that are currently redeemable for the authenticated account.
|
|
236
|
+
|
|
237
|
+
**Returns:**
|
|
238
|
+
- `list[dict]`: List of redeem results; empty list if no redeemable positions. If the list contains `None`, the redeem failed and should be retried.
|
|
239
|
+
|
|
240
|
+
**Examples:**
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
# Redeem all positions that can be redeemed
|
|
244
|
+
service.redeem_all(batch_size=10)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
##### `split(condition_id: str, amount: int | float | str)`
|
|
248
|
+
|
|
249
|
+
Split a binary (Yes/No) position. `amount` is in human USDC units.
|
|
250
|
+
|
|
251
|
+
**Parameters:**
|
|
252
|
+
- `condition_id` (str): Condition ID
|
|
253
|
+
- `amount` (int | float | str): Amount in USDC
|
|
254
|
+
|
|
255
|
+
**Returns:**
|
|
256
|
+
- `dict | None`: Transaction result
|
|
257
|
+
|
|
258
|
+
**Examples:**
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
result = service.split("0x...", 1.25)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
##### `merge(condition_id: str, amount: int | float | str)`
|
|
265
|
+
|
|
266
|
+
Merge a binary (Yes/No) position. `amount` is in human USDC units.
|
|
267
|
+
|
|
268
|
+
**Parameters:**
|
|
269
|
+
- `condition_id` (str): Condition ID
|
|
270
|
+
- `amount` (int | float | str): Amount in USDC
|
|
271
|
+
|
|
272
|
+
**Returns:**
|
|
273
|
+
- `dict | None`: Transaction result
|
|
274
|
+
|
|
275
|
+
**Examples:**
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
result = service.merge("0x...", 1.25)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### Optional APIs
|
|
282
|
+
|
|
174
283
|
##### `is_condition_resolved(condition_id: str) -> bool`
|
|
175
284
|
|
|
176
285
|
Check if the specified condition is resolved.
|
|
@@ -202,36 +311,22 @@ Get redeemable indexes and balances for the specified address.
|
|
|
202
311
|
**Returns:**
|
|
203
312
|
- `list[tuple]`: List of tuples containing (index, balance), balance is in USDC units
|
|
204
313
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Execute redeem operation.
|
|
208
|
-
|
|
209
|
-
**Parameters:**
|
|
210
|
-
- `condition_ids` (list[str]): List of condition IDs
|
|
211
|
-
- `batch_size` (int): Batch size for redeem requests
|
|
212
|
-
|
|
213
|
-
**Returns:**
|
|
214
|
-
- `dict | list[dict]`: Transaction result(s) containing transaction status and related information
|
|
314
|
+
## Optional: Query Operations
|
|
215
315
|
|
|
216
|
-
|
|
316
|
+
Before executing redeem, you can optionally check the condition status and query redeemable balances:
|
|
217
317
|
|
|
218
318
|
```python
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
##### `redeem_all(batch_size: int = 20) -> list[dict] | None`
|
|
224
|
-
|
|
225
|
-
Redeem all positions that are currently redeemable for the authenticated account.
|
|
226
|
-
|
|
227
|
-
**Returns:**
|
|
228
|
-
- `list[dict] | None`: List of redeem results, or `None` if no redeemable positions
|
|
319
|
+
# Check if condition is resolved
|
|
320
|
+
condition_id = "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c"
|
|
321
|
+
can_redeem = service.is_condition_resolved(condition_id)
|
|
229
322
|
|
|
230
|
-
|
|
323
|
+
# Get redeemable indexes and balances
|
|
324
|
+
redeem_balance = service.get_redeemable_index_and_balance(
|
|
325
|
+
condition_id, owner=client.builder.funder
|
|
326
|
+
)
|
|
231
327
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
service.redeem_all(batch_size=20)
|
|
328
|
+
print(f"Can redeem: {can_redeem}")
|
|
329
|
+
print(f"Redeemable balance: {redeem_balance}")
|
|
235
330
|
```
|
|
236
331
|
|
|
237
332
|
## Project Structure
|
|
@@ -249,14 +344,14 @@ poly_web3/
|
|
|
249
344
|
├── base.py # Base service class
|
|
250
345
|
├── proxy_service.py # Proxy wallet service (✅ Implemented)
|
|
251
346
|
├── eoa_service.py # EOA wallet service (🚧 Under development)
|
|
252
|
-
└── safe_service.py # Safe wallet service (
|
|
347
|
+
└── safe_service.py # Safe wallet service (✅ Implemented)
|
|
253
348
|
```
|
|
254
349
|
|
|
255
350
|
## Notes
|
|
256
351
|
|
|
257
352
|
1. **Environment Variable Security**: Make sure `.env` file is added to `.gitignore`, do not commit sensitive information to the code repository
|
|
258
353
|
2. **Network Support**: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
|
|
259
|
-
3. **Wallet Type**:
|
|
354
|
+
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
|
|
260
355
|
4. **Gas Fees**: Transactions are executed through Relayer, gas fees are handled by the Relayer
|
|
261
356
|
|
|
262
357
|
## Development
|
|
@@ -271,18 +366,18 @@ uv pip install -e ".[dev]"
|
|
|
271
366
|
|
|
272
367
|
```bash
|
|
273
368
|
python examples/example_redeem.py
|
|
369
|
+
python examples/example_split_merge.py
|
|
274
370
|
```
|
|
275
371
|
|
|
276
372
|
### Contributing
|
|
277
373
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- Implement Safe or EOA wallet support
|
|
281
|
-
- Fix bugs or improve existing functionality
|
|
282
|
-
- Add new features or improve documentation
|
|
283
|
-
- Make suggestions or report issues
|
|
374
|
+
Simple contribution flow:
|
|
284
375
|
|
|
285
|
-
|
|
376
|
+
1. Open an Issue to describe the change (bug/feature/doc).
|
|
377
|
+
2. Fork and create a branch: `feat/xxx` or `fix/xxx`.
|
|
378
|
+
3. Make changes and update/add docs if needed.
|
|
379
|
+
4. Run: `uv run python -m examples.example_redeem` or `uv run python -m examples.example_split_merge` (if applicable).
|
|
380
|
+
5. Open a Pull Request and link the Issue.
|
|
286
381
|
|
|
287
382
|
## License
|
|
288
383
|
|
|
@@ -1,15 +1,60 @@
|
|
|
1
1
|
# poly-web3
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Python SDK for redeeming and splitting/merging Polymarket positions via Proxy/Safe wallets (gas-free).
|
|
4
8
|
|
|
5
9
|
[English](README.md) | [中文](README.zh.md)
|
|
6
10
|
|
|
11
|
+
```bash
|
|
12
|
+
Python >= 3.11
|
|
13
|
+
pip install poly-web3
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from poly_web3 import PolyWeb3Service
|
|
18
|
+
|
|
19
|
+
service = PolyWeb3Service(
|
|
20
|
+
clob_client=client,
|
|
21
|
+
relayer_client=relayer_client,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Redeem all redeemable positions for the current account.
|
|
25
|
+
service.redeem_all(batch_size=10)
|
|
26
|
+
|
|
27
|
+
# Split/Merge for binary markets (amount in human USDC units).
|
|
28
|
+
service.split("0x...", 10)
|
|
29
|
+
service.merge("0x...", 10)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
[See the full example](#quick-start)
|
|
33
|
+
|
|
34
|
+
## Redeem Behavior Notes
|
|
35
|
+
|
|
36
|
+
- Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
|
|
37
|
+
- `redeem_all` returns an empty list if there are no redeemable positions. If the returned list contains `None`, the redeem failed and should be retried.
|
|
38
|
+
|
|
39
|
+
## Split/Merge Notes
|
|
40
|
+
|
|
41
|
+
- `split`/`merge` are designed for binary markets (Yes/No) and use the default partition internally.
|
|
42
|
+
- `amount` is in human units (USDC), and is converted to base units internally.
|
|
43
|
+
|
|
44
|
+
## FAQ
|
|
45
|
+
|
|
46
|
+
1. **UI shows redeemable, but `redeem_all` returns `[]`**: The official Positions API can be delayed by 1–3 minutes. Wait a bit and retry.
|
|
47
|
+
2. **RPC error during redeem**: Switch RPC endpoints by setting `rpc_url` when instantiating `PolyWeb3Service`.
|
|
48
|
+
3. **Redeem stuck in `execute`**: The official relayer may be congested. Stop redeeming for 1 hour to avoid nonce looping from repeated submissions.
|
|
49
|
+
4. **Relayer client returns 403**: You need to apply for Builder API access and use a valid key. Reference: Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
50
|
+
5. **Relayer daily limit**: The official relayer typically limits to 100 requests per day. Prefer batch redeem (`batch_size`) to reduce the number of requests and avoid hitting the limit.
|
|
51
|
+
|
|
7
52
|
## About the Project
|
|
8
53
|
|
|
9
|
-
This project is a Python rewrite of Polymarket's official TypeScript implementation of `builder-relayer-client`, designed to provide Python developers with a convenient tool for executing
|
|
54
|
+
This project is a Python rewrite of Polymarket's official TypeScript implementation of `builder-relayer-client`, designed to provide Python developers with a convenient tool for executing Proxy and Safe wallet redeem operations on Polymarket.
|
|
10
55
|
|
|
11
56
|
**Important Notes:**
|
|
12
|
-
- This project
|
|
57
|
+
- This project implements official CTF redeem plus binary split/merge operations
|
|
13
58
|
- Other features (such as trading, order placement, etc.) are not within the scope of this project
|
|
14
59
|
|
|
15
60
|
**Some Polymarket-related redeem or write operations implemented in this project depend on access granted through Polymarket's Builder program. To perform real redeem operations against Polymarket, you must apply for and obtain a Builder key/credentials via Polymarket's official Builder application process. After approval you will receive the credentials required to use the Builder API—only then will the redeem flows in this repository work against the live service. For local development or automated tests, use mocks or testnet setups instead of real keys to avoid exposing production credentials.**
|
|
@@ -18,19 +63,11 @@ Reference:
|
|
|
18
63
|
- Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
19
64
|
|
|
20
65
|
**Current Status:**
|
|
21
|
-
- ✅ **Proxy Wallet** - Fully supported for redeem
|
|
22
|
-
-
|
|
66
|
+
- ✅ **Proxy Wallet** - Fully supported for redeem/split/merge
|
|
67
|
+
- ✅ **Safe Wallet** - Fully supported for redeem/split/merge
|
|
23
68
|
- 🚧 **EOA Wallet** - Under development
|
|
24
69
|
|
|
25
|
-
We welcome community contributions! If you'd like to help implement
|
|
26
|
-
|
|
27
|
-
## Features
|
|
28
|
-
|
|
29
|
-
- ✅ Support for Polymarket Proxy wallet redeem operations (currently only Proxy wallet is supported)
|
|
30
|
-
- ✅ Check if conditions are resolved
|
|
31
|
-
- ✅ Get redeemable indexes and balances
|
|
32
|
-
- ✅ Support for standard CTF redeem and negative risk (neg_risk) redeem
|
|
33
|
-
- ✅ Automatic transaction execution through Relayer service
|
|
70
|
+
We welcome community contributions! If you'd like to help implement EOA wallet redeem functionality, or have other improvement suggestions, please feel free to submit a Pull Request.
|
|
34
71
|
|
|
35
72
|
## Installation
|
|
36
73
|
|
|
@@ -52,7 +89,7 @@ uv add poly-web3
|
|
|
52
89
|
|
|
53
90
|
- `py-clob-client >= 0.25.0` - Polymarket CLOB client
|
|
54
91
|
- `py-builder-relayer-client >= 0.0.1` - Builder Relayer client
|
|
55
|
-
- `web3
|
|
92
|
+
- `web3 >= 7.0.0` - Web3.py library
|
|
56
93
|
- `eth-utils == 5.3.1` - Ethereum utilities library
|
|
57
94
|
|
|
58
95
|
## Quick Start
|
|
@@ -77,7 +114,7 @@ client = ClobClient(
|
|
|
77
114
|
host,
|
|
78
115
|
key=os.getenv("POLY_API_KEY"),
|
|
79
116
|
chain_id=chain_id,
|
|
80
|
-
signature_type=1, # Proxy wallet type
|
|
117
|
+
signature_type=1, # Proxy wallet type (signature_type=2 for Safe)
|
|
81
118
|
funder=os.getenv("POLYMARKET_PROXY_ADDRESS"),
|
|
82
119
|
)
|
|
83
120
|
|
|
@@ -105,35 +142,39 @@ service = PolyWeb3Service(
|
|
|
105
142
|
)
|
|
106
143
|
|
|
107
144
|
|
|
145
|
+
# Redeem all positions that are currently redeemable
|
|
146
|
+
redeem_all_result = service.redeem_all(batch_size=10)
|
|
147
|
+
print(f"Redeem all result: {redeem_all_result}")
|
|
148
|
+
# If redeem_all_result contains None, refer to README FAQ and retry.
|
|
149
|
+
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
150
|
+
print("Redeem failed for some items; please retry.")
|
|
151
|
+
|
|
108
152
|
# Execute redeem operation (batch)
|
|
109
153
|
condition_ids = [
|
|
110
154
|
"0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
|
|
111
155
|
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
112
156
|
]
|
|
113
|
-
redeem_batch_result = service.redeem(condition_ids, batch_size=
|
|
157
|
+
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
|
|
114
158
|
print(f"Redeem batch result: {redeem_batch_result}")
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
redeem_all_result = service.redeem_all(batch_size=20)
|
|
118
|
-
print(f"Redeem all result: {redeem_all_result}")
|
|
159
|
+
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
160
|
+
print("Redeem failed for some items; please retry.")
|
|
119
161
|
```
|
|
120
162
|
|
|
121
|
-
###
|
|
122
|
-
|
|
123
|
-
Before executing redeem, you can optionally check the condition status and query redeemable balances:
|
|
163
|
+
### Basic Usage - Split/Merge (Binary Markets)
|
|
124
164
|
|
|
125
165
|
```python
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
# Get redeemable indexes and balances
|
|
131
|
-
redeem_balance = service.get_redeemable_index_and_balance(
|
|
132
|
-
condition_id, owner=client.builder.funder
|
|
166
|
+
# amount is in human units (USDC)
|
|
167
|
+
split_result = service.split(
|
|
168
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
169
|
+
1.5,
|
|
133
170
|
)
|
|
171
|
+
print(f"Split result: {split_result}")
|
|
134
172
|
|
|
135
|
-
|
|
136
|
-
|
|
173
|
+
merge_result = service.merge(
|
|
174
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
175
|
+
1.5,
|
|
176
|
+
)
|
|
177
|
+
print(f"Merge result: {merge_result}")
|
|
137
178
|
```
|
|
138
179
|
|
|
139
180
|
## API Documentation
|
|
@@ -144,6 +185,74 @@ The main service class that automatically selects the appropriate service implem
|
|
|
144
185
|
|
|
145
186
|
#### Methods
|
|
146
187
|
|
|
188
|
+
##### `redeem(condition_ids: list[str], batch_size: int = 20)`
|
|
189
|
+
|
|
190
|
+
Execute redeem operation.
|
|
191
|
+
|
|
192
|
+
**Parameters:**
|
|
193
|
+
- `condition_ids` (list[str]): List of condition IDs
|
|
194
|
+
- `batch_size` (int): Batch size for redeem requests
|
|
195
|
+
|
|
196
|
+
**Returns:**
|
|
197
|
+
- `dict | list[dict]`: Transaction result(s) containing transaction status and related information
|
|
198
|
+
|
|
199
|
+
**Examples:**
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# Batch redeem
|
|
203
|
+
result = service.redeem(["0x...", "0x..."], batch_size=10)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
##### `redeem_all(batch_size: int = 20) -> list[dict]`
|
|
207
|
+
|
|
208
|
+
Redeem all positions that are currently redeemable for the authenticated account.
|
|
209
|
+
|
|
210
|
+
**Returns:**
|
|
211
|
+
- `list[dict]`: List of redeem results; empty list if no redeemable positions. If the list contains `None`, the redeem failed and should be retried.
|
|
212
|
+
|
|
213
|
+
**Examples:**
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
# Redeem all positions that can be redeemed
|
|
217
|
+
service.redeem_all(batch_size=10)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
##### `split(condition_id: str, amount: int | float | str)`
|
|
221
|
+
|
|
222
|
+
Split a binary (Yes/No) position. `amount` is in human USDC units.
|
|
223
|
+
|
|
224
|
+
**Parameters:**
|
|
225
|
+
- `condition_id` (str): Condition ID
|
|
226
|
+
- `amount` (int | float | str): Amount in USDC
|
|
227
|
+
|
|
228
|
+
**Returns:**
|
|
229
|
+
- `dict | None`: Transaction result
|
|
230
|
+
|
|
231
|
+
**Examples:**
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
result = service.split("0x...", 1.25)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
##### `merge(condition_id: str, amount: int | float | str)`
|
|
238
|
+
|
|
239
|
+
Merge a binary (Yes/No) position. `amount` is in human USDC units.
|
|
240
|
+
|
|
241
|
+
**Parameters:**
|
|
242
|
+
- `condition_id` (str): Condition ID
|
|
243
|
+
- `amount` (int | float | str): Amount in USDC
|
|
244
|
+
|
|
245
|
+
**Returns:**
|
|
246
|
+
- `dict | None`: Transaction result
|
|
247
|
+
|
|
248
|
+
**Examples:**
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
result = service.merge("0x...", 1.25)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### Optional APIs
|
|
255
|
+
|
|
147
256
|
##### `is_condition_resolved(condition_id: str) -> bool`
|
|
148
257
|
|
|
149
258
|
Check if the specified condition is resolved.
|
|
@@ -175,36 +284,22 @@ Get redeemable indexes and balances for the specified address.
|
|
|
175
284
|
**Returns:**
|
|
176
285
|
- `list[tuple]`: List of tuples containing (index, balance), balance is in USDC units
|
|
177
286
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
Execute redeem operation.
|
|
181
|
-
|
|
182
|
-
**Parameters:**
|
|
183
|
-
- `condition_ids` (list[str]): List of condition IDs
|
|
184
|
-
- `batch_size` (int): Batch size for redeem requests
|
|
185
|
-
|
|
186
|
-
**Returns:**
|
|
187
|
-
- `dict | list[dict]`: Transaction result(s) containing transaction status and related information
|
|
287
|
+
## Optional: Query Operations
|
|
188
288
|
|
|
189
|
-
|
|
289
|
+
Before executing redeem, you can optionally check the condition status and query redeemable balances:
|
|
190
290
|
|
|
191
291
|
```python
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
##### `redeem_all(batch_size: int = 20) -> list[dict] | None`
|
|
197
|
-
|
|
198
|
-
Redeem all positions that are currently redeemable for the authenticated account.
|
|
199
|
-
|
|
200
|
-
**Returns:**
|
|
201
|
-
- `list[dict] | None`: List of redeem results, or `None` if no redeemable positions
|
|
292
|
+
# Check if condition is resolved
|
|
293
|
+
condition_id = "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c"
|
|
294
|
+
can_redeem = service.is_condition_resolved(condition_id)
|
|
202
295
|
|
|
203
|
-
|
|
296
|
+
# Get redeemable indexes and balances
|
|
297
|
+
redeem_balance = service.get_redeemable_index_and_balance(
|
|
298
|
+
condition_id, owner=client.builder.funder
|
|
299
|
+
)
|
|
204
300
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
service.redeem_all(batch_size=20)
|
|
301
|
+
print(f"Can redeem: {can_redeem}")
|
|
302
|
+
print(f"Redeemable balance: {redeem_balance}")
|
|
208
303
|
```
|
|
209
304
|
|
|
210
305
|
## Project Structure
|
|
@@ -222,14 +317,14 @@ poly_web3/
|
|
|
222
317
|
├── base.py # Base service class
|
|
223
318
|
├── proxy_service.py # Proxy wallet service (✅ Implemented)
|
|
224
319
|
├── eoa_service.py # EOA wallet service (🚧 Under development)
|
|
225
|
-
└── safe_service.py # Safe wallet service (
|
|
320
|
+
└── safe_service.py # Safe wallet service (✅ Implemented)
|
|
226
321
|
```
|
|
227
322
|
|
|
228
323
|
## Notes
|
|
229
324
|
|
|
230
325
|
1. **Environment Variable Security**: Make sure `.env` file is added to `.gitignore`, do not commit sensitive information to the code repository
|
|
231
326
|
2. **Network Support**: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
|
|
232
|
-
3. **Wallet Type**:
|
|
327
|
+
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
|
|
233
328
|
4. **Gas Fees**: Transactions are executed through Relayer, gas fees are handled by the Relayer
|
|
234
329
|
|
|
235
330
|
## Development
|
|
@@ -244,18 +339,18 @@ uv pip install -e ".[dev]"
|
|
|
244
339
|
|
|
245
340
|
```bash
|
|
246
341
|
python examples/example_redeem.py
|
|
342
|
+
python examples/example_split_merge.py
|
|
247
343
|
```
|
|
248
344
|
|
|
249
345
|
### Contributing
|
|
250
346
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- Implement Safe or EOA wallet support
|
|
254
|
-
- Fix bugs or improve existing functionality
|
|
255
|
-
- Add new features or improve documentation
|
|
256
|
-
- Make suggestions or report issues
|
|
347
|
+
Simple contribution flow:
|
|
257
348
|
|
|
258
|
-
|
|
349
|
+
1. Open an Issue to describe the change (bug/feature/doc).
|
|
350
|
+
2. Fork and create a branch: `feat/xxx` or `fix/xxx`.
|
|
351
|
+
3. Make changes and update/add docs if needed.
|
|
352
|
+
4. Run: `uv run python -m examples.example_redeem` or `uv run python -m examples.example_split_merge` (if applicable).
|
|
353
|
+
5. Open a Pull Request and link the Issue.
|
|
259
354
|
|
|
260
355
|
## License
|
|
261
356
|
|