poly-web3 0.0.1__py3-none-any.whl → 0.0.3__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.
@@ -41,11 +41,20 @@ if __name__ == "__main__":
41
41
  )
42
42
  ),
43
43
  )
44
- condition_id = "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c"
45
- service = PolyWeb3Service(clob_client=client, relayer_client=relayer_client)
44
+ condition_id = "0x38124532c68bf16aa5800433118463acdbf09152237b45bb9e11bd4b73e0d1c4"
45
+ service = PolyWeb3Service(
46
+ clob_client=client,
47
+ relayer_client=relayer_client,
48
+ rpc_url="https://polygon-bor.publicnode.com",
49
+ )
50
+ # Redeem by condition_id
46
51
  redeem = service.redeem(condition_id=condition_id)
47
52
  print(redeem)
48
53
 
54
+ # Redeem all positions that are currently redeemable (returns list or None)
55
+ redeem_all = service.redeem_all()
56
+ print(redeem_all)
57
+
49
58
  # Optional - Query operations (可选操作,用于查询)
50
59
  # can_redeem = service.is_condition_resolved(condition_id)
51
60
  # redeem_balance = service.get_redeemable_index_and_balance(
poly_web3/__init__.py CHANGED
@@ -16,7 +16,9 @@ from poly_web3.web3_service import SafeWeb3Service, EOAWeb3Service, ProxyWeb3Ser
16
16
 
17
17
 
18
18
  def PolyWeb3Service(
19
- clob_client: ClobClient, relayer_client: RelayClient = None
19
+ clob_client: ClobClient,
20
+ relayer_client: RelayClient = None,
21
+ rpc_url: str | None = None,
20
22
  ) -> Union[SafeWeb3Service, EOAWeb3Service, ProxyWeb3Service]: # noqa
21
23
  services = {
22
24
  WalletType.EOA: EOAWeb3Service,
@@ -26,6 +28,6 @@ def PolyWeb3Service(
26
28
 
27
29
  wallet_type = WalletType.get_with_code(clob_client.builder.sig_type)
28
30
  if service := services.get(wallet_type):
29
- return service(clob_client, relayer_client)
31
+ return service(clob_client, relayer_client, rpc_url=rpc_url)
30
32
  else:
31
33
  raise Exception(f"Unknown wallet type: {wallet_type}")
poly_web3/const.py CHANGED
@@ -12,7 +12,7 @@ GET_TRANSACTION = "/transaction"
12
12
  GET_TRANSACTIONS = "/transactions"
13
13
  SUBMIT_TRANSACTION = "/submit"
14
14
  GET_DEPLOYED = "/deployed"
15
- RPC_URL = "https://polygon-rpc.com"
15
+ RPC_URL = "https://polygon-bor.publicnode.com" # "https://polygon-rpc.com"
16
16
  RELAYER_URL = "https://relayer-v2.polymarket.com"
17
17
 
18
18
  STATE_NEW = ("STATE_NEW",)
poly_web3/log.py ADDED
@@ -0,0 +1,18 @@
1
+ # -*- coding = utf-8 -*-
2
+ # @Time: 2026-01-13 14:40:20
3
+ # @Author: PinBar
4
+ # @Site:
5
+ # @File: log.py
6
+ # @Software: PyCharm
7
+ import sys
8
+ import logging
9
+
10
+ logger = logging.getLogger("poly_web3")
11
+ handler = logging.StreamHandler(sys.stdout)
12
+ formatter = logging.Formatter(
13
+ '%(asctime)s | %(levelname)s | %(name)s | %(filename)s:%(lineno)d %(message)s',
14
+ datefmt='%Y-%m-%d %H:%M:%S'
15
+ )
16
+ handler.setFormatter(formatter)
17
+ logger.addHandler(handler)
18
+ logger.setLevel(logging.INFO)
@@ -24,13 +24,15 @@ from poly_web3.const import (
24
24
  NEG_RISK_ADAPTER_ABI_REDEEM,
25
25
  )
26
26
  from poly_web3.schema import WalletType
27
+ from poly_web3.log import logger
27
28
 
28
29
 
29
30
  class BaseWeb3Service:
30
31
  def __init__(
31
- self,
32
- clob_client: ClobClient = None,
33
- relayer_client: RelayClient = None,
32
+ self,
33
+ clob_client: ClobClient = None,
34
+ relayer_client: RelayClient = None,
35
+ rpc_url: str | None = None,
34
36
  ):
35
37
  self.relayer_client = relayer_client
36
38
  self.clob_client: ClobClient = clob_client
@@ -40,10 +42,43 @@ class BaseWeb3Service:
40
42
  )
41
43
  else:
42
44
  self.wallet_type = WalletType.PROXY
43
- self.w3: Web3 = Web3(Web3.HTTPProvider(RPC_URL))
45
+ self.rpc_url = rpc_url or RPC_URL
46
+ self.w3: Web3 = Web3(Web3.HTTPProvider(self.rpc_url))
44
47
  if self.wallet_type == WalletType.PROXY and relayer_client is None:
45
48
  raise Exception("relayer_client must be provided")
46
49
 
50
+ def _resolve_user_address(self):
51
+ funder = getattr(getattr(self.clob_client, "builder", None), "funder", None)
52
+ if funder:
53
+ return funder
54
+ return self.clob_client.get_address()
55
+
56
+ @classmethod
57
+ def fetch_positions(cls, user_address: str) -> list[dict]:
58
+ """
59
+ Fetches current positions for a user from the official Polymarket API.
60
+
61
+ :param user_address: User wallet address (0x-prefixed, 40 hex chars)
62
+ :return: List of position dictionaries from the API
63
+ """
64
+ url = "https://data-api.polymarket.com/positions"
65
+ params = {
66
+ "user": user_address,
67
+ "sizeThreshold": 1,
68
+ "limit": 100,
69
+ "redeemable": True,
70
+ "sortBy": "RESOLVING",
71
+ "sortDirection": "DESC",
72
+ }
73
+ try:
74
+ response = requests.get(url, params=params)
75
+ response.raise_for_status()
76
+ positions = response.json()
77
+ return [i for i in positions if i.get("percentPnl") > 0]
78
+ except Exception as e:
79
+ logger.error(f"Failed to fetch positions from API: {e}")
80
+ return []
81
+
47
82
  def is_condition_resolved(self, condition_id: str) -> bool:
48
83
  ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
49
84
  return ctf.functions.payoutDenominator(condition_id).call() > 0
@@ -60,8 +95,9 @@ class BaseWeb3Service:
60
95
  return winners
61
96
 
62
97
  def get_redeemable_index_and_balance(
63
- self, condition_id: str, owner: str
98
+ self, condition_id: str
64
99
  ) -> list[tuple]:
100
+ owner = self._resolve_user_address()
65
101
  winners = self.get_winning_indexes(condition_id)
66
102
  if not winners:
67
103
  return []
@@ -92,7 +128,7 @@ class BaseWeb3Service:
92
128
  )._encode_transaction_data()
