poly-web3 0.0.2__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.
@@ -1,181 +1,182 @@
1
- # -*- coding = utf-8 -*-
2
- # @Time: 2025-12-27 15:57:07
3
- # @Author: PinBar
4
- # @Site:
5
- # @File: base.py
6
- # @Software: PyCharm
7
- import requests
8
- from py_builder_relayer_client.client import RelayClient
9
- from py_clob_client.client import ClobClient
10
- from web3 import Web3
11
-
12
- from poly_web3.const import (
13
- RPC_URL,
14
- CTF_ADDRESS,
15
- CTF_ABI_PAYOUT,
16
- ZERO_BYTES32,
17
- USDC_POLYGON,
18
- CTF_ABI_REDEEM,
19
- NEG_RISK_ADAPTER_ADDRESS,
20
- RELAYER_URL,
21
- POL,
22
- AMOY,
23
- GET_RELAY_PAYLOAD,
24
- NEG_RISK_ADAPTER_ABI_REDEEM,
25
- )
26
- from poly_web3.schema import WalletType
27
-
28
-
29
- class BaseWeb3Service:
30
- def __init__(
31
- self,
32
- clob_client: ClobClient = None,
33
- relayer_client: RelayClient = None,
34
- rpc_url: str | None = None,
35
- ):
36
- self.relayer_client = relayer_client
37
- self.clob_client: ClobClient = clob_client
38
- if self.clob_client:
39
- self.wallet_type: WalletType = WalletType.get_with_code(
40
- self.clob_client.builder.sig_type
41
- )
42
- else:
43
- self.wallet_type = WalletType.PROXY
44
- self.rpc_url = rpc_url or RPC_URL
45
- self.w3: Web3 = Web3(Web3.HTTPProvider(self.rpc_url))
46
- if self.wallet_type == WalletType.PROXY and relayer_client is None:
47
- raise Exception("relayer_client must be provided")
48
-
49
- def _resolve_user_address(self):
50
- funder = getattr(getattr(self.clob_client, "builder", None), "funder", None)
51
- if funder:
52
- return funder
53
- return self.clob_client.get_address()
54
-
55
- @classmethod
56
- def fetch_positions(cls, user_address: str) -> list[dict]:
57
- """
58
- Fetches current positions for a user from the official Polymarket API.
59
-
60
- :param user_address: User wallet address (0x-prefixed, 40 hex chars)
61
- :return: List of position dictionaries from the API
62
- """
63
- url = "https://data-api.polymarket.com/positions"
64
- params = {
65
- "user": user_address,
66
- "sizeThreshold": 1,
67
- "limit": 100,
68
- "redeemable": True,
69
- "sortBy": "RESOLVING",
70
- "sortDirection": "DESC",
71
- }
72
- try:
73
- response = requests.get(url, params=params)
74
- response.raise_for_status()
75
- positions = response.json()
76
- return [i for i in positions if i.get("percentPnl") > 0]
77
- except Exception as e:
78
- print(f"Failed to fetch positions from API: {e}")
79
- return []
80
-
81
- def is_condition_resolved(self, condition_id: str) -> bool:
82
- ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
83
- return ctf.functions.payoutDenominator(condition_id).call() > 0
84
-
85
- def get_winning_indexes(self, condition_id: str) -> list[int]:
86
- ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
87
- if not self.is_condition_resolved(condition_id):
88
- return []
89
- outcome_count = ctf.functions.getOutcomeSlotCount(condition_id).call()
90
- winners: list[int] = []
91
- for i in range(outcome_count):
92
- if ctf.functions.payoutNumerators(condition_id, i).call() > 0:
93
- winners.append(i)
94
- return winners
95
-
96
- def get_redeemable_index_and_balance(
97
- self, condition_id: str
98
- ) -> list[tuple]:
99
- owner = self._resolve_user_address()
100
- winners = self.get_winning_indexes(condition_id)
101
- if not winners:
102
- return []
103
- ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
104
- owner_checksum = Web3.to_checksum_address(owner)
105
- redeemable: list[tuple] = []
106
- for index in winners:
107
- index_set = 1 << index
108
- collection_id = ctf.functions.getCollectionId(
109
- ZERO_BYTES32, condition_id, index_set
110
- ).call()
111
- position_id = ctf.functions.getPositionId(
112
- USDC_POLYGON, collection_id
113
- ).call()
114
- balance = ctf.functions.balanceOf(owner_checksum, position_id).call()
115
- if balance > 0:
116
- redeemable.append((index, balance / 1000000))
117
- return redeemable
118
-
119
- def build_ctf_redeem_tx_data(self, condition_id: str) -> str:
120
- ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_REDEEM)
121
- # 只需要 calldata:encodeABI 即可
122
- return ctf.functions.redeemPositions(
123
- USDC_POLYGON,
124
- ZERO_BYTES32,
125
- condition_id,
126
- [1, 2],
127
- )._encode_transaction_data()
128
-
129
- def build_neg_risk_redeem_tx_data(
130
- self, condition_id: str, redeem_amounts: list[int]
131
- ) -> str:
132
- nr_adapter = self.w3.eth.contract(
133
- address=NEG_RISK_ADAPTER_ADDRESS, abi=NEG_RISK_ADAPTER_ABI_REDEEM
134
- )
135
- return nr_adapter.functions.redeemPositions(
136
- condition_id,
137
- redeem_amounts,
138
- )._encode_transaction_data()
139
-
140
- @classmethod
141
- def _get_relay_payload(cls, address: str, wallet_type: WalletType):
142
- return requests.get(
143
- RELAYER_URL + GET_RELAY_PAYLOAD,
144
- params={"address": address, "type": wallet_type},
145
- ).json()
146
-
147
- def get_contract_config(self) -> dict:
148
- if self.clob_client.chain_id == 137:
149
- return POL
150
- elif self.clob_client.chain_id == 80002:
151
- return AMOY
152
- raise Exception("Invalid network")
153
-
154
- def estimate_gas(self, tx):
155
- payload = {
156
- "jsonrpc": "2.0",
157
- "method": "eth_estimateGas",
158
- "params": [tx],
159
- "id": 1,
160
- }
161
-
162
- response = requests.post(self.rpc_url, json=payload)
163
- result = response.json()
164
-
165
- if "result" in result:
166
- # 返回的是16进制 gas 数量
167
- gas_hex = result["result"]
168
- return str(int(gas_hex, 16))
169
- else:
170
- raise Exception("Estimate gas error: " + str(result))
171
-
172
- def redeem(
173
- self,
174
- condition_id: str,
175
- neg_risk: bool = False,
176
- redeem_amounts: list[int] | None = None,
177
- ): # noqa:
178
- raise ImportError()
179
-
180
- def redeem_all(self) -> list[dict] | None:
181
- raise ImportError()
1
+ # -*- coding = utf-8 -*-
2
+ # @Time: 2025-12-27 15:57:07
3
+ # @Author: PinBar
4
+ # @Site:
5
+ # @File: base.py
6
+ # @Software: PyCharm
7
+ import requests
8
+ from py_builder_relayer_client.client import RelayClient
9
+ from py_clob_client.client import ClobClient
10
+ from web3 import Web3
11
+
12
+ from poly_web3.const import (
13
+ RPC_URL,
14
+ CTF_ADDRESS,
15
+ CTF_ABI_PAYOUT,
16
+ ZERO_BYTES32,
17
+ USDC_POLYGON,
18
+ CTF_ABI_REDEEM,
19
+ NEG_RISK_ADAPTER_ADDRESS,
20
+ RELAYER_URL,
21
+ POL,
22
+ AMOY,
23
+ GET_RELAY_PAYLOAD,
24
+ NEG_RISK_ADAPTER_ABI_REDEEM,
25
+ )
26
+ from poly_web3.schema import WalletType
27
+ from poly_web3.log import logger
28
+
29
+
30
+ class BaseWeb3Service:
31
+ def __init__(
32
+ self,
33
+ clob_client: ClobClient = None,
34
+ relayer_client: RelayClient = None,
35
+ rpc_url: str | None = None,
36
+ ):
37
+ self.relayer_client = relayer_client
38
+ self.clob_client: ClobClient = clob_client
39
+ if self.clob_client:
40
+ self.wallet_type: WalletType = WalletType.get_with_code(
41
+ self.clob_client.builder.sig_type
42
+ )
43
+ else:
44
+ self.wallet_type = WalletType.PROXY
45
+ self.rpc_url = rpc_url or RPC_URL
46
+ self.w3: Web3 = Web3(Web3.HTTPProvider(self.rpc_url))
47
+ if self.wallet_type == WalletType.PROXY and relayer_client is None:
48
+ raise Exception("relayer_client must be provided")
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
+
82
+ def is_condition_resolved(self, condition_id: str) -> bool:
83
+ ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
84
+ return ctf.functions.payoutDenominator(condition_id).call() > 0
85
+
86
+ def get_winning_indexes(self, condition_id: str) -> list[int]:
87
+ ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
88
+ if not self.is_condition_resolved(condition_id):
89
+ return []
90
+ outcome_count = ctf.functions.getOutcomeSlotCount(condition_id).call()
91
+ winners: list[int] = []
92
+ for i in range(outcome_count):
93
+ if ctf.functions.payoutNumerators(condition_id, i).call() > 0:
94
+ winners.append(i)
95
+ return winners
96
+
97
+ def get_redeemable_index_and_balance(
98
+ self, condition_id: str
99
+ ) -> list[tuple]:
100
+ owner = self._resolve_user_address()
101
+ winners = self.get_winning_indexes(condition_id)
102
+ if not winners:
103
+ return []
104
+ ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_PAYOUT)
105
+ owner_checksum = Web3.to_checksum_address(owner)
106
+ redeemable: list[tuple] = []
107
+ for index in winners:
108
+ index_set = 1 << index
109
+ collection_id = ctf.functions.getCollectionId(
110
+ ZERO_BYTES32, condition_id, index_set
111
+ ).call()
112
+ position_id = ctf.functions.getPositionId(
113
+ USDC_POLYGON, collection_id
114
+ ).call()
115
+ balance = ctf.functions.balanceOf(owner_checksum, position_id).call()
116
+ if balance > 0:
117
+ redeemable.append((index, balance / 1000000))
118
+ return redeemable
119
+
120
+ def build_ctf_redeem_tx_data(self, condition_id: str) -> str:
121
+ ctf = self.w3.eth.contract(address=CTF_ADDRESS, abi=CTF_ABI_REDEEM)
122
+ # 只需要 calldata:encodeABI 即可
123
+ return ctf.functions.redeemPositions(
124
+ USDC_POLYGON,
125
+ ZERO_BYTES32,
126
+ condition_id,
127
+ [1, 2],
128
+ )._encode_transaction_data()
129
+
130
+ def build_neg_risk_redeem_tx_data(
131
+ self, condition_id: str, redeem_amounts: list[int]
132
+ ) -> str:
133
+ nr_adapter = self.w3.eth.contract(
134
+ address=NEG_RISK_ADAPTER_ADDRESS, abi=NEG_RISK_ADAPTER_ABI_REDEEM
135
+ )
136
+ return nr_adapter.functions.redeemPositions(
137
+ condition_id,
138
+ redeem_amounts,
139
+ )._encode_transaction_data()
140
+
141
+ @classmethod
142
+ def _get_relay_payload(cls, address: str, wallet_type: WalletType):
143
+ return requests.get(
144
+ RELAYER_URL + GET_RELAY_PAYLOAD,
145
+ params={"address": address, "type": wallet_type},
146
+ ).json()
147
+
148
+ def get_contract_config(self) -> dict:
149
+ if self.clob_client.chain_id == 137:
150
+ return POL
151
+ elif self.clob_client.chain_id == 80002:
152
+ return AMOY
153
+ raise Exception("Invalid network")
154
+
155
+ def estimate_gas(self, tx):
156
+ payload = {
157
+ "jsonrpc": "2.0",
158
+ "method": "eth_estimateGas",
159
+ "params": [tx],
160
+ "id": 1,
161
+ }
162
+
163
+ response = requests.post(self.rpc_url, json=payload)
164
+ result = response.json()
165
+
166
+ if "result" in result:
167
+ # 返回的是16进制 gas 数量
168
+ gas_hex = result["result"]
169
+ return str(int(gas_hex, 16))
170
+ else:
171
+ raise Exception("Estimate gas error: " + str(result))
172
+
173
+ def redeem(
174
+ self,
175
+ condition_id: str,
176
+ neg_risk: bool = False,
177
+ redeem_amounts: list[int] | None = None,
178
+ ): # noqa:
179
+ raise ImportError()
180
+
181
+ def redeem_all(self) -> list[dict] | None:
182
+ raise ImportError()
@@ -1,17 +1,17 @@
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_id: str,
14
- neg_risk: bool = False,
15
- redeem_amounts: list[int] | None = None,
16
- ):
17
- raise ImportError("EOA wallet redeem not supported")
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_id: str,
14
+ neg_risk: bool = False,
15
+ redeem_amounts: list[int] | None = None,
16
+ ):
17
+ raise ImportError("EOA wallet redeem not supported")