Skip to content

Commit 04f25f2

Browse files
committed
Version 0.4.0
1 parent f50707f commit 04f25f2

File tree

8 files changed

+89
-123
lines changed

8 files changed

+89
-123
lines changed

examples/basic_order_modify.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from hyperliquid.utils import constants
2+
from hyperliquid.utils.types import Cloid
23
import example_utils
34

45

56
def main():
67
address, info, exchange = example_utils.setup(base_url=constants.TESTNET_API_URL, skip_ws=True)
78

9+
cloid = Cloid.from_str("0x00000000000000000000000000000001")
810
# Place an order that should rest by setting the price very low
9-
order_result = exchange.order("ETH", True, 0.2, 1100, {"limit": {"tif": "Gtc"}})
11+
order_result = exchange.order("ETH", True, 0.2, 1100, {"limit": {"tif": "Gtc"}}, cloid=cloid)
1012
print(order_result)
1113

1214
# Modify the order by oid
@@ -17,8 +19,11 @@ def main():
1719
order_status = info.query_order_by_oid(address, oid)
1820
print("Order status by oid:", order_status)
1921

20-
modify_result = exchange.modify_order(oid, "ETH", True, 0.1, 1105, {"limit": {"tif": "Gtc"}})
21-
print("modify result:", modify_result)
22+
modify_result = exchange.modify_order(oid, "ETH", True, 0.1, 1105, {"limit": {"tif": "Gtc"}}, cloid=cloid)
23+
print("modify result with oid:", modify_result)
24+
25+
modify_result = exchange.modify_order(cloid, "ETH", True, 0.1, 1105, {"limit": {"tif": "Gtc"}})
26+
print("modify result with cloid:", modify_result)
2227

2328

2429
if __name__ == "__main__":

hyperliquid/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def _handle_exception(self, response):
3535
err = json.loads(response.text)
3636
except JSONDecodeError:
3737
raise ClientError(status_code, None, response.text, None, response.headers)
38+
if err is None:
39+
raise ClientError(status_code, None, response.text, None, response.headers)
3840
error_data = err.get("data")
3941
raise ClientError(status_code, err["code"], err["msg"], response.headers, error_data)
4042
raise ServerError(status_code, response.text)

hyperliquid/exchange.py

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
import logging
33
import secrets
44

5-
from eth_abi import encode
65
from eth_account.signers.local import LocalAccount
7-
from eth_utils import keccak, to_hex
86

97
from hyperliquid.api import API
108
from hyperliquid.info import Info
@@ -16,6 +14,7 @@
1614
OrderRequest,
1715
OrderType,
1816
OrderWire,
17+
OidOrCloid,
1918
ScheduleCancelAction,
2019
float_to_usd_int,
2120
get_timestamp_ms,
@@ -30,7 +29,6 @@
3029

3130

3231
class Exchange(API):
33-
3432
# Default Max Slippage for Market Orders 5%
3533
DEFAULT_SLIPPAGE = 0.05
3634

@@ -81,7 +79,6 @@ def _slippage_price(
8179
slippage: float,
8280
px: Optional[float] = None,
8381
) -> float:
84-
8582
if not px:
8683
# Get midprice
8784
px = float(self.info.all_mids()[coin])
@@ -136,7 +133,7 @@ def bulk_orders(self, order_requests: List[OrderRequest]) -> Any:
136133

137134
def modify_order(
138135
self,
139-
oid: int,
136+
oid: OidOrCloid,
140137
coin: str,
141138
is_buy: bool,
142139
sz: float,
@@ -145,7 +142,6 @@ def modify_order(
145142
reduce_only: bool = False,
146143
cloid: Optional[Cloid] = None,
147144
) -> Any:
148-
149145
modify: ModifyRequest = {
150146
"oid": oid,
151147
"order": {
@@ -164,7 +160,7 @@ def bulk_modify_orders_new(self, modify_requests: List[ModifyRequest]) -> Any:
164160
timestamp = get_timestamp_ms()
165161
modify_wires = [
166162
{
167-
"oid": modify["oid"],
163+
"oid": modify["oid"].to_raw() if isinstance(modify["oid"], Cloid) else modify["oid"],
168164
"order": order_request_to_order_wire(modify["order"], self.coin_to_asset[modify["order"]["coin"]]),
169165
}
170166
for modify in modify_requests
@@ -198,7 +194,6 @@ def market_open(
198194
slippage: float = DEFAULT_SLIPPAGE,
199195
cloid: Optional[Cloid] = None,
200196
) -> Any:
201-
202197
# Get aggressive Market Price
203198
px = self._slippage_price(coin, is_buy, slippage, px)
204199
# Market Order is an aggressive Limit Order IoC
@@ -446,65 +441,41 @@ def sub_account_transfer(self, sub_account_user: str, is_deposit: bool, usd: int
446441

447442
def usd_transfer(self, amount: float, destination: str) -> Any:
448443
timestamp = get_timestamp_ms()
449-
payload = {
450-
"destination": destination,
451-
"amount": str(amount),
452-
"time": timestamp,
453-
}
444+
action = {"destination": destination, "amount": str(amount), "time": timestamp, "type": "usdSend"}
454445
is_mainnet = self.base_url == MAINNET_API_URL
455-
signature = sign_usd_transfer_action(self.wallet, payload, is_mainnet)
446+
signature = sign_usd_transfer_action(self.wallet, action, is_mainnet)
456447
return self._post_action(
457-
{
458-
"chain": "Arbitrum" if is_mainnet else "ArbitrumTestnet",
459-
"payload": payload,
460-
"type": "usdTransfer",
461-
},
448+
action,
462449
signature,
463450
timestamp,
464451
)
465452

466-
def withdraw_from_bridge(self, usd: float, destination: str) -> Any:
453+
def withdraw_from_bridge(self, amount: float, destination: str) -> Any:
467454
timestamp = get_timestamp_ms()
468-
payload = {
469-
"destination": destination,
470-
"usd": str(usd),
471-
"time": timestamp,
472-
}
455+
action = {"destination": destination, "amount": str(amount), "time": timestamp, "type": "withdraw3"}
473456
is_mainnet = self.base_url == MAINNET_API_URL
474-
signature = sign_withdraw_from_bridge_action(self.wallet, payload, is_mainnet)
457+
signature = sign_withdraw_from_bridge_action(self.wallet, action, is_mainnet)
475458
return self._post_action(
476-
{
477-
"chain": "Arbitrum" if is_mainnet else "ArbitrumTestnet",
478-
"payload": payload,
479-
"type": "withdraw2",
480-
},
459+
action,
481460
signature,
482461
timestamp,
483462
)
484463

485464
def approve_agent(self, name: Optional[str] = None) -> Tuple[Any, str]:
486465
agent_key = "0x" + secrets.token_hex(32)
487466
account = eth_account.Account.from_key(agent_key)
488-
if name is not None:
489-
connection_id = keccak(encode(["address", "string"], [account.address, name]))
490-
else:
491-
connection_id = keccak(encode(["address"], [account.address]))
492-
agent = {
493-
"source": "https://hyperliquid.xyz",
494-
"connectionId": connection_id,
495-
}
496467
timestamp = get_timestamp_ms()
497468
is_mainnet = self.base_url == MAINNET_API_URL
498-
signature = sign_agent(self.wallet, agent, is_mainnet)
499-
agent["connectionId"] = to_hex(agent["connectionId"])
500469
action = {
501-
"chain": "Arbitrum" if is_mainnet else "ArbitrumTestnet",
502-
"agent": agent,
470+
"type": "approveAgent",
503471
"agentAddress": account.address,
504-
"type": "connect",
472+
"agentName": name or "",
473+
"nonce": timestamp,
505474
}
506-
if name is not None:
507-
action["extraAgentName"] = name
475+
signature = sign_agent(self.wallet, action, is_mainnet)
476+
if name is None:
477+
del action["agentName"]
478+
508479
return (
509480
self._post_action(
510481
action,

hyperliquid/info.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def user_state(self, address: str) -> Any:
5353
}
5454
"""
5555
return self.post("/info", {"type": "clearinghouseState", "user": address})
56-
56+
5757
def spot_user_state(self, address: str) -> Any:
5858
return self.post("/info", {"type": "spotClearinghouseState", "user": address})
5959

@@ -303,13 +303,10 @@ def candles_snapshot(self, coin: str, interval: str, startTime: int, endTime: in
303303

304304
def user_fees(self, address: str) -> Any:
305305
"""Retrieve the volume of trading activity associated with a user.
306-
307306
POST /info
308-
309307
Args:
310308
address (str): Onchain address in 42-character hexadecimal format;
311309
e.g. 0x0000000000000000000000000000000000000000.
312-
313310
Returns:
314311
{
315312
activeReferralDiscount: float string,

hyperliquid/utils/signing.py

Lines changed: 53 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@
2727
},
2828
total=False,
2929
)
30+
OidOrCloid = Union[int, Cloid]
3031
ModifyRequest = TypedDict(
3132
"ModifyRequest",
3233
{
33-
"oid": int,
34+
"oid": OidOrCloid,
3435
"order": OrderRequest,
3536
},
3637
total=False,
@@ -135,84 +136,74 @@ def sign_l1_action(wallet, action, active_pool, nonce, is_mainnet):
135136
return sign_inner(wallet, data)
136137

137138

138-
def sign_usd_transfer_action(wallet, message, is_mainnet):
139-
data = {
140-
"domain": {
141-
"name": "Exchange",
142-
"version": "1",
143-
"chainId": 42161 if is_mainnet else 421614,
144-
"verifyingContract": "0x0000000000000000000000000000000000000000",
145-
},
146-
"types": {
147-
"UsdTransferSignPayload": [
148-
{"name": "destination", "type": "string"},
149-
{"name": "amount", "type": "string"},
150-
{"name": "time", "type": "uint64"},
151-
],
152-
"EIP712Domain": [
153-
{"name": "name", "type": "string"},
154-
{"name": "version", "type": "string"},
155-
{"name": "chainId", "type": "uint256"},
156-
{"name": "verifyingContract", "type": "address"},
157-
],
158-
},
159-
"primaryType": "UsdTransferSignPayload",
160-
"message": message,
161-
}
162-
return sign_inner(wallet, data)
163-
164-
165-
def sign_withdraw_from_bridge_action(wallet, message, is_mainnet):
139+
def sign_user_signed_action(wallet, action, payload_types, primary_type, is_mainnet):
140+
action["signatureChainId"] = "0x66eee"
141+
action["hyperliquidChain"] = "Mainnet" if is_mainnet else "Testnet"
166142
data = {
167143
"domain": {
168-
"name": "Exchange",
144+
"name": "HyperliquidSignTransaction",
169145
"version": "1",
170-
"chainId": 42161 if is_mainnet else 421614,
146+
"chainId": 421614,
171147
"verifyingContract": "0x0000000000000000000000000000000000000000",
172148
},
173149
"types": {
174-
"WithdrawFromBridge2SignPayload": [
175-
{"name": "destination", "type": "string"},
176-
{"name": "usd", "type": "string"},
177-
{"name": "time", "type": "uint64"},
178-
],
150+
primary_type: payload_types,
179151
"EIP712Domain": [
180152
{"name": "name", "type": "string"},
181153
{"name": "version", "type": "string"},
182154
{"name": "chainId", "type": "uint256"},
183155
{"name": "verifyingContract", "type": "address"},
184156
],
185157
},
186-
"primaryType": "WithdrawFromBridge2SignPayload",
187-
"message": message,
158+
"primaryType": primary_type,
159+
"message": action,
188160
}
189161
return sign_inner(wallet, data)
190162

191163

192-
def sign_agent(wallet, agent, is_mainnet):
193-
data = {
194-
"domain": {
195-
"name": "Exchange",
196-
"version": "1",
197-
"chainId": 42161 if is_mainnet else 421614,
198-
"verifyingContract": "0x0000000000000000000000000000000000000000",
199-
},
200-
"types": {
201-
"Agent": [
202-
{"name": "source", "type": "string"},
203-
{"name": "connectionId", "type": "bytes32"},
204-
],
205-
"EIP712Domain": [
206-
{"name": "name", "type": "string"},
207-
{"name": "version", "type": "string"},
208-
{"name": "chainId", "type": "uint256"},
209-
{"name": "verifyingContract", "type": "address"},
210-
],
211-
},
212-
"primaryType": "Agent",
213-
"message": agent,
214-
}
215-
return sign_inner(wallet, data)
164+
def sign_usd_transfer_action(wallet, action, is_mainnet):
165+
return sign_user_signed_action(
166+
wallet,
167+
action,
168+
[
169+
{"name": "hyperliquidChain", "type": "string"},
170+
{"name": "destination", "type": "string"},
171+
{"name": "amount", "type": "string"},
172+
{"name": "time", "type": "uint64"},
173+
],
174+
"HyperliquidTransaction:UsdSend",
175+
is_mainnet,
176+
)
177+
178+
179+
def sign_withdraw_from_bridge_action(wallet, action, is_mainnet):
180+
return sign_user_signed_action(
181+
wallet,
182+
action,
183+
[
184+
{"name": "hyperliquidChain", "type": "string"},
185+
{"name": "destination", "type": "string"},
186+
{"name": "amount", "type": "string"},
187+
{"name": "time", "type": "uint64"},
188+
],
189+
"HyperliquidTransaction:Withdraw",
190+
is_mainnet,
191+
)
192+
193+
194+
def sign_agent(wallet, action, is_mainnet):
195+
return sign_user_signed_action(
196+
wallet,
197+
action,
198+
[
199+
{"name": "hyperliquidChain", "type": "string"},
200+
{"name": "agentAddress", "type": "address"},
201+
{"name": "agentName", "type": "string"},
202+
{"name": "nonce", "type": "uint64"},
203+
],
204+
"HyperliquidTransaction:ApproveAgent",
205+
is_mainnet,
206+
)
216207

217208

218209
def sign_inner(wallet, data):

hyperliquid/websocket_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def on_message(self, _ws, message):
8383
def on_open(self, _ws):
8484
logging.debug("on_open")
8585
self.ws_ready = True
86-
for (subscription, active_subscription) in self.queued_subscriptions:
86+
for subscription, active_subscription in self.queued_subscriptions:
8787
self.subscribe(subscription, active_subscription.callback, active_subscription.subscription_id)
8888

8989
def subscribe(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
55

66
[tool.poetry]
77
name = "hyperliquid-python-sdk"
8-
version = "0.3.0"
8+
version = "0.4.0"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <[email protected]>"]

0 commit comments

Comments
 (0)