Skip to content

Commit 3d5cd6e

Browse files
committed
Version 0.1.7
1 parent 2157380 commit 3d5cd6e

File tree

9 files changed

+223
-32
lines changed

9 files changed

+223
-32
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,4 +618,4 @@ MigrationBackup/
618618
testing.py
619619

620620
# End of https://www.gitignore.io/api/osx,python,pycharm,windows,visualstudio,visualstudiocode
621-
examples/config.json
621+
examples/config.json

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
[![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit)
99
[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/.pre-commit-config.yaml)
1010
[![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/releases)
11-
[![License](https://img.shields.io/pypi/l/hyperliquid-python-sdk)](LICENSE.md)
12-
![Coverage Report](assets/images/coverage.svg)
11+
[![License](https://img.shields.io/pypi/l/hyperliquid-python-sdk)](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/LICENSE.md)
12+
![Coverage Report](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/assets/images/coverage.svg)
1313

1414
SDK for Hyperliquid API trading with Python.
1515

api/components.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
components:
2+
schemas:
3+
FloatString:
4+
type: string
5+
pattern: "^\\d+\\.?\\d*$"
6+
Address:
7+
type: string
8+
pattern: "^0x[a-fA-F0-9]{40}$"
9+
AssetPosition:
10+
type: object
11+
MarginSummary:
12+
type: object

api/info/allmids.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
openapi: 3.0.1
2+
info:
3+
title: Hyperliquid
4+
description: API for interacting with the Hyperliquid DEX
5+
version: '0.1'
6+
servers:
7+
- url: https://api.hyperliquid.xyz
8+
description: Mainnet
9+
- url: https://api.hyperliquid-testnet.xyz
10+
description: Testnet
11+
- url: http://localhost:3001
12+
description: Local
13+
paths:
14+
/info:
15+
post:
16+
summary: Retrieve all mids for all actively traded coins
17+
requestBody:
18+
required: true
19+
content:
20+
application/json:
21+
schema:
22+
type: object
23+
properties:
24+
type:
25+
type: string
26+
enum: [allMids]
27+
required:
28+
- type
29+
example:
30+
type: allMids
31+
responses:
32+
'200':
33+
description: A successful response
34+
content:
35+
application/json:
36+
schema:
37+
type: object
38+
additionalProperties:
39+
$ref: "../components.yaml#/components/schemas/FloatString"
40+
example:
41+
APE: "4.36255"
42+
ARB: "1.22965"
43+
ATOM: "11.2585"
44+
AVAX: "18.3695"

api/info/userstate.yaml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
openapi: 3.0.1
2+
info:
3+
title: Hyperliquid
4+
description: API for interacting with the Hyperliquid DEX
5+
version: '0.1'
6+
servers:
7+
- url: https://api.hyperliquid.xyz
8+
description: Mainnet
9+
- url: https://api.hyperliquid-testnet.xyz
10+
description: Testnet
11+
- url: http://localhost:3001
12+
description: Local
13+
paths:
14+
/info:
15+
post:
16+
summary: Retrieve a user's state
17+
description: See a user's open positions and margin summary
18+
requestBody:
19+
required: true
20+
content:
21+
application/json:
22+
schema:
23+
type: object
24+
properties:
25+
type:
26+
type: string
27+
enum: [clearinghouseState]
28+
user:
29+
$ref: "../components.yaml#/components/schemas/Address"
30+
required:
31+
- type
32+
- user
33+
example:
34+
type: "clearinghouseState"
35+
user: "0x0000000000000000000000000000000000000000"
36+
responses:
37+
'200':
38+
description: A successful response
39+
content:
40+
application/json:
41+
schema:
42+
type: object
43+
properties:
44+
assetPositions:
45+
type: array
46+
items:
47+
$ref: "../components.yaml#/components/schemas/AssetPosition"
48+
marginSummary:
49+
$ref: "../components.yaml#/components/schemas/MarginSummary"
50+
crossMarginSummary:
51+
$ref: "../components.yaml#/components/schemas/MarginSummary"
52+
example:
53+
assetPositions:
54+
- position:
55+
coin: "BTC"
56+
entryPx: null
57+
leverage:
58+
type: "cross"
59+
value: 20
60+
liquidationPx: "NaN"
61+
marginUsed: "0.0"
62+
maxTradeSzs: ["0.0", "0.0"]
63+
positionValue: "0.0"
64+
returnOnEquity: "0.0"
65+
szi: "0.0"
66+
unrealizedPnl: "0.0"
67+
type: "oneWay"
68+
marginSummary:
69+
accountValue: "0.0"
70+
totalMarginUsed: "0.0"
71+
totalNtlPos: "0.0"
72+
totalRawUsd: "0.0"
73+
withdrawable: "0.0"
74+
crossMarginSummary:
75+
accountValue: "0.0"
76+
totalMarginUsed: "0.0"
77+
totalNtlPos: "0.0"
78+
totalRawUsd: "0.0"
79+
withdrawable: "0.0"

examples/basic_vault.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from eth_account.signers.local import LocalAccount
2+
import eth_account
3+
import json
4+
import utils
5+
6+
from hyperliquid.info import Info
7+
from hyperliquid.exchange import Exchange
8+
from hyperliquid.utils import constants
9+
10+
11+
def main():
12+
config = utils.get_config()
13+
account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
14+
# Change this address to a vault that you lead
15+
vault = "0x1719884eb866cb12b2287399b15f7db5e7d775ea"
16+
print("Running with account address:", account.address)
17+
info = Info(constants.TESTNET_API_URL, skip_ws=True)
18+
19+
# Get the user state and print out position information
20+
user_state = info.user_state(account.address)
21+
positions = []
22+
for position in user_state["assetPositions"]:
23+
if float(position["position"]["szi"]) != 0:
24+
positions.append(position["position"])
25+
if len(positions) > 0:
26+
print("positions:")
27+
for position in positions:
28+
print(json.dumps(position, indent=2))
29+
else:
30+
print("no open positions")
31+
32+
# Place an order that should rest by setting the price very low
33+
exchange = Exchange(account, constants.TESTNET_API_URL, vault_address=vault)
34+
order_result = exchange.order("ETH", True, 0.2, 100, {"limit": {"tif": "Gtc"}})
35+
print(order_result)
36+
37+
# Cancel the order
38+
if order_result["status"] == "ok":
39+
status = order_result["response"]["data"]["statuses"][0]
40+
if "resting" in status:
41+
cancel_result = exchange.cancel("ETH", status["resting"]["oid"])
42+
print(cancel_result)
43+
44+
45+
if __name__ == "__main__":
46+
main()

hyperliquid/exchange.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@
1818

1919

2020
class Exchange(API):
21-
def __init__(self, wallet: LocalAccount, base_url: Optional[str] = None, meta: Optional[Meta] = None):
21+
def __init__(
22+
self,
23+
wallet: LocalAccount,
24+
base_url: Optional[str] = None,
25+
meta: Optional[Meta] = None,
26+
vault_address: Optional[str] = None,
27+
):
2228
super().__init__(base_url)
2329
self.wallet = wallet
30+
self.vault_address = vault_address
2431
if meta is None:
2532
info = Info(base_url, skip_ws=True)
2633
self.meta = info.meta()
@@ -48,39 +55,32 @@ def order(
4855
self.wallet,
4956
["(uint32,bool,uint64,uint64,bool,uint8,uint64)[]", "uint8"],
5057
[[order_spec_preprocessing(order_spec)], order_grouping_to_number(grouping)],
51-
ZERO_ADDRESS,
58+
ZERO_ADDRESS if self.vault_address is None else self.vault_address,
5259
timestamp,
5360
)
54-
logging.debug(
55-
{
56-
"action": {
57-
"type": "order",
58-
"grouping": grouping,
59-
"orders": [order_spec_to_order_wire(order_spec)],
60-
},
61-
"nonce": timestamp,
62-
"signature": signature,
63-
"vaultAddress": None,
64-
}
65-
)
66-
return self.post(
67-
"/exchange",
68-
{
69-
"action": {
70-
"type": "order",
71-
"grouping": grouping,
72-
"orders": [order_spec_to_order_wire(order_spec)],
73-
},
74-
"nonce": timestamp,
75-
"signature": signature,
76-
"vaultAddress": None,
61+
payload = {
62+
"action": {
63+
"type": "order",
64+
"grouping": grouping,
65+
"orders": [order_spec_to_order_wire(order_spec)],
7766
},
78-
)
67+
"nonce": timestamp,
68+
"signature": signature,
69+
"vaultAddress": self.vault_address,
70+
}
71+
logging.debug(payload)
72+
return self.post("/exchange", payload)
7973

8074
def cancel(self, coin: str, oid: int) -> Any:
8175
timestamp = get_timestamp_ms()
8276
asset = self.coin_to_asset[coin]
83-
signature = sign_l1_action(self.wallet, ["(uint32,uint64)[]"], [[(asset, oid)]], ZERO_ADDRESS, timestamp)
77+
signature = sign_l1_action(
78+
self.wallet,
79+
["(uint32,uint64)[]"],
80+
[[(asset, oid)]],
81+
ZERO_ADDRESS if self.vault_address is None else self.vault_address,
82+
timestamp,
83+
)
8484
return self.post(
8585
"/exchange",
8686
{
@@ -95,6 +95,6 @@ def cancel(self, coin: str, oid: int) -> Any:
9595
},
9696
"nonce": timestamp,
9797
"signature": signature,
98-
"vaultAddress": None,
98+
"vaultAddress": self.vault_address,
9999
},
100100
)

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.6"
8+
version = "0.1.7"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <[email protected]>"]

tests/signing_test.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ def test_l1_action_signing_matches():
4444
assert signature["v"] == 27
4545

4646

47+
def test_l1_action_signing_matches_with_vault():
48+
wallet = eth_account.Account.from_key("0x0123456789012345678901234567890123456789012345678901234567890123")
49+
signature = sign_l1_action(
50+
wallet, ["uint64"], [float_to_int_for_hashing(1000)], "0x1719884eb866cb12b2287399b15f7db5e7d775ea", 0
51+
)
52+
assert signature["r"] == "0x9358ee731732877d9a6d1a761fb6db43f93cb7c69ca74ecb382bd0773ac9b093"
53+
assert signature["s"] == "0x2879b3d8384b80664346c4286c34f947fc970c3a84c2f3995555f68493adf60b"
54+
assert signature["v"] == 27
55+
56+
4757
def test_float_to_int_for_hashing():
4858
assert float_to_int_for_hashing(123123123123) == 12312312312300000000
4959
assert float_to_int_for_hashing(0.00001231) == 1231

0 commit comments

Comments
 (0)