Skip to content

Commit 9b64059

Browse files
authored
Merge pull request #3 from quiknode-labs/update-examples-to-rest-api
rest overhaul
2 parents 08f4eb5 + b6eff53 commit 9b64059

42 files changed

Lines changed: 841 additions & 469 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

python/approve.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"""Approve builder fee (one-time setup)."""
22

3-
from client import rpc, sign_hash
3+
from client import exchange, sign_hash
44

55
MAX_FEE = "1%"
66

7-
res = rpc("hl_buildApproveBuilderFee", {"maxFeeRate": MAX_FEE})
8-
sig = sign_hash(res["result"]["hash"])
7+
res = exchange({
8+
"action": {"type": "approveBuilderFee", "maxFeeRate": MAX_FEE},
9+
})
10+
sig = sign_hash(res["hash"])
911

10-
rpc("hl_sendApproval", {
11-
"nonce": res["result"]["nonce"],
12+
exchange({
13+
"action": {"type": "approveBuilderFee", "maxFeeRate": MAX_FEE},
14+
"nonce": res["nonce"],
1215
"signature": sig,
13-
"maxFeeRate": MAX_FEE,
1416
})
1517

1618
print("Builder fee approved.")

python/cancel_order.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Place a resting order (GTC, 3% below mid), then cancel it."""
22

33
import json
4-
from client import rpc, sign_hash, get_mid
4+
from client import exchange, sign_hash, get_mid
55

66
COIN = "BTC"
77

@@ -16,30 +16,28 @@
1616
print(f"{COIN} mid: ${mid:,.2f}")
1717
print(f"Placing resting BUY {sz} @ {rest_px} (GTC, 3% below mid)\n")
1818

19-
order_action = {
20-
"type": "order",
21-
"orders": [{
22-
"a": COIN,
23-
"b": True,
24-
"p": str(rest_px),
25-
"s": str(sz),
26-
"r": False,
27-
"t": {"limit": {"tif": "Gtc"}},
28-
}],
29-
"grouping": "na",
30-
}
31-
32-
res = rpc("hl_buildOrder", {"action": order_action})
33-
sig = sign_hash(res["result"]["hash"])
19+
res = exchange({
20+
"action": {
21+
"type": "order",
22+
"orders": [{
23+
"asset": COIN,
24+
"side": "buy",
25+
"price": str(rest_px),
26+
"size": str(sz),
27+
"tif": "gtc",
28+
}],
29+
},
30+
})
31+
sig = sign_hash(res["hash"])
3432

35-
result = rpc("hl_sendOrder", {
36-
"action": res["result"].get("action", order_action),
37-
"nonce": res["result"]["nonce"],
33+
result = exchange({
34+
"action": res["action"],
35+
"nonce": res["nonce"],
3836
"signature": sig,
3937
})
4038

41-
exchange = result["result"]["exchangeResponse"]
42-
statuses = exchange.get("response", {}).get("data", {}).get("statuses", [])
39+
exchange_resp = result["exchangeResponse"]
40+
statuses = exchange_resp.get("response", {}).get("data", {}).get("statuses", [])
4341

4442
oid = None
4543
for s in statuses:
@@ -49,7 +47,7 @@
4947

5048
if oid is None:
5149
print("Could not extract OID from resting order")
52-
print(json.dumps(exchange, indent=2))
50+
print(json.dumps(exchange_resp, indent=2))
5351
raise SystemExit(1)
5452

5553
print(f"Order resting (OID: {oid})")
@@ -60,14 +58,14 @@
6058
"cancels": [{"a": COIN, "o": oid}],
6159
}
6260

63-
res = rpc("hl_buildCancel", {"action": cancel_action})
64-
sig = sign_hash(res["result"]["hash"])
61+
res = exchange({"action": cancel_action})
62+
sig = sign_hash(res["hash"])
6563

66-
result = rpc("hl_sendCancel", {
64+
result = exchange({
6765
"action": cancel_action,
68-
"nonce": res["result"]["nonce"],
66+
"nonce": res["nonce"],
6967
"signature": sig,
7068
})
7169

72-
print(json.dumps(result["result"]["exchangeResponse"], indent=2))
70+
print(json.dumps(result["exchangeResponse"], indent=2))
7371
print("\nOrder cancelled.")

python/check_status.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Check builder fee approval status."""
22

33
import json
4-
from client import rpc, wallet
4+
from client import get_approval, address
55

6-
res = rpc("hl_getMaxBuilderFee", {"user": wallet.address})
7-
print(json.dumps(res["result"], indent=2))
6+
res = get_approval(address)
7+
print(json.dumps(res, indent=2))

python/client.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
"""Thin RPC client for Hyperliquid API builder API."""
1+
"""REST client for Hyperliquid API (via QuickNode builder API).
2+
3+
No SDK required -- just requests + eth_account.
4+
"""
25

36
import json
47
import os
@@ -7,41 +10,52 @@
710
import requests
811
from eth_account import Account
912

10-
ENDPOINT = "https://send.hyperliquidapi.com"
13+
API_URL = "https://send.hyperliquidapi.com"
14+
HL_INFO_URL = "https://api.hyperliquid.xyz/info"
1115

1216
_pk = os.environ.get("PRIVATE_KEY")
1317
if not _pk:
1418
print("Set PRIVATE_KEY environment variable (hex, with or without 0x)")
1519
sys.exit(1)
1620

1721
wallet = Account.from_key(_pk)
18-
print(f"Wallet: {wallet.address}")
19-
20-
_req_id = 0
22+
address = wallet.address
23+
print(f"Wallet: {address}")
2124

2225

23-
def rpc(method, params=None):
24-
global _req_id
25-
_req_id += 1
26-
r = requests.post(ENDPOINT, json={
27-
"jsonrpc": "2.0",
28-
"method": method,
29-
"params": params or {},
30-
"id": _req_id,
31-
})
26+
def exchange(body):
27+
"""POST /exchange -- build (no signature) or send (with signature)."""
28+
r = requests.post(f"{API_URL}/exchange", json=body)
3229
data = r.json()
3330
if data.get("error"):
34-
err = data["error"]
35-
print(f"\nRPC error ({method}):")
36-
print(f" code: {err.get('code')}")
37-
print(f" message: {err.get('message')}")
38-
guidance = err.get("data", {}).get("guidance")
31+
print(f"\nError ({r.status_code}):")
32+
print(f" error: {data.get('error')}")
33+
print(f" message: {data.get('message')}")
34+
guidance = data.get("guidance")
3935
if guidance:
4036
print(f" guidance: {guidance}")
4137
sys.exit(1)
4238
return data
4339

4440

41+
def get_approval(user):
42+
"""GET /approval?user=<addr> -- check builder fee approval status."""
43+
r = requests.get(f"{API_URL}/approval", params={"user": user})
44+
return r.json()
45+
46+
47+
def get_markets():
48+
"""GET /markets -- list all available markets."""
49+
r = requests.get(f"{API_URL}/markets")
50+
return r.json()
51+
52+
53+
def post_endpoint(path, body):
54+
"""POST to a utility endpoint (e.g. /openOrders, /orderStatus, /preflight)."""
55+
r = requests.post(f"{API_URL}{path}", json=body)
56+
return r.json()
57+
58+
4559
def sign_hash(hash_hex):
4660
h = bytes.fromhex(hash_hex.removeprefix("0x"))
4761
s = wallet.unsafe_sign_hash(h)
@@ -50,5 +64,12 @@ def sign_hash(hash_hex):
5064

5165
def get_mid(coin):
5266
"""Get the current mid price for a coin from Hyperliquid."""
53-
r = requests.post("https://api.hyperliquid.xyz/info", json={"type": "allMids"})
67+
r = requests.post(HL_INFO_URL, json={"type": "allMids"})
68+
return float(r.json().get(coin, 0))
69+
70+
71+
def get_hip3_mid(coin):
72+
"""Get mid price for a HIP-3 market (requires dex parameter)."""
73+
dex = coin.split(":")[0]
74+
r = requests.post(HL_INFO_URL, json={"type": "allMids", "dex": dex})
5475
return float(r.json().get(coin, 0))

python/close_position.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Close a position — worker queries your position and builds the counter-order."""
2+
3+
import json
4+
from client import exchange, sign_hash, address
5+
6+
COIN = "HYPE"
7+
8+
print(f"Closing {COIN} position for {address}\n")
9+
10+
res = exchange({
11+
"action": {
12+
"type": "closePosition",
13+
"asset": COIN,
14+
"user": address,
15+
},
16+
})
17+
18+
ctx = res.get("closePositionContext", {})
19+
print(f"Position: {ctx.get('positionSize')} {ctx.get('positionSide')}")
20+
print(f"Close: {ctx.get('closeSide')} {ctx.get('closeSize')} @ {ctx.get('slippedPrice')}")
21+
22+
sig = sign_hash(res["hash"])
23+
24+
result = exchange({
25+
"action": res["action"],
26+
"nonce": res["nonce"],
27+
"signature": sig,
28+
})
29+
30+
print(json.dumps(result["exchangeResponse"], indent=2))
31+
print("\nPosition closed.")

python/hip3_order.py

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,39 @@
11
"""Place an IOC order on a HIP-3 market by name (e.g. xyz:SILVER)."""
22

33
import json
4-
import requests
5-
from client import rpc, sign_hash
4+
from client import exchange, sign_hash, get_hip3_mid
65

76
COIN = "xyz:SILVER"
8-
DEX = COIN.split(":")[0]
97

10-
r = requests.post("https://api.hyperliquid.xyz/info", json={"type": "allMids", "dex": DEX})
11-
mid = float(r.json().get(COIN, 0))
8+
mid = get_hip3_mid(COIN)
129
if mid == 0:
1310
print(f"Could not fetch {COIN} mid price, using fallback")
1411
mid = 78.0
1512

16-
sz = round(10.0 / mid, 2)
13+
sz = round(11.0 / mid, 2)
1714
buy_px = round(mid * 1.03, 2)
1815

1916
print(f"{COIN} mid: ${mid:,.2f}")
2017
print(f"BUY {sz} @ {buy_px} (IOC, ~${sz * mid:.2f} notional)\n")
2118

22-
action = {
23-
"type": "order",
24-
"orders": [{
25-
"a": COIN,
26-
"b": True,
27-
"p": str(buy_px),
28-
"s": str(sz),
29-
"r": False,
30-
"t": {"limit": {"tif": "Ioc"}},
31-
}],
32-
"grouping": "na",
33-
}
34-
35-
res = rpc("hl_buildOrder", {"action": action})
36-
sig = sign_hash(res["result"]["hash"])
37-
38-
result = rpc("hl_sendOrder", {
39-
"action": res["result"].get("action", action),
40-
"nonce": res["result"]["nonce"],
19+
res = exchange({
20+
"action": {
21+
"type": "order",
22+
"orders": [{
23+
"asset": COIN,
24+
"side": "buy",
25+
"price": str(buy_px),
26+
"size": str(sz),
27+
"tif": "ioc",
28+
}],
29+
},
30+
})
31+
sig = sign_hash(res["hash"])
32+
33+
result = exchange({
34+
"action": res["action"],
35+
"nonce": res["nonce"],
4136
"signature": sig,
4237
})
4338

44-
print(json.dumps(result["result"]["exchangeResponse"], indent=2))
39+
print(json.dumps(result["exchangeResponse"], indent=2))

python/list_markets.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""List all available markets (perps, spot, HIP-3)."""
22

3-
from client import rpc
3+
from client import get_markets
44

5-
res = rpc("hl_listMarkets")
6-
data = res["result"]
5+
data = get_markets()
76

87
perps = data.get("perps", [])
98
spot = data.get("spot", [])

python/market_order.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Place a market order — no price needed, worker computes it automatically."""
2+
3+
import json
4+
from client import exchange, sign_hash
5+
6+
COIN = "BTC"
7+
SIZE = "0.00011"
8+
9+
print(f"Market BUY {SIZE} {COIN}\n")
10+
11+
res = exchange({
12+
"action": {
13+
"type": "order",
14+
"orders": [{
15+
"asset": COIN,
16+
"side": "buy",
17+
"size": SIZE,
18+
"tif": "market",
19+
}],
20+
},
21+
})
22+
23+
computed_price = res["action"]["orders"][0]["p"]
24+
print(f"Computed price (mid + 3% slippage): {computed_price}")
25+
print(f"Builder fee: {res['builderFee']}")
26+
27+
sig = sign_hash(res["hash"])
28+
29+
result = exchange({
30+
"action": res["action"],
31+
"nonce": res["nonce"],
32+
"signature": sig,
33+
})
34+
35+
print(json.dumps(result["exchangeResponse"], indent=2))

0 commit comments

Comments
 (0)