polynode 0.6.3__py3-none-any.whl → 0.7.0__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.
@@ -12,6 +12,7 @@ from .privy import PrivySigner, PrivyConfig
12
12
  __all__ = [
13
13
  "PolyNodeTrader",
14
14
  "BuilderCredentials",
15
+ "ExchangeVersion",
15
16
  "PrivySigner",
16
17
  "PrivyConfig",
17
18
  "NormalizedSigner",
@@ -6,8 +6,9 @@ from typing import Any
6
6
 
7
7
  import httpx
8
8
 
9
- from .constants import CLOB_HOST
9
+ from .constants import CLOB_HOST, CLOB_HOST_V2
10
10
  from .cosigner import build_l2_headers
11
+ from .types import ExchangeVersion
11
12
 
12
13
 
13
14
  async def post_order(
@@ -18,6 +19,7 @@ async def post_order(
18
19
  wallet_address: str,
19
20
  order_body: str,
20
21
  builder_credentials: Any | None = None,
22
+ clob_host: str | None = None,
21
23
  ) -> dict[str, Any]:
22
24
  """Submit a signed order to the CLOB."""
23
25
  from .cosigner import send_via_cosigner
@@ -38,6 +40,7 @@ async def post_order(
38
40
  fallback_direct,
39
41
  {"method": "POST", "path": "/order", "body": order_body, "headers": headers},
40
42
  builder_credentials=builder_credentials,
43
+ clob_host=clob_host,
41
44
  )
42
45
 
43
46
 
@@ -49,6 +52,7 @@ async def cancel_order(
49
52
  wallet_address: str,
50
53
  order_id: str,
51
54
  builder_credentials: Any | None = None,
55
+ clob_host: str | None = None,
52
56
  ) -> dict[str, Any]:
53
57
  """Cancel a specific order."""
54
58
  from .cosigner import send_via_cosigner
@@ -71,6 +75,7 @@ async def cancel_order(
71
75
  fallback_direct,
72
76
  {"method": "DELETE", "path": "/order", "body": body, "headers": headers},
73
77
  builder_credentials=builder_credentials,
78
+ clob_host=clob_host,
74
79
  )
75
80
 
76
81
 
@@ -82,6 +87,7 @@ async def cancel_all_orders(
82
87
  wallet_address: str,
83
88
  market: str | None = None,
84
89
  builder_credentials: Any | None = None,
90
+ clob_host: str | None = None,
85
91
  ) -> dict[str, Any]:
86
92
  """Cancel all orders, optionally for a specific market."""
87
93
  from .cosigner import send_via_cosigner
@@ -105,6 +111,7 @@ async def cancel_all_orders(
105
111
  fallback_direct,
106
112
  {"method": "DELETE", "path": path, "body": body, "headers": headers},
107
113
  builder_credentials=builder_credentials,
114
+ clob_host=clob_host,
108
115
  )
109
116
 
110
117
 
@@ -117,6 +124,7 @@ async def get_open_orders(
117
124
  market: str | None = None,
118
125
  asset_id: str | None = None,
119
126
  builder_credentials: Any | None = None,
127
+ clob_host: str | None = None,
120
128
  ) -> list[dict[str, Any]]:
121
129
  """Get open orders from the CLOB."""
122
130
  from .cosigner import send_via_cosigner
@@ -145,6 +153,7 @@ async def get_open_orders(
145
153
  fallback_direct,
146
154
  {"method": "GET", "path": path, "headers": headers},
147
155
  builder_credentials=builder_credentials,
156
+ clob_host=clob_host,
148
157
  )
149
158
 
150
159
  if isinstance(result, list):
@@ -152,17 +161,30 @@ async def get_open_orders(
152
161
  return result.get("data") or result.get("orders") or []
153
162
 
154
163
 
155
- async def fetch_tick_size(token_id: str) -> str:
164
+ def _clob_host(version: ExchangeVersion = ExchangeVersion.V1) -> str:
165
+ """Return the CLOB host URL for the given exchange version."""
166
+ return CLOB_HOST_V2 if version == ExchangeVersion.V2 else CLOB_HOST
167
+
168
+
169
+ async def fetch_tick_size(
170
+ token_id: str,
171
+ version: ExchangeVersion = ExchangeVersion.V1,
172
+ ) -> str:
156
173
  """Fetch tick size for a token from the CLOB."""
174
+ host = _clob_host(version)
157
175
  async with httpx.AsyncClient(timeout=5.0) as http:
158
- resp = await http.get(f"{CLOB_HOST}/tick-size?token_id={token_id}")
176
+ resp = await http.get(f"{host}/tick-size?token_id={token_id}")
159
177
  data = resp.json()
160
178
  return str(data.get("minimum_tick_size", data) if isinstance(data, dict) else data)
161
179
 
162
180
 
163
- async def fetch_neg_risk(token_id: str) -> bool:
181
+ async def fetch_neg_risk(
182
+ token_id: str,
183
+ version: ExchangeVersion = ExchangeVersion.V1,
184
+ ) -> bool:
164
185
  """Fetch neg-risk flag for a token from the CLOB."""
186
+ host = _clob_host(version)
165
187
  async with httpx.AsyncClient(timeout=5.0) as http:
166
- resp = await http.get(f"{CLOB_HOST}/neg-risk?token_id={token_id}")
188
+ resp = await http.get(f"{host}/neg-risk?token_id={token_id}")
167
189
  data = resp.json()
168
190
  return bool(data.get("neg_risk", data) if isinstance(data, dict) else data)
@@ -1,16 +1,22 @@
1
1
  """Contract addresses and constants for Polymarket on Polygon (chain 137)."""
2
2
 
3
3
  CHAIN_ID = 137
4
- CLOB_HOST = "https://clob.polymarket.com"
5
4
  RELAYER_HOST = "https://relayer-v2.polymarket.com"
6
5
  DEFAULT_RPC = "https://polygon-bor-rpc.publicnode.com"
7
6
  DEFAULT_COSIGNER = "https://trade.polynode.dev"
8
7
 
9
- # Exchange contracts
8
+ # ── V1 ──
9
+ CLOB_HOST = "https://clob.polymarket.com"
10
10
  CTF_EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"
11
11
  NEG_RISK_CTF_EXCHANGE = "0xC5d563A36AE78145C45a50134d48A1215220f80a"
12
12
  NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"
13
13
 
14
+ # ── V2 ──
15
+ CLOB_HOST_V2 = "https://clob-v2.polymarket.com"
16
+ CTF_EXCHANGE_V2 = "0xe111180000d2663c0091e4f400237545b87b996b"
17
+ NEG_RISK_CTF_EXCHANGE_V2_A = "0xe2222d279d744050d28e00520010520000310f59"
18
+ NEG_RISK_CTF_EXCHANGE_V2_B = "0xe2222d002000ba0053cef3375333610f64600036"
19
+
14
20
  # Token contracts
15
21
  USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
16
22
  CTF = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
@@ -26,6 +32,12 @@ PROXY_INIT_CODE_HASH = "0xd21df8dc65880a8606f09fe0ce3df9b8869287ab0b058be05aa9e8
26
32
 
27
33
  # All spender contracts that need approval
28
34
  SPENDERS = [CTF_EXCHANGE, NEG_RISK_CTF_EXCHANGE, NEG_RISK_ADAPTER]
35
+ V2_SPENDERS = [CTF_EXCHANGE_V2, NEG_RISK_CTF_EXCHANGE_V2_A, NEG_RISK_CTF_EXCHANGE_V2_B]
36
+
37
+ # PolyUSD wrapping contracts
38
+ POLY_USD = "0xc011a7e12a19f7b1f670d46f03b03f3342e82dfb"
39
+ COLLATERAL_ONRAMP = "0x93070a847efef7f70739046a929d47a521f5b8ee"
40
+ COLLATERAL_OFFRAMP = "0x2957922eb93258b93368531d39facca3b4dc5854"
29
41
 
30
42
  # Metadata cache TTL (5 minutes)
31
43
  META_TTL_SECONDS = 300
@@ -49,6 +49,7 @@ async def send_via_cosigner(
49
49
  fallback_direct: bool,
50
50
  request: dict[str, Any],
51
51
  builder_credentials: Any | None = None,
52
+ clob_host: str | None = None,
52
53
  ) -> Any:
53
54
  """Send a request through the co-signer with optional fallback to direct CLOB."""
54
55
  if cosigner_url:
@@ -70,19 +71,20 @@ async def send_via_cosigner(
70
71
  )
71
72
  data = resp.json()
72
73
  if resp.status_code >= 500 and fallback_direct:
73
- return await _send_direct(request)
74
+ return await _send_direct(request, clob_host=clob_host)
74
75
  return data
75
76
  except Exception as e:
76
77
  if fallback_direct:
77
- return await _send_direct(request)
78
+ return await _send_direct(request, clob_host=clob_host)
78
79
  raise RuntimeError(f"Co-signer unreachable: {e}")
79
80
 
80
- return await _send_direct(request)
81
+ return await _send_direct(request, clob_host=clob_host)
81
82
 
82
83
 
83
- async def _send_direct(request: dict[str, Any]) -> Any:
84
+ async def _send_direct(request: dict[str, Any], clob_host: str | None = None) -> Any:
84
85
  """Send directly to Polymarket CLOB (no builder attribution)."""
85
- url = f"{CLOB_HOST}{request['path']}"
86
+ host = clob_host or CLOB_HOST
87
+ url = f"{host}{request['path']}"
86
88
  async with httpx.AsyncClient(timeout=10.0) as http:
87
89
  resp = await http.request(
88
90
  request["method"],
@@ -5,11 +5,17 @@ from __future__ import annotations
5
5
  import math
6
6
  from typing import Any
7
7
 
8
- from .constants import CTF_EXCHANGE, NEG_RISK_CTF_EXCHANGE
9
- from .types import Eip712Payload, SignatureType
8
+ from .constants import (
9
+ CTF_EXCHANGE,
10
+ CTF_EXCHANGE_V2,
11
+ NEG_RISK_CTF_EXCHANGE,
12
+ NEG_RISK_CTF_EXCHANGE_V2_A,
13
+ )
14
+ from .types import Eip712Payload, ExchangeVersion, SignatureType
10
15
 
11
16
 
12
- # EIP-712 domain for Polymarket Exchange
17
+ # ── V1 EIP-712 domains ──
18
+
13
19
  EXCHANGE_DOMAIN = {
14
20
  "name": "Polymarket CTF Exchange",
15
21
  "version": "1",
@@ -41,6 +47,38 @@ ORDER_TYPES = {
41
47
  ],
42
48
  }
43
49
 
50
+ # ── V2 EIP-712 domains ──
51
+
52
+ EXCHANGE_DOMAIN_V2 = {
53
+ "name": "Polymarket CTF Exchange",
54
+ "version": "2",
55
+ "chainId": 137,
56
+ "verifyingContract": CTF_EXCHANGE_V2,
57
+ }
58
+
59
+ NEG_RISK_DOMAIN_V2 = {
60
+ "name": "Polymarket CTF Exchange",
61
+ "version": "2",
62
+ "chainId": 137,
63
+ "verifyingContract": NEG_RISK_CTF_EXCHANGE_V2_A,
64
+ }
65
+
66
+ ORDER_TYPES_V2 = {
67
+ "Order": [
68
+ {"name": "salt", "type": "uint256"},
69
+ {"name": "maker", "type": "address"},
70
+ {"name": "signer", "type": "address"},
71
+ {"name": "tokenId", "type": "uint256"},
72
+ {"name": "makerAmount", "type": "uint256"},
73
+ {"name": "takerAmount", "type": "uint256"},
74
+ {"name": "side", "type": "uint8"},
75
+ {"name": "signatureType", "type": "uint8"},
76
+ {"name": "timestamp", "type": "uint256"},
77
+ {"name": "metadata", "type": "bytes32"},
78
+ {"name": "builder", "type": "bytes32"},
79
+ ],
80
+ }
81
+
44
82
 
45
83
  def compute_amounts(
46
84
  price: float,
@@ -125,7 +163,7 @@ async def create_signed_order(
125
163
  expiration: int = 0,
126
164
  nonce: int = 0,
127
165
  ) -> dict[str, Any]:
128
- """Create and sign a Polymarket order. Returns the order dict ready for CLOB submission."""
166
+ """Create and sign a V1 Polymarket order. Returns the order dict ready for CLOB submission."""
129
167
  maker_amount, taker_amount = compute_amounts(price, size, side, tick_size)
130
168
 
131
169
  payload = build_order_payload(
@@ -167,3 +205,126 @@ async def create_signed_order(
167
205
  "signatureType": int(signature_type),
168
206
  "signature": signature,
169
207
  }
208
+
209
+
210
+ # ── V2 order building ──
211
+
212
+
213
+ def build_order_payload_v2(
214
+ *,
215
+ maker: str,
216
+ signer: str,
217
+ token_id: str,
218
+ maker_amount: int,
219
+ taker_amount: int,
220
+ side: str,
221
+ signature_type: SignatureType,
222
+ neg_risk: bool = False,
223
+ timestamp_ms: int | None = None,
224
+ metadata: str = "0x" + "00" * 32,
225
+ builder: str = "0x" + "00" * 32,
226
+ ) -> Eip712Payload:
227
+ """Build a V2 EIP-712 typed data payload for a Polymarket order."""
228
+ import time
229
+
230
+ if timestamp_ms is None:
231
+ timestamp_ms = int(time.time() * 1000)
232
+
233
+ salt = timestamp_ms
234
+ side_int = 0 if side == "BUY" else 1
235
+ domain = NEG_RISK_DOMAIN_V2 if neg_risk else EXCHANGE_DOMAIN_V2
236
+
237
+ # Pad metadata/builder to bytes32
238
+ meta_hex = metadata if metadata.startswith("0x") else f"0x{metadata}"
239
+ meta_hex = meta_hex[:2] + meta_hex[2:].ljust(64, "0")
240
+ builder_hex = builder if builder.startswith("0x") else f"0x{builder}"
241
+ builder_hex = builder_hex[:2] + builder_hex[2:].ljust(64, "0")
242
+
243
+ message = {
244
+ "salt": salt,
245
+ "maker": maker,
246
+ "signer": signer,
247
+ "tokenId": int(token_id),
248
+ "makerAmount": maker_amount,
249
+ "takerAmount": taker_amount,
250
+ "side": side_int,
251
+ "signatureType": int(signature_type),
252
+ "timestamp": timestamp_ms,
253
+ "metadata": meta_hex,
254
+ "builder": builder_hex,
255
+ }
256
+
257
+ return Eip712Payload(
258
+ domain=domain,
259
+ types=ORDER_TYPES_V2,
260
+ primary_type="Order",
261
+ message=message,
262
+ )
263
+
264
+
265
+ async def create_signed_order_v2(
266
+ sign_typed_data: Any,
267
+ *,
268
+ signer_address: str,
269
+ funder_address: str,
270
+ token_id: str,
271
+ price: float,
272
+ size: float,
273
+ side: str,
274
+ signature_type: SignatureType,
275
+ tick_size: str = "0.01",
276
+ neg_risk: bool = False,
277
+ timestamp_ms: int | None = None,
278
+ metadata: str = "0x" + "00" * 32,
279
+ builder: str = "0x" + "00" * 32,
280
+ ) -> dict[str, Any]:
281
+ """Create and sign a V2 Polymarket order. Returns the order dict ready for V2 CLOB submission.
282
+
283
+ V2 differences from V1:
284
+ - No taker, expiration, nonce, or feeRateBps fields
285
+ - Adds timestamp (ms), metadata (bytes32), builder (bytes32)
286
+ - Domain version "2" with V2 exchange addresses
287
+ - salt is a number (not string) in the payload
288
+ - side is a string ("BUY"/"SELL") in the payload
289
+ - timestamp is a string in the payload
290
+ """
291
+ maker_amount, taker_amount = compute_amounts(price, size, side, tick_size)
292
+
293
+ payload = build_order_payload_v2(
294
+ maker=funder_address,
295
+ signer=signer_address,
296
+ token_id=token_id,
297
+ maker_amount=maker_amount,
298
+ taker_amount=taker_amount,
299
+ side=side,
300
+ signature_type=signature_type,
301
+ neg_risk=neg_risk,
302
+ timestamp_ms=timestamp_ms,
303
+ metadata=metadata,
304
+ builder=builder,
305
+ )
306
+
307
+ signature = await sign_typed_data(payload)
308
+ if not signature.startswith("0x"):
309
+ signature = f"0x{signature}"
310
+
311
+ # Normalize v to raw recovery id (0/1): CLOB expects v=0/1 for order signatures
312
+ sig_bytes = bytearray(bytes.fromhex(signature[2:]))
313
+ if len(sig_bytes) == 65 and sig_bytes[-1] >= 27:
314
+ sig_bytes[-1] -= 27
315
+ signature = "0x" + sig_bytes.hex()
316
+
317
+ return {
318
+ "salt": payload.message["salt"], # number
319
+ "maker": funder_address,
320
+ "signer": signer_address,
321
+ "tokenId": token_id,
322
+ "makerAmount": str(maker_amount),
323
+ "takerAmount": str(taker_amount),
324
+ "side": side, # string "BUY"/"SELL"
325
+ "signatureType": int(signature_type), # number
326
+ "timestamp": str(payload.message["timestamp"]), # string
327
+ "metadata": payload.message["metadata"],
328
+ "builder": payload.message["builder"],
329
+ "signature": signature,
330
+ }
@@ -9,10 +9,17 @@ import httpx
9
9
  from .constants import (
10
10
  CHAIN_ID,
11
11
  CLOB_HOST,
12
+ CLOB_HOST_V2,
13
+ COLLATERAL_OFFRAMP,
14
+ COLLATERAL_ONRAMP,
12
15
  CTF,
13
16
  CTF_EXCHANGE,
17
+ CTF_EXCHANGE_V2,
14
18
  NEG_RISK_ADAPTER,
15
19
  NEG_RISK_CTF_EXCHANGE,
20
+ NEG_RISK_CTF_EXCHANGE_V2_A,
21
+ NEG_RISK_CTF_EXCHANGE_V2_B,
22
+ POLY_USD,
16
23
  PROXY_FACTORY,
17
24
  PROXY_INIT_CODE_HASH,
18
25
  RELAYER_HOST,
@@ -20,8 +27,9 @@ from .constants import (
20
27
  SAFE_INIT_CODE_HASH,
21
28
  SPENDERS,
22
29
  USDC,
30
+ V2_SPENDERS,
23
31
  )
24
- from .types import ApprovalStatus, BalanceInfo, SignatureType
32
+ from .types import ApprovalStatus, BalanceInfo, ExchangeVersion, SignatureType
25
33
 
26
34
 
27
35
  def derive_safe_address(eoa: str) -> str:
@@ -126,8 +134,17 @@ async def is_safe_deployed(funder_address: str) -> bool:
126
134
  return False
127
135
 
128
136
 
129
- async def check_approvals(funder_address: str, rpc_url: str) -> ApprovalStatus:
130
- """Check token approvals for all spender contracts."""
137
+ async def check_approvals(
138
+ funder_address: str,
139
+ rpc_url: str,
140
+ version: ExchangeVersion = ExchangeVersion.V1,
141
+ ) -> ApprovalStatus:
142
+ """Check token approvals for all spender contracts.
143
+
144
+ V1: checks USDC allowance to SPENDERS (CTF Exchange, NegRisk, Adapter)
145
+ V2: checks PolyUSD allowance to V2_SPENDERS (V2 Exchange, NegRisk V2 A/B)
146
+ Both: checks CTF isApprovedForAll to the respective spenders
147
+ """
131
148
  try:
132
149
  from web3 import Web3
133
150
  except ImportError:
@@ -137,7 +154,22 @@ async def check_approvals(funder_address: str, rpc_url: str) -> ApprovalStatus:
137
154
  erc20_abi = [{"inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"}]
138
155
  erc1155_abi = [{"inputs": [{"name": "account", "type": "address"}, {"name": "operator", "type": "address"}], "name": "isApprovedForAll", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "view", "type": "function"}]
139
156
 
140
- usdc_contract = w3.eth.contract(address=Web3.to_checksum_address(USDC), abi=erc20_abi)
157
+ if version == ExchangeVersion.V2:
158
+ collateral_token = POLY_USD
159
+ spender_list = [
160
+ (CTF_EXCHANGE_V2, "ctf_exchange_v2"),
161
+ (NEG_RISK_CTF_EXCHANGE_V2_A, "neg_risk_ctf_exchange_v2_a"),
162
+ (NEG_RISK_CTF_EXCHANGE_V2_B, "neg_risk_ctf_exchange_v2_b"),
163
+ ]
164
+ else:
165
+ collateral_token = USDC
166
+ spender_list = [
167
+ (CTF_EXCHANGE, "ctf_exchange"),
168
+ (NEG_RISK_CTF_EXCHANGE, "neg_risk_ctf_exchange"),
169
+ (NEG_RISK_ADAPTER, "neg_risk_adapter"),
170
+ ]
171
+
172
+ token_contract = w3.eth.contract(address=Web3.to_checksum_address(collateral_token), abi=erc20_abi)
141
173
  ctf_contract = w3.eth.contract(address=Web3.to_checksum_address(CTF), abi=erc1155_abi)
142
174
 
143
175
  funder = Web3.to_checksum_address(funder_address)
@@ -146,9 +178,9 @@ async def check_approvals(funder_address: str, rpc_url: str) -> ApprovalStatus:
146
178
 
147
179
  usdc_status = {}
148
180
  ctf_status = {}
149
- for spender, name in [(CTF_EXCHANGE, "ctf_exchange"), (NEG_RISK_CTF_EXCHANGE, "neg_risk_ctf_exchange"), (NEG_RISK_ADAPTER, "neg_risk_adapter")]:
181
+ for spender, name in spender_list:
150
182
  spender_addr = Web3.to_checksum_address(spender)
151
- allowance = usdc_contract.functions.allowance(funder, spender_addr).call()
183
+ allowance = token_contract.functions.allowance(funder, spender_addr).call()
152
184
  usdc_status[name] = allowance >= threshold
153
185
  approved = ctf_contract.functions.isApprovedForAll(funder, spender_addr).call()
154
186
  ctf_status[name] = approved
@@ -163,6 +195,78 @@ async def check_approvals(funder_address: str, rpc_url: str) -> ApprovalStatus:
163
195
  )
164
196
 
165
197
 
198
+ async def set_approvals(
199
+ signer_key: str,
200
+ funder_address: str,
201
+ rpc_url: str,
202
+ version: ExchangeVersion = ExchangeVersion.V1,
203
+ ) -> list[str]:
204
+ """Set max approvals for collateral and CTF tokens to exchange spenders.
205
+
206
+ V1: approve USDC to SPENDERS
207
+ V2: approve PolyUSD to V2_SPENDERS
208
+ Both: setApprovalForAll on CTF to the respective spenders
209
+
210
+ Returns a list of submitted transaction hashes.
211
+ """
212
+ try:
213
+ from web3 import Web3
214
+ from eth_account import Account
215
+ except ImportError:
216
+ raise ImportError("web3 required. Install with: pip install polynode[trading]")
217
+
218
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
219
+ account = Account.from_key(signer_key if signer_key.startswith("0x") else f"0x{signer_key}")
220
+ max_uint = 2**256 - 1
221
+
222
+ erc20_abi = [
223
+ {"inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function"},
224
+ ]
225
+ erc1155_abi = [
226
+ {"inputs": [{"name": "operator", "type": "address"}, {"name": "approved", "type": "bool"}], "name": "setApprovalForAll", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
227
+ ]
228
+
229
+ if version == ExchangeVersion.V2:
230
+ collateral_token = POLY_USD
231
+ spender_list = V2_SPENDERS
232
+ else:
233
+ collateral_token = USDC
234
+ spender_list = SPENDERS
235
+
236
+ token_contract = w3.eth.contract(address=Web3.to_checksum_address(collateral_token), abi=erc20_abi)
237
+ ctf_contract = w3.eth.contract(address=Web3.to_checksum_address(CTF), abi=erc1155_abi)
238
+
239
+ tx_hashes: list[str] = []
240
+ nonce = w3.eth.get_transaction_count(account.address)
241
+
242
+ for spender in spender_list:
243
+ spender_addr = Web3.to_checksum_address(spender)
244
+
245
+ # Approve collateral token
246
+ tx = token_contract.functions.approve(spender_addr, max_uint).build_transaction({
247
+ "from": account.address,
248
+ "nonce": nonce,
249
+ "chainId": CHAIN_ID,
250
+ })
251
+ signed = account.sign_transaction(tx)
252
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
253
+ tx_hashes.append(tx_hash.hex())
254
+ nonce += 1
255
+
256
+ # Approve CTF (ERC-1155)
257
+ tx = ctf_contract.functions.setApprovalForAll(spender_addr, True).build_transaction({
258
+ "from": account.address,
259
+ "nonce": nonce,
260
+ "chainId": CHAIN_ID,
261
+ })
262
+ signed = account.sign_transaction(tx)
263
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
264
+ tx_hashes.append(tx_hash.hex())
265
+ nonce += 1
266
+
267
+ return tx_hashes
268
+
269
+
166
270
  async def check_balance(funder_address: str, rpc_url: str) -> BalanceInfo:
167
271
  """Check USDC and MATIC balance."""
168
272
  try:
@@ -243,17 +347,26 @@ def _parse_clob_credentials(data: dict) -> dict[str, str]:
243
347
  }
244
348
 
245
349
 
246
- async def create_clob_credentials(signer: Any, funder_address: str) -> dict[str, str]:
350
+ async def create_clob_credentials(
351
+ signer: Any,
352
+ funder_address: str,
353
+ version: ExchangeVersion = ExchangeVersion.V1,
354
+ ) -> dict[str, str]:
247
355
  """Create or derive CLOB API credentials via the Polymarket CLOB API.
248
356
 
249
357
  Tries POST /auth/api-key first (create new), falls back to
250
358
  GET /auth/derive-api-key (derive existing). Matches Rust SDK flow.
359
+
360
+ Same API key works on both V1 and V2 CLOBs (shared database),
361
+ but we route to the correct CLOB host for the version.
251
362
  """
363
+ host = CLOB_HOST_V2 if version == ExchangeVersion.V2 else CLOB_HOST
364
+
252
365
  async with httpx.AsyncClient(timeout=10.0) as http:
253
366
  # Step 1: Try creating a new API key
254
367
  headers = await _build_l1_headers(signer, funder_address)
255
368
  resp = await http.post(
256
- f"{CLOB_HOST}/auth/api-key",
369
+ f"{host}/auth/api-key",
257
370
  headers={**headers, "Accept": "*/*", "Content-Type": "application/json"},
258
371
  )
259
372
  if resp.is_success:
@@ -265,7 +378,7 @@ async def create_clob_credentials(signer: Any, funder_address: str) -> dict[str,
265
378
  # Step 2: Fall back to deriving existing key
266
379
  headers = await _build_l1_headers(signer, funder_address)
267
380
  resp = await http.get(
268
- f"{CLOB_HOST}/auth/derive-api-key",
381
+ f"{host}/auth/derive-api-key",
269
382
  headers={**headers, "Accept": "*/*", "Content-Type": "application/json"},
270
383
  )
271
384
  if not resp.is_success:
@@ -15,6 +15,7 @@ class NormalizedSigner:
15
15
  sign_message: Any # callable or None
16
16
  signature_type: SignatureType
17
17
  funder_address: str | None = None
18
+ _private_key: str | None = None # stored for on-chain txn signing (wrap/unwrap)
18
19
 
19
20
 
20
21
  async def normalize_signer(
@@ -66,6 +67,7 @@ async def normalize_signer(
66
67
  sign_typed_data=_sign_typed_data,
67
68
  sign_message=_sign_message,
68
69
  signature_type=default_signature_type,
70
+ _private_key=key,
69
71
  )
70
72
 
71
73
  if isinstance(signer, RouterSigner):
@@ -7,6 +7,7 @@ import time
7
7
  from typing import Any
8
8
 
9
9
  from .clob_api import (
10
+ _clob_host,
10
11
  cancel_all_orders,
11
12
  cancel_order as clob_cancel_order,
12
13
  fetch_neg_risk,
@@ -14,9 +15,15 @@ from .clob_api import (
14
15
  get_open_orders as clob_get_open_orders,
15
16
  post_order,
16
17
  )
17
- from .constants import DEFAULT_COSIGNER
18
+ from .constants import (
19
+ COLLATERAL_OFFRAMP,
20
+ COLLATERAL_ONRAMP,
21
+ DEFAULT_COSIGNER,
22
+ POLY_USD,
23
+ USDC,
24
+ )
18
25
  from .cosigner import build_l2_headers, send_via_cosigner
19
- from .eip712 import create_signed_order
26
+ from .eip712 import create_signed_order, create_signed_order_v2
20
27
  from .onboarding import (
21
28
  check_approvals as check_approvals_onchain,
22
29
  check_balance as check_balance_onchain,
@@ -24,6 +31,7 @@ from .onboarding import (
24
31
  derive_funder_address,
25
32
  detect_wallet_type,
26
33
  is_safe_deployed,
34
+ set_approvals as set_approvals_onchain,
27
35
  )
28
36
  from .signer import NormalizedSigner, normalize_signer
29
37
  from .sqlite_backend import TradingSqliteBackend
@@ -31,6 +39,7 @@ from .types import (
31
39
  ApprovalStatus,
32
40
  BalanceInfo,
33
41
  CancelResult,
42
+ ExchangeVersion,
34
43
  GeneratedWallet,
35
44
  HistoryParams,
36
45
  LinkResult,
@@ -61,6 +70,7 @@ class PolyNodeTrader:
61
70
  self._default_sig_type = c.default_signature_type
62
71
  self._rpc_url = c.rpc_url
63
72
  self._builder_credentials = c.builder_credentials
73
+ self._exchange_version = c.exchange_version
64
74
 
65
75
  self._db: TradingSqliteBackend | None = None
66
76
  self._active_signer: NormalizedSigner | None = None
@@ -123,7 +133,7 @@ class PolyNodeTrader:
123
133
  # Check approvals
124
134
  if sig_type in (SignatureType.POLY_GNOSIS_SAFE, SignatureType.EOA) and not approvals_set:
125
135
  try:
126
- approval_status = await check_approvals_onchain(funder, self._rpc_url)
136
+ approval_status = await check_approvals_onchain(funder, self._rpc_url, self._exchange_version)
127
137
  if approval_status.all_approved:
128
138
  approvals_set = True
129
139
  actions.append("approvals_already_set")
@@ -134,7 +144,7 @@ class PolyNodeTrader:
134
144
 
135
145
  # Create CLOB credentials
136
146
  if not stored:
137
- creds = await create_clob_credentials(normalized, funder)
147
+ creds = await create_clob_credentials(normalized, funder, self._exchange_version)
138
148
  stored = StoredCredentials(
139
149
  wallet_address=normalized.address,
140
150
  funder_address=funder,
@@ -195,7 +205,7 @@ class PolyNodeTrader:
195
205
  "apiPassphrase": existing.api_passphrase,
196
206
  }
197
207
  else:
198
- creds_dict = await create_clob_credentials(normalized, funder)
208
+ creds_dict = await create_clob_credentials(normalized, funder, self._exchange_version)
199
209
 
200
210
  db.upsert_credentials(StoredCredentials(
201
211
  wallet_address=normalized.address,
@@ -326,34 +336,223 @@ class PolyNodeTrader:
326
336
 
327
337
  async def check_approvals(self, wallet: str | None = None) -> ApprovalStatus:
328
338
  creds = self._get_stored_creds(wallet)
329
- return await check_approvals_onchain(creds.funder_address or creds.wallet_address, self._rpc_url)
339
+ return await check_approvals_onchain(creds.funder_address or creds.wallet_address, self._rpc_url, self._exchange_version)
330
340
 
331
341
  async def check_balance(self, wallet: str | None = None) -> BalanceInfo:
332
342
  creds = self._get_stored_creds(wallet)
333
343
  return await check_balance_onchain(creds.funder_address or creds.wallet_address, self._rpc_url)
334
344
 
345
+ # ── PolyUSD Wrap / Unwrap ──
346
+
347
+ async def wrap_to_polyusd(self, amount: int) -> str:
348
+ """Wrap USDC.e into PolyUSD via the Collateral Onramp.
349
+
350
+ Args:
351
+ amount: Amount in raw units (6 decimals, e.g. 1_000_000 = $1).
352
+
353
+ Returns:
354
+ Transaction hash as a hex string.
355
+ """
356
+ try:
357
+ from web3 import Web3
358
+ from eth_account import Account
359
+ except ImportError:
360
+ raise ImportError("web3 required. Install with: pip install polynode[trading]")
361
+
362
+ signer = self._require_signer()
363
+ creds = self._get_stored_creds()
364
+ funder = creds.funder_address or creds.wallet_address
365
+
366
+ w3 = Web3(Web3.HTTPProvider(self._rpc_url))
367
+
368
+ erc20_abi = [
369
+ {"inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function"},
370
+ {"inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
371
+ ]
372
+
373
+ usdc_contract = w3.eth.contract(address=Web3.to_checksum_address(USDC), abi=erc20_abi)
374
+ onramp_addr = Web3.to_checksum_address(COLLATERAL_ONRAMP)
375
+ signer_addr = Web3.to_checksum_address(signer.address)
376
+
377
+ # Build account from the signer's private key (requires sign_message support)
378
+ # For private-key signers, we can reconstruct the account
379
+ if not signer._private_key:
380
+ raise RuntimeError("wrap_to_polyusd requires a private key signer (not RouterSigner)")
381
+
382
+ nonce = w3.eth.get_transaction_count(signer_addr)
383
+
384
+ # Step 1: Check and set USDC.e allowance for Onramp
385
+ allowance = usdc_contract.functions.allowance(signer_addr, onramp_addr).call()
386
+ if allowance < amount:
387
+ max_uint = 2**256 - 1
388
+ tx = usdc_contract.functions.approve(onramp_addr, max_uint).build_transaction({
389
+ "from": signer_addr,
390
+ "nonce": nonce,
391
+ "chainId": 137,
392
+ })
393
+ signed = w3.eth.account.sign_transaction(tx, signer._private_key)
394
+ w3.eth.send_raw_transaction(signed.raw_transaction)
395
+ nonce += 1
396
+
397
+ # Step 2: Call wrap(address token, address funder, uint256 amount) on Onramp
398
+ # wrap selector: 0x62355638
399
+ wrap_abi = [
400
+ {"inputs": [{"name": "token", "type": "address"}, {"name": "funder", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "wrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
401
+ ]
402
+ onramp_contract = w3.eth.contract(address=onramp_addr, abi=wrap_abi)
403
+ tx = onramp_contract.functions.wrap(
404
+ Web3.to_checksum_address(USDC),
405
+ Web3.to_checksum_address(funder),
406
+ amount,
407
+ ).build_transaction({
408
+ "from": signer_addr,
409
+ "nonce": nonce,
410
+ "chainId": 137,
411
+ })
412
+ signed = w3.eth.account.sign_transaction(tx, signer._private_key)
413
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
414
+ return tx_hash.hex()
415
+
416
+ async def unwrap_from_polyusd(self, amount: int) -> str:
417
+ """Unwrap PolyUSD back into USDC.e via the Collateral Offramp.
418
+
419
+ Args:
420
+ amount: Amount in raw units (6 decimals, e.g. 1_000_000 = $1).
421
+
422
+ Returns:
423
+ Transaction hash as a hex string.
424
+ """
425
+ try:
426
+ from web3 import Web3
427
+ except ImportError:
428
+ raise ImportError("web3 required. Install with: pip install polynode[trading]")
429
+
430
+ signer = self._require_signer()
431
+ creds = self._get_stored_creds()
432
+ funder = creds.funder_address or creds.wallet_address
433
+
434
+ w3 = Web3(Web3.HTTPProvider(self._rpc_url))
435
+
436
+ if not signer._private_key:
437
+ raise RuntimeError("unwrap_from_polyusd requires a private key signer (not RouterSigner)")
438
+
439
+ signer_addr = Web3.to_checksum_address(signer.address)
440
+ offramp_addr = Web3.to_checksum_address(COLLATERAL_OFFRAMP)
441
+
442
+ # Check and set PolyUSD allowance for Offramp
443
+ erc20_abi = [
444
+ {"inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function"},
445
+ {"inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
446
+ ]
447
+ polyusd_contract = w3.eth.contract(address=Web3.to_checksum_address(POLY_USD), abi=erc20_abi)
448
+
449
+ nonce = w3.eth.get_transaction_count(signer_addr)
450
+
451
+ allowance = polyusd_contract.functions.allowance(signer_addr, offramp_addr).call()
452
+ if allowance < amount:
453
+ max_uint = 2**256 - 1
454
+ tx = polyusd_contract.functions.approve(offramp_addr, max_uint).build_transaction({
455
+ "from": signer_addr,
456
+ "nonce": nonce,
457
+ "chainId": 137,
458
+ })
459
+ signed = w3.eth.account.sign_transaction(tx, signer._private_key)
460
+ w3.eth.send_raw_transaction(signed.raw_transaction)
461
+ nonce += 1
462
+
463
+ # Call unwrap(address token, address receiver, uint256 amount) on Offramp
464
+ unwrap_abi = [
465
+ {"inputs": [{"name": "token", "type": "address"}, {"name": "receiver", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "unwrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
466
+ ]
467
+ offramp_contract = w3.eth.contract(address=offramp_addr, abi=unwrap_abi)
468
+ tx = offramp_contract.functions.unwrap(
469
+ Web3.to_checksum_address(USDC),
470
+ Web3.to_checksum_address(funder),
471
+ amount,
472
+ ).build_transaction({
473
+ "from": signer_addr,
474
+ "nonce": nonce,
475
+ "chainId": 137,
476
+ })
477
+ signed = w3.eth.account.sign_transaction(tx, signer._private_key)
478
+ tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
479
+ return tx_hash.hex()
480
+
481
+ def get_polyusd_balance(self) -> int:
482
+ """Get PolyUSD balance for the active funder address.
483
+
484
+ Returns:
485
+ Balance in raw units (6 decimals).
486
+ """
487
+ try:
488
+ from web3 import Web3
489
+ except ImportError:
490
+ raise ImportError("web3 required. Install with: pip install polynode[trading]")
491
+
492
+ creds = self._get_stored_creds()
493
+ funder = creds.funder_address or creds.wallet_address
494
+
495
+ w3 = Web3(Web3.HTTPProvider(self._rpc_url))
496
+ erc20_abi = [{"inputs": [{"name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"}]
497
+ contract = w3.eth.contract(address=Web3.to_checksum_address(POLY_USD), abi=erc20_abi)
498
+ return contract.functions.balanceOf(Web3.to_checksum_address(funder)).call()
499
+
500
+ def get_usdce_balance(self) -> int:
501
+ """Get USDC.e balance for the active funder address.
502
+
503
+ Returns:
504
+ Balance in raw units (6 decimals).
505
+ """
506
+ try:
507
+ from web3 import Web3
508
+ except ImportError:
509
+ raise ImportError("web3 required. Install with: pip install polynode[trading]")
510
+
511
+ creds = self._get_stored_creds()
512
+ funder = creds.funder_address or creds.wallet_address
513
+
514
+ w3 = Web3(Web3.HTTPProvider(self._rpc_url))
515
+ erc20_abi = [{"inputs": [{"name": "account", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"}]
516
+ contract = w3.eth.contract(address=Web3.to_checksum_address(USDC), abi=erc20_abi)
517
+ return contract.functions.balanceOf(Web3.to_checksum_address(funder)).call()
518
+
335
519
  # ── Trading ──
336
520
 
337
521
  async def order(self, params: OrderParams) -> OrderResult:
338
- """Place an order on Polymarket."""
522
+ """Place an order on Polymarket. Routes to V1 or V2 CLOB based on exchange_version."""
339
523
  creds = self._get_stored_creds()
340
524
  signer = self._require_signer()
341
525
  meta = await self._fetch_meta(params.token_id)
342
-
343
- signed_order = await create_signed_order(
344
- signer.sign_typed_data,
345
- signer_address=signer.address,
346
- funder_address=creds.funder_address or creds.wallet_address,
347
- token_id=params.token_id,
348
- price=params.price,
349
- size=params.size,
350
- side=params.side,
351
- signature_type=creds.signature_type,
352
- tick_size=meta.tick_size,
353
- neg_risk=meta.neg_risk,
354
- fee_rate_bps=meta.fee_rate_bps,
355
- expiration=params.expiration or 0,
356
- )
526
+ clob_host = _clob_host(self._exchange_version)
527
+
528
+ if self._exchange_version == ExchangeVersion.V2:
529
+ signed_order = await create_signed_order_v2(
530
+ signer.sign_typed_data,
531
+ signer_address=signer.address,
532
+ funder_address=creds.funder_address or creds.wallet_address,
533
+ token_id=params.token_id,
534
+ price=params.price,
535
+ size=params.size,
536
+ side=params.side,
537
+ signature_type=creds.signature_type,
538
+ tick_size=meta.tick_size,
539
+ neg_risk=meta.neg_risk,
540
+ )
541
+ else:
542
+ signed_order = await create_signed_order(
543
+ signer.sign_typed_data,
544
+ signer_address=signer.address,
545
+ funder_address=creds.funder_address or creds.wallet_address,
546
+ token_id=params.token_id,
547
+ price=params.price,
548
+ size=params.size,
549
+ side=params.side,
550
+ signature_type=creds.signature_type,
551
+ tick_size=meta.tick_size,
552
+ neg_risk=meta.neg_risk,
553
+ fee_rate_bps=meta.fee_rate_bps,
554
+ expiration=params.expiration or 0,
555
+ )
357
556
 
358
557
  payload = {
359
558
  "order": signed_order,
@@ -384,6 +583,7 @@ class PolyNodeTrader:
384
583
  self._cosigner_url, self._polynode_key, self._fallback_direct,
385
584
  {"method": "POST", "path": "/order", "body": body_str, "headers": headers},
386
585
  builder_credentials=self._builder_credentials,
586
+ clob_host=clob_host,
387
587
  )
388
588
 
389
589
  order_id = result.get("orderID") or result.get("orderId")
@@ -407,11 +607,13 @@ class PolyNodeTrader:
407
607
 
408
608
  async def cancel_order(self, order_id: str) -> CancelResult:
409
609
  creds = self._get_stored_creds()
610
+ clob_host = _clob_host(self._exchange_version)
410
611
  result = await clob_cancel_order(
411
612
  self._cosigner_url, self._polynode_key, self._fallback_direct,
412
613
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
413
614
  creds.wallet_address, order_id,
414
615
  builder_credentials=self._builder_credentials,
616
+ clob_host=clob_host,
415
617
  )
416
618
  return CancelResult(
417
619
  canceled=result.get("canceled", []),
@@ -420,11 +622,13 @@ class PolyNodeTrader:
420
622
 
421
623
  async def cancel_all(self, market: str | None = None) -> CancelResult:
422
624
  creds = self._get_stored_creds()
625
+ clob_host = _clob_host(self._exchange_version)
423
626
  result = await cancel_all_orders(
424
627
  self._cosigner_url, self._polynode_key, self._fallback_direct,
425
628
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
426
629
  creds.wallet_address, market,
427
630
  builder_credentials=self._builder_credentials,
631
+ clob_host=clob_host,
428
632
  )
429
633
  return CancelResult(
430
634
  canceled=result.get("canceled", []),
@@ -435,11 +639,13 @@ class PolyNodeTrader:
435
639
  self, *, market: str | None = None, asset_id: str | None = None
436
640
  ) -> list[OpenOrder]:
437
641
  creds = self._get_stored_creds()
642
+ clob_host = _clob_host(self._exchange_version)
438
643
  result = await clob_get_open_orders(
439
644
  self._cosigner_url, self._polynode_key, self._fallback_direct,
440
645
  {"apiKey": creds.api_key, "apiSecret": creds.api_secret, "apiPassphrase": creds.api_passphrase},
441
646
  creds.wallet_address, market, asset_id,
442
647
  builder_credentials=self._builder_credentials,
648
+ clob_host=clob_host,
443
649
  )
444
650
  return [
445
651
  OpenOrder(
@@ -467,7 +673,10 @@ class PolyNodeTrader:
467
673
  return cached
468
674
 
469
675
  import asyncio
470
- tick, neg = await asyncio.gather(fetch_tick_size(token_id), fetch_neg_risk(token_id))
676
+ tick, neg = await asyncio.gather(
677
+ fetch_tick_size(token_id, self._exchange_version),
678
+ fetch_neg_risk(token_id, self._exchange_version),
679
+ )
471
680
  meta = MarketMeta(
472
681
  token_id=token_id,
473
682
  tick_size=tick,
polynode/trading/types.py CHANGED
@@ -13,6 +13,11 @@ class SignatureType(IntEnum):
13
13
  POLY_GNOSIS_SAFE = 2
14
14
 
15
15
 
16
+ class ExchangeVersion(IntEnum):
17
+ V1 = 1
18
+ V2 = 2
19
+
20
+
16
21
  @runtime_checkable
17
22
  class RouterSigner(Protocol):
18
23
  async def get_address(self) -> str: ...
@@ -50,6 +55,7 @@ class TraderConfig:
50
55
  default_signature_type: SignatureType = SignatureType.POLY_GNOSIS_SAFE
51
56
  rpc_url: str = "https://polygon-bor-rpc.publicnode.com"
52
57
  builder_credentials: BuilderCredentials | None = None
58
+ exchange_version: ExchangeVersion = ExchangeVersion.V1
53
59
 
54
60
 
55
61
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polynode
3
- Version: 0.6.3
3
+ Version: 0.7.0
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
@@ -11,18 +11,18 @@ polynode/subscription.py,sha256=O2yKfdllI0px-1qrVK-0Qq9S4KUmIiXjjYCNC7QW0jM,4121
11
11
  polynode/testing.py,sha256=bSSgV18ZSaRoLZPlNRCCLmRNwNIQDS35qi7NhuTg8Bg,2802
12
12
  polynode/ws.py,sha256=VZdn2dkUJH2-CYBu_L45YnvCU0kQejxHcy2Ji2pRu60,9653
13
13
  polynode/cache/__init__.py,sha256=A9lXcIE3ISKKo1r0o3o-CkmQpBhLdKwdQfCR0eW5i9o,519
14
- polynode/trading/__init__.py,sha256=grC3dembNNSuRzgbOHC6nJZ6YEfj5iDdh4n-H0bJnNo,883
15
- polynode/trading/clob_api.py,sha256=X_07Zp__GxG0_pkGtaKnNju9f6lw5dTOkEg8fM0WJ9M,4628
16
- polynode/trading/constants.py,sha256=q_QTi1w8XVw-6PRNltP9hinE4PUH0GFGgKNbkEyMdR4,1200
17
- polynode/trading/cosigner.py,sha256=aQGbKVNWp3VaEBZNoStcsj4goWKILOyOnGuFjT1RkRQ,2902
18
- polynode/trading/eip712.py,sha256=ekD0l0wocVl-OQiBOuLEENyZP89_KqDA0QtjNg5HiEg,4796
19
- polynode/trading/onboarding.py,sha256=UgPLP9WJcETzGXUrP7zrBUUe5dHBxWhFZMI6e3dM7A4,9933
14
+ polynode/trading/__init__.py,sha256=0mtvY4w2k1x000YOX7_ua9JZj4CY27Cz7p0u6L3Hfrw,906
15
+ polynode/trading/clob_api.py,sha256=ygaOoMIg4PGtMzNBT70wk4XrSbVX0EyCCU6PmXK3xrs,5308
16
+ polynode/trading/constants.py,sha256=CjHcfaOTzPFvlorY4XeyGtMS8XNKH4jjoxqKtpN6NmY,1782
17
+ polynode/trading/cosigner.py,sha256=Fww9OgaKdHQAHd8Kom1AJBtmMGC8FbNZxdFaFALNBY8,3058
18
+ polynode/trading/eip712.py,sha256=pZf0Pf4DDIPpK4qbz4DWfcdOeU8PlCQhJPpKSQ97pC4,9553
19
+ polynode/trading/onboarding.py,sha256=ouP9bzioFHCt4fnluAXLWL8O8U6d_1ePeu5JmBXeDyc,13768
20
20
  polynode/trading/position_management.py,sha256=z2pYls4ErhyRE6thQdOs45BF4llKvKNNruQgcMEFT_U,3827
21
21
  polynode/trading/privy.py,sha256=iiUQZsBqjj29C7oUiFFktoT2muZY06bcMzABKstFKqE,4523
22
- polynode/trading/signer.py,sha256=5KTARp6BnSorRt6n3YB11wpmivX_zOhVm-jzn098iAA,3298
22
+ polynode/trading/signer.py,sha256=piifRquwnwFwrKLL24jsRQYMh_cdkz8wNz99Vx8_KTY,3413
23
23
  polynode/trading/sqlite_backend.py,sha256=9ho7DnQVE_nJqmPH5yPSAYcVjVmTS1XmD3NebR-UAwA,7986
24
- polynode/trading/trader.py,sha256=_zZhHrCGd33SCy6DAnCzN4sA4BYz_t08HZ89JcbDJyM,19970
25
- polynode/trading/types.py,sha256=W42iikSyQ6NZ4xAnxQ4mELbSKJflzN-tkbTQT3_y6tM,4829
24
+ polynode/trading/trader.py,sha256=VVNQ_DEkum5u3IC2M5IfLml7s06A7fCqjGXvSX3ayCg,29497
25
+ polynode/trading/types.py,sha256=WGSH25c1LIZax4KNPavHz0G6HEPLUK9ak9VnEYd8fxM,4944
26
26
  polynode/types/__init__.py,sha256=0v_CByqG-zT1xucbT2So_77YDmsPj9u956at94nyrLM,280
27
27
  polynode/types/enums.py,sha256=2cx9l10A-LUeDNBJZn7l0XBTka2YfxRQcXW0s7s3_4k,1082
28
28
  polynode/types/events.py,sha256=MJGZqxBFKfT1y44dZ96bHbwvoPpGvtutBwYE6OTT6nU,6880
@@ -30,6 +30,6 @@ polynode/types/orderbook.py,sha256=MwD6P3poo3F70bONUZ5i3ajk1sgw39hHTOw6D0fP5_8,1
30
30
  polynode/types/rest.py,sha256=ftJKQZH0wBrR_l199rDMqnVS8cyt8YnCvo9cKIsTcCw,7012
31
31
  polynode/types/short_form.py,sha256=xfGklDi587u6K2aXaRWiwbxqkipW8BiEPeNIDT45v2Y,799
32
32
  polynode/types/ws.py,sha256=EzE9Vzrb6cILemfssmnqM8fbHt4USn3latpUHKatwk4,994
33
- polynode-0.6.3.dist-info/METADATA,sha256=Faq2Cb3B797tpO1snnRm9xWtx5de9mGEPMwIDfmZDhc,3302
34
- polynode-0.6.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
35
- polynode-0.6.3.dist-info/RECORD,,
33
+ polynode-0.7.0.dist-info/METADATA,sha256=qIF1aMlx-nQAUySzKpoqj3y9Mx66vRNnG4Cuuwvbt0Q,3302
34
+ polynode-0.7.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
35
+ polynode-0.7.0.dist-info/RECORD,,