polynode 0.10.6__tar.gz → 0.10.7__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.
- {polynode-0.10.6 → polynode-0.10.7}/PKG-INFO +3 -1
- {polynode-0.10.6 → polynode-0.10.7}/README.md +2 -0
- polynode-0.10.7/polynode/_version.py +1 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/eip712.py +3 -2
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/onboarding.py +17 -4
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/trader.py +22 -2
- {polynode-0.10.6 → polynode-0.10.7}/pyproject.toml +1 -1
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_trading.py +121 -0
- polynode-0.10.6/polynode/_version.py +0 -1
- {polynode-0.10.6 → polynode-0.10.7}/.gitignore +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/__init__.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/cache/__init__.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/client.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/engine.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/errors.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/orderbook.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/orderbook_state.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/redemption_watcher.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/short_form.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/subscription.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/testing.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/V2_ORDER_FLOW.md +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/__init__.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/clob_api.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/constants.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/cosigner.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/escrow.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/position_management.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/privy.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/relayer.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/signer.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/sqlite_backend.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/trading/types.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/__init__.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/enums.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/events.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/orderbook.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/rest.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/short_form.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/types/ws.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/polynode/ws.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/__init__.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/conftest.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_client.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_orderbook.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_orderbook_protocol.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_types.py +0 -0
- {polynode-0.10.6 → polynode-0.10.7}/tests/test_ws_protocol.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polynode
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.7
|
|
4
4
|
Summary: Python SDK for the PolyNode real-time prediction market data platform
|
|
5
5
|
Project-URL: Homepage, https://polynode.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.polynode.dev
|
|
@@ -35,6 +35,8 @@ Description-Content-Type: text/markdown
|
|
|
35
35
|
|
|
36
36
|
Python SDK for the [PolyNode](https://polynode.dev) real-time prediction market data platform.
|
|
37
37
|
|
|
38
|
+
**New in v0.10.7:** Polymarket V2 deposit-wallet trading fixes. `ensure_ready()` detects deployed `POLY_1271` wallets correctly, V2 type-3 orders use the deposit wallet as both maker and signer, and existing local credentials can be repaired by rerunning `ensure_ready()`.
|
|
39
|
+
|
|
38
40
|
## Install
|
|
39
41
|
|
|
40
42
|
```bash
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Python SDK for the [PolyNode](https://polynode.dev) real-time prediction market data platform.
|
|
4
4
|
|
|
5
|
+
**New in v0.10.7:** Polymarket V2 deposit-wallet trading fixes. `ensure_ready()` detects deployed `POLY_1271` wallets correctly, V2 type-3 orders use the deposit wallet as both maker and signer, and existing local credentials can be repaired by rerunning `ensure_ready()`.
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.10.7"
|
|
@@ -305,10 +305,11 @@ async def create_signed_order_v2(
|
|
|
305
305
|
- timestamp is a string in the payload
|
|
306
306
|
"""
|
|
307
307
|
maker_amount, taker_amount = compute_amounts(price, size, side, tick_size)
|
|
308
|
+
order_signer = funder_address if signature_type == SignatureType.POLY_1271 else signer_address
|
|
308
309
|
|
|
309
310
|
payload = build_order_payload_v2(
|
|
310
311
|
maker=funder_address,
|
|
311
|
-
signer=
|
|
312
|
+
signer=order_signer,
|
|
312
313
|
token_id=token_id,
|
|
313
314
|
maker_amount=maker_amount,
|
|
314
315
|
taker_amount=taker_amount,
|
|
@@ -388,7 +389,7 @@ async def create_signed_order_v2(
|
|
|
388
389
|
return {
|
|
389
390
|
"salt": payload.message["salt"], # number
|
|
390
391
|
"maker": funder_address,
|
|
391
|
-
"signer":
|
|
392
|
+
"signer": order_signer,
|
|
392
393
|
"taker": "0x0000000000000000000000000000000000000000",
|
|
393
394
|
"tokenId": token_id,
|
|
394
395
|
"makerAmount": str(maker_amount),
|
|
@@ -124,6 +124,7 @@ async def detect_wallet_type(eoa: str) -> dict:
|
|
|
124
124
|
"""Auto-detect if a Safe, Proxy, or deposit wallet is deployed for this EOA."""
|
|
125
125
|
safe_addr = derive_safe_address(eoa)
|
|
126
126
|
proxy_addr = derive_proxy_address(eoa)
|
|
127
|
+
deposit_wallet_addr = derive_deposit_wallet_address(eoa)
|
|
127
128
|
|
|
128
129
|
async with httpx.AsyncClient(timeout=10.0) as http:
|
|
129
130
|
# Check Safe first (most common for existing users)
|
|
@@ -152,13 +153,13 @@ async def detect_wallet_type(eoa: str) -> dict:
|
|
|
152
153
|
except Exception:
|
|
153
154
|
pass
|
|
154
155
|
|
|
155
|
-
# Check deposit wallet (newer accounts)
|
|
156
|
+
# Check deposit wallet (newer accounts). Polymarket's relayer defaults
|
|
157
|
+
# /deployed to SAFE, so deposit wallets must be checked with type=WALLET.
|
|
156
158
|
try:
|
|
157
|
-
|
|
158
|
-
if await is_safe_deployed(dw_addr):
|
|
159
|
+
if await is_deposit_wallet_deployed(deposit_wallet_addr):
|
|
159
160
|
return {
|
|
160
161
|
"signature_type": SignatureType.POLY_1271,
|
|
161
|
-
"funder_address":
|
|
162
|
+
"funder_address": deposit_wallet_addr,
|
|
162
163
|
}
|
|
163
164
|
except Exception:
|
|
164
165
|
pass
|
|
@@ -182,6 +183,18 @@ async def is_safe_deployed(funder_address: str) -> bool:
|
|
|
182
183
|
return False
|
|
183
184
|
|
|
184
185
|
|
|
186
|
+
async def is_deposit_wallet_deployed(funder_address: str) -> bool:
|
|
187
|
+
"""Check if a deposit wallet is deployed at the given address."""
|
|
188
|
+
async with httpx.AsyncClient(timeout=10.0) as http:
|
|
189
|
+
try:
|
|
190
|
+
resp = await http.get(f"{RELAYER_HOST}/deployed?address={funder_address}&type=WALLET")
|
|
191
|
+
if resp.is_success:
|
|
192
|
+
return resp.json().get("deployed", False)
|
|
193
|
+
except Exception:
|
|
194
|
+
pass
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
|
|
185
198
|
async def check_approvals(
|
|
186
199
|
funder_address: str,
|
|
187
200
|
rpc_url: str,
|
|
@@ -33,6 +33,7 @@ from .onboarding import (
|
|
|
33
33
|
create_clob_credentials,
|
|
34
34
|
derive_funder_address,
|
|
35
35
|
detect_wallet_type,
|
|
36
|
+
is_deposit_wallet_deployed,
|
|
36
37
|
is_safe_deployed,
|
|
37
38
|
set_approvals as set_approvals_onchain,
|
|
38
39
|
)
|
|
@@ -103,14 +104,19 @@ class PolyNodeTrader:
|
|
|
103
104
|
signer: str | RouterSigner,
|
|
104
105
|
*,
|
|
105
106
|
type: SignatureType | None = None,
|
|
107
|
+
funder_address: str | None = None,
|
|
106
108
|
) -> ReadyStatus:
|
|
107
109
|
"""One-call onboarding: detect wallet type, deploy Safe, set approvals, create CLOB creds."""
|
|
110
|
+
if funder_address is not None and type is None:
|
|
111
|
+
raise ValueError("funder_address requires type so the wallet identity is unambiguous")
|
|
112
|
+
|
|
108
113
|
temp = await normalize_signer(signer, type or SignatureType.POLY_GNOSIS_SAFE)
|
|
109
114
|
actions: list[str] = []
|
|
110
115
|
|
|
111
116
|
if type is not None:
|
|
112
117
|
sig_type = type
|
|
113
|
-
funder = temp.funder_address or await derive_funder_address(temp.address, sig_type)
|
|
118
|
+
funder = funder_address or temp.funder_address or await derive_funder_address(temp.address, sig_type)
|
|
119
|
+
actions.append(f"explicit_type_{int(sig_type)}")
|
|
114
120
|
else:
|
|
115
121
|
detected = await detect_wallet_type(temp.address)
|
|
116
122
|
sig_type = detected["signature_type"]
|
|
@@ -135,6 +141,14 @@ class PolyNodeTrader:
|
|
|
135
141
|
else:
|
|
136
142
|
actions.append("safe_deploy_needed")
|
|
137
143
|
|
|
144
|
+
if sig_type == SignatureType.POLY_1271 and not safe_deployed:
|
|
145
|
+
deployed = await is_deposit_wallet_deployed(funder)
|
|
146
|
+
if deployed:
|
|
147
|
+
safe_deployed = True
|
|
148
|
+
actions.append("deposit_wallet_already_deployed")
|
|
149
|
+
else:
|
|
150
|
+
actions.append("deposit_wallet_deploy_needed")
|
|
151
|
+
|
|
138
152
|
# Check approvals
|
|
139
153
|
if sig_type in (SignatureType.POLY_GNOSIS_SAFE, SignatureType.EOA) and not approvals_set:
|
|
140
154
|
try:
|
|
@@ -165,6 +179,8 @@ class PolyNodeTrader:
|
|
|
165
179
|
db.upsert_credentials(stored)
|
|
166
180
|
actions.append("credentials_created")
|
|
167
181
|
else:
|
|
182
|
+
stored.funder_address = funder
|
|
183
|
+
stored.signature_type = sig_type
|
|
168
184
|
stored.safe_deployed = safe_deployed
|
|
169
185
|
stored.approvals_set = approvals_set
|
|
170
186
|
stored.updated_at = time.time()
|
|
@@ -214,11 +230,15 @@ class PolyNodeTrader:
|
|
|
214
230
|
signer: str | RouterSigner,
|
|
215
231
|
*,
|
|
216
232
|
type: SignatureType | None = None,
|
|
233
|
+
funder_address: str | None = None,
|
|
217
234
|
) -> LinkResult:
|
|
218
235
|
"""Link a wallet manually (derive credentials, store locally)."""
|
|
236
|
+
if funder_address is not None and type is None:
|
|
237
|
+
raise ValueError("funder_address requires type so the wallet identity is unambiguous")
|
|
238
|
+
|
|
219
239
|
sig_type = type or self._default_sig_type
|
|
220
240
|
normalized = await normalize_signer(signer, sig_type)
|
|
221
|
-
funder = normalized.funder_address or await derive_funder_address(normalized.address, sig_type)
|
|
241
|
+
funder = funder_address or normalized.funder_address or await derive_funder_address(normalized.address, sig_type)
|
|
222
242
|
|
|
223
243
|
db = self._get_db()
|
|
224
244
|
existing = db.get_credentials(normalized.address)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""Tests for trading module — EIP-712 signing, HMAC headers, V1 and V2."""
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
|
+
import asyncio
|
|
4
5
|
|
|
5
6
|
from polynode.trading.cosigner import build_l2_headers
|
|
6
7
|
from polynode.trading.eip712 import (
|
|
7
8
|
build_order_payload,
|
|
8
9
|
build_order_payload_v2,
|
|
10
|
+
create_signed_order_v2,
|
|
9
11
|
compute_amounts,
|
|
10
12
|
EXCHANGE_DOMAIN,
|
|
11
13
|
EXCHANGE_DOMAIN_V2,
|
|
@@ -76,6 +78,125 @@ def test_safe_address_derivation():
|
|
|
76
78
|
pass # Skip if trading deps not installed
|
|
77
79
|
|
|
78
80
|
|
|
81
|
+
def test_detect_wallet_type_uses_deposit_wallet_check(monkeypatch):
|
|
82
|
+
"""Regression: deposit wallets must be checked as WALLET, not SAFE."""
|
|
83
|
+
from polynode.trading import onboarding
|
|
84
|
+
|
|
85
|
+
eoa = "0x7b3099fe73372D900a9F0F62CdcA261755f7EA60"
|
|
86
|
+
deposit_wallet = "0x58b8048C8f09C2FCc4914258587a8E7E0cc21D76"
|
|
87
|
+
|
|
88
|
+
async def safe_not_deployed(_address: str) -> bool:
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
async def deposit_wallet_deployed(address: str) -> bool:
|
|
92
|
+
return address == deposit_wallet
|
|
93
|
+
|
|
94
|
+
monkeypatch.setattr(onboarding, "is_safe_deployed", safe_not_deployed)
|
|
95
|
+
monkeypatch.setattr(onboarding, "is_deposit_wallet_deployed", deposit_wallet_deployed)
|
|
96
|
+
|
|
97
|
+
detected = asyncio.run(onboarding.detect_wallet_type(eoa))
|
|
98
|
+
|
|
99
|
+
assert detected["signature_type"] == SignatureType.POLY_1271
|
|
100
|
+
assert detected["funder_address"] == deposit_wallet
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_v2_poly1271_order_uses_deposit_wallet_as_signer():
|
|
104
|
+
"""POLY_1271 V2 orders need maker and signer set to the deposit wallet."""
|
|
105
|
+
signer_address = "0x7b3099fe73372D900a9F0F62CdcA261755f7EA60"
|
|
106
|
+
deposit_wallet = "0x58b8048C8f09C2FCc4914258587a8E7E0cc21D76"
|
|
107
|
+
signed_payload = {}
|
|
108
|
+
|
|
109
|
+
async def fake_sign(payload):
|
|
110
|
+
signed_payload.update(payload.message["contents"])
|
|
111
|
+
return "0x" + "11" * 65
|
|
112
|
+
|
|
113
|
+
order = asyncio.run(create_signed_order_v2(
|
|
114
|
+
fake_sign,
|
|
115
|
+
signer_address=signer_address,
|
|
116
|
+
funder_address=deposit_wallet,
|
|
117
|
+
token_id="12345",
|
|
118
|
+
price=0.5,
|
|
119
|
+
size=1,
|
|
120
|
+
side="BUY",
|
|
121
|
+
signature_type=SignatureType.POLY_1271,
|
|
122
|
+
timestamp_ms=1712345678000,
|
|
123
|
+
))
|
|
124
|
+
|
|
125
|
+
assert signed_payload["maker"] == deposit_wallet
|
|
126
|
+
assert signed_payload["signer"] == deposit_wallet
|
|
127
|
+
assert order["maker"] == deposit_wallet
|
|
128
|
+
assert order["signer"] == deposit_wallet
|
|
129
|
+
assert order["signatureType"] == 3
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_ensure_ready_allows_explicit_poly1271_funder(monkeypatch, tmp_path):
|
|
133
|
+
"""Existing deposit-wallet users can force type 3 plus a known funder."""
|
|
134
|
+
from polynode.trading import trader as trader_mod
|
|
135
|
+
from polynode.trading.trader import PolyNodeTrader
|
|
136
|
+
from polynode.trading.types import ExchangeVersion, TraderConfig
|
|
137
|
+
|
|
138
|
+
private_key = "0x" + "01" * 32
|
|
139
|
+
deposit_wallet = "0x58b8048C8f09C2FCc4914258587a8E7E0cc21D76"
|
|
140
|
+
|
|
141
|
+
async def deposit_wallet_deployed(address: str) -> bool:
|
|
142
|
+
return address == deposit_wallet
|
|
143
|
+
|
|
144
|
+
async def fake_create_clob_credentials(*_args, **_kwargs):
|
|
145
|
+
return {
|
|
146
|
+
"apiKey": "key",
|
|
147
|
+
"apiSecret": base64.b64encode(b"secret").decode(),
|
|
148
|
+
"apiPassphrase": "pass",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
monkeypatch.setattr(trader_mod, "is_deposit_wallet_deployed", deposit_wallet_deployed)
|
|
152
|
+
monkeypatch.setattr(trader_mod, "create_clob_credentials", fake_create_clob_credentials)
|
|
153
|
+
|
|
154
|
+
trader = PolyNodeTrader(TraderConfig(
|
|
155
|
+
db_path=str(tmp_path / "trading.db"),
|
|
156
|
+
exchange_version=ExchangeVersion.V2,
|
|
157
|
+
))
|
|
158
|
+
|
|
159
|
+
status = asyncio.run(trader.ensure_ready(
|
|
160
|
+
private_key,
|
|
161
|
+
type=SignatureType.POLY_1271,
|
|
162
|
+
funder_address=deposit_wallet,
|
|
163
|
+
))
|
|
164
|
+
|
|
165
|
+
assert status.signature_type == SignatureType.POLY_1271
|
|
166
|
+
assert status.funder_address == deposit_wallet
|
|
167
|
+
assert status.safe_deployed is True
|
|
168
|
+
assert "explicit_type_3" in status.actions
|
|
169
|
+
assert "deposit_wallet_already_deployed" in status.actions
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
from eth_account import Account
|
|
173
|
+
except ImportError:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
wallet = Account.from_key(private_key).address
|
|
177
|
+
wrong_safe = "0xb8f9248a67962c9FE37E70e7a6E38e100b7613f1"
|
|
178
|
+
trader.link_credentials(
|
|
179
|
+
wallet=wallet,
|
|
180
|
+
api_key="key",
|
|
181
|
+
api_secret=base64.b64encode(b"secret").decode(),
|
|
182
|
+
api_passphrase="pass",
|
|
183
|
+
signature_type=SignatureType.POLY_GNOSIS_SAFE,
|
|
184
|
+
funder_address=wrong_safe,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
status = asyncio.run(trader.ensure_ready(
|
|
188
|
+
private_key,
|
|
189
|
+
type=SignatureType.POLY_1271,
|
|
190
|
+
funder_address=deposit_wallet,
|
|
191
|
+
))
|
|
192
|
+
exported = trader.export_wallet(wallet)
|
|
193
|
+
|
|
194
|
+
assert status.signature_type == SignatureType.POLY_1271
|
|
195
|
+
assert exported is not None
|
|
196
|
+
assert exported.signature_type == SignatureType.POLY_1271
|
|
197
|
+
assert exported.funder_address == deposit_wallet
|
|
198
|
+
|
|
199
|
+
|
|
79
200
|
# ── V2 constants ──
|
|
80
201
|
|
|
81
202
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.10.6"
|
|
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
|
|
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
|
|
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
|