93
129
 
94
130
  def build_neg_risk_redeem_tx_data(
95
- self, condition_id: str, redeem_amounts: list[int]
131
+ self, condition_id: str, redeem_amounts: list[int]
96
132
  ) -> str:
97
133
  nr_adapter = self.w3.eth.contract(
98
134
  address=NEG_RISK_ADAPTER_ADDRESS, abi=NEG_RISK_ADAPTER_ABI_REDEEM
@@ -124,7 +160,7 @@ class BaseWeb3Service:
124
160
  "id": 1,
125
161
  }
126
162
 
127
- response = requests.post(RPC_URL, json=payload)
163
+ response = requests.post(self.rpc_url, json=payload)
128
164
  result = response.json()
129
165
 
130
166
  if "result" in result:
@@ -135,9 +171,12 @@ class BaseWeb3Service:
135
171
  raise Exception("Estimate gas error: " + str(result))
136
172
 
137
173
  def redeem(
138
- self,
139
- condition_id: str,
140
- neg_risk: bool = False,
141
- redeem_amounts: list[int] | None = None,
174
+ self,
175
+ condition_id: str,
176
+ neg_risk: bool = False,
177
+ redeem_amounts: list[int] | None = None,
142
178
  ): # noqa:
143
179
  raise ImportError()
180
+
181
+ def redeem_all(self) -> list[dict] | None:
182
+ raise ImportError()
@@ -24,6 +24,7 @@ from poly_web3.web3_service.base import BaseWeb3Service
24
24
  from poly_web3.signature.build import derive_proxy_wallet, create_struct_hash
25
25
  from poly_web3.signature.hash_message import hash_message
26
26
  from poly_web3.signature import secp256k1
27
+ from poly_web3.log import logger
27
28
 
28
29
 
29
30
  class ProxyWeb3Service(BaseWeb3Service):
@@ -94,10 +95,10 @@ class ProxyWeb3Service(BaseWeb3Service):
94
95
  return function_data
