polynode 0.10.2__py3-none-any.whl → 0.10.4__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.
@@ -25,8 +25,8 @@ from typing import Any
25
25
 
26
26
  import httpx
27
27
 
28
- from .constants import CHAIN_ID, RELAYER_HOST, SAFE_FACTORY, SAFE_MULTISEND
29
- from .onboarding import derive_safe_address
28
+ from .constants import CHAIN_ID, RELAYER_HOST, SAFE_FACTORY, SAFE_MULTISEND, CTF, DEPOSIT_WALLET_FACTORY
29
+ from .onboarding import derive_safe_address, derive_deposit_wallet_address
30
30
 
31
31
 
32
32
  @dataclass
@@ -301,3 +301,122 @@ class RelayClient:
301
301
  if state in ("STATE_FAILED", "STATE_REVERTED"):
302
302
  raise RuntimeError(f"tx {tx_id} failed on-chain: state={state} hash={row.get('transactionHash')}")
303
303
  raise RuntimeError(f"tx {tx_id} timed out waiting for mined state")
304
+
305
+ async def _poll_tx(self, tx_id: str) -> str:
306
+ """Poll relayer transaction until mined/confirmed/failed."""
307
+ for _ in range(45):
308
+ await asyncio.sleep(2)
309
+ async with httpx.AsyncClient(timeout=10.0) as client:
310
+ r = await client.get(f"{self.relayer_url}/transaction", params={"id": tx_id})
311
+ if not r.is_success:
312
+ continue
313
+ rows = r.json()
314
+ row = rows[0] if isinstance(rows, list) else rows
315
+ state = row.get("state", "")
316
+ if state in ("STATE_MINED", "STATE_CONFIRMED"):
317
+ return row["transactionHash"]
318
+ if state in ("STATE_FAILED", "STATE_REVERTED"):
319
+ raise RuntimeError(f"tx {tx_id} failed: {state}")
320
+ raise RuntimeError(f"tx {tx_id} timed out")
321
+
322
+ async def _submit_with_auth(self, body: dict) -> str:
323
+ """Submit to relayer with builder HMAC headers, poll until confirmed."""
324
+ body_str = json.dumps(body, separators=(",", ":"))
325
+ headers = _build_hmac_headers(self.builder_creds, "POST", "/submit", body_str)
326
+ headers["Content-Type"] = "application/json"
327
+ async with httpx.AsyncClient(timeout=30.0) as client:
328
+ r = await client.post(f"{self.relayer_url}/submit", content=body_str, headers=headers)
329
+ if not r.is_success:
330
+ raise RuntimeError(f"relayer /submit {r.status_code}: {r.text}")
331
+ resp = r.json()
332
+ tx_id = resp["transactionID"]
333
+ return await self._poll_tx(tx_id)
334
+
335
+ async def deploy_deposit_wallet(self, eoa_address: str) -> str:
336
+ """Deploy a deposit wallet for an EOA via the relayer (gasless)."""
337
+ body = {
338
+ "type": "WALLET-CREATE",
339
+ "from": eoa_address,
340
+ "to": DEPOSIT_WALLET_FACTORY,
341
+ }
342
+ return await self._submit_with_auth(body)
343
+
344
+ async def set_deposit_wallet_approvals(
345
+ self, signer_pk: str, eoa_address: str, wallet_address: str,
346
+ ) -> str:
347
+ """Set token approvals for a deposit wallet via EIP-712 batch + relayer (gasless)."""
348
+ try:
349
+ from eth_account import Account
350
+ from eth_abi import encode
351
+ except ImportError:
352
+ raise ImportError("eth-account and eth-abi required for deposit wallet approvals")
353
+
354
+ from .constants import V2_SPENDERS, POLY_USD
355
+
356
+ # Build approval calls
357
+ max_uint = (1 << 256) - 1
358
+ approve_sel = bytes.fromhex("095ea7b3")
359
+ set_approval_sel = bytes.fromhex("a22cb465")
360
+ calls = []
361
+ for spender in V2_SPENDERS[:3]:
362
+ # pUSD approve(spender, maxUint256)
363
+ data = approve_sel + encode(["address", "uint256"], [spender, max_uint])
364
+ calls.append({"target": POLY_USD, "value": "0", "data": "0x" + data.hex()})
365
+ # CTF setApprovalForAll(spender, true)
366
+ data2 = set_approval_sel + encode(["address", "bool"], [spender, True])
367
+ calls.append({"target": CTF, "value": "0", "data": "0x" + data2.hex()})
368
+
369
+ # Get nonce
370
+ async with httpx.AsyncClient(timeout=20.0) as client:
371
+ r = await client.get(f"{self.relayer_url}/nonce", params={"address": eoa_address, "type": "WALLET"})
372
+ r.raise_for_status()
373
+ nonce = int(r.json().get("nonce", "0"))
374
+
375
+ deadline = int(time.time()) + 3600
376
+
377
+ # EIP-712 sign the Batch message
378
+ domain = {
379
+ "name": "DepositWallet",
380
+ "version": "1",
381
+ "chainId": self.chain_id,
382
+ "verifyingContract": wallet_address,
383
+ }
384
+ batch_types = {
385
+ "Batch": [
386
+ {"name": "wallet", "type": "address"},
387
+ {"name": "nonce", "type": "uint256"},
388
+ {"name": "deadline", "type": "uint256"},
389
+ {"name": "calls", "type": "Call[]"},
390
+ ],
391
+ "Call": [
392
+ {"name": "target", "type": "address"},
393
+ {"name": "value", "type": "uint256"},
394
+ {"name": "data", "type": "bytes"},
395
+ ],
396
+ }
397
+ message = {
398
+ "wallet": wallet_address,
399
+ "nonce": nonce,
400
+ "deadline": deadline,
401
+ "calls": [{"target": c["target"], "value": 0, "data": bytes.fromhex(c["data"][2:])} for c in calls],
402
+ }
403
+
404
+ pk_hex = signer_pk if signer_pk.startswith("0x") else f"0x{signer_pk}"
405
+ full_types = {"EIP712Domain": [{"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"}], **batch_types}
406
+ full_message = {"types": full_types, "primaryType": "Batch", "domain": domain, "message": message}
407
+ signed = Account.sign_typed_data(pk_hex, full_message=full_message)
408
+ sig_hex = "0x" + signed.signature.hex()
409
+
410
+ body = {
411
+ "type": "WALLET",
412
+ "from": eoa_address,
413
+ "to": DEPOSIT_WALLET_FACTORY,
414
+ "nonce": str(nonce),
415
+ "signature": sig_hex,
416
+ "depositWalletParams": {
417
+ "depositWallet": wallet_address,
418
+ "deadline": str(deadline),
419
+ "calls": calls,
420
+ },
421
+ }
422
+ return await self._submit_with_auth(body)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polynode
3
- Version: 0.10.2
3
+ Version: 0.10.4
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
@@ -21,7 +21,7 @@ polynode/trading/escrow.py,sha256=T2v5bv3wIyQTcFoxcHXlDBlzyeWDYbxRIqQamqlhaog,65
21
21
  polynode/trading/onboarding.py,sha256=zCKK1HxHxNJoVUE9NC10K7dzNy5D3GY13i7FZo3iEIk,17534
