Skip to content

Commit 7fc1d0e

Browse files
traderbenChengzhi Li
authored andcommitted
Version 0.22.0 (hyperliquid-dex#260)
Add support for general user abstraction actions
1 parent 9d19e18 commit 7fc1d0e

7 files changed

Lines changed: 144 additions & 47 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ jobs:
3333
python-version: ${{ matrix.python-version }}
3434

3535
- name: Install poetry
36-
run: make poetry-download
36+
uses: snok/install-poetry@v1
37+
with:
38+
version: 1.8.5
39+
virtualenvs-create: true
40+
virtualenvs-in-project: true
3741

3842
- name: Set up cache
3943
uses: actions/cache@v4
@@ -42,9 +46,7 @@ jobs:
4246
key: venv-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}
4347

4448
- name: Install dependencies
45-
run: |
46-
poetry config virtualenvs.in-project true
47-
poetry install
49+
run: poetry install
4850

4951
- name: Run tests
5052
run: |

examples/user_abstraction.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import example_utils
2+
3+
from hyperliquid.exchange import Exchange
4+
from hyperliquid.utils import constants
5+
6+
# set sub-account user address here
7+
SUB_ACCOUNT_USER = "0x0000000000000000000000000000000000000000"
8+
9+
10+
def main():
11+
address, info, exchange = example_utils.setup(constants.TESTNET_API_URL, skip_ws=True)
12+
13+
# set abstraction for user via agent
14+
# Note: the account must be in "default" mode to succeed
15+
user = exchange.account_address
16+
print("current user abstraction state:", info.query_user_abstraction_state(user))
17+
agent_set_abstraction_result = exchange.agent_set_abstraction("u")
18+
print(agent_set_abstraction_result)
19+
20+
if user == exchange.wallet.address:
21+
# set abstraction for user back to disabled
22+
user_set_abstraction_result = exchange.user_set_abstraction(user, "unifiedAccount")
23+
print(user_set_abstraction_result)
24+
print("current user abstraction state:", info.query_user_abstraction_state(user))
25+
26+
# set dex abstraction for sub-account of user
27+
print("setting abstraction for", SUB_ACCOUNT_USER)
28+
29+
# set abstraction for user via agent by setting the vault_address to SUB_ACCOUNT_USER
30+
exchange_with_sub_account = Exchange(exchange.wallet, exchange.base_url, vault_address=SUB_ACCOUNT_USER)
31+
agent_set_abstraction_result = exchange_with_sub_account.agent_set_abstraction("u")
32+
print("sub-account agent_set_abstraction result:", agent_set_abstraction_result)
33+
34+
for abstraction in ["disabled", "portfolioMargin"]:
35+
user_set_abstraction_result = exchange.user_set_abstraction(SUB_ACCOUNT_USER, abstraction)
36+
print(user_set_abstraction_result)
37+
print(
38+
"current sub-account user abstraction state:",
39+
info.query_user_abstraction_state(SUB_ACCOUNT_USER),
40+
)
41+
42+
else:
43+
print("not performing user set abstraction because not user", exchange.account_address, exchange.wallet.address)
44+
45+
46+
if __name__ == "__main__":
47+
main()