95
96
 
96
97
  def redeem(
97
- self,
98
- condition_id: str,
99
- neg_risk: bool = False,
100
- redeem_amounts: list[int] | None = None,
98
+ self,
99
+ condition_id: str,
100
+ neg_risk: bool = False,
101
+ redeem_amounts: list[int] | None = None,
101
102
  ):
102
103
  if neg_risk:
103
104
  if redeem_amounts is None or len(redeem_amounts) != 2:
@@ -133,3 +134,33 @@ class ProxyWeb3Service(BaseWeb3Service):
133
134
  max_polls=100,
134
135
  )
135
136
  return redeem_res
137
+
138
+ def redeem_all(self) -> list[dict]:
139
+ logger.info("test")
140
+ return
141
+ positions = self.fetch_positions(user_address=self._resolve_user_address())
142
+ if not positions:
143
+ return []
144
+ redeem_list = []
145
+ for pos in positions:
146
+ condition_id = pos.get("conditionId")
147
+ try:
148
+ can_redeem = self.get_redeemable_index_and_balance(condition_id)
149
+ if not can_redeem:
150
+ continue
151
+ if pos.get("negativeRisk"):
152
+ amounts = [0, 0]
153
+ amounts[pos.get("outcomeIndex")] = pos.get("size")
154
+ int_amounts = [int(amount * 1e6) for amount in amounts]
155
+ redeem_res = self.redeem(condition_id=condition_id, redeem_amounts=int_amounts, neg_risk=True)
156
+ else:
157
+ redeem_res = self.redeem(condition_id=condition_id)
158
+ except Exception as e:
159
+ logger.error(f"redeem error, {condition_id=}, error={e}")
160
+ else:
161
+ redeem_list.append(redeem_res)
162
+ buy_price = pos.get("avgPrice")
163
+ size = pos.get("size")
164
+ volume = 1 / buy_price * (buy_price * size)
165
+ logger.info(f"slug={pos.get('slug')} redeem success, volume={volume:.4f} usdc")
166
+ return redeem_list
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: poly-web3
3
- Version: 0.0.1
3
+ Version: 0.0.3
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,7 +27,7 @@ Dynamic: requires-python
27
27
 
28
28
  # poly-web3
29
29
 
30
- Python SDK for Polymarket Proxy wallet redeem operations. Supports executing Conditional Token Fund (CTF) redeem operations on Polymarket through proxy wallets.
30
+ Python SDK for Polymarket Proxy wallet redeem operations. Supports executing Conditional Token Fund (CTF) redeem operations on Polymarket through proxy wallets, Free gas.
31
31
 
32
32
  [English](README.md) | [中文](README.zh.md)
33
33
 
@@ -39,6 +39,11 @@ This project is a Python rewrite of Polymarket's official TypeScript implementat
39
39
  - This project **only implements the official redeem functionality**, focusing on Conditional Token Fund (CTF) redeem operations
40
40
  - Other features (such as trading, order placement, etc.) are not within the scope of this project
41
41
 
42
+ **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.**
43
+
44
+ Reference:
45
+ - Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
46
+
42
47
  **Current Status:**
43
48
  - ✅ **Proxy Wallet** - Fully supported for redeem functionality
44
49
  - 🚧 **Safe Wallet** - Under development
@@ -120,12 +125,20 @@ relayer_client = RelayClient(
120
125
  )
121
126
 
122
127
  # Create service instance
123
- service = PolyWeb3Service(clob_client=client, relayer_client=relayer_client)
128
+ service = PolyWeb3Service(
129
+ clob_client=client,
130
+ relayer_client=relayer_client,
131
+ rpc_url="https://polygon-bor.publicnode.com", # optional
132
+ )
124
133
 
125
134
  # Execute redeem operation
126
135
  condition_id = "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c"
127
136
  redeem_result = service.redeem(condition_id=condition_id)
128
137
  print(f"Redeem result: {redeem_result}")
138
+
139
+ # Redeem all positions that are currently redeemable
140
+ redeem_all_result = service.redeem_all()
141
+ print(f"Redeem all result: {redeem_all_result}")
129
142
  ```
130
143
 
131
144
  ### Optional - Query Operations
@@ -211,6 +224,20 @@ result = service.redeem(
211
224
  )
212
225
  ```
213
226
 
