polynode 0.14.0__tar.gz → 0.14.1__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.14.0 → polynode-0.14.1}/PKG-INFO +69 -14
- {polynode-0.14.0 → polynode-0.14.1}/README.md +68 -13
- polynode-0.14.1/polynode/_version.py +1 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/eip712.py +29 -7
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/types.py +66 -8
- {polynode-0.14.0 → polynode-0.14.1}/pyproject.toml +1 -1
- polynode-0.14.0/polynode/_version.py +0 -1
- {polynode-0.14.0 → polynode-0.14.1}/.gitignore +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/core-contract-v1.json +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/core-fixtures-v1.json +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/__init__.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/cache/__init__.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/client.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/engine.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/errors.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/orderbook.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/orderbook_integrity.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/orderbook_state.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/perps.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/redemption_watcher.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/short_form.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/subscription.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/testing.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/__init__.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/clob_api.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/constants.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/cosigner.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/escrow.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/onboarding.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/position_management.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/privy.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/relayer.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/signer.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/sqlite_backend.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/trader.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/trading/user_relayer.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/__init__.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/enums.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/events.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/orderbook.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/perps.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/rest.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/short_form.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/types/ws.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/v3.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/v3_operations.py +0 -0
- {polynode-0.14.0 → polynode-0.14.1}/polynode/ws.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: polynode
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.1
|
|
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
|
|
@@ -36,7 +36,9 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
|
|
37
37
|
Python SDK for the [Polynode](https://polynode.dev) real-time prediction market data platform.
|
|
38
38
|
|
|
39
|
-
**New in v0.14.
|
|
39
|
+
**New in v0.14.1:** Every prepared user-owned order exposes its canonical V2 exchange `order_hash` before submission. This is the exact CLOB/open-order ID and fill `order_hash`; deposit-wallet orders hash the standard inner `Order`, not the POLY_1271 wallet-signing wrapper. Record it before the network attempt so timeouts can be reconciled without risking a duplicate.
|
|
40
|
+
|
|
41
|
+
**In v0.14.0:** Web platforms can take a user from wallet authorization through an exact browser-signed user-owned order without exposing backend credentials. The SDK imports the shared versioned browser bundle into memory, produces a credential-free signing request with a complete order preview, supports one-time multi-worker state, validates canonical signatures and wallet identity, checks BUY collateral before prompting, and submits with exact zero builder attribution.
|
|
40
42
|
|
|
41
43
|
**In v0.13.0:** Trading added explicit `user_owned` execution. Existing builder mode remains the default; opted-in wallets use zero builder attribution, one wallet-ownership authorization, and strict wallet-bound gasless credentials. Builder credentials and nonzero builder codes fail closed in this mode. EOA-controlled Safe and deposit wallets are supported; legacy Magic/proxy wallets are intentionally excluded from the first release.
|
|
42
44
|
|
|
@@ -536,29 +538,82 @@ const result = await fetch("/api/orders/submit", {
|
|
|
536
538
|
}).then((response) => response.json());
|
|
537
539
|
```
|
|
538
540
|
|
|
539
|
-
On the backend, atomically take the prepared object from session state, then validate and submit it:
|
|
541
|
+
On the backend, atomically take the prepared object from session state, durably record the deterministic order identity, then validate and submit it:
|
|
540
542
|
|
|
541
543
|
```python
|
|
542
|
-
async def submit_order(trader, browser_result, prepared_orders):
|
|
544
|
+
async def submit_order(trader, browser_result, prepared_orders, attempts):
|
|
543
545
|
# `take_once` must delete atomically so two workers cannot submit the same request.
|
|
544
546
|
prepared = await prepared_orders.take_once(browser_result.request_id)
|
|
545
547
|
if prepared is None:
|
|
546
548
|
raise ValueError("Unknown or already-used signing request")
|
|
547
549
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
+
order_hash = prepared.order_hash
|
|
551
|
+
# This durable, idempotent write must commit before submission starts.
|
|
552
|
+
await attempts.create_once(
|
|
553
|
+
order_hash=order_hash,
|
|
550
554
|
request_id=browser_result.request_id,
|
|
551
|
-
|
|
552
|
-
|
|
555
|
+
wallet=prepared.signing_request.address,
|
|
556
|
+
token_id=prepared.signing_request.order["tokenId"],
|
|
557
|
+
side=prepared.signing_request.order["side"],
|
|
558
|
+
status="submitting",
|
|
553
559
|
)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
560
|
+
|
|
561
|
+
try:
|
|
562
|
+
result = await trader.submit_prepared_user_owned_order(
|
|
563
|
+
prepared,
|
|
564
|
+
request_id=browser_result.request_id,
|
|
565
|
+
address=browser_result.address,
|
|
566
|
+
signature=browser_result.signature,
|
|
567
|
+
)
|
|
568
|
+
except BaseException:
|
|
569
|
+
# Once consumed, a local/network exception has an ambiguous outcome.
|
|
570
|
+
status = (
|
|
571
|
+
"outcome_unknown"
|
|
572
|
+
if prepared.consumed
|
|
573
|
+
else "rejected_before_submission"
|
|
574
|
+
)
|
|
575
|
+
await attempts.set_status(order_hash, status)
|
|
576
|
+
raise
|
|
577
|
+
else:
|
|
578
|
+
if result.order_id and result.order_id.lower() != order_hash.lower():
|
|
579
|
+
await attempts.set_status(order_hash, "identity_mismatch")
|
|
580
|
+
raise RuntimeError("CLOB returned an unexpected order ID")
|
|
581
|
+
await attempts.record_response(order_hash, result)
|
|
582
|
+
return {
|
|
583
|
+
"success": result.success,
|
|
584
|
+
"orderId": result.order_id or order_hash,
|
|
585
|
+
"error": result.error,
|
|
586
|
+
}
|
|
559
587
|
```
|
|
560
588
|
|
|
561
|
-
`
|
|
589
|
+
`prepared.order_hash` is the lowercase `0x`-prefixed EIP-712 digest of the canonical standard exchange `Order`. For EOA and Safe orders it also matches the browser-signing digest. POLY_1271 browsers sign an outer `TypedDataSign` wrapper, whose digest is intentionally different; `order_hash` always identifies the inner order that the CLOB and fills report. The property is computed on demand, is unchanged by trusted-state export/import, and is not added to the serialized schema.
|
|
590
|
+
|
|
591
|
+
`prepare_user_owned_order()` supports V2 user-owned execution only, expires after five minutes by default, forces zero builder attribution, rejects positive fee authentication, and performs no submission. GTC, FOK, and FAK orders must omit expiration (zero is accepted and canonicalized to no expiration); GTD requires a fresh Unix-seconds expiration with the 60-second safety buffer, and the signing request is clamped to that window. `submit_prepared_user_owned_order()` verifies the request ID, expiry, connected wallet, stored wallet/funder identity, exact typed data, and recovered signer before consuming the request. A consumed request cannot be replayed.
|
|
592
|
+
|
|
593
|
+
Treat any exception after `prepared.consumed` becomes true—including an HTTP timeout, disconnect, cancellation, or local failure around the network call—as `outcome_unknown`. Never submit that hash again and never automatically prepare a replacement. Reconcile by exact hash against both `await trader.get_open_orders(asset_id=token_id)` (`OpenOrder.id`) and your durable authenticated V2 fill source (`fill.order_hash`):
|
|
594
|
+
|
|
595
|
+
```python
|
|
596
|
+
async def reconcile_unknown(trader, attempt, fills):
|
|
597
|
+
open_orders = await trader.get_open_orders(asset_id=attempt.token_id)
|
|
598
|
+
open_order = next(
|
|
599
|
+
(order for order in open_orders if order.id.lower() == attempt.order_hash.lower()),
|
|
600
|
+
None,
|
|
601
|
+
)
|
|
602
|
+
matching_fills = await fills.by_order_hash(attempt.order_hash)
|
|
603
|
+
return {"openOrder": open_order, "fills": matching_fills}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
An open order may already be partially filled (`size_matched` is nonzero), while a fully filled or canceled order will not remain open. Group all partial fills for the exact hash. Finding neither an open order nor a fill is not proof that submission failed: keep the attempt unknown and retry reconciliation with bounded backoff. The application-owned attempt store and fill source must be durable, wallet-scoped, and idempotent by `order_hash`; do not infer identity from token, side, amounts, timestamps, balances, or request ID.
|
|
607
|
+
|
|
608
|
+
If a request-scoped trader was populated from a browser bundle or decrypted vault record, clear its credentials even when validation, signing, submission, or reconciliation fails:
|
|
609
|
+
|
|
610
|
+
```python
|
|
611
|
+
trader = await trader_from_browser_bundle(request_json)
|
|
612
|
+
try:
|
|
613
|
+
result = await submit_order(trader, browser_result, prepared_orders, attempts)
|
|
614
|
+
finally:
|
|
615
|
+
trader.close()
|
|
616
|
+
```
|
|
562
617
|
|
|
563
618
|
For a single-process application, a backend-only in-memory dictionary is sufficient. For multiple workers, serialize only with the SDK's trusted server-state methods and use a server-side store that can atomically take/delete by `requestId`:
|
|
564
619
|
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Python SDK for the [Polynode](https://polynode.dev) real-time prediction market data platform.
|
|
4
4
|
|
|
5
|
-
**New in v0.14.
|
|
5
|
+
**New in v0.14.1:** Every prepared user-owned order exposes its canonical V2 exchange `order_hash` before submission. This is the exact CLOB/open-order ID and fill `order_hash`; deposit-wallet orders hash the standard inner `Order`, not the POLY_1271 wallet-signing wrapper. Record it before the network attempt so timeouts can be reconciled without risking a duplicate.
|
|
6
|
+
|
|
7
|
+
**In v0.14.0:** Web platforms can take a user from wallet authorization through an exact browser-signed user-owned order without exposing backend credentials. The SDK imports the shared versioned browser bundle into memory, produces a credential-free signing request with a complete order preview, supports one-time multi-worker state, validates canonical signatures and wallet identity, checks BUY collateral before prompting, and submits with exact zero builder attribution.
|
|
6
8
|
|
|
7
9
|
**In v0.13.0:** Trading added explicit `user_owned` execution. Existing builder mode remains the default; opted-in wallets use zero builder attribution, one wallet-ownership authorization, and strict wallet-bound gasless credentials. Builder credentials and nonzero builder codes fail closed in this mode. EOA-controlled Safe and deposit wallets are supported; legacy Magic/proxy wallets are intentionally excluded from the first release.
|
|
8
10
|
|
|
@@ -502,29 +504,82 @@ const result = await fetch("/api/orders/submit", {
|
|
|
502
504
|
}).then((response) => response.json());
|
|
503
505
|
```
|
|
504
506
|
|
|
505
|
-
On the backend, atomically take the prepared object from session state, then validate and submit it:
|
|
507
|
+
On the backend, atomically take the prepared object from session state, durably record the deterministic order identity, then validate and submit it:
|
|
506
508
|
|
|
507
509
|
```python
|
|
508
|
-
async def submit_order(trader, browser_result, prepared_orders):
|
|
510
|
+
async def submit_order(trader, browser_result, prepared_orders, attempts):
|
|
509
511
|
# `take_once` must delete atomically so two workers cannot submit the same request.
|
|
510
512
|
prepared = await prepared_orders.take_once(browser_result.request_id)
|
|
511
513
|
if prepared is None:
|
|
512
514
|
raise ValueError("Unknown or already-used signing request")
|
|
513
515
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
+
order_hash = prepared.order_hash
|
|
517
|
+
# This durable, idempotent write must commit before submission starts.
|
|
518
|
+
await attempts.create_once(
|
|
519
|
+
order_hash=order_hash,
|
|
516
520
|
request_id=browser_result.request_id,
|
|
517
|
-
|
|
518
|
-
|
|
521
|
+
wallet=prepared.signing_request.address,
|
|
522
|
+
token_id=prepared.signing_request.order["tokenId"],
|
|
523
|
+
side=prepared.signing_request.order["side"],
|
|
524
|
+
status="submitting",
|
|
519
525
|
)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
526
|
+
|
|
527
|
+
try:
|
|
528
|
+
result = await trader.submit_prepared_user_owned_order(
|
|
529
|
+
prepared,
|
|
530
|
+
request_id=browser_result.request_id,
|
|
531
|
+
address=browser_result.address,
|
|
532
|
+
signature=browser_result.signature,
|
|
533
|
+
)
|
|
534
|
+
except BaseException:
|
|
535
|
+
# Once consumed, a local/network exception has an ambiguous outcome.
|
|
536
|
+
status = (
|
|
537
|
+
"outcome_unknown"
|
|
538
|
+
if prepared.consumed
|
|
539
|
+
else "rejected_before_submission"
|
|
540
|
+
)
|
|
541
|
+
await attempts.set_status(order_hash, status)
|
|
542
|
+
raise
|
|
543
|
+
else:
|
|
544
|
+
if result.order_id and result.order_id.lower() != order_hash.lower():
|
|
545
|
+
await attempts.set_status(order_hash, "identity_mismatch")
|
|
546
|
+
raise RuntimeError("CLOB returned an unexpected order ID")
|
|
547
|
+
await attempts.record_response(order_hash, result)
|
|
548
|
+
return {
|
|
549
|
+
"success": result.success,
|
|
550
|
+
"orderId": result.order_id or order_hash,
|
|
551
|
+
"error": result.error,
|
|
552
|
+
}
|
|
525
553
|
```
|
|
526
554
|
|
|
527
|
-
`
|
|
555
|
+
`prepared.order_hash` is the lowercase `0x`-prefixed EIP-712 digest of the canonical standard exchange `Order`. For EOA and Safe orders it also matches the browser-signing digest. POLY_1271 browsers sign an outer `TypedDataSign` wrapper, whose digest is intentionally different; `order_hash` always identifies the inner order that the CLOB and fills report. The property is computed on demand, is unchanged by trusted-state export/import, and is not added to the serialized schema.
|
|
556
|
+
|
|
557
|
+
`prepare_user_owned_order()` supports V2 user-owned execution only, expires after five minutes by default, forces zero builder attribution, rejects positive fee authentication, and performs no submission. GTC, FOK, and FAK orders must omit expiration (zero is accepted and canonicalized to no expiration); GTD requires a fresh Unix-seconds expiration with the 60-second safety buffer, and the signing request is clamped to that window. `submit_prepared_user_owned_order()` verifies the request ID, expiry, connected wallet, stored wallet/funder identity, exact typed data, and recovered signer before consuming the request. A consumed request cannot be replayed.
|
|
558
|
+
|
|
559
|
+
Treat any exception after `prepared.consumed` becomes true—including an HTTP timeout, disconnect, cancellation, or local failure around the network call—as `outcome_unknown`. Never submit that hash again and never automatically prepare a replacement. Reconcile by exact hash against both `await trader.get_open_orders(asset_id=token_id)` (`OpenOrder.id`) and your durable authenticated V2 fill source (`fill.order_hash`):
|
|
560
|
+
|
|
561
|
+
```python
|
|
562
|
+
async def reconcile_unknown(trader, attempt, fills):
|
|
563
|
+
open_orders = await trader.get_open_orders(asset_id=attempt.token_id)
|
|
564
|
+
open_order = next(
|
|
565
|
+
(order for order in open_orders if order.id.lower() == attempt.order_hash.lower()),
|
|
566
|
+
None,
|
|
567
|
+
)
|
|
568
|
+
matching_fills = await fills.by_order_hash(attempt.order_hash)
|
|
569
|
+
return {"openOrder": open_order, "fills": matching_fills}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
An open order may already be partially filled (`size_matched` is nonzero), while a fully filled or canceled order will not remain open. Group all partial fills for the exact hash. Finding neither an open order nor a fill is not proof that submission failed: keep the attempt unknown and retry reconciliation with bounded backoff. The application-owned attempt store and fill source must be durable, wallet-scoped, and idempotent by `order_hash`; do not infer identity from token, side, amounts, timestamps, balances, or request ID.
|
|
573
|
+
|
|
574
|
+
If a request-scoped trader was populated from a browser bundle or decrypted vault record, clear its credentials even when validation, signing, submission, or reconciliation fails:
|
|
575
|
+
|
|
576
|
+
```python
|
|
577
|
+
trader = await trader_from_browser_bundle(request_json)
|
|
578
|
+
try:
|
|
579
|
+
result = await submit_order(trader, browser_result, prepared_orders, attempts)
|
|
580
|
+
finally:
|
|
581
|
+
trader.close()
|
|
582
|
+
```
|
|
528
583
|
|
|
529
584
|
For a single-process application, a backend-only in-memory dictionary is sufficient. For multiple workers, serialize only with the SDK's trusted server-state methods and use a server-side store that can atomically take/delete by `requestId`:
|
|
530
585
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.14.1"
|
|
@@ -477,6 +477,18 @@ def validate_order_payload_pair_v2(
|
|
|
477
477
|
funder_address: str,
|
|
478
478
|
) -> None:
|
|
479
479
|
"""Fail if prepared V2 order and wallet-signing payloads diverge."""
|
|
480
|
+
_validate_standard_order_payload_v2(order_payload)
|
|
481
|
+
expected_signing_payload = signing_payload_for_order_v2(
|
|
482
|
+
order_payload,
|
|
483
|
+
signature_type=signature_type,
|
|
484
|
+
funder_address=funder_address,
|
|
485
|
+
)
|
|
486
|
+
if signing_payload.to_dict() != expected_signing_payload.to_dict():
|
|
487
|
+
raise ValueError("prepared user-owned order and signing payload do not match")
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _validate_standard_order_payload_v2(order_payload: Eip712Payload) -> None:
|
|
491
|
+
"""Fail unless a payload is an exact standard V2 exchange Order."""
|
|
480
492
|
if order_payload.primary_type != "Order":
|
|
481
493
|
raise ValueError("prepared user-owned order has an invalid primary type")
|
|
482
494
|
if order_payload.types != ORDER_TYPES_V2:
|
|
@@ -486,13 +498,23 @@ def validate_order_payload_pair_v2(
|
|
|
486
498
|
expected_fields = {field["name"] for field in ORDER_TYPES_V2["Order"]}
|
|
487
499
|
if set(order_payload.message) != expected_fields:
|
|
488
500
|
raise ValueError("prepared user-owned order has invalid EIP-712 fields")
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
)
|
|
494
|
-
|
|
495
|
-
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _standard_order_hash_v2(order_payload: Eip712Payload) -> str:
|
|
504
|
+
"""Return the canonical exchange digest for one standard V2 Order."""
|
|
505
|
+
_validate_standard_order_payload_v2(order_payload)
|
|
506
|
+
try:
|
|
507
|
+
from eth_account.messages import encode_typed_data
|
|
508
|
+
from eth_utils import keccak
|
|
509
|
+
except ImportError:
|
|
510
|
+
raise ImportError(
|
|
511
|
+
"eth-account is required for trading. Install with: "
|
|
512
|
+
"pip install polynode[trading]"
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
signable = encode_typed_data(full_message=order_payload.to_dict())
|
|
516
|
+
digest = keccak(b"\x19\x01" + signable.header + signable.body)
|
|
517
|
+
return f"0x{digest.hex()}"
|
|
496
518
|
|
|
497
519
|
|
|
498
520
|
def validate_canonical_user_owned_order_v2(
|
|
@@ -157,14 +157,15 @@ class Eip712Payload:
|
|
|
157
157
|
"verifyingContract": "address",
|
|
158
158
|
"salt": "bytes32",
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
unsupported_fields = set(self.domain) - set(domain_field_types)
|
|
161
|
+
if unsupported_fields:
|
|
162
|
+
unsupported_field = sorted(unsupported_fields)[0]
|
|
163
|
+
raise ValueError(f"unsupported EIP-712 domain field: {unsupported_field}")
|
|
164
|
+
domain_type = [
|
|
165
|
+
{"name": name, "type": type_name}
|
|
166
|
+
for name, type_name in domain_field_types.items()
|
|
167
|
+
if name in self.domain
|
|
168
|
+
]
|
|
168
169
|
return deepcopy(
|
|
169
170
|
{
|
|
170
171
|
"domain": self.domain,
|
|
@@ -515,6 +516,63 @@ class PreparedUserOwnedOrder:
|
|
|
515
516
|
"""Whether this prepared order was discarded or used for submission."""
|
|
516
517
|
return self._consumed
|
|
517
518
|
|
|
519
|
+
@property
|
|
520
|
+
def order_hash(self) -> str:
|
|
521
|
+
"""Return the canonical V2 exchange Order EIP-712 digest.
|
|
522
|
+
|
|
523
|
+
This is the CLOB/open-order ID and fill ``order_hash``. For POLY_1271,
|
|
524
|
+
it hashes the inner standard Order, never the wallet-signing
|
|
525
|
+
``TypedDataSign`` wrapper.
|
|
526
|
+
"""
|
|
527
|
+
from .eip712 import (
|
|
528
|
+
_standard_order_hash_v2,
|
|
529
|
+
canonical_order_values_v2,
|
|
530
|
+
user_owned_order_preview_v2,
|
|
531
|
+
validate_canonical_user_owned_order_v2,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
canonical_price, canonical_size = canonical_order_values_v2(
|
|
535
|
+
self._price,
|
|
536
|
+
self._size,
|
|
537
|
+
self._tick_size,
|
|
538
|
+
)
|
|
539
|
+
if self._price != canonical_price or self._size != canonical_size:
|
|
540
|
+
raise ValueError("prepared user-owned order price or size is not canonical")
|
|
541
|
+
validate_canonical_user_owned_order_v2(
|
|
542
|
+
self._order_payload,
|
|
543
|
+
self._signing_payload,
|
|
544
|
+
wallet_address=self._wallet_address,
|
|
545
|
+
funder_address=self._funder_address,
|
|
546
|
+
signature_type=self._signature_type,
|
|
547
|
+
token_id=self._token_id,
|
|
548
|
+
price=self._price,
|
|
549
|
+
size=self._size,
|
|
550
|
+
side=self._side,
|
|
551
|
+
tick_size=self._tick_size,
|
|
552
|
+
neg_risk=self._neg_risk,
|
|
553
|
+
)
|
|
554
|
+
if (
|
|
555
|
+
self.signing_request.version != "1"
|
|
556
|
+
or not isinstance(self.signing_request.address, str)
|
|
557
|
+
or self.signing_request.address.lower() != self._wallet_address.lower()
|
|
558
|
+
):
|
|
559
|
+
raise ValueError("prepared user-owned order wallet binding is invalid")
|
|
560
|
+
if self.signing_request.typed_data != self._signing_payload.to_dict():
|
|
561
|
+
raise ValueError("prepared user-owned order signing request was modified")
|
|
562
|
+
expected_preview = user_owned_order_preview_v2(
|
|
563
|
+
self._order_payload,
|
|
564
|
+
token_id=self._token_id,
|
|
565
|
+
side=self._side,
|
|
566
|
+
price=self._price,
|
|
567
|
+
size=self._size,
|
|
568
|
+
order_type=self._order_type,
|
|
569
|
+
post_only=self._post_only,
|
|
570
|
+
expiration=self._expiration,
|
|
571
|
+
)
|
|
572
|
+
if self.signing_request.order != expected_preview:
|
|
573
|
+
raise ValueError("prepared user-owned order preview was modified")
|
|
574
|
+
return _standard_order_hash_v2(self._order_payload)
|
|
575
|
+
|
|
518
576
|
def _unsigned_server_dict(self) -> dict[str, Any]:
|
|
519
577
|
return {
|
|
520
578
|
"version": "1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.14.0"
|
|
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
|