hyperliquid/exchange.py

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@
3131
sign_token_delegate_action,
3232
sign_usd_class_transfer_action,
3333
sign_usd_transfer_action,
34+
sign_user_dex_abstraction_action,
35+
sign_user_set_abstraction_action,
3436
sign_withdraw_from_bridge_action,
3537
)
3638
from hyperliquid.utils.types import (
39+
Abstraction,
40+
AgentAbstraction,
3741
Any,
3842
BuilderInfo,
3943
Cloid,
@@ -1089,67 +1093,37 @@ def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_addre
10891093
signature,
10901094
nonce,
10911095
)
1092-
1093-
def enable_user_dex_abstraction(self, enabled: bool) -> Any:
1094-
"""
1095-
Enable or disable HIP-3 DEX abstraction.
10961096

1097-
If set, actions on HIP-3 perps will automatically transfer collateral from
1098-
validator-operated USDC perps balance for HIP-3 DEXs where USDC is the collateral token,
1099-
and spot otherwise.
1100-
"""
1097+
def use_big_blocks(self, enable: bool) -> Any:
11011098
timestamp = get_timestamp_ms()
1102-
is_mainnet = self.base_url == MAINNET_API_URL
1103-
1104-
# Determine Chain IDs and Names based on environment
1105-
# Mainnet -> Arbitrum One (42161 -> 0xa4b1)
1106-
# Testnet -> Arbitrum Sepolia (421614 -> 0x66eee)
1107-
signature_chain_id = "0xa4b1" if is_mainnet else "0x66eee"
1108-
hyperliquid_chain = "Mainnet" if is_mainnet else "Testnet"
1109-
1110-
# Determine the target user address
1111-
# Priority: Vault Address (Sub-account) > Account Address > Wallet Address
1112-
user_address = self.wallet.address
1113-
if self.account_address:
1114-
user_address = self.account_address
1115-
if self.vault_address:
1116-
user_address = self.vault_address
1117-
11181099
action = {
1119-
"type": "userDexAbstraction",
1120-
"hyperliquidChain": hyperliquid_chain,
1121-
"signatureChainId": signature_chain_id,
1122-
"user": user_address,
1123-
"enabled": enabled,
1124-
"nonce": timestamp,
1100+
"type": "evmUserModify",
1101+
"usingBigBlocks": enable,
11251102
}
1126-
1127-
# Sign the action
11281103
signature = sign_l1_action(
11291104
self.wallet,
11301105
action,
1131-
None,
1106+
None,
11321107
timestamp,
11331108
self.expires_after,
1134-
is_mainnet,
1109+
self.base_url == MAINNET_API_URL,
11351110
)
1136-
11371111
return self._post_action(
11381112
action,
11391113
signature,
11401114
timestamp,
11411115
)
1142-
1143-
def use_big_blocks(self, enable: bool) -> Any:
1116+
1117+
def agent_set_abstraction(self, abstraction: AgentAbstraction) -> Any:
11441118
timestamp = get_timestamp_ms()
11451119
action = {
1146-
"type": "evmUserModify",
1147-
"usingBigBlocks": enable,
1120+
"type": "agentSetAbstraction",
1121+
"abstraction": abstraction,
11481122
}
11491123
signature = sign_l1_action(
11501124
self.wallet,
11511125
action,
1152-
None,
1126+
self.vault_address,
11531127
timestamp,
11541128
self.expires_after,
11551129
self.base_url == MAINNET_API_URL,
@@ -1160,6 +1134,36 @@ def use_big_blocks(self, enable: bool) -> Any:
11601134
timestamp,
11611135
)
11621136