227
+ ##### `redeem_all() -> list[dict] | None`
228
+
229
+ Redeem all positions that are currently redeemable for the authenticated account.
230
+
231
+ **Returns:**
232
+ - `list[dict] | None`: List of redeem results, or `None` if no redeemable positions
233
+
234
+ **Examples:**
235
+
236
+ ```python
237
+ # Redeem all positions that can be redeemed
238
+ service.redeem_all()
239
+ ```
240
+
214
241
  ## Project Structure
215
242
 
216
243
  ```
@@ -1,17 +1,18 @@
1
- examples/example_redeem.py,sha256=2KxsDjEtFrm9eo8Fme0CVAQ3NDu24G0Gfgwk4GdbzSo,1807
2
- poly_web3/__init__.py,sha256=sOPcaBjGctd_wT-UjztKq8rvcYBRA_HGhCr7rLXJOPM,1055
3
- poly_web3/const.py,sha256=VhH-UdsHN2DO993f60vnO093vSHAbaDwd6UIxb-GMuI,5091
1
+ examples/example_redeem.py,sha256=jBTHM_qHbTMDwBfeD_M2oc9sDXbd-8GseBUHxpQ0M3Q,2063
2
+ poly_web3/__init__.py,sha256=9SfcOZtFSMErtmGG7rTtd5L2xb3pg9I7e9ctto3u3qE,1111
3
+ poly_web3/const.py,sha256=mmNbN4rUMHjH_65aIGoVIB1c7My_8dB1uL0eiH36f_I,5130
4
+ poly_web3/log.py,sha256=UaMJkBntAuWFO4yLFVQRUQHqFTiRLXnexdaBrWIGkOI,487
4
5
  poly_web3/schema.py,sha256=sxZOzYglLLyQLwbSlQGpQzQ3IB7R059XQzJAO9T5frg,457
5
6
  poly_web3/signature/__init__.py,sha256=g5z1cPO_o-LPdvKb3VQu4aEcpJMMjJW-a9h9Mo6qczo,129
6
7
  poly_web3/signature/build.py,sha256=iSOzrMQOte6NWA981gQYPkCtRSoXhlFEcmV0bI7XMrA,3727
7
8
  poly_web3/signature/hash_message.py,sha256=IYCtqdV9HKE6bG6DZXt3wedrrXUn8KQ4RBgcqZq45aI,1367
8
9
  poly_web3/signature/secp256k1.py,sha256=AGpItsDFTAJR9ZpAfyC26llKaNmIfpD8v6HM3qqMAIA,1557
9
10
  poly_web3/web3_service/__init__.py,sha256=pxW7XqlSDZeoYc3ohwCcdtd_9aK3gJBIQ2ANXwHXUcE,324
10
- poly_web3/web3_service/base.py,sha256=5lfTKubdazwSNcG1o6Q0oTM6JM4-kgtzWkfohR3dBkI,4909
11
+ poly_web3/web3_service/base.py,sha256=7GIDYseLFuGfpfxmrfePNwywlMOUc9DGoj1U1XNpLIA,6377
11
12
  poly_web3/web3_service/eoa_service.py,sha256=MmwaAmpD_WLk4M-2w99_F7iHTZ-pKtAnPw4m5x2h6CA,446
12
- poly_web3/web3_service/proxy_service.py,sha256=qIhBqa8ESSTH0e5W9WVvqLPVpJpEM5sYeRLrkHLvC1Y,4760
13
+ poly_web3/web3_service/proxy_service.py,sha256=L_bNWzKAKv1nQ3cBn5AxbuP6Hv3D3SbqMZgp19DtNsw,6204
13
14
  poly_web3/web3_service/safe_service.py,sha256=KPmToVjqxFqFhFrDbY0hfR3Z00Alu9w31QKMyyluzX8,449
14
- poly_web3-0.0.1.dist-info/METADATA,sha256=3hej2oW7wH7z9KGMAEPW2UrmnirviY6ra__cJjbgAMI,8914
15
- poly_web3-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- poly_web3-0.0.1.dist-info/top_level.txt,sha256=wW40wsocHfhFgqSWWvYWrhwKGQU5IWkhyaz5J90vmHo,19
17
- poly_web3-0.0.1.dist-info/RECORD,,
15
+ poly_web3-0.0.3.dist-info/METADATA,sha256=wiZzR2j8aDtjj3meYr1kXmZKaRtlojHEe6LSrRptd0Y,10212
16
+ poly_web3-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ poly_web3-0.0.3.dist-info/top_level.txt,sha256=wW40wsocHfhFgqSWWvYWrhwKGQU5IWkhyaz5J90vmHo,19
18
+ poly_web3-0.0.3.dist-info/RECORD,,