poly-web3 1.0.1__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.1 → poly_web3-1.0.2}/PKG-INFO +92 -22
- {poly_web3-1.0.1 → poly_web3-1.0.2}/README.md +91 -21
- poly_web3-1.0.2/examples/example_split_merge.py +56 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/const.py +65 -1
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/web3_service/base.py +110 -1
- poly_web3-1.0.2/poly_web3/web3_service/eoa_service.py +40 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/web3_service/proxy_service.py +9 -4
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/web3_service/safe_service.py +7 -2
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3.egg-info/PKG-INFO +92 -22
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3.egg-info/SOURCES.txt +1 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/pyproject.toml +1 -1
- poly_web3-1.0.1/poly_web3/web3_service/eoa_service.py +0 -19
- {poly_web3-1.0.1 → poly_web3-1.0.2}/examples/example_redeem.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/__init__.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/log.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/schema.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/signature/__init__.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/signature/build.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/signature/hash_message.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/signature/secp256k1.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3/web3_service/__init__.py +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3.egg-info/dependency_links.txt +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3.egg-info/requires.txt +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/poly_web3.egg-info/top_level.txt +0 -0
- {poly_web3-1.0.1 → poly_web3-1.0.2}/setup.cfg +0 -0
- {poly_web3-1.0.1 → 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
|
|
@@ -27,11 +27,16 @@ 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
|
|
|
34
38
|
```bash
|
|
39
|
+
Python >= 3.11
|
|
35
40
|
pip install poly-web3
|
|
36
41
|
```
|
|
37
42
|
|
|
@@ -44,7 +49,11 @@ service = PolyWeb3Service(
|
|
|
44
49
|
)
|
|
45
50
|
|
|
46
51
|
# Redeem all redeemable positions for the current account.
|
|
47
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
[See the full example](#quick-start)
|
|
@@ -54,18 +63,25 @@ service.redeem_all(batch_size=20)
|
|
|
54
63
|
- Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
|
|
55
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.
|
|
56
65
|
|
|
57
|
-
##
|
|
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
|
|
58
72
|
|
|
59
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.
|
|
60
74
|
2. **RPC error during redeem**: Switch RPC endpoints by setting `rpc_url` when instantiating `PolyWeb3Service`.
|
|
61
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.
|
|
62
78
|
|
|
63
79
|
## About the Project
|
|
64
80
|
|
|
65
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.
|
|
66
82
|
|
|
67
83
|
**Important Notes:**
|
|
68
|
-
- This project
|
|
84
|
+
- This project implements official CTF redeem plus binary split/merge operations
|
|
69
85
|
- Other features (such as trading, order placement, etc.) are not within the scope of this project
|
|
70
86
|
|
|
71
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.**
|
|
@@ -74,8 +90,8 @@ Reference:
|
|
|
74
90
|
- Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
75
91
|
|
|
76
92
|
**Current Status:**
|
|
77
|
-
- ✅ **Proxy Wallet** - Fully supported for redeem
|
|
78
|
-
- ✅ **Safe Wallet** - Fully supported for redeem
|
|
93
|
+
- ✅ **Proxy Wallet** - Fully supported for redeem/split/merge
|
|
94
|
+
- ✅ **Safe Wallet** - Fully supported for redeem/split/merge
|
|
79
95
|
- 🚧 **EOA Wallet** - Under development
|
|
80
96
|
|
|
81
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.
|
|
@@ -153,21 +169,41 @@ service = PolyWeb3Service(
|
|
|
153
169
|
)
|
|
154
170
|
|
|
155
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
|
+
|
|
156
179
|
# Execute redeem operation (batch)
|
|
157
180
|
condition_ids = [
|
|
158
181
|
"0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
|
|
159
182
|
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
160
183
|
]
|
|
161
|
-
redeem_batch_result = service.redeem(condition_ids, batch_size=
|
|
184
|
+
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
|
|
162
185
|
print(f"Redeem batch result: {redeem_batch_result}")
|
|
163
|
-
|
|
164
|
-
# Redeem all positions that are currently redeemable
|
|
165
|
-
redeem_all_result = service.redeem_all(batch_size=20)
|
|
166
|
-
print(f"Redeem all result: {redeem_all_result}")
|
|
167
186
|
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
168
187
|
print("Redeem failed for some items; please retry.")
|
|
169
188
|
```
|
|
170
189
|
|
|
190
|
+
### Basic Usage - Split/Merge (Binary Markets)
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
# amount is in human units (USDC)
|
|
194
|
+
split_result = service.split(
|
|
195
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
196
|
+
1.5,
|
|
197
|
+
)
|
|
198
|
+
print(f"Split result: {split_result}")
|
|
199
|
+
|
|
200
|
+
merge_result = service.merge(
|
|
201
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
202
|
+
1.5,
|
|
203
|
+
)
|
|
204
|
+
print(f"Merge result: {merge_result}")
|
|
205
|
+
```
|
|
206
|
+
|
|
171
207
|
## API Documentation
|
|
172
208
|
|
|
173
209
|
### PolyWeb3Service
|
|
@@ -191,7 +227,7 @@ Execute redeem operation.
|
|
|
191
227
|
|
|
192
228
|
```python
|
|
193
229
|
# Batch redeem
|
|
194
|
-
result = service.redeem(["0x...", "0x..."], batch_size=
|
|
230
|
+
result = service.redeem(["0x...", "0x..."], batch_size=10)
|
|
195
231
|
```
|
|
196
232
|
|
|
197
233
|
##### `redeem_all(batch_size: int = 20) -> list[dict]`
|
|
@@ -205,7 +241,41 @@ Redeem all positions that are currently redeemable for the authenticated account
|
|
|
205
241
|
|
|
206
242
|
```python
|
|
207
243
|
# Redeem all positions that can be redeemed
|
|
208
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
209
279
|
```
|
|
210
280
|
|
|
211
281
|
#### Optional APIs
|
|
@@ -281,7 +351,7 @@ poly_web3/
|
|
|
281
351
|
|
|
282
352
|
1. **Environment Variable Security**: Make sure `.env` file is added to `.gitignore`, do not commit sensitive information to the code repository
|
|
283
353
|
2. **Network Support**: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
|
|
284
|
-
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet
|
|
354
|
+
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
|
|
285
355
|
4. **Gas Fees**: Transactions are executed through Relayer, gas fees are handled by the Relayer
|
|
286
356
|
|
|
287
357
|
## Development
|
|
@@ -296,18 +366,18 @@ uv pip install -e ".[dev]"
|
|
|
296
366
|
|
|
297
367
|
```bash
|
|
298
368
|
python examples/example_redeem.py
|
|
369
|
+
python examples/example_split_merge.py
|
|
299
370
|
```
|
|
300
371
|
|
|
301
372
|
### Contributing
|
|
302
373
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
- Implement EOA wallet support
|
|
306
|
-
- Fix bugs or improve existing functionality
|
|
307
|
-
- Add new features or improve documentation
|
|
308
|
-
- Make suggestions or report issues
|
|
374
|
+
Simple contribution flow:
|
|
309
375
|
|
|
310
|
-
|
|
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.
|
|
311
381
|
|
|
312
382
|
## License
|
|
313
383
|
|
|
@@ -1,10 +1,15 @@
|
|
|
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
|
|
|
7
11
|
```bash
|
|
12
|
+
Python >= 3.11
|
|
8
13
|
pip install poly-web3
|
|
9
14
|
```
|
|
10
15
|
|
|
@@ -17,7 +22,11 @@ service = PolyWeb3Service(
|
|
|
17
22
|
)
|
|
18
23
|
|
|
19
24
|
# Redeem all redeemable positions for the current account.
|
|
20
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
21
30
|
```
|
|
22
31
|
|
|
23
32
|
[See the full example](#quick-start)
|
|
@@ -27,18 +36,25 @@ service.redeem_all(batch_size=20)
|
|
|
27
36
|
- Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
|
|
28
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.
|
|
29
38
|
|
|
30
|
-
##
|
|
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
|
|
31
45
|
|
|
32
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.
|
|
33
47
|
2. **RPC error during redeem**: Switch RPC endpoints by setting `rpc_url` when instantiating `PolyWeb3Service`.
|
|
34
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.
|
|
35
51
|
|
|
36
52
|
## About the Project
|
|
37
53
|
|
|
38
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.
|
|
39
55
|
|
|
40
56
|
**Important Notes:**
|
|
41
|
-
- This project
|
|
57
|
+
- This project implements official CTF redeem plus binary split/merge operations
|
|
42
58
|
- Other features (such as trading, order placement, etc.) are not within the scope of this project
|
|
43
59
|
|
|
44
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.**
|
|
@@ -47,8 +63,8 @@ Reference:
|
|
|
47
63
|
- Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
48
64
|
|
|
49
65
|
**Current Status:**
|
|
50
|
-
- ✅ **Proxy Wallet** - Fully supported for redeem
|
|
51
|
-
- ✅ **Safe Wallet** - Fully supported for redeem
|
|
66
|
+
- ✅ **Proxy Wallet** - Fully supported for redeem/split/merge
|
|
67
|
+
- ✅ **Safe Wallet** - Fully supported for redeem/split/merge
|
|
52
68
|
- 🚧 **EOA Wallet** - Under development
|
|
53
69
|
|
|
54
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.
|
|
@@ -126,21 +142,41 @@ service = PolyWeb3Service(
|
|
|
126
142
|
)
|
|
127
143
|
|
|
128
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
|
+
|
|
129
152
|
# Execute redeem operation (batch)
|
|
130
153
|
condition_ids = [
|
|
131
154
|
"0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
|
|
132
155
|
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
133
156
|
]
|
|
134
|
-
redeem_batch_result = service.redeem(condition_ids, batch_size=
|
|
157
|
+
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
|
|
135
158
|
print(f"Redeem batch result: {redeem_batch_result}")
|
|
136
|
-
|
|
137
|
-
# Redeem all positions that are currently redeemable
|
|
138
|
-
redeem_all_result = service.redeem_all(batch_size=20)
|
|
139
|
-
print(f"Redeem all result: {redeem_all_result}")
|
|
140
159
|
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
141
160
|
print("Redeem failed for some items; please retry.")
|
|
142
161
|
```
|
|
143
162
|
|
|
163
|
+
### Basic Usage - Split/Merge (Binary Markets)
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# amount is in human units (USDC)
|
|
167
|
+
split_result = service.split(
|
|
168
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
169
|
+
1.5,
|
|
170
|
+
)
|
|
171
|
+
print(f"Split result: {split_result}")
|
|
172
|
+
|
|
173
|
+
merge_result = service.merge(
|
|
174
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
175
|
+
1.5,
|
|
176
|
+
)
|
|
177
|
+
print(f"Merge result: {merge_result}")
|
|
178
|
+
```
|
|
179
|
+
|
|
144
180
|
## API Documentation
|
|
145
181
|
|
|
146
182
|
### PolyWeb3Service
|
|
@@ -164,7 +200,7 @@ Execute redeem operation.
|
|
|
164
200
|
|
|
165
201
|
```python
|
|
166
202
|
# Batch redeem
|
|
167
|
-
result = service.redeem(["0x...", "0x..."], batch_size=
|
|
203
|
+
result = service.redeem(["0x...", "0x..."], batch_size=10)
|
|
168
204
|
```
|
|
169
205
|
|
|
170
206
|
##### `redeem_all(batch_size: int = 20) -> list[dict]`
|
|
@@ -178,7 +214,41 @@ Redeem all positions that are currently redeemable for the authenticated account
|
|
|
178
214
|
|
|
179
215
|
```python
|
|
180
216
|
# Redeem all positions that can be redeemed
|
|
181
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
182
252
|
```
|
|
183
253
|
|
|
184
254
|
#### Optional APIs
|
|
@@ -254,7 +324,7 @@ poly_web3/
|
|
|
254
324
|
|
|
255
325
|
1. **Environment Variable Security**: Make sure `.env` file is added to `.gitignore`, do not commit sensitive information to the code repository
|
|
256
326
|
2. **Network Support**: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
|
|
257
|
-
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet
|
|
327
|
+
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
|
|
258
328
|
4. **Gas Fees**: Transactions are executed through Relayer, gas fees are handled by the Relayer
|
|
259
329
|
|
|
260
330
|
## Development
|
|
@@ -269,18 +339,18 @@ uv pip install -e ".[dev]"
|
|
|
269
339
|
|
|
270
340
|
```bash
|
|
271
341
|
python examples/example_redeem.py
|
|
342
|
+
python examples/example_split_merge.py
|
|
272
343
|
```
|
|
273
344
|
|
|
274
345
|
### Contributing
|
|
275
346
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
- Implement EOA wallet support
|
|
279
|
-
- Fix bugs or improve existing functionality
|
|
280
|
-
- Add new features or improve documentation
|
|
281
|
-
- Make suggestions or report issues
|
|
347
|
+
Simple contribution flow:
|
|
282
348
|
|
|
283
|
-
|
|
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.
|
|
284
354
|
|
|
285
355
|
## License
|
|
286
356
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025-12-30 12:00:00
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: example_split_merge.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
import dotenv
|
|
10
|
+
from py_builder_relayer_client.client import RelayClient
|
|
11
|
+
from py_builder_signing_sdk.config import BuilderConfig
|
|
12
|
+
from py_builder_signing_sdk.sdk_types import BuilderApiKeyCreds
|
|
13
|
+
from py_clob_client.client import ClobClient
|
|
14
|
+
|
|
15
|
+
from poly_web3 import RELAYER_URL, PolyWeb3Service
|
|
16
|
+
|
|
17
|
+
dotenv.load_dotenv()
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
host: str = "https://clob.polymarket.com"
|
|
21
|
+
chain_id: int = 137 # No need to adjust this
|
|
22
|
+
client = ClobClient(
|
|
23
|
+
host,
|
|
24
|
+
key=os.getenv("POLY_API_KEY"),
|
|
25
|
+
chain_id=chain_id,
|
|
26
|
+
signature_type=1, # signature_type=2 for Safe
|
|
27
|
+
funder=os.getenv("POLYMARKET_PROXY_ADDRESS"),
|
|
28
|
+
)
|
|
29
|
+
creds = client.create_or_derive_api_creds()
|
|
30
|
+
client.set_api_creds(creds)
|
|
31
|
+
relayer_client = RelayClient(
|
|
32
|
+
RELAYER_URL,
|
|
33
|
+
chain_id,
|
|
34
|
+
os.getenv("POLY_API_KEY"),
|
|
35
|
+
BuilderConfig(
|
|
36
|
+
local_builder_creds=BuilderApiKeyCreds(
|
|
37
|
+
key=os.getenv("BUILDER_KEY"),
|
|
38
|
+
secret=os.getenv("BUILDER_SECRET"),
|
|
39
|
+
passphrase=os.getenv("BUILDER_PASSPHRASE"),
|
|
40
|
+
)
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
service = PolyWeb3Service(
|
|
44
|
+
clob_client=client,
|
|
45
|
+
relayer_client=relayer_client,
|
|
46
|
+
rpc_url="https://polygon-bor.publicnode.com",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
condition_id = "0x58ec217262554683a6c1fa2de9d87addef26dd3348367824d6890c68a98809b0"
|
|
50
|
+
amount = 10 # amount in human USDC units
|
|
51
|
+
|
|
52
|
+
split_result = service.split(condition_id, amount)
|
|
53
|
+
print(split_result)
|
|
54
|
+
|
|
55
|
+
merge_result = service.merge(condition_id, amount)
|
|
56
|
+
print(merge_result)
|
|
@@ -15,7 +15,7 @@ GET_DEPLOYED = "/deployed"
|
|
|
15
15
|
RPC_URL = "https://polygon-bor.publicnode.com" # "https://polygon-rpc.com"
|
|
16
16
|
RELAYER_URL = "https://relayer-v2.polymarket.com"
|
|
17
17
|
|
|
18
|
-
STATE_NEW =
|
|
18
|
+
STATE_NEW = "STATE_NEW"
|
|
19
19
|
STATE_EXECUTED = "STATE_EXECUTED"
|
|
20
20
|
STATE_MINED = "STATE_MINED"
|
|
21
21
|
STATE_INVALID = "STATE_INVALID"
|
|
@@ -78,6 +78,70 @@ CTF_ABI_REDEEM = [
|
|
|
78
78
|
}
|
|
79
79
|
]
|
|
80
80
|
|
|
81
|
+
CTF_ABI_SPLIT = [
|
|
82
|
+
{
|
|
83
|
+
"name": "splitPosition",
|
|
84
|
+
"type": "function",
|
|
85
|
+
"stateMutability": "nonpayable",
|
|
86
|
+
"inputs": [
|
|
87
|
+
{"name": "collateralToken", "type": "address"},
|
|
88
|
+
{"name": "parentCollectionId", "type": "bytes32"},
|
|
89
|
+
{"name": "conditionId", "type": "bytes32"},
|
|
90
|
+
{"name": "partition", "type": "uint256[]"},
|
|
91
|
+
{"name": "amount", "type": "uint256"},
|
|
92
|
+
],
|
|
93
|
+
"outputs": [],
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
CTF_ABI_MERGE = [
|
|
98
|
+
{
|
|
99
|
+
"name": "mergePositions",
|
|
100
|
+
"type": "function",
|
|
101
|
+
"stateMutability": "nonpayable",
|
|
102
|
+
"inputs": [
|
|
103
|
+
{"name": "collateralToken", "type": "address"},
|
|
104
|
+
{"name": "parentCollectionId", "type": "bytes32"},
|
|
105
|
+
{"name": "conditionId", "type": "bytes32"},
|
|
106
|
+
{"name": "partition", "type": "uint256[]"},
|
|
107
|
+
{"name": "amount", "type": "uint256"},
|
|
108
|
+
],
|
|
109
|
+
"outputs": [],
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
CTF_ABI_SPLIT = [
|
|
114
|
+
{
|
|
115
|
+
"name": "splitPosition",
|
|
116
|
+
"type": "function",
|
|
117
|
+
"stateMutability": "nonpayable",
|
|
118
|
+
"inputs": [
|
|
119
|
+
{"name": "collateralToken", "type": "address"},
|
|
120
|
+
{"name": "parentCollectionId", "type": "bytes32"},
|
|
121
|
+
{"name": "conditionId", "type": "bytes32"},
|
|
122
|
+
{"name": "partition", "type": "uint256[]"},
|
|
123
|
+
{"name": "amount", "type": "uint256"},
|
|
124
|
+
],
|
|
125
|
+
"outputs": [],
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
CTF_ABI_MERGE = [
|
|
130
|
+
{
|
|
131
|
+
"name": "mergePositions",
|
|
132
|
+
"type": "function",
|
|
133
|
+
"stateMutability": "nonpayable",
|
|
134
|
+
"inputs": [
|
|
135
|
+
{"name": "collateralToken", "type": "address"},
|
|
136
|
+
{"name": "parentCollectionId", "type": "bytes32"},
|
|
137
|
+
{"name": "conditionId", "type": "bytes32"},
|
|
138
|
+
{"name": "partition", "type": "uint256[]"},
|
|
139
|
+
{"name": "amount", "type": "uint256"},
|
|
140
|
+
],
|
|
141
|
+
"outputs": [],
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
|
|
81
145
|
NEG_RISK_ADAPTER_ABI_REDEEM = [
|
|
82
146
|
{
|
|
83
147
|
"name": "redeemPositions",
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# @File: base.py
|
|
6
6
|
# @Software: PyCharm
|
|
7
7
|
from typing import Any
|
|
8
|
+
from decimal import Decimal, InvalidOperation, ROUND_DOWN
|
|
8
9
|
|
|
9
10
|
from py_builder_relayer_client.client import RelayClient
|
|
10
11
|
from py_clob_client.client import ClobClient
|
|
@@ -19,6 +20,8 @@ from poly_web3.const import (
|
|
|
19
20
|
ZERO_BYTES32,
|
|
20
21
|
USDC_POLYGON,
|
|
21
22
|
CTF_ABI_REDEEM,
|
|
23
|
+
CTF_ABI_SPLIT,
|
|
24
|
+
CTF_ABI_MERGE,
|
|
22
25
|
NEG_RISK_ADAPTER_ADDRESS,
|
|
23
26
|
RELAYER_URL,
|
|
24
27
|
POL,
|
|
@@ -157,6 +160,40 @@ class BaseWeb3Service:
|
|
|
157
160
|
[1, 2],
|
|
158
161
|
)._encode_transaction_data()
|
|
159
162
|
|
|
163
|
+
def build_ctf_split_tx_data(
|
|
164
|
+
self,
|
|
165
|
+
condition_id: str,
|
|
166
|
+
partition: list[int],
|
|
167
|
+
amount: int,
|
|
168
|
+
collateral_token: str = USDC_POLYGON,
|
|
169
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
170
|
+
) -> str:
|
|
171
|
+
ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_SPLIT)
|
|
172
|
+
return ctf.functions.splitPosition(
|
|
173
|
+
collateral_token,
|
|
174
|
+
parent_collection_id,
|
|
175
|
+
condition_id,
|
|
176
|
+
partition,
|
|
177
|
+
amount,
|
|
178
|
+
)._encode_transaction_data()
|
|
179
|
+
|
|
180
|
+
def build_ctf_merge_tx_data(
|
|
181
|
+
self,
|
|
182
|
+
condition_id: str,
|
|
183
|
+
partition: list[int],
|
|
184
|
+
amount: int,
|
|
185
|
+
collateral_token: str = USDC_POLYGON,
|
|
186
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
187
|
+
) -> str:
|
|
188
|
+
ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_MERGE)
|
|
189
|
+
return ctf.functions.mergePositions(
|
|
190
|
+
collateral_token,
|
|
191
|
+
parent_collection_id,
|
|
192
|
+
condition_id,
|
|
193
|
+
partition,
|
|
194
|
+
amount,
|
|
195
|
+
)._encode_transaction_data()
|
|
196
|
+
|
|
160
197
|
def build_neg_risk_redeem_tx_data(
|
|
161
198
|
self, condition_id: str, redeem_amounts: list[int]
|
|
162
199
|
) -> str:
|
|
@@ -171,6 +208,9 @@ class BaseWeb3Service:
|
|
|
171
208
|
def _build_redeem_tx(self, to: str, data: str) -> Any:
|
|
172
209
|
raise NotImplementedError("redeem tx builder not implemented")
|
|
173
210
|
|
|
211
|
+
def _build_ctf_tx(self, to: str, data: str) -> Any:
|
|
212
|
+
return self._build_redeem_tx(to, data)
|
|
213
|
+
|
|
174
214
|
def _build_redeem_txs_from_positions(self, positions: list[dict]) -> list[Any]:
|
|
175
215
|
neg_amounts_by_condition: dict[str, list[float]] = {}
|
|
176
216
|
normal_conditions: set[str] = set()
|
|
@@ -207,8 +247,11 @@ class BaseWeb3Service:
|
|
|
207
247
|
)
|
|
208
248
|
return txs
|
|
209
249
|
|
|
250
|
+
def _submit_transactions(self, txs: list[Any], metadata: str) -> dict | None:
|
|
251
|
+
raise NotImplementedError("transaction submit not implemented")
|
|
252
|
+
|
|
210
253
|
def _submit_redeem(self, txs: list[Any]) -> dict | None:
|
|
211
|
-
|
|
254
|
+
return self._submit_transactions(txs, "redeem")
|
|
212
255
|
|
|
213
256
|
def _redeem_batch(self, condition_ids: list[str], batch_size: int) -> list[dict]:
|
|
214
257
|
"""
|
|
@@ -312,6 +355,26 @@ class BaseWeb3Service:
|
|
|
312
355
|
for i in range(0, len(condition_ids), batch_size)
|
|
313
356
|
]
|
|
314
357
|
|
|
358
|
+
@staticmethod
|
|
359
|
+
def _to_usdc_base_units(amount: int | float | str | Decimal) -> int:
|
|
360
|
+
try:
|
|
361
|
+
if isinstance(amount, Decimal):
|
|
362
|
+
human = amount
|
|
363
|
+
elif isinstance(amount, int):
|
|
364
|
+
human = Decimal(amount)
|
|
365
|
+
else:
|
|
366
|
+
human = Decimal(str(amount))
|
|
367
|
+
except (InvalidOperation, ValueError) as exc:
|
|
368
|
+
raise Exception(f"invalid amount: {amount}") from exc
|
|
369
|
+
if human <= 0:
|
|
370
|
+
raise Exception("amount must be greater than 0")
|
|
371
|
+
base_units = (human * Decimal("1000000")).quantize(
|
|
372
|
+
Decimal("1"), rounding=ROUND_DOWN
|
|
373
|
+
)
|
|
374
|
+
if base_units <= 0:
|
|
375
|
+
raise Exception("amount too small after conversion")
|
|
376
|
+
return int(base_units)
|
|
377
|
+
|
|
315
378
|
def redeem(
|
|
316
379
|
self,
|
|
317
380
|
condition_ids: str | list[str],
|
|
@@ -330,3 +393,49 @@ class BaseWeb3Service:
|
|
|
330
393
|
"""
|
|
331
394
|
positions = self.fetch_positions(user_address=self._resolve_user_address())
|
|
332
395
|
return self._redeem_from_positions(positions, batch_size)
|
|
396
|
+
|
|
397
|
+
def split(
|
|
398
|
+
self,
|
|
399
|
+
condition_id: str,
|
|
400
|
+
amount: int | float | str | Decimal,
|
|
401
|
+
collateral_token: str = USDC_POLYGON,
|
|
402
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
403
|
+
) -> dict | None:
|
|
404
|
+
"""
|
|
405
|
+
Split a position for binary markets (Yes/No), amount in human units.
|
|
406
|
+
"""
|
|
407
|
+
amount_base_units = self._to_usdc_base_units(amount)
|
|
408
|
+
tx = self._build_ctf_tx(
|
|
409
|
+
CTF_ADDRESS,
|
|
410
|
+
self.build_ctf_split_tx_data(
|
|
411
|
+
condition_id=condition_id,
|
|
412
|
+
partition=[1, 2],
|
|
413
|
+
amount=amount_base_units,
|
|
414
|
+
collateral_token=collateral_token,
|
|
415
|
+
parent_collection_id=parent_collection_id,
|
|
416
|
+
),
|
|
417
|
+
)
|
|
418
|
+
return self._submit_transactions([tx], "split")
|
|
419
|
+
|
|
420
|
+
def merge(
|
|
421
|
+
self,
|
|
422
|
+
condition_id: str,
|
|
423
|
+
amount: int | float | str | Decimal,
|
|
424
|
+
collateral_token: str = USDC_POLYGON,
|
|
425
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
426
|
+
) -> dict | None:
|
|
427
|
+
"""
|
|
428
|
+
Merge binary positions (Yes/No) back into a single position, amount in human units.
|
|
429
|
+
"""
|
|
430
|
+
amount_base_units = self._to_usdc_base_units(amount)
|
|
431
|
+
tx = self._build_ctf_tx(
|
|
432
|
+
CTF_ADDRESS,
|
|
433
|
+
self.build_ctf_merge_tx_data(
|
|
434
|
+
condition_id=condition_id,
|
|
435
|
+
partition=[1, 2],
|
|
436
|
+
amount=amount_base_units,
|
|
437
|
+
collateral_token=collateral_token,
|
|
438
|
+
parent_collection_id=parent_collection_id,
|
|
439
|
+
),
|
|
440
|
+
)
|
|
441
|
+
return self._submit_transactions([tx], "merge")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025-12-27 16:01:09
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: eoa_service.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
|
|
9
|
+
from poly_web3.const import USDC_POLYGON, ZERO_BYTES32
|
|
10
|
+
from poly_web3.web3_service.base import BaseWeb3Service
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EOAWeb3Service(BaseWeb3Service):
|
|
14
|
+
def redeem(
|
|
15
|
+
self,
|
|
16
|
+
condition_ids: str | list[str],
|
|
17
|
+
batch_size: int = 10,
|
|
18
|
+
):
|
|
19
|
+
raise ImportError("EOA wallet redeem not supported")
|
|
20
|
+
|
|
21
|
+
def redeem_all(self, batch_size: int = 10) -> list[dict]:
|
|
22
|
+
raise ImportError("EOA wallet redeem not supported")
|
|
23
|
+
|
|
24
|
+
def split(
|
|
25
|
+
self,
|
|
26
|
+
condition_id: str,
|
|
27
|
+
amount: int | float | str | Decimal,
|
|
28
|
+
collateral_token: str = USDC_POLYGON,
|
|
29
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
30
|
+
) -> dict | None:
|
|
31
|
+
raise ImportError("EOA wallet split not supported")
|
|
32
|
+
|
|
33
|
+
def merge(
|
|
34
|
+
self,
|
|
35
|
+
condition_id: str,
|
|
36
|
+
amount: int | float | str | Decimal,
|
|
37
|
+
collateral_token: str = USDC_POLYGON,
|
|
38
|
+
parent_collection_id: str = ZERO_BYTES32,
|
|
39
|
+
) -> dict | None:
|
|
40
|
+
raise ImportError("EOA wallet merge not supported")
|
|
@@ -32,7 +32,9 @@ class ProxyWeb3Service(BaseWeb3Service):
|
|
|
32
32
|
"typeCode": 1,
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
def build_proxy_transaction_request(
|
|
35
|
+
def build_proxy_transaction_request(
|
|
36
|
+
self, args: dict, metadata: str = "redeem"
|
|
37
|
+
) -> dict:
|
|
36
38
|
proxy_contract_config = self.get_contract_config()["ProxyContracts"]
|
|
37
39
|
to = proxy_contract_config["ProxyFactory"]
|
|
38
40
|
proxy = derive_proxy_wallet(args["from"], to, PROXY_INIT_CODE_HASH)
|
|
@@ -80,7 +82,7 @@ class ProxyWeb3Service(BaseWeb3Service):
|
|
|
80
82
|
"signature": final_sig,
|
|
81
83
|
"signatureParams": sig_params,
|
|
82
84
|
"type": self.wallet_type.value,
|
|
83
|
-
"metadata":
|
|
85
|
+
"metadata": metadata,
|
|
84
86
|
}
|
|
85
87
|
return req
|
|
86
88
|
|
|
@@ -96,7 +98,7 @@ class ProxyWeb3Service(BaseWeb3Service):
|
|
|
96
98
|
# Encode function data (compatible with web3 6/7)
|
|
97
99
|
return contract.functions.proxy(calls_data)._encode_transaction_data()
|
|
98
100
|
|
|
99
|
-
def
|
|
101
|
+
def _submit_transactions(self, txs: list[dict], metadata: str) -> dict:
|
|
100
102
|
if self.clob_client is None:
|
|
101
103
|
raise Exception("signer not found")
|
|
102
104
|
_from = to_checksum_address(self.clob_client.get_address())
|
|
@@ -108,7 +110,7 @@ class ProxyWeb3Service(BaseWeb3Service):
|
|
|
108
110
|
"relay": rp["address"],
|
|
109
111
|
"nonce": rp["nonce"],
|
|
110
112
|
}
|
|
111
|
-
req = self.build_proxy_transaction_request(args)
|
|
113
|
+
req = self.build_proxy_transaction_request(args, metadata=metadata)
|
|
112
114
|
headers = self.relayer_client._generate_builder_headers(
|
|
113
115
|
"POST", SUBMIT_TRANSACTION, req
|
|
114
116
|
)
|
|
@@ -122,3 +124,6 @@ class ProxyWeb3Service(BaseWeb3Service):
|
|
|
122
124
|
max_polls=100,
|
|
123
125
|
)
|
|
124
126
|
return redeem_res
|
|
127
|
+
|
|
128
|
+
def _submit_redeem(self, txs: list[dict]) -> dict:
|
|
129
|
+
return self._submit_transactions(txs, "redeem")
|
|
@@ -17,8 +17,13 @@ class SafeWeb3Service(BaseWeb3Service):
|
|
|
17
17
|
operation=OperationType.Call,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
def
|
|
20
|
+
def _submit_transactions(
|
|
21
|
+
self, txs: list[SafeTransaction], metadata: str
|
|
22
|
+
) -> dict | None:
|
|
21
23
|
if self.relayer_client is None:
|
|
22
24
|
raise Exception("relayer_client not found")
|
|
23
|
-
resp = self.relayer_client.execute(txs,
|
|
25
|
+
resp = self.relayer_client.execute(txs, metadata)
|
|
24
26
|
return resp.wait()
|
|
27
|
+
|
|
28
|
+
def _submit_redeem(self, txs: list[SafeTransaction]) -> dict | None:
|
|
29
|
+
return self._submit_transactions(txs, "redeem")
|
|
@@ -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
|
|
@@ -27,11 +27,16 @@ 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
|
|
|
34
38
|
```bash
|
|
39
|
+
Python >= 3.11
|
|
35
40
|
pip install poly-web3
|
|
36
41
|
```
|
|
37
42
|
|
|
@@ -44,7 +49,11 @@ service = PolyWeb3Service(
|
|
|
44
49
|
)
|
|
45
50
|
|
|
46
51
|
# Redeem all redeemable positions for the current account.
|
|
47
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
[See the full example](#quick-start)
|
|
@@ -54,18 +63,25 @@ service.redeem_all(batch_size=20)
|
|
|
54
63
|
- Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
|
|
55
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.
|
|
56
65
|
|
|
57
|
-
##
|
|
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
|
|
58
72
|
|
|
59
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.
|
|
60
74
|
2. **RPC error during redeem**: Switch RPC endpoints by setting `rpc_url` when instantiating `PolyWeb3Service`.
|
|
61
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.
|
|
62
78
|
|
|
63
79
|
## About the Project
|
|
64
80
|
|
|
65
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.
|
|
66
82
|
|
|
67
83
|
**Important Notes:**
|
|
68
|
-
- This project
|
|
84
|
+
- This project implements official CTF redeem plus binary split/merge operations
|
|
69
85
|
- Other features (such as trading, order placement, etc.) are not within the scope of this project
|
|
70
86
|
|
|
71
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.**
|
|
@@ -74,8 +90,8 @@ Reference:
|
|
|
74
90
|
- Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
|
|
75
91
|
|
|
76
92
|
**Current Status:**
|
|
77
|
-
- ✅ **Proxy Wallet** - Fully supported for redeem
|
|
78
|
-
- ✅ **Safe Wallet** - Fully supported for redeem
|
|
93
|
+
- ✅ **Proxy Wallet** - Fully supported for redeem/split/merge
|
|
94
|
+
- ✅ **Safe Wallet** - Fully supported for redeem/split/merge
|
|
79
95
|
- 🚧 **EOA Wallet** - Under development
|
|
80
96
|
|
|
81
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.
|
|
@@ -153,21 +169,41 @@ service = PolyWeb3Service(
|
|
|
153
169
|
)
|
|
154
170
|
|
|
155
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
|
+
|
|
156
179
|
# Execute redeem operation (batch)
|
|
157
180
|
condition_ids = [
|
|
158
181
|
"0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
|
|
159
182
|
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
160
183
|
]
|
|
161
|
-
redeem_batch_result = service.redeem(condition_ids, batch_size=
|
|
184
|
+
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
|
|
162
185
|
print(f"Redeem batch result: {redeem_batch_result}")
|
|
163
|
-
|
|
164
|
-
# Redeem all positions that are currently redeemable
|
|
165
|
-
redeem_all_result = service.redeem_all(batch_size=20)
|
|
166
|
-
print(f"Redeem all result: {redeem_all_result}")
|
|
167
186
|
if redeem_all_result and any(item is None for item in redeem_all_result):
|
|
168
187
|
print("Redeem failed for some items; please retry.")
|
|
169
188
|
```
|
|
170
189
|
|
|
190
|
+
### Basic Usage - Split/Merge (Binary Markets)
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
# amount is in human units (USDC)
|
|
194
|
+
split_result = service.split(
|
|
195
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
196
|
+
1.5,
|
|
197
|
+
)
|
|
198
|
+
print(f"Split result: {split_result}")
|
|
199
|
+
|
|
200
|
+
merge_result = service.merge(
|
|
201
|
+
"0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
|
|
202
|
+
1.5,
|
|
203
|
+
)
|
|
204
|
+
print(f"Merge result: {merge_result}")
|
|
205
|
+
```
|
|
206
|
+
|
|
171
207
|
## API Documentation
|
|
172
208
|
|
|
173
209
|
### PolyWeb3Service
|
|
@@ -191,7 +227,7 @@ Execute redeem operation.
|
|
|
191
227
|
|
|
192
228
|
```python
|
|
193
229
|
# Batch redeem
|
|
194
|
-
result = service.redeem(["0x...", "0x..."], batch_size=
|
|
230
|
+
result = service.redeem(["0x...", "0x..."], batch_size=10)
|
|
195
231
|
```
|
|
196
232
|
|
|
197
233
|
##### `redeem_all(batch_size: int = 20) -> list[dict]`
|
|
@@ -205,7 +241,41 @@ Redeem all positions that are currently redeemable for the authenticated account
|
|
|
205
241
|
|
|
206
242
|
```python
|
|
207
243
|
# Redeem all positions that can be redeemed
|
|
208
|
-
service.redeem_all(batch_size=
|
|
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)
|
|
209
279
|
```
|
|
210
280
|
|
|
211
281
|
#### Optional APIs
|
|
@@ -281,7 +351,7 @@ poly_web3/
|
|
|
281
351
|
|
|
282
352
|
1. **Environment Variable Security**: Make sure `.env` file is added to `.gitignore`, do not commit sensitive information to the code repository
|
|
283
353
|
2. **Network Support**: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
|
|
284
|
-
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet
|
|
354
|
+
3. **Wallet Type**: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
|
|
285
355
|
4. **Gas Fees**: Transactions are executed through Relayer, gas fees are handled by the Relayer
|
|
286
356
|
|
|
287
357
|
## Development
|
|
@@ -296,18 +366,18 @@ uv pip install -e ".[dev]"
|
|
|
296
366
|
|
|
297
367
|
```bash
|
|
298
368
|
python examples/example_redeem.py
|
|
369
|
+
python examples/example_split_merge.py
|
|
299
370
|
```
|
|
300
371
|
|
|
301
372
|
### Contributing
|
|
302
373
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
- Implement EOA wallet support
|
|
306
|
-
- Fix bugs or improve existing functionality
|
|
307
|
-
- Add new features or improve documentation
|
|
308
|
-
- Make suggestions or report issues
|
|
374
|
+
Simple contribution flow:
|
|
309
375
|
|
|
310
|
-
|
|
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.
|
|
311
381
|
|
|
312
382
|
## License
|
|
313
383
|
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025-12-27 16:01:09
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: eoa_service.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from poly_web3.web3_service.base import BaseWeb3Service
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class EOAWeb3Service(BaseWeb3Service):
|
|
11
|
-
def redeem(
|
|
12
|
-
self,
|
|
13
|
-
condition_ids: str | list[str],
|
|
14
|
-
batch_size: int = 10,
|
|
15
|
-
):
|
|
16
|
-
raise ImportError("EOA wallet redeem not supported")
|
|
17
|
-
|
|
18
|
-
def redeem_all(self, batch_size: int = 10) -> list[dict]:
|
|
19
|
-
raise ImportError("EOA wallet redeem not supported")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|