1137+
def user_dex_abstraction(self, user: str, enabled: bool) -> Any:
1138+
timestamp = get_timestamp_ms()
1139+
action = {
1140+
"type": "userDexAbstraction",
1141+
"user": user.lower(),
1142+
"enabled": enabled,
1143+
"nonce": timestamp,
1144+
}
1145+
signature = sign_user_dex_abstraction_action(self.wallet, action, self.base_url == MAINNET_API_URL)
1146+
return self._post_action(
1147+
action,
1148+
signature,
1149+
timestamp,
1150+
)
1151+
1152+
def user_set_abstraction(self, user: str, abstraction: Abstraction) -> Any:
1153+
timestamp = get_timestamp_ms()
1154+
action = {
1155+
"type": "userSetAbstraction",
1156+
"user": user.lower(),
1157+
"abstraction": abstraction,
1158+
"nonce": timestamp,
1159+
}
1160+
signature = sign_user_set_abstraction_action(self.wallet, action, self.base_url == MAINNET_API_URL)
1161+
return self._post_action(
1162+
action,
1163+
signature,
1164+
timestamp,
1165+
)
1166+
11631167
def noop(self, nonce):
11641168
action = {"type": "noop"}
11651169
signature = sign_l1_action(

hyperliquid/info.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ def query_user_to_multi_sig_signers(self, multi_sig_user: str) -> Any:
632632
def query_perp_deploy_auction_status(self) -> Any:
633633
return self.post("/info", {"type": "perpDeployAuctionStatus"})
634634

635+
def query_user_dex_abstraction_state(self, user: str) -> Any:
636+
return self.post("/info", {"type": "userDexAbstraction", "user": user})
637+
638+
def query_user_abstraction_state(self, user: str) -> Any:
639+
return self.post("/info", {"type": "userAbstraction", "user": user})
640+
635641
def historical_orders(self, user: str) -> Any:
636642
"""Retrieve a user's historical orders.
637643

hyperliquid/utils/signing.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
import time
24
from decimal import Decimal
35

@@ -115,6 +117,20 @@
115117
{"name": "nonce", "type": "uint64"},
116118
]
117119

120+
USER_DEX_ABSTRACTION_SIGN_TYPES = [
121+
{"name": "hyperliquidChain", "type": "string"},
122+
{"name": "user", "type": "address"},
123+
{"name": "enabled", "type": "bool"},
124+
{"name": "nonce", "type": "uint64"},
125+
]
126+
127+
USER_SET_ABSTRACTION_SIGN_TYPES = [
128+
{"name": "hyperliquidChain", "type": "string"},
129+
{"name": "user", "type": "address"},
130+
{"name": "abstraction", "type": "string"},
131+
{"name": "nonce", "type": "uint64"},
132+
]
133+
118134
TOKEN_DELEGATE_TYPES = [
119135
{"name": "hyperliquidChain", "type": "string"},
120136
{"name": "validator", "type": "address"},
@@ -362,6 +378,26 @@ def sign_send_asset_action(wallet, action, is_mainnet):
362378
)
363379

364380

381+
def sign_user_dex_abstraction_action(wallet, action, is_mainnet):
382+
return sign_user_signed_action(
383+
wallet,
384+
action,
385+
USER_DEX_ABSTRACTION_SIGN_TYPES,
386+
"HyperliquidTransaction:UserDexAbstraction",
387+
is_mainnet,
388+
)
389+
390+
391+
def sign_user_set_abstraction_action(wallet, action, is_mainnet):
392+
return sign_user_signed_action(
393+
wallet,
394+
action,
395+
USER_SET_ABSTRACTION_SIGN_TYPES,
396+
"HyperliquidTransaction:UserSetAbstraction",
397+
is_mainnet,
398+
)
399+
400+
365401
def sign_convert_to_multi_sig_user_action(wallet, action, is_mainnet):
366402
return sign_user_signed_action(
367403
wallet,
@@ -479,11 +515,11 @@ def order_request_to_order_wire(order: OrderRequest, asset: int) -> OrderWire:
479515
return order_wire
480516

481517

482-
def order_wires_to_order_action(order_wires, builder=None):
518+
def order_wires_to_order_action(order_wires: list[OrderWire], builder: Any = None, grouping: Grouping = "na") -> Any:
483519
action = {
484520
"type": "order",
485521
"orders": order_wires,
486-
"grouping": "na",
522+
"grouping": grouping,
487523
}
488524
if builder:
489525
action["builder"] = builder

hyperliquid/utils/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@
183183

184184
# 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
185185
BuilderInfo = TypedDict("BuilderInfo", {"b": str, "f": int})
186+
Abstraction = Literal["unifiedAccount", "portfolioMargin", "disabled"]
187+
AgentAbstraction = Literal["u", "p", "i"]
186188

187189
PerpDexSchemaInput = TypedDict(
188190
"PerpDexSchemaInput", {"fullName": str, "collateralToken": int, "oracleUpdater": Optional[str]}

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.19.0"
8+
version = "0.22.0"
99
description = "SDK for Hyperliquid API trading with Python."
1010
readme = "README.md"
1111
authors = ["Hyperliquid <hello@hyperliquid.xyz>"]

0 commit comments

Comments
 (0)