polynode 0.9.1__py3-none-any.whl → 0.9.2__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.
- polynode/trading/constants.py +3 -0
- polynode/trading/escrow.py +50 -8
- polynode/trading/onboarding.py +21 -6
- polynode/trading/trader.py +6 -2
- {polynode-0.9.1.dist-info → polynode-0.9.2.dist-info}/METADATA +1 -1
- {polynode-0.9.1.dist-info → polynode-0.9.2.dist-info}/RECORD +7 -7
- {polynode-0.9.1.dist-info → polynode-0.9.2.dist-info}/WHEEL +0 -0
polynode/trading/constants.py
CHANGED
|
@@ -54,7 +54,10 @@ COLLATERAL_ONRAMP = "0x93070a847efef7f70739046a929d47a521f5b8ee"
|
|
|
54
54
|
COLLATERAL_OFFRAMP = "0x2957922eb93258b93368531d39facca3b4dc5854"
|
|
55
55
|
|
|
56
56
|
# Fee Escrow
|
|
57
|
+
# V1 (USDC.e collateral, paired with the V1 CLOB)
|
|
57
58
|
FEE_ESCROW_ADDRESS = "0xa11D28433B79D0A88F3119b16A090075752258EA"
|
|
59
|
+
# V2 (pUSD collateral, paired with the V2 CLOB). Deployed 2026-04-24.
|
|
60
|
+
FEE_ESCROW_ADDRESS_V2 = "0x3A43D88ef8Aae4dF5a50B3abf67122CAAeEF7c9F"
|
|
58
61
|
|
|
59
62
|
# Metadata cache TTL (5 minutes)
|
|
60
63
|
META_TTL_SECONDS = 300
|
polynode/trading/escrow.py
CHANGED
|
@@ -14,10 +14,15 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
import httpx
|
|
16
16
|
|
|
17
|
-
from .constants import CHAIN_ID, FEE_ESCROW_ADDRESS
|
|
18
|
-
from .types import Eip712Payload
|
|
17
|
+
from .constants import CHAIN_ID, FEE_ESCROW_ADDRESS, FEE_ESCROW_ADDRESS_V2
|
|
18
|
+
from .types import Eip712Payload, ExchangeVersion
|
|
19
19
|
|
|
20
20
|
# ── EIP-712 Types ──
|
|
21
|
+
#
|
|
22
|
+
# Both V1 and V2 FeeEscrow share the same FeeAuth struct typehash. They differ only in
|
|
23
|
+
# the EIP-712 domain: V1 is ("PolyNodeFeeEscrow", "1", V1 contract); V2 is
|
|
24
|
+
# ("PolyNodeFeeEscrowV2", "2", V2 contract). Signing against the wrong domain produces
|
|
25
|
+
# a digest the contract won't accept — `pullFee` reverts with `InvalidSignature`.
|
|
21
26
|
|
|
22
27
|
FEE_AUTH_DOMAIN = {
|
|
23
28
|
"name": "PolyNodeFeeEscrow",
|
|
@@ -26,6 +31,13 @@ FEE_AUTH_DOMAIN = {
|
|
|
26
31
|
"verifyingContract": FEE_ESCROW_ADDRESS,
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
FEE_AUTH_DOMAIN_V2 = {
|
|
35
|
+
"name": "PolyNodeFeeEscrowV2",
|
|
36
|
+
"version": "2",
|
|
37
|
+
"chainId": CHAIN_ID,
|
|
38
|
+
"verifyingContract": FEE_ESCROW_ADDRESS_V2,
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
FEE_AUTH_TYPES = {
|
|
30
42
|
"FeeAuth": [
|
|
31
43
|
{"name": "orderId", "type": "bytes32"},
|
|
@@ -40,6 +52,16 @@ FEE_AUTH_TYPES = {
|
|
|
40
52
|
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
41
53
|
|
|
42
54
|
|
|
55
|
+
def fee_auth_domain_for(version: ExchangeVersion) -> dict:
|
|
56
|
+
"""Return the EIP-712 FeeAuth domain for a given exchange version."""
|
|
57
|
+
return FEE_AUTH_DOMAIN_V2 if version == ExchangeVersion.V2 else FEE_AUTH_DOMAIN
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fee_escrow_address_for(version: ExchangeVersion) -> str:
|
|
61
|
+
"""Return the FeeEscrow contract address for a given exchange version."""
|
|
62
|
+
return FEE_ESCROW_ADDRESS_V2 if version == ExchangeVersion.V2 else FEE_ESCROW_ADDRESS
|
|
63
|
+
|
|
64
|
+
|
|
43
65
|
# ── Fee Calculation ──
|
|
44
66
|
|
|
45
67
|
def calculate_fee(price: float, size: float, fee_bps: int) -> int:
|
|
@@ -67,8 +89,17 @@ def generate_escrow_order_id() -> str:
|
|
|
67
89
|
|
|
68
90
|
# ── Nonce Fetching ──
|
|
69
91
|
|
|
70
|
-
async def fetch_escrow_nonce(
|
|
71
|
-
|
|
92
|
+
async def fetch_escrow_nonce(
|
|
93
|
+
rpc_url: str,
|
|
94
|
+
signer_address: str,
|
|
95
|
+
escrow_address: str = FEE_ESCROW_ADDRESS,
|
|
96
|
+
) -> int:
|
|
97
|
+
"""Fetch the current nonce for a signer from a FeeEscrow contract via eth_call.
|
|
98
|
+
|
|
99
|
+
Pass `fee_escrow_address_for(exchange_version)` for the `escrow_address` argument
|
|
100
|
+
when the caller's exchange version is known (V2 customers must read nonces from
|
|
101
|
+
the V2 contract — nonces on V1 and V2 are tracked independently).
|
|
102
|
+
"""
|
|
72
103
|
# getNonce(address) selector = 0x2d0335ab
|
|
73
104
|
addr = signer_address.lower().replace("0x", "").zfill(64)
|
|
74
105
|
data = "0x2d0335ab" + addr
|
|
@@ -80,7 +111,7 @@ async def fetch_escrow_nonce(rpc_url: str, signer_address: str) -> int:
|
|
|
80
111
|
"jsonrpc": "2.0",
|
|
81
112
|
"id": 1,
|
|
82
113
|
"method": "eth_call",
|
|
83
|
-
"params": [{"to":
|
|
114
|
+
"params": [{"to": escrow_address, "data": data}, "latest"],
|
|
84
115
|
},
|
|
85
116
|
)
|
|
86
117
|
result = resp.json()
|
|
@@ -103,6 +134,9 @@ class FeeAuthRequest:
|
|
|
103
134
|
signature: str
|
|
104
135
|
affiliate: str
|
|
105
136
|
affiliate_share_bps: int
|
|
137
|
+
# V2-routing hint for the cosigner. Set when exchange_version == V2 so the
|
|
138
|
+
# cosigner picks the V2 operator; absent for V1 (preserves backwards compat).
|
|
139
|
+
escrow_contract: str | None = None
|
|
106
140
|
|
|
107
141
|
|
|
108
142
|
async def sign_fee_auth(
|
|
@@ -116,19 +150,22 @@ async def sign_fee_auth(
|
|
|
116
150
|
nonce: int,
|
|
117
151
|
affiliate: str | None = None,
|
|
118
152
|
affiliate_share_bps: int | None = None,
|
|
153
|
+
exchange_version: ExchangeVersion = ExchangeVersion.V1,
|
|
119
154
|
) -> FeeAuthRequest:
|
|
120
155
|
"""Sign a FeeAuth EIP-712 message.
|
|
121
156
|
|
|
122
157
|
Args:
|
|
123
158
|
sign_typed_data: Callable that signs an Eip712Payload and returns a hex signature.
|
|
124
159
|
escrow_order_id: Random bytes32 hex string.
|
|
125
|
-
payer: Safe wallet address (where
|
|
160
|
+
payer: Safe wallet address (where collateral is pulled from — USDC.e on V1, pUSD on V2).
|
|
126
161
|
signer_address: EOA address (signs the message).
|
|
127
|
-
fee_amount: Fee in raw
|
|
162
|
+
fee_amount: Fee in raw collateral units (6 decimals).
|
|
128
163
|
deadline: Unix timestamp — authorization expires after this.
|
|
129
164
|
nonce: Signer's current nonce from the escrow contract.
|
|
130
165
|
affiliate: Partner wallet address.
|
|
131
166
|
affiliate_share_bps: Partner's share of fee in bps.
|
|
167
|
+
exchange_version: V1 (default, USDC.e) or V2 (pUSD). Selects the EIP-712 domain
|
|
168
|
+
and the escrow contract address the cosigner should route to.
|
|
132
169
|
|
|
133
170
|
Returns:
|
|
134
171
|
FeeAuthRequest ready for the cosigner.
|
|
@@ -143,7 +180,7 @@ async def sign_fee_auth(
|
|
|
143
180
|
}
|
|
144
181
|
|
|
145
182
|
payload = Eip712Payload(
|
|
146
|
-
domain=
|
|
183
|
+
domain=fee_auth_domain_for(exchange_version),
|
|
147
184
|
types=FEE_AUTH_TYPES,
|
|
148
185
|
primary_type="FeeAuth",
|
|
149
186
|
message=message,
|
|
@@ -153,6 +190,10 @@ async def sign_fee_auth(
|
|
|
153
190
|
if not signature.startswith("0x"):
|
|
154
191
|
signature = f"0x{signature}"
|
|
155
192
|
|
|
193
|
+
escrow_contract = (
|
|
194
|
+
FEE_ESCROW_ADDRESS_V2 if exchange_version == ExchangeVersion.V2 else None
|
|
195
|
+
)
|
|
196
|
+
|
|
156
197
|
return FeeAuthRequest(
|
|
157
198
|
escrow_order_id=escrow_order_id,
|
|
158
199
|
payer=payer,
|
|
@@ -163,4 +204,5 @@ async def sign_fee_auth(
|
|
|
163
204
|
signature=signature,
|
|
164
205
|
affiliate=affiliate or ZERO_ADDRESS,
|
|
165
206
|
affiliate_share_bps=affiliate_share_bps if affiliate_share_bps is not None else 10000,
|
|
207
|
+
escrow_contract=escrow_contract,
|
|
166
208
|
)
|
polynode/trading/onboarding.py
CHANGED
|
@@ -29,6 +29,7 @@ from .constants import (
|
|
|
29
29
|
USDC,
|
|
30
30
|
V2_SPENDERS,
|
|
31
31
|
FEE_ESCROW_ADDRESS,
|
|
32
|
+
FEE_ESCROW_ADDRESS_V2,
|
|
32
33
|
)
|
|
33
34
|
from .types import ApprovalStatus, BalanceInfo, ExchangeVersion, SignatureType
|
|
34
35
|
|
|
@@ -189,9 +190,16 @@ async def check_approvals(
|
|
|
189
190
|
approved = ctf_contract.functions.isApprovedForAll(funder, spender_addr).call()
|
|
190
191
|
ctf_status[name] = approved
|
|
191
192
|
|
|
192
|
-
# Fee Escrow
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
# Fee Escrow approval. V1 approves USDC.e for the V1 contract; V2 approves pUSD
|
|
194
|
+
# for the V2 contract. pullFee fails with `TransferFailed` if the approval is
|
|
195
|
+
# missing on the version the caller's orders are routed to.
|
|
196
|
+
if version == ExchangeVersion.V2:
|
|
197
|
+
escrow_token_contract = w3.eth.contract(address=Web3.to_checksum_address(POLY_USD), abi=erc20_abi)
|
|
198
|
+
escrow_addr = Web3.to_checksum_address(FEE_ESCROW_ADDRESS_V2)
|
|
199
|
+
else:
|
|
200
|
+
escrow_token_contract = w3.eth.contract(address=Web3.to_checksum_address(USDC), abi=erc20_abi)
|
|
201
|
+
escrow_addr = Web3.to_checksum_address(FEE_ESCROW_ADDRESS)
|
|
202
|
+
escrow_allowance = escrow_token_contract.functions.allowance(funder, escrow_addr).call()
|
|
195
203
|
usdc_status["fee_escrow"] = escrow_allowance >= threshold
|
|
196
204
|
|
|
197
205
|
all_approved = all(usdc_status.values()) and all(ctf_status.values())
|
|
@@ -273,9 +281,16 @@ async def set_approvals(
|
|
|
273
281
|
tx_hashes.append(tx_hash.hex())
|
|
274
282
|
nonce += 1
|
|
275
283
|
|
|
276
|
-
# Fee Escrow
|
|
277
|
-
|
|
278
|
-
|
|
284
|
+
# Fee Escrow collateral approval — V1 approves USDC.e for V1 FeeEscrow;
|
|
285
|
+
# V2 approves pUSD for V2 FeeEscrow.
|
|
286
|
+
if version == ExchangeVersion.V2:
|
|
287
|
+
escrow_token = POLY_USD
|
|
288
|
+
escrow_target = FEE_ESCROW_ADDRESS_V2
|
|
289
|
+
else:
|
|
290
|
+
escrow_token = USDC
|
|
291
|
+
escrow_target = FEE_ESCROW_ADDRESS
|
|
292
|
+
escrow_token_contract = w3.eth.contract(address=Web3.to_checksum_address(escrow_token), abi=erc20_abi)
|
|
293
|
+
tx = escrow_token_contract.functions.approve(Web3.to_checksum_address(escrow_target), max_uint).build_transaction({
|
|
279
294
|
"from": account.address,
|
|
280
295
|
"nonce": nonce,
|
|
281
296
|
"chainId": CHAIN_ID,
|
polynode/trading/trader.py
CHANGED
|
@@ -26,7 +26,7 @@ from .constants import (
|
|
|
26
26
|
)
|
|
27
27
|
from .cosigner import build_l2_headers, send_via_cosigner
|
|
28
28
|
from .eip712 import create_signed_order, create_signed_order_v2
|
|
29
|
-
from .escrow import calculate_fee,
|
|
29
|
+
from .escrow import calculate_fee, fee_escrow_address_for, fetch_escrow_nonce, generate_escrow_order_id, sign_fee_auth
|
|
30
30
|
from .onboarding import (
|
|
31
31
|
check_approvals as check_approvals_onchain,
|
|
32
32
|
check_balance as check_balance_onchain,
|
|
@@ -699,7 +699,8 @@ class PolyNodeTrader:
|
|
|
699
699
|
fee_amount_raw = calculate_fee(params.price, params.size, fee_config.fee_bps)
|
|
700
700
|
if fee_amount_raw > 0:
|
|
701
701
|
funder = creds.funder_address or creds.wallet_address
|
|
702
|
-
|
|
702
|
+
escrow_address = fee_escrow_address_for(self._exchange_version)
|
|
703
|
+
nonce = await fetch_escrow_nonce(self._rpc_url, signer.address, escrow_address)
|
|
703
704
|
deadline = int(time.time()) + 300
|
|
704
705
|
escrow_oid = generate_escrow_order_id()
|
|
705
706
|
fee_auth = await sign_fee_auth(
|
|
@@ -712,6 +713,7 @@ class PolyNodeTrader:
|
|
|
712
713
|
nonce=nonce,
|
|
713
714
|
affiliate=fee_config.affiliate,
|
|
714
715
|
affiliate_share_bps=fee_config.affiliate_share_bps,
|
|
716
|
+
exchange_version=self._exchange_version,
|
|
715
717
|
)
|
|
716
718
|
fee_auth_dict = {
|
|
717
719
|
"escrow_order_id": fee_auth.escrow_order_id,
|
|
@@ -724,6 +726,8 @@ class PolyNodeTrader:
|
|
|
724
726
|
"affiliate": fee_auth.affiliate,
|
|
725
727
|
"affiliate_share_bps": fee_auth.affiliate_share_bps,
|
|
726
728
|
}
|
|
729
|
+
if fee_auth.escrow_contract:
|
|
730
|
+
fee_auth_dict["escrow_contract"] = fee_auth.escrow_contract
|
|
727
731
|
|
|
728
732
|
request_data: dict[str, Any] = {"method": "POST", "path": "/order", "body": body_str, "headers": headers}
|
|
729
733
|
if fee_auth_dict:
|
|
@@ -14,17 +14,17 @@ polynode/cache/__init__.py,sha256=A9lXcIE3ISKKo1r0o3o-CkmQpBhLdKwdQfCR0eW5i9o,51
|
|
|
14
14
|
polynode/trading/V2_ORDER_FLOW.md,sha256=Ppd7iwZjtmVwok-fP4Ofo4UBQ9pIAg0t57RJ-OzqRHs,10926
|
|
15
15
|
polynode/trading/__init__.py,sha256=sPv30j9eyvqjnUBxGAYaKpAPQZ2jTw3UwA4DZXYZ21s,923
|
|
16
16
|
polynode/trading/clob_api.py,sha256=Sfb8XLN9MMzVE6jARNqEQxiD608OC_6Xjc7uiJbDQEc,7274
|
|
17
|
-
polynode/trading/constants.py,sha256=
|
|
17
|
+
polynode/trading/constants.py,sha256=IEdrwC4bPjo4MoVlDKYqXphlmIKsoxO4f_s_pyZ3x08,2702
|
|
18
18
|
polynode/trading/cosigner.py,sha256=Fww9OgaKdHQAHd8Kom1AJBtmMGC8FbNZxdFaFALNBY8,3058
|
|
19
19
|
polynode/trading/eip712.py,sha256=PKLfMt58Diy419jiQXysnX1kSlU_QcWDRofGEb3u_JI,9744
|
|
20
|
-
polynode/trading/escrow.py,sha256=
|
|
21
|
-
polynode/trading/onboarding.py,sha256=
|
|
20
|
+
polynode/trading/escrow.py,sha256=T2v5bv3wIyQTcFoxcHXlDBlzyeWDYbxRIqQamqlhaog,6513
|
|
21
|
+
polynode/trading/onboarding.py,sha256=GdQh1aM9F0LuCL3uqNTguXX5Sxe1_0hSVdHR7kksKZ0,15777
|
|
22
22
|
polynode/trading/position_management.py,sha256=z2pYls4ErhyRE6thQdOs45BF4llKvKNNruQgcMEFT_U,3827
|
|
23
23
|
polynode/trading/privy.py,sha256=iiUQZsBqjj29C7oUiFFktoT2muZY06bcMzABKstFKqE,4523
|
|
24
24
|
polynode/trading/relayer.py,sha256=adTo-aclWnaqrFtfWEOQ2Ye859CZMc9q_fsd3DePDGA,10542
|
|
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
|
-
polynode/trading/trader.py,sha256=
|
|
27
|
+
polynode/trading/trader.py,sha256=EEwF_U2DXVPRnENY5azk8FgzSx_zXns27apQQj9isus,37188
|
|
28
28
|
polynode/trading/types.py,sha256=mo5zJRlfQLWuXwrkS9AaGqKWrt7FMf6bPGRE-0T62J0,5698
|
|
29
29
|
polynode/types/__init__.py,sha256=0v_CByqG-zT1xucbT2So_77YDmsPj9u956at94nyrLM,280
|
|
30
30
|
polynode/types/enums.py,sha256=2cx9l10A-LUeDNBJZn7l0XBTka2YfxRQcXW0s7s3_4k,1082
|
|
@@ -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.9.
|
|
37
|
-
polynode-0.9.
|
|
38
|
-
polynode-0.9.
|
|
36
|
+
polynode-0.9.2.dist-info/METADATA,sha256=-FqTxtI_g04JQ7AxM9kzp9RxfnF6dGNADA3CmfXORK8,3777
|
|
37
|
+
polynode-0.9.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
38
|
+
polynode-0.9.2.dist-info/RECORD,,
|
|
File without changes
|