polynode 0.9.0__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/_version.py +1 -1
- polynode/trading/constants.py +3 -0
- polynode/trading/escrow.py +50 -8
- polynode/trading/onboarding.py +21 -6
- polynode/trading/relayer.py +303 -0
- polynode/trading/trader.py +87 -51
- {polynode-0.9.0.dist-info → polynode-0.9.2.dist-info}/METADATA +1 -1
- {polynode-0.9.0.dist-info → polynode-0.9.2.dist-info}/RECORD +9 -8
- {polynode-0.9.0.dist-info → polynode-0.9.2.dist-info}/WHEEL +0 -0
polynode/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.1"
|
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,
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""Polymarket builder-relayer client (Python port).
|
|
2
|
+
|
|
3
|
+
Ports the minimum needed from @polymarket/builder-relayer-client +
|
|
4
|
+
@polymarket/builder-signing-sdk to submit gasless Safe transactions.
|
|
5
|
+
|
|
6
|
+
Flow:
|
|
7
|
+
1. GET {relayer}/nonce?address={eoa}&type=SAFE → next SafeTx nonce
|
|
8
|
+
2. Build multisend (if >1 tx) or single tx
|
|
9
|
+
3. EIP-712 hash SafeTx(domain={chainId, verifyingContract=safe}, types, msg)
|
|
10
|
+
4. EIP-191 personal_sign the hash with EOA private key
|
|
11
|
+
5. Pack r||s||v (v adjusted by +4 for Gnosis)
|
|
12
|
+
6. POST {relayer}/submit with builder HMAC headers
|
|
13
|
+
7. Poll {relayer}/transaction?id=... until STATE_MINED / STATE_CONFIRMED
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import base64
|
|
19
|
+
import hashlib
|
|
20
|
+
import hmac
|
|
21
|
+
import json
|
|
22
|
+
import time
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import httpx
|
|
27
|
+
|
|
28
|
+
from .constants import CHAIN_ID, RELAYER_HOST, SAFE_FACTORY, SAFE_MULTISEND
|
|
29
|
+
from .onboarding import derive_safe_address
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class BuilderCreds:
|
|
34
|
+
key: str
|
|
35
|
+
secret: str
|
|
36
|
+
passphrase: str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
ZERO_ADDR = "0x" + "0" * 40
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── EIP-712 helpers ──
|
|
43
|
+
|
|
44
|
+
def _keccak(data: bytes) -> bytes:
|
|
45
|
+
from eth_utils import keccak as _k
|
|
46
|
+
return _k(data)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Compute once on module load
|
|
50
|
+
def _safe_tx_type_hash() -> bytes:
|
|
51
|
+
return _keccak(
|
|
52
|
+
b"SafeTx(address to,uint256 value,bytes data,uint8 operation,"
|
|
53
|
+
b"uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,"
|
|
54
|
+
b"address refundReceiver,uint256 nonce)"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _domain_type_hash() -> bytes:
|
|
59
|
+
return _keccak(b"EIP712Domain(uint256 chainId,address verifyingContract)")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _pad32(h: bytes) -> bytes:
|
|
63
|
+
return h if len(h) == 32 else (b"\x00" * (32 - len(h))) + h
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _encode_address(addr: str) -> bytes:
|
|
67
|
+
return _pad32(bytes.fromhex(addr.lower().removeprefix("0x")))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _encode_uint256(n: int) -> bytes:
|
|
71
|
+
return int(n).to_bytes(32, "big", signed=False)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _domain_separator(chain_id: int, verifying_contract: str) -> bytes:
|
|
75
|
+
return _keccak(
|
|
76
|
+
_domain_type_hash() + _encode_uint256(chain_id) + _encode_address(verifying_contract)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _safe_tx_struct_hash(
|
|
81
|
+
to: str, value: int, data: bytes, operation: int,
|
|
82
|
+
safe_tx_gas: int, base_gas: int, gas_price: int,
|
|
83
|
+
gas_token: str, refund_receiver: str, nonce: int,
|
|
84
|
+
) -> bytes:
|
|
85
|
+
data_hash = _keccak(data)
|
|
86
|
+
encoded = (
|
|
87
|
+
_safe_tx_type_hash()
|
|
88
|
+
+ _encode_address(to)
|
|
89
|
+
+ _encode_uint256(value)
|
|
90
|
+
+ data_hash
|
|
91
|
+
+ _encode_uint256(operation)
|
|
92
|
+
+ _encode_uint256(safe_tx_gas)
|
|
93
|
+
+ _encode_uint256(base_gas)
|
|
94
|
+
+ _encode_uint256(gas_price)
|
|
95
|
+
+ _encode_address(gas_token)
|
|
96
|
+
+ _encode_address(refund_receiver)
|
|
97
|
+
+ _encode_uint256(nonce)
|
|
98
|
+
)
|
|
99
|
+
return _keccak(encoded)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _eip712_digest(chain_id: int, safe_addr: str, struct_hash: bytes) -> bytes:
|
|
103
|
+
domain_sep = _domain_separator(chain_id, safe_addr)
|
|
104
|
+
return _keccak(b"\x19\x01" + domain_sep + struct_hash)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ── Safe multisend encoding ──
|
|
108
|
+
|
|
109
|
+
def _encode_multisend(txns: list[dict[str, Any]]) -> bytes:
|
|
110
|
+
"""Encode txns into the multiSend bytes arg.
|
|
111
|
+
Each tx packed as: uint8 operation | address to | uint256 value | uint256 dataLen | bytes data
|
|
112
|
+
"""
|
|
113
|
+
parts: list[bytes] = []
|
|
114
|
+
for tx in txns:
|
|
115
|
+
op = int(tx.get("operation", 0))
|
|
116
|
+
to = tx["to"]
|
|
117
|
+
value = int(tx.get("value", 0))
|
|
118
|
+
data = tx["data"]
|
|
119
|
+
if isinstance(data, str):
|
|
120
|
+
data_bytes = bytes.fromhex(data.removeprefix("0x"))
|
|
121
|
+
else:
|
|
122
|
+
data_bytes = data
|
|
123
|
+
parts.append(
|
|
124
|
+
op.to_bytes(1, "big")
|
|
125
|
+
+ bytes.fromhex(to.lower().removeprefix("0x"))
|
|
126
|
+
+ value.to_bytes(32, "big")
|
|
127
|
+
+ len(data_bytes).to_bytes(32, "big")
|
|
128
|
+
+ data_bytes
|
|
129
|
+
)
|
|
130
|
+
return b"".join(parts)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _encode_multisend_calldata(txns: list[dict[str, Any]]) -> str:
|
|
134
|
+
"""Wrap encoded txns in multiSend(bytes) ABI call."""
|
|
135
|
+
# multiSend(bytes) selector = keccak256("multiSend(bytes)")[:4] = 0x8d80ff0a
|
|
136
|
+
inner = _encode_multisend(txns)
|
|
137
|
+
# ABI-encode single `bytes` arg: offset(32) + length(32) + data (padded to 32)
|
|
138
|
+
padded_len = ((len(inner) + 31) // 32) * 32
|
|
139
|
+
padded = inner + b"\x00" * (padded_len - len(inner))
|
|
140
|
+
encoded = (
|
|
141
|
+
bytes.fromhex("8d80ff0a")
|
|
142
|
+
+ _encode_uint256(32) # offset to bytes arg
|
|
143
|
+
+ _encode_uint256(len(inner))
|
|
144
|
+
+ padded
|
|
145
|
+
)
|
|
146
|
+
return "0x" + encoded.hex()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ── Builder HMAC ──
|
|
150
|
+
|
|
151
|
+
def _build_hmac_headers(creds: BuilderCreds, method: str, path: str, body: str | None = None) -> dict[str, str]:
|
|
152
|
+
timestamp = str(int(time.time()))
|
|
153
|
+
message = timestamp + method + path + (body or "")
|
|
154
|
+
# Secret may be url-safe base64 (`-` / `_`); normalize before decoding.
|
|
155
|
+
secret_norm = creds.secret.replace("-", "+").replace("_", "/")
|
|
156
|
+
key_bytes = base64.b64decode(secret_norm)
|
|
157
|
+
mac = hmac.new(key_bytes, message.encode(), hashlib.sha256).digest()
|
|
158
|
+
sig = base64.b64encode(mac).decode().replace("+", "-").replace("/", "_")
|
|
159
|
+
return {
|
|
160
|
+
"POLY_BUILDER_API_KEY": creds.key,
|
|
161
|
+
"POLY_BUILDER_PASSPHRASE": creds.passphrase,
|
|
162
|
+
"POLY_BUILDER_SIGNATURE": sig,
|
|
163
|
+
"POLY_BUILDER_TIMESTAMP": timestamp,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ── Signature packing ──
|
|
168
|
+
|
|
169
|
+
def _pack_sig(sig_hex: str) -> str:
|
|
170
|
+
"""Split 65-byte sig into r||s||v with Gnosis v-adjustment (+4)."""
|
|
171
|
+
raw = bytes.fromhex(sig_hex.removeprefix("0x"))
|
|
172
|
+
assert len(raw) == 65, f"expected 65-byte sig, got {len(raw)}"
|
|
173
|
+
r, s, v = raw[:32], raw[32:64], raw[64]
|
|
174
|
+
# Adjust v: 0/1 → 31/32, 27/28 → 31/32 (eth_sign variant per Gnosis spec)
|
|
175
|
+
if v in (0, 1):
|
|
176
|
+
v += 31
|
|
177
|
+
elif v in (27, 28):
|
|
178
|
+
v += 4
|
|
179
|
+
else:
|
|
180
|
+
raise ValueError(f"invalid sig v byte: {v}")
|
|
181
|
+
return "0x" + (r + s + v.to_bytes(1, "big")).hex()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ── RelayClient ──
|
|
185
|
+
|
|
186
|
+
class RelayClient:
|
|
187
|
+
"""Minimal Polymarket relayer client for Safe transactions."""
|
|
188
|
+
|
|
189
|
+
def __init__(self, builder_creds: BuilderCreds, relayer_url: str = RELAYER_HOST, chain_id: int = CHAIN_ID):
|
|
190
|
+
self.relayer_url = relayer_url.rstrip("/")
|
|
191
|
+
self.chain_id = chain_id
|
|
192
|
+
self.builder_creds = builder_creds
|
|
193
|
+
|
|
194
|
+
async def execute_safe(
|
|
195
|
+
self,
|
|
196
|
+
signer_pk: str,
|
|
197
|
+
eoa_address: str,
|
|
198
|
+
txns: list[dict[str, Any]],
|
|
199
|
+
) -> str:
|
|
200
|
+
"""Build, sign, submit, and wait for a batch of Safe transactions.
|
|
201
|
+
|
|
202
|
+
Returns the on-chain transaction hash.
|
|
203
|
+
"""
|
|
204
|
+
if not txns:
|
|
205
|
+
raise ValueError("no transactions to execute")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
from eth_account import Account
|
|
209
|
+
from eth_account.messages import encode_defunct
|
|
210
|
+
except ImportError:
|
|
211
|
+
raise ImportError("eth-account required for Safe relayer")
|
|
212
|
+
|
|
213
|
+
safe_addr = derive_safe_address(eoa_address)
|
|
214
|
+
|
|
215
|
+
# 1. Get nonce
|
|
216
|
+
async with httpx.AsyncClient(timeout=20.0) as client:
|
|
217
|
+
r = await client.get(
|
|
218
|
+
f"{self.relayer_url}/nonce",
|
|
219
|
+
params={"address": eoa_address, "type": "SAFE"},
|
|
220
|
+
)
|
|
221
|
+
r.raise_for_status()
|
|
222
|
+
nonce_payload = r.json()
|
|
223
|
+
nonce = int(nonce_payload["nonce"])
|
|
224
|
+
|
|
225
|
+
# 2. Aggregate (single tx → direct, multi → multisend DelegateCall)
|
|
226
|
+
if len(txns) == 1:
|
|
227
|
+
tx = txns[0]
|
|
228
|
+
to_addr = tx["to"]
|
|
229
|
+
data = tx["data"]
|
|
230
|
+
operation = int(tx.get("operation", 0))
|
|
231
|
+
else:
|
|
232
|
+
to_addr = SAFE_MULTISEND
|
|
233
|
+
data = _encode_multisend_calldata(
|
|
234
|
+
[{**t, "operation": int(t.get("operation", 0))} for t in txns]
|
|
235
|
+
)
|
|
236
|
+
operation = 1 # DelegateCall
|
|
237
|
+
|
|
238
|
+
# 3. Compute EIP-712 digest
|
|
239
|
+
data_bytes = bytes.fromhex(data.removeprefix("0x")) if isinstance(data, str) else data
|
|
240
|
+
struct_hash = _safe_tx_struct_hash(
|
|
241
|
+
to=to_addr, value=0, data=data_bytes, operation=operation,
|
|
242
|
+
safe_tx_gas=0, base_gas=0, gas_price=0,
|
|
243
|
+
gas_token=ZERO_ADDR, refund_receiver=ZERO_ADDR,
|
|
244
|
+
nonce=nonce,
|
|
245
|
+
)
|
|
246
|
+
digest = _eip712_digest(self.chain_id, safe_addr, struct_hash)
|
|
247
|
+
|
|
248
|
+
# 4. EIP-191 personal_sign on the digest
|
|
249
|
+
account = Account.from_key(signer_pk if signer_pk.startswith("0x") else f"0x{signer_pk}")
|
|
250
|
+
signed = account.sign_message(encode_defunct(primitive=digest))
|
|
251
|
+
packed_sig = _pack_sig(signed.signature.hex())
|
|
252
|
+
|
|
253
|
+
# 5. Build submit payload
|
|
254
|
+
req = {
|
|
255
|
+
"from": eoa_address,
|
|
256
|
+
"to": to_addr,
|
|
257
|
+
"proxyWallet": safe_addr,
|
|
258
|
+
"data": data if isinstance(data, str) else "0x" + data.hex(),
|
|
259
|
+
"nonce": str(nonce),
|
|
260
|
+
"signature": packed_sig,
|
|
261
|
+
"signatureParams": {
|
|
262
|
+
"gasPrice": "0",
|
|
263
|
+
"operation": str(operation),
|
|
264
|
+
"safeTxnGas": "0",
|
|
265
|
+
"baseGas": "0",
|
|
266
|
+
"gasToken": ZERO_ADDR,
|
|
267
|
+
"refundReceiver": ZERO_ADDR,
|
|
268
|
+
},
|
|
269
|
+
"type": "SAFE",
|
|
270
|
+
"metadata": "",
|
|
271
|
+
}
|
|
272
|
+
body = json.dumps(req, separators=(",", ":"))
|
|
273
|
+
|
|
274
|
+
# 6. POST /submit with builder HMAC headers
|
|
275
|
+
headers = _build_hmac_headers(self.builder_creds, "POST", "/submit", body)
|
|
276
|
+
headers["Content-Type"] = "application/json"
|
|
277
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
278
|
+
r = await client.post(f"{self.relayer_url}/submit", content=body, headers=headers)
|
|
279
|
+
if not r.is_success:
|
|
280
|
+
raise RuntimeError(f"relayer /submit failed: {r.status_code} {r.text}")
|
|
281
|
+
resp = r.json()
|
|
282
|
+
tx_id = resp["transactionID"]
|
|
283
|
+
|
|
284
|
+
# 7. Poll /transaction until mined
|
|
285
|
+
for _ in range(30): # up to 60s
|
|
286
|
+
await asyncio.sleep(2)
|
|
287
|
+
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
288
|
+
r = await client.get(
|
|
289
|
+
f"{self.relayer_url}/transaction",
|
|
290
|
+
params={"id": tx_id},
|
|
291
|
+
)
|
|
292
|
+
if not r.is_success:
|
|
293
|
+
continue
|
|
294
|
+
rows = r.json()
|
|
295
|
+
if not rows:
|
|
296
|
+
continue
|
|
297
|
+
row = rows[0] if isinstance(rows, list) else rows
|
|
298
|
+
state = row.get("state")
|
|
299
|
+
if state in ("STATE_MINED", "STATE_CONFIRMED"):
|
|
300
|
+
return row["transactionHash"]
|
|
301
|
+
if state in ("STATE_FAILED", "STATE_REVERTED"):
|
|
302
|
+
raise RuntimeError(f"tx {tx_id} failed on-chain: state={state} hash={row.get('transactionHash')}")
|
|
303
|
+
raise RuntimeError(f"tx {tx_id} timed out waiting for mined state")
|
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,
|
|
@@ -420,6 +420,9 @@ class PolyNodeTrader:
|
|
|
420
420
|
async def wrap_to_polyusd(self, amount: int) -> str:
|
|
421
421
|
"""Wrap USDC.e into PolyUSD via the Collateral Onramp.
|
|
422
422
|
|
|
423
|
+
Routes through the Polymarket relayer (gasless) for Safe/Proxy wallets,
|
|
424
|
+
and sends directly for EOA wallets.
|
|
425
|
+
|
|
423
426
|
Args:
|
|
424
427
|
amount: Amount in raw units (6 decimals, e.g. 1_000_000 = $1).
|
|
425
428
|
|
|
@@ -428,7 +431,6 @@ class PolyNodeTrader:
|
|
|
428
431
|
"""
|
|
429
432
|
try:
|
|
430
433
|
from web3 import Web3
|
|
431
|
-
from eth_account import Account
|
|
432
434
|
except ImportError:
|
|
433
435
|
raise ImportError("web3 required. Install with: pip install polynode[trading]")
|
|
434
436
|
|
|
@@ -445,42 +447,55 @@ class PolyNodeTrader:
|
|
|
445
447
|
|
|
446
448
|
usdc_contract = w3.eth.contract(address=Web3.to_checksum_address(USDC), abi=erc20_abi)
|
|
447
449
|
onramp_addr = Web3.to_checksum_address(COLLATERAL_ONRAMP)
|
|
448
|
-
|
|
450
|
+
funder_addr = Web3.to_checksum_address(funder)
|
|
449
451
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
allowance = usdc_contract.functions.allowance(funder_addr, onramp_addr).call()
|
|
453
|
+
|
|
454
|
+
# Build calldata for both ops
|
|
455
|
+
approve_data = usdc_contract.encode_abi("approve", args=[onramp_addr, 2**256 - 1])
|
|
454
456
|
|
|
455
|
-
|
|
457
|
+
wrap_abi = [
|
|
458
|
+
{"inputs": [{"name": "token", "type": "address"}, {"name": "funder", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "wrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
|
|
459
|
+
]
|
|
460
|
+
onramp_contract = w3.eth.contract(address=onramp_addr, abi=wrap_abi)
|
|
461
|
+
wrap_data = onramp_contract.encode_abi("wrap", args=[
|
|
462
|
+
Web3.to_checksum_address(USDC), funder_addr, int(amount)
|
|
463
|
+
])
|
|
464
|
+
|
|
465
|
+
# Safe / Proxy wallets → route via relayer (gasless)
|
|
466
|
+
if creds.signature_type in (SignatureType.POLY_GNOSIS_SAFE, SignatureType.POLY_PROXY):
|
|
467
|
+
from .relayer import BuilderCreds, RelayClient
|
|
468
|
+
bc_raw = self._builder_credentials
|
|
469
|
+
if not bc_raw:
|
|
470
|
+
raise RuntimeError(
|
|
471
|
+
"wrap_to_polyusd on a Safe/Proxy wallet requires builder_credentials "
|
|
472
|
+
"in TraderConfig. Pass your Polymarket builder key/secret/passphrase."
|
|
473
|
+
)
|
|
474
|
+
bc = BuilderCreds(key=bc_raw.key, secret=bc_raw.secret, passphrase=bc_raw.passphrase)
|
|
475
|
+
rc = RelayClient(bc, rpc_url=self._rpc_url) if False else RelayClient(bc) # default relayer
|
|
476
|
+
txns = []
|
|
477
|
+
if allowance < amount:
|
|
478
|
+
txns.append({"to": USDC, "data": approve_data, "value": 0})
|
|
479
|
+
txns.append({"to": COLLATERAL_ONRAMP, "data": wrap_data, "value": 0})
|
|
480
|
+
return await rc.execute_safe(signer._private_key or "", signer.address, txns)
|
|
481
|
+
|
|
482
|
+
# EOA path: direct send
|
|
483
|
+
if not signer._private_key:
|
|
484
|
+
raise RuntimeError("wrap_to_polyusd requires a private key signer for EOA wallets")
|
|
456
485
|
|
|
457
|
-
|
|
458
|
-
allowance = usdc_contract.functions.allowance(signer_addr, onramp_addr).call()
|
|
486
|
+
nonce = w3.eth.get_transaction_count(funder_addr)
|
|
459
487
|
if allowance < amount:
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
"from": signer_addr,
|
|
463
|
-
"nonce": nonce,
|
|
464
|
-
"chainId": 137,
|
|
488
|
+
tx = usdc_contract.functions.approve(onramp_addr, 2**256 - 1).build_transaction({
|
|
489
|
+
"from": funder_addr, "nonce": nonce, "chainId": 137,
|
|
465
490
|
})
|
|
466
491
|
signed = w3.eth.account.sign_transaction(tx, signer._private_key)
|
|
467
492
|
w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
468
493
|
nonce += 1
|
|
469
494
|
|
|
470
|
-
# Step 2: Call wrap(address token, address funder, uint256 amount) on Onramp
|
|
471
|
-
# wrap selector: 0x62355638
|
|
472
|
-
wrap_abi = [
|
|
473
|
-
{"inputs": [{"name": "token", "type": "address"}, {"name": "funder", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "wrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
|
|
474
|
-
]
|
|
475
|
-
onramp_contract = w3.eth.contract(address=onramp_addr, abi=wrap_abi)
|
|
476
495
|
tx = onramp_contract.functions.wrap(
|
|
477
|
-
Web3.to_checksum_address(USDC),
|
|
478
|
-
Web3.to_checksum_address(funder),
|
|
479
|
-
amount,
|
|
496
|
+
Web3.to_checksum_address(USDC), funder_addr, int(amount),
|
|
480
497
|
).build_transaction({
|
|
481
|
-
"from":
|
|
482
|
-
"nonce": nonce,
|
|
483
|
-
"chainId": 137,
|
|
498
|
+
"from": funder_addr, "nonce": nonce, "chainId": 137,
|
|
484
499
|
})
|
|
485
500
|
signed = w3.eth.account.sign_transaction(tx, signer._private_key)
|
|
486
501
|
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
@@ -489,6 +504,9 @@ class PolyNodeTrader:
|
|
|
489
504
|
async def unwrap_from_polyusd(self, amount: int) -> str:
|
|
490
505
|
"""Unwrap PolyUSD back into USDC.e via the Collateral Offramp.
|
|
491
506
|
|
|
507
|
+
Routes through the Polymarket relayer (gasless) for Safe/Proxy wallets,
|
|
508
|
+
and sends directly for EOA wallets.
|
|
509
|
+
|
|
492
510
|
Args:
|
|
493
511
|
amount: Amount in raw units (6 decimals, e.g. 1_000_000 = $1).
|
|
494
512
|
|
|
@@ -506,46 +524,60 @@ class PolyNodeTrader:
|
|
|
506
524
|
|
|
507
525
|
w3 = Web3(Web3.HTTPProvider(self._rpc_url))
|
|
508
526
|
|
|
509
|
-
|
|
510
|
-
raise RuntimeError("unwrap_from_polyusd requires a private key signer (not RouterSigner)")
|
|
511
|
-
|
|
512
|
-
signer_addr = Web3.to_checksum_address(signer.address)
|
|
527
|
+
funder_addr = Web3.to_checksum_address(funder)
|
|
513
528
|
offramp_addr = Web3.to_checksum_address(COLLATERAL_OFFRAMP)
|
|
514
529
|
|
|
515
|
-
# Check and set PolyUSD allowance for Offramp
|
|
516
530
|
erc20_abi = [
|
|
517
531
|
{"inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "approve", "outputs": [{"name": "", "type": "bool"}], "stateMutability": "nonpayable", "type": "function"},
|
|
518
532
|
{"inputs": [{"name": "owner", "type": "address"}, {"name": "spender", "type": "address"}], "name": "allowance", "outputs": [{"name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
|
|
519
533
|
]
|
|
520
534
|
polyusd_contract = w3.eth.contract(address=Web3.to_checksum_address(POLY_USD), abi=erc20_abi)
|
|
535
|
+
allowance = polyusd_contract.functions.allowance(funder_addr, offramp_addr).call()
|
|
536
|
+
|
|
537
|
+
approve_data = polyusd_contract.encode_abi("approve", args=[offramp_addr, 2**256 - 1])
|
|
521
538
|
|
|
522
|
-
|
|
539
|
+
unwrap_abi = [
|
|
540
|
+
{"inputs": [{"name": "token", "type": "address"}, {"name": "receiver", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "unwrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
|
|
541
|
+
]
|
|
542
|
+
offramp_contract = w3.eth.contract(address=offramp_addr, abi=unwrap_abi)
|
|
543
|
+
unwrap_data = offramp_contract.encode_abi("unwrap", args=[
|
|
544
|
+
Web3.to_checksum_address(USDC), funder_addr, int(amount)
|
|
545
|
+
])
|
|
546
|
+
|
|
547
|
+
# Safe / Proxy wallets → route via relayer (gasless)
|
|
548
|
+
if creds.signature_type in (SignatureType.POLY_GNOSIS_SAFE, SignatureType.POLY_PROXY):
|
|
549
|
+
from .relayer import BuilderCreds, RelayClient
|
|
550
|
+
bc_raw = self._builder_credentials
|
|
551
|
+
if not bc_raw:
|
|
552
|
+
raise RuntimeError(
|
|
553
|
+
"unwrap_from_polyusd on a Safe/Proxy wallet requires builder_credentials "
|
|
554
|
+
"in TraderConfig. Pass your Polymarket builder key/secret/passphrase."
|
|
555
|
+
)
|
|
556
|
+
bc = BuilderCreds(key=bc_raw.key, secret=bc_raw.secret, passphrase=bc_raw.passphrase)
|
|
557
|
+
rc = RelayClient(bc)
|
|
558
|
+
txns = []
|
|
559
|
+
if allowance < amount:
|
|
560
|
+
txns.append({"to": POLY_USD, "data": approve_data, "value": 0})
|
|
561
|
+
txns.append({"to": COLLATERAL_OFFRAMP, "data": unwrap_data, "value": 0})
|
|
562
|
+
return await rc.execute_safe(signer._private_key or "", signer.address, txns)
|
|
563
|
+
|
|
564
|
+
# EOA path
|
|
565
|
+
if not signer._private_key:
|
|
566
|
+
raise RuntimeError("unwrap_from_polyusd requires a private key signer for EOA wallets")
|
|
523
567
|
|
|
524
|
-
|
|
568
|
+
nonce = w3.eth.get_transaction_count(funder_addr)
|
|
525
569
|
if allowance < amount:
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
"from": signer_addr,
|
|
529
|
-
"nonce": nonce,
|
|
530
|
-
"chainId": 137,
|
|
570
|
+
tx = polyusd_contract.functions.approve(offramp_addr, 2**256 - 1).build_transaction({
|
|
571
|
+
"from": funder_addr, "nonce": nonce, "chainId": 137,
|
|
531
572
|
})
|
|
532
573
|
signed = w3.eth.account.sign_transaction(tx, signer._private_key)
|
|
533
574
|
w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
534
575
|
nonce += 1
|
|
535
576
|
|
|
536
|
-
# Call unwrap(address token, address receiver, uint256 amount) on Offramp
|
|
537
|
-
unwrap_abi = [
|
|
538
|
-
{"inputs": [{"name": "token", "type": "address"}, {"name": "receiver", "type": "address"}, {"name": "amount", "type": "uint256"}], "name": "unwrap", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
|
|
539
|
-
]
|
|
540
|
-
offramp_contract = w3.eth.contract(address=offramp_addr, abi=unwrap_abi)
|
|
541
577
|
tx = offramp_contract.functions.unwrap(
|
|
542
|
-
Web3.to_checksum_address(USDC),
|
|
543
|
-
Web3.to_checksum_address(funder),
|
|
544
|
-
amount,
|
|
578
|
+
Web3.to_checksum_address(USDC), funder_addr, int(amount),
|
|
545
579
|
).build_transaction({
|
|
546
|
-
"from":
|
|
547
|
-
"nonce": nonce,
|
|
548
|
-
"chainId": 137,
|
|
580
|
+
"from": funder_addr, "nonce": nonce, "chainId": 137,
|
|
549
581
|
})
|
|
550
582
|
signed = w3.eth.account.sign_transaction(tx, signer._private_key)
|
|
551
583
|
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
@@ -667,7 +699,8 @@ class PolyNodeTrader:
|
|
|
667
699
|
fee_amount_raw = calculate_fee(params.price, params.size, fee_config.fee_bps)
|
|
668
700
|
if fee_amount_raw > 0:
|
|
669
701
|
funder = creds.funder_address or creds.wallet_address
|
|
670
|
-
|
|
702
|
+
escrow_address = fee_escrow_address_for(self._exchange_version)
|
|
703
|
+
nonce = await fetch_escrow_nonce(self._rpc_url, signer.address, escrow_address)
|
|
671
704
|
deadline = int(time.time()) + 300
|
|
672
705
|
escrow_oid = generate_escrow_order_id()
|
|
673
706
|
fee_auth = await sign_fee_auth(
|
|
@@ -680,6 +713,7 @@ class PolyNodeTrader:
|
|
|
680
713
|
nonce=nonce,
|
|
681
714
|
affiliate=fee_config.affiliate,
|
|
682
715
|
affiliate_share_bps=fee_config.affiliate_share_bps,
|
|
716
|
+
exchange_version=self._exchange_version,
|
|
683
717
|
)
|
|
684
718
|
fee_auth_dict = {
|
|
685
719
|
"escrow_order_id": fee_auth.escrow_order_id,
|
|
@@ -692,6 +726,8 @@ class PolyNodeTrader:
|
|
|
692
726
|
"affiliate": fee_auth.affiliate,
|
|
693
727
|
"affiliate_share_bps": fee_auth.affiliate_share_bps,
|
|
694
728
|
}
|
|
729
|
+
if fee_auth.escrow_contract:
|
|
730
|
+
fee_auth_dict["escrow_contract"] = fee_auth.escrow_contract
|
|
695
731
|
|
|
696
732
|
request_data: dict[str, Any] = {"method": "POST", "path": "/order", "body": body_str, "headers": headers}
|
|
697
733
|
if fee_auth_dict:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
polynode/__init__.py,sha256=ucqXzFPMdEcfGEAdbgOeJczREEiSCNBX1G3Il7y8-UI,1127
|
|
2
|
-
polynode/_version.py,sha256=
|
|
2
|
+
polynode/_version.py,sha256=UwJXM8JY2T3tE2id0K2k_lEaVThbRTrGO1mNibyzIz8,22
|
|
3
3
|
polynode/client.py,sha256=eSCKpuZFg2ssQC5tmsfRYF3cTqCk1Gdw4KPjbd5MFHQ,21831
|
|
4
4
|
polynode/engine.py,sha256=DSXBmx3DXPh-PN0ihkXn7LSiyxeOoo0iY3z4YinQ3XI,6488
|
|
5
5
|
polynode/errors.py,sha256=5qUCI7K76VKbF5R5G5UYhPzcnOPlGLCA8-sJnKbrK-4,925
|
|
@@ -14,16 +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
|
+
polynode/trading/relayer.py,sha256=adTo-aclWnaqrFtfWEOQ2Ye859CZMc9q_fsd3DePDGA,10542
|
|
24
25
|
polynode/trading/signer.py,sha256=piifRquwnwFwrKLL24jsRQYMh_cdkz8wNz99Vx8_KTY,3413
|
|
25
26
|
polynode/trading/sqlite_backend.py,sha256=zfo4WevuunWV-7i6Z3gQi_e7Tonw-7m1TsEd88O5I4w,9486
|
|
26
|
-
polynode/trading/trader.py,sha256=
|
|
27
|
+
polynode/trading/trader.py,sha256=EEwF_U2DXVPRnENY5azk8FgzSx_zXns27apQQj9isus,37188
|
|
27
28
|
polynode/trading/types.py,sha256=mo5zJRlfQLWuXwrkS9AaGqKWrt7FMf6bPGRE-0T62J0,5698
|
|
28
29
|
polynode/types/__init__.py,sha256=0v_CByqG-zT1xucbT2So_77YDmsPj9u956at94nyrLM,280
|
|
29
30
|
polynode/types/enums.py,sha256=2cx9l10A-LUeDNBJZn7l0XBTka2YfxRQcXW0s7s3_4k,1082
|
|
@@ -32,6 +33,6 @@ polynode/types/orderbook.py,sha256=MwD6P3poo3F70bONUZ5i3ajk1sgw39hHTOw6D0fP5_8,1
|
|
|
32
33
|
polynode/types/rest.py,sha256=ftJKQZH0wBrR_l199rDMqnVS8cyt8YnCvo9cKIsTcCw,7012
|
|
33
34
|
polynode/types/short_form.py,sha256=xfGklDi587u6K2aXaRWiwbxqkipW8BiEPeNIDT45v2Y,799
|
|
34
35
|
polynode/types/ws.py,sha256=EzE9Vzrb6cILemfssmnqM8fbHt4USn3latpUHKatwk4,994
|
|
35
|
-
polynode-0.9.
|
|
36
|
-
polynode-0.9.
|
|
37
|
-
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
|