22
22
  polynode/trading/position_management.py,sha256=z2pYls4ErhyRE6thQdOs45BF4llKvKNNruQgcMEFT_U,3827
23
23
  polynode/trading/privy.py,sha256=iiUQZsBqjj29C7oUiFFktoT2muZY06bcMzABKstFKqE,4523
24
- polynode/trading/relayer.py,sha256=adTo-aclWnaqrFtfWEOQ2Ye859CZMc9q_fsd3DePDGA,10542
24
+ polynode/trading/relayer.py,sha256=Rhc11uh37gcz70kp-6d8A8t6NZW_o1N84IROxKIzEyM,15910
25
25
  polynode/trading/signer.py,sha256=piifRquwnwFwrKLL24jsRQYMh_cdkz8wNz99Vx8_KTY,3413
26
26
  polynode/trading/sqlite_backend.py,sha256=zfo4WevuunWV-7i6Z3gQi_e7Tonw-7m1TsEd88O5I4w,9486
27
27
  polynode/trading/trader.py,sha256=EEwF_U2DXVPRnENY5azk8FgzSx_zXns27apQQj9isus,37188
@@ -33,6 +33,6 @@ polynode/types/orderbook.py,sha256=MwD6P3poo3F70bONUZ5i3ajk1sgw39hHTOw6D0fP5_8,1
33
33
  polynode/types/rest.py,sha256=ftJKQZH0wBrR_l199rDMqnVS8cyt8YnCvo9cKIsTcCw,7012
34
34
  polynode/types/short_form.py,sha256=xfGklDi587u6K2aXaRWiwbxqkipW8BiEPeNIDT45v2Y,799
35
35
  polynode/types/ws.py,sha256=EzE9Vzrb6cILemfssmnqM8fbHt4USn3latpUHKatwk4,994
36
- polynode-0.10.2.dist-info/METADATA,sha256=MAvJgdBzzVHkChH0QZC71vvzBYVSr4xVGReV7ZES3pk,3778
37
- polynode-0.10.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
38
- polynode-0.10.2.dist-info/RECORD,,
36
+ polynode-0.10.4.dist-info/METADATA,sha256=sElDkSeZN1Xp_a640BGDI6veMwptE-aRnw3oRdvz9bc,3778
37
+ polynode-0.10.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
38
+ polynode-0.10.4.dist-info/RECORD,,