polynode 0.10.3__tar.gz → 0.10.4__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.3 → polynode-0.10.4}/PKG-INFO +1 -1
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/relayer.py +121 -2
- {polynode-0.10.3 → polynode-0.10.4}/pyproject.toml +1 -1
- {polynode-0.10.3 → polynode-0.10.4}/.gitignore +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/README.md +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/__init__.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/_version.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/cache/__init__.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/client.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/engine.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/errors.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/orderbook.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/orderbook_state.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/redemption_watcher.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/short_form.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/subscription.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/testing.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/V2_ORDER_FLOW.md +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/__init__.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/clob_api.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/constants.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/cosigner.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/eip712.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/escrow.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/onboarding.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/position_management.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/privy.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/signer.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/sqlite_backend.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/trader.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/trading/types.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/__init__.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/enums.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/events.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/orderbook.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/rest.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/short_form.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/types/ws.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/polynode/ws.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/__init__.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/conftest.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/test_client.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/test_orderbook.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/test_trading.py +0 -0
- {polynode-0.10.3 → polynode-0.10.4}/tests/test_types.py +0 -0
|
@@ -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)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|