Skip to content

Commit 7eaf6f5

Browse files
committed
Version 0.7.0
1 parent dfd6e9f commit 7eaf6f5

File tree

12 files changed

+317
-198
lines changed

12 files changed

+317
-198
lines changed

assets/images/coverage.svg

Lines changed: 2 additions & 2 deletions
Loading

examples/basic_builder_fee.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from hyperliquid.utils import constants
2+
import example_utils
3+
4+
5+
def main():
6+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
7+
8+
if exchange.account_address != exchange.wallet.address:
9+
raise Exception("Only the main wallet has permission to approve a builder fee")
10+
11+
# approve setting a builder fee
12+
approve_result = exchange.approve_builder_fee("0x0000000000000000000000000000000000000000", "0.001%")
13+
print(approve_result)
14+
15+
# place an order with builder set, this will cause an additional fee to be added to the order which is sent to the builder
16+
order_result = exchange.market_open(
17+
"ETH", True, 0.05, None, 0.01, builder={"b": "0x0000000000000000000000000000000000000000", "f": 1}
18+
)
19+
print(order_result)
20+
21+
22+
if __name__ == "__main__":
23+
main()

examples/basic_spot_transfer.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@
33

44

55
def main():
6-
address, info, exchange = example_utils.setup(
7-
constants.TESTNET_API_URL, skip_ws=True)
6+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
87

98
if exchange.account_address != exchange.wallet.address:
10-
raise Exception(
11-
"Agents do not have permission to perform internal transfers")
9+
raise Exception("Agents do not have permission to perform internal transfers")
1210

1311
# Transfer 1 PURR token to the zero address for demonstration purposes
1412
transfer_result = exchange.spot_transfer(
15-
1,
16-
"0x0000000000000000000000000000000000000000",
17-
"PURR:0xc4bf3f870c0e9465323c0b6ed28096c2"
13+
1, "0x0000000000000000000000000000000000000000", "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2"
1814
)
1915
print(transfer_result)
2016

examples/basic_sub_account.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import example_utils
33

44

5+
# This example shows how to create, query, and transfer funds to a subaccount.
6+
# To trade as a subaccount set vault_address to the subaccount's address. See basic_vault.py for an example.
57
def main():
68
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
79

examples/basic_vault.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
def main():
77
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
88

9-
# Change this address to a vault that you lead
9+
# Change this address to a vault that you lead or a subaccount that you own
1010
vault = "0x1719884eb866cb12b2287399b15f7db5e7d775ea"
1111

1212
# Place an order that should rest by setting the price very low

hyperliquid/exchange.py

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
get_timestamp_ms,
2121
order_request_to_order_wire,
2222
order_wires_to_order_action,
23+
sign_approve_builder_fee,
2324
sign_l1_action,
25+
sign_usd_class_transfer_action,
2426
sign_usd_transfer_action,
2527
sign_spot_transfer_action,
2628
sign_withdraw_from_bridge_action,
2729
sign_agent,
2830
)
29-
from hyperliquid.utils.types import Any, List, Meta, SpotMeta, Optional, Tuple, Cloid
31+
from hyperliquid.utils.types import Any, List, Meta, SpotMeta, Optional, Tuple, Cloid, BuilderInfo
3032

3133

3234
class Exchange(API):
@@ -87,6 +89,7 @@ def order(
8789
order_type: OrderType,
8890
reduce_only: bool = False,
8991
cloid: Optional[Cloid] = None,
92+
builder: Optional[BuilderInfo] = None,
9093
) -> Any:
9194
order: OrderRequest = {
9295
"coin": name,
@@ -98,15 +101,15 @@ def order(
98101
}
99102
if cloid:
100103
order["cloid"] = cloid
101-
return self.bulk_orders([order])
104+
return self.bulk_orders([order], builder)
102105

103-
def bulk_orders(self, order_requests: List[OrderRequest]) -> Any:
106+
def bulk_orders(self, order_requests: List[OrderRequest], builder: Optional[BuilderInfo] = None) -> Any:
104107
order_wires: List[OrderWire] = [
105108
order_request_to_order_wire(order, self.info.name_to_asset(order["coin"])) for order in order_requests
106109
]
107110
timestamp = get_timestamp_ms()
108111

109-
order_action = order_wires_to_order_action(order_wires)
112+
order_action = order_wires_to_order_action(order_wires, builder)
110113

111114
signature = sign_l1_action(
112115
self.wallet,
@@ -184,11 +187,14 @@ def market_open(
184187
px: Optional[float] = None,
185188
slippage: float = DEFAULT_SLIPPAGE,
186189
cloid: Optional[Cloid] = None,
190+
builder: Optional[BuilderInfo] = None,
187191
) -> Any:
188192
# Get aggressive Market Price
189193
px = self._slippage_price(name, is_buy, slippage, px)
190194
# Market Order is an aggressive Limit Order IoC
191-
return self.order(name, is_buy, sz, px, order_type={"limit": {"tif": "Ioc"}}, reduce_only=False, cloid=cloid)
195+
return self.order(
196+
name, is_buy, sz, px, order_type={"limit": {"tif": "Ioc"}}, reduce_only=False, cloid=cloid, builder=builder
197+
)
192198

193199
def market_close(
194200
self,
@@ -197,6 +203,7 @@ def market_close(
197203
px: Optional[float] = None,
198204
slippage: float = DEFAULT_SLIPPAGE,
199205
cloid: Optional[Cloid] = None,
206+
builder: Optional[BuilderInfo] = None,
200207
) -> Any:
201208
address = self.wallet.address
202209
if self.account_address:
@@ -215,7 +222,16 @@ def market_close(
215222
# Get aggressive Market Price
216223
px = self._slippage_price(coin, is_buy, slippage, px)
217224
# Market Order is an aggressive Limit Order IoC
218-
return self.order(coin, is_buy, sz, px, order_type={"limit": {"tif": "Ioc"}}, reduce_only=True, cloid=cloid)
225+
return self.order(
226+
coin,
227+
is_buy,
228+
sz,
229+
px,
230+
order_type={"limit": {"tif": "Ioc"}},
231+
reduce_only=True,
232+
cloid=cloid,
233+
builder=builder,
234+
)
219235

220236
def cancel(self, name: str, oid: int) -> Any:
221237
return self.bulk_cancel([{"coin": name, "oid": oid}])
@@ -384,6 +400,22 @@ def create_sub_account(self, name: str) -> Any:
384400
timestamp,
385401
)
386402

403+
def usd_class_transfer(self, amount: float, to_perp: bool) -> Any:
404+
timestamp = get_timestamp_ms()
405+
action = {
406+
"type": "usdClassTransfer",
407+
"amount": str(amount),
408+
"toPerp": to_perp,
409+
"nonce": timestamp,
410+
}
411+
signature = sign_usd_class_transfer_action(self.wallet, action, self.base_url == MAINNET_API_URL)
412+
return self._post_action(
413+
action,
414+
signature,
415+
timestamp,
416+
)
417+
418+
# Deprecated in favor of usd_class_transfer
387419
def user_spot_transfer(self, usdc: float, to_perp: bool) -> Any:
388420
usdc = int(round(usdc, 2) * 1e6)
389421
timestamp = get_timestamp_ms()
@@ -427,22 +459,21 @@ def sub_account_transfer(self, sub_account_user: str, is_deposit: bool, usd: int
427459
signature,
428460
timestamp,
429461
)
430-
462+
431463
def vault_usd_transfer(self, vault_address: str, is_deposit: bool, usd: int) -> Any:
432464
timestamp = get_timestamp_ms()
433465
vault_transfer_action = {
434466
"type": "vaultTransfer",
435467
"vaultAddress": vault_address,
436468
"isDeposit": is_deposit,
437-
"usd": usd}
469+
"usd": usd,
470+
}
438471
is_mainnet = self.base_url == MAINNET_API_URL
439472
signature = sign_l1_action(self.wallet, vault_transfer_action, None, timestamp, is_mainnet)
440-
return (
441-
self._post_action(
442-
vault_transfer_action,
443-
signature,
444-
timestamp,
445-
)
473+
return self._post_action(
474+
vault_transfer_action,
475+
signature,
476+
timestamp,
446477
)
447478

448479
def usd_transfer(self, amount: float, destination: str) -> Any:
@@ -458,8 +489,13 @@ def usd_transfer(self, amount: float, destination: str) -> Any:
458489

459490
def spot_transfer(self, amount: float, destination: str, token: str) -> Any:
460491
timestamp = get_timestamp_ms()
461-
action = {"destination": destination, "amount": str(
462-
amount), "token": token, "time": timestamp, "type": "spotSend"}
492+
action = {
493+
"destination": destination,
494+
"amount": str(amount),
495+
"token": token,
496+
"time": timestamp,
497+
"type": "spotSend",
498+
}
463499
is_mainnet = self.base_url == MAINNET_API_URL
464500
signature = sign_spot_transfer_action(self.wallet, action, is_mainnet)
465501
return self._post_action(
@@ -502,3 +538,10 @@ def approve_agent(self, name: Optional[str] = None) -> Tuple[Any, str]:
502538
),
503539
agent_key,
504540
)
541+
542+
def approve_builder_fee(self, builder: str, max_fee_rate: str) -> Any:
543+
timestamp = get_timestamp_ms()
544+
545+
action = {"maxFeeRate": max_fee_rate, "builder": builder, "nonce": timestamp, "type": "approveBuilderFee"}
546+
signature = sign_approve_builder_fee(self.wallet, action, self.base_url == MAINNET_API_URL)
547+
return self._post_action(action, signature, timestamp)

hyperliquid/utils/signing.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,21 @@ def sign_withdraw_from_bridge_action(wallet, action, is_mainnet):
207207
)
208208

209209

210+
def sign_usd_class_transfer_action(wallet, action, is_mainnet):
211+
return sign_user_signed_action(
212+
wallet,
213+
action,
214+
[
215+
{"name": "hyperliquidChain", "type": "string"},
216+
{"name": "amount", "type": "string"},
217+
{"name": "toPerp", "type": "bool"},
218+
{"name": "nonce", "type": "uint64"},
219+
],
220+
"HyperliquidTransaction:UsdClassTransfer",
221+
is_mainnet,
222+
)
223+
224+
210225
def sign_agent(wallet, action, is_mainnet):
211226
return sign_user_signed_action(
212227
wallet,
@@ -222,6 +237,21 @@ def sign_agent(wallet, action, is_mainnet):
222237
)
223238

224239

240+
def sign_approve_builder_fee(wallet, action, is_mainnet):
241+
return sign_user_signed_action(
242+
wallet,
243+
action,
244+
[
245+
{"name": "hyperliquidChain", "type": "string"},
246+
{"name": "maxFeeRate", "type": "string"},
247+
{"name": "builder", "type": "address"},
248+
{"name": "nonce", "type": "uint64"},
249+
],
250+
"HyperliquidTransaction:ApproveBuilderFee",
251+
is_mainnet,
252+
)
253+
254+
225255
def sign_inner(wallet, data):
226256
structured_data = encode_structured_data(data)
227257
signed = wallet.sign_message(structured_data)
@@ -272,9 +302,12 @@ def order_request_to_order_wire(order: OrderRequest, asset: int) -> OrderWire:
272302
return order_wire
273303

274304

275-
def order_wires_to_order_action(order_wires):
276-
return {
305+
def order_wires_to_order_action(order_wires, builder=None):
306+
action = {
277307
"type": "order",
278308
"orders": order_wires,
279309
"grouping": "na",
280310
}
311+
if builder:
312+
action["builder"] = builder
313+
return action

hyperliquid/utils/types.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
from __future__ import annotations
22

3-
import sys
4-
5-
if sys.version_info >= (3, 8):
6-
from typing import Literal, TypedDict
7-
from typing_extensions import NotRequired
8-
else:
9-
from typing_extensions import TypedDict, Literal, NotRequired
10-
11-
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple, Union, cast
3+
from typing_extensions import NotRequired
4+
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple, Union, cast, Literal, TypedDict
125

136
Any = Any
147
Option = Optional
@@ -111,6 +104,9 @@
111104
)
112105
WsMsg = Union[AllMidsMsg, L2BookMsg, TradesMsg, UserEventsMsg, PongMsg, UserFillsMsg, OtherWsMsg]
113106

107+
# b is the public address of the builder, f is the amount of the fee in tenths of basis points. e.g. 10 means 1 basis point
108+
BuilderInfo = TypedDict("BuilderInfo", {"b": str, "f": int})
109+
114110

115111
class Cloid:
116112
def __init__(self, raw_cloid: str):

0 commit comments

Comments
 (0)