Skip to content

Commit 25c42a1

Browse files
author
Adam
committed
Version 0.1.15
1 parent cb63a3c commit 25c42a1

File tree

8 files changed

+132
-15
lines changed

8 files changed

+132
-15
lines changed

assets/images/coverage.svg

Lines changed: 2 additions & 2 deletions
Loading

examples/basic_agent.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def main():
2323

2424
# Place an order that should rest by setting the price very low
2525
agent_account: LocalAccount = eth_account.Account.from_key(agent_key)
26-
print("Running with agent address:", account.address)
26+
print("Running with agent address:", agent_account.address)
2727
exchange = Exchange(agent_account, constants.TESTNET_API_URL)
2828
order_result = exchange.order("ETH", True, 0.2, 1000, {"limit": {"tif": "Gtc"}})
2929
print(order_result)
@@ -35,6 +35,28 @@ def main():
3535
cancel_result = exchange.cancel("ETH", status["resting"]["oid"])
3636
print(cancel_result)
3737

38+
# Create an extra named agent
39+
exchange = Exchange(account, constants.TESTNET_API_URL)
40+
approve_result, extra_agent_key = exchange.approve_agent("persist")
41+
if approve_result["status"] != "ok":
42+
print("approving extra agent failed", approve_result)
43+
return
44+
45+
extra_agent_account: LocalAccount = eth_account.Account.from_key(extra_agent_key)
46+
print("Running with extra agent address:", extra_agent_account.address)
47+
48+
print("Placing order with original agent")
49+
order_result = exchange.order("ETH", True, 0.2, 1000, {"limit": {"tif": "Gtc"}})
50+
print(order_result)
51+
52+
if order_result["status"] == "ok":
53+
status = order_result["response"]["data"]["statuses"][0]
54+
if "resting" in status:
55+
print("Canceling order with extra agent")
56+
exchange = Exchange(extra_agent_account, constants.TESTNET_API_URL)
57+
cancel_result = exchange.cancel("ETH", status["resting"]["oid"])
58+
print(cancel_result)
59+
3860

3961
if __name__ == "__main__":
4062
main()

hyperliquid/exchange.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,33 +239,40 @@ def usd_tranfer(self, amount: float, destination: str) -> Any:
239239
signature = sign_usd_transfer_action(self.wallet, payload, is_mainnet)
240240
return self._post_action(
241241
{
242-
"chain": "Arbitrum" if is_mainnet else "ArbitrumGoerli",
242+
"chain": "Arbitrum" if is_mainnet else "ArbitrumTestnet",
243243
"payload": payload,
244244
"type": "usdTransfer",
245245
},
246246
signature,
247247
timestamp,
248248
)
249249

250-
def approve_agent(self) -> Tuple[Any, str]:
250+
def approve_agent(self, name: Optional[str] = None) -> Tuple[Any, str]:
251251
agent_key = "0x" + secrets.token_hex(32)
252252
account = eth_account.Account.from_key(agent_key)
253+
if name is not None:
254+
connection_id = keccak(encode(["address", "string"], [account.address, name]))
255+
else:
256+
connection_id = keccak(encode(["address"], [account.address]))
253257
agent = {
254258
"source": "https://hyperliquid.xyz",
255-
"connectionId": keccak(encode(["address"], [account.address])),
259+
"connectionId": connection_id,
256260
}
257261
timestamp = get_timestamp_ms()
258262
is_mainnet = self.base_url == MAINNET_API_URL
259263
signature = sign_agent(self.wallet, agent, is_mainnet)
260264
agent["connectionId"] = to_hex(agent["connectionId"])
265+
action = {
266+
"chain": "Arbitrum" if is_mainnet else "ArbitrumTestnet",
267+
"agent": agent,
268+
"agentAddress": account.address,
269+
"type": "connect",
270+
}
271+
if name is not None:
272+
action["extraAgentName"] = name
261273
return (
262274
self._post_action(
263-
{
264-
"chain": "Arbitrum" if is_mainnet else "ArbitrumGoerli",
265-
"agent": agent,
266-
"agentAddress": account.address,
267-
"type": "connect",
268-
},
275+
action,
269276
signature,
270277
timestamp,
271278
),

hyperliquid/info.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,39 @@ def open_orders(self, address: str) -> Any:
7575
"""
7676
return self.post("/info", {"type": "openOrders", "user": address})
7777

78+
def frontend_open_orders(self, address: str) -> Any:
79+
"""Retrieve a user's open orders with additional frontend info.
80+
81+
POST /info
82+
83+
Args:
84+
address (str): Onchain address in 42-character hexadecimal format;
85+
e.g. 0x0000000000000000000000000000000000000000.
86+
Returns: [
87+
{
88+
children:
89+
[
90+
dict of frontend orders
91+
]
92+
coin: str,
93+
isPositoinTpsl: bool,
94+
isTrigger: bool,
95+
limitPx: float string,
96+
oid: int,
97+
orderType: str,
98+
origSz: float string,
99+
reduceOnly: bool,
100+
side: "A" | "B",
101+
sz: float string,
102+
tif: str,
103+
timestamp: int,
104+
triggerCondition: str,
105+
triggerPx: float str
106+
}
107+
]
108+
"""
109+
return self.post("/info", {"type": "frontendOpenOrders", "user": address})
110+
78111
def all_mids(self) -> Any:
79112
"""Retrieve all mids for all actively traded coins.
80113

hyperliquid/utils/signing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def sign_usd_transfer_action(wallet, message, is_mainnet):
181181
"domain": {
182182
"name": "Exchange",
183183
"version": "1",
184-
"chainId": 42161 if is_mainnet else 421613,
184+
"chainId": 42161 if is_mainnet else 421614,
185185
"verifyingContract": "0x0000000000000000000000000000000000000000",
186186
},
187187
"types": {
@@ -208,7 +208,7 @@ def sign_agent(wallet, agent, is_mainnet):
208208
"domain": {
209209
"name": "Exchange",
210210
"version": "1",
211-
"chainId": 42161 if is_mainnet else 421613,
211+
"chainId": 42161 if is_mainnet else 421614,
212212
"verifyingContract": "0x0000000000000000000000000000000000000000",
213213
},
214214
"types": {

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.1.14"
8+
version = "0.1.15"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <[email protected]>"]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
interactions:
2+
- request:
3+
body: '{"type": "frontendOpenOrders", "user": "0xCB331197E84f135AB9Ed6FB51Cd9757c0bd29d0D"}'
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '84'
13+
Content-Type:
14+
- application/json
15+
User-Agent:
16+
- python-requests/2.31.0
17+
method: POST
18+
uri: https://api.hyperliquid.xyz/info
19+
response:
20+
body:
21+
string: '[{"children":[],"coin":"INJ","isPositionTpsl":false,"isTrigger":true,"limitPx":"9.2037","oid":3184595907,"orderType":"Take
22+
Profit Market","origSz":"12.5","reduceOnly":true,"side":"A","sz":"12.5","tif":null,"timestamp":1700126022555,"triggerCondition":"Price
23+
above 10.004","triggerPx":"10.004"},{"children":[],"coin":"INJ","isPositionTpsl":false,"isTrigger":true,"limitPx":"9.1954","oid":3184595906,"orderType":"Stop
24+
Market","origSz":"12.5","reduceOnly":true,"side":"A","sz":"12.5","tif":null,"timestamp":1700126022555,"triggerCondition":"Price
25+
below 9.995","triggerPx":"9.995"},{"children":[{"children":[],"coin":"INJ","isPositionTpsl":false,"isTrigger":true,"limitPx":"9.1954","oid":3184595906,"orderType":"Stop
26+
Market","origSz":"12.5","reduceOnly":true,"side":"A","sz":"12.5","tif":null,"timestamp":1700126022555,"triggerCondition":"Price
27+
below 9.995","triggerPx":"9.995"},{"children":[],"coin":"INJ","isPositionTpsl":false,"isTrigger":true,"limitPx":"9.2037","oid":3184595907,"orderType":"Take
28+
Profit Market","origSz":"12.5","reduceOnly":true,"side":"A","sz":"12.5","tif":null,"timestamp":1700126022555,"triggerCondition":"Price
29+
above 10.004","triggerPx":"10.004"}],"coin":"INJ","isPositionTpsl":false,"isTrigger":false,"limitPx":"10.0","oid":3184595905,"orderType":"Limit","origSz":"12.5","reduceOnly":false,"side":"B","sz":"12.5","tif":"Gtc","timestamp":1700126022555,"triggerCondition":"N/A","triggerPx":"0.0"}]'
30+
headers:
31+
access-control-allow-origin:
32+
- '*'
33+
access-control-expose-headers:
34+
- '*'
35+
content-length:
36+
- '1417'
37+
content-type:
38+
- application/json
39+
date:
40+
- Thu, 16 Nov 2023 09:16:00 GMT
41+
vary:
42+
- origin
43+
- access-control-request-method
44+
- access-control-request-headers
45+
status:
46+
code: 200
47+
message: OK
48+
version: 1

tests/info_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ def test_get_open_orders():
1818
assert len(response) == 196
1919

2020

21+
@pytest.mark.vcr()
22+
def test_get_frontend_open_orders():
23+
info = Info(skip_ws=True)
24+
response = info.frontend_open_orders("0xCB331197E84f135AB9Ed6FB51Cd9757c0bd29d0D")
25+
assert len(response) == 3
26+
27+
2128
@pytest.mark.vcr()
2229
def test_get_all_mids():
2330
info = Info(skip_ws=True)

0 commit comments

Comments
 (0)