Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions hyperliquid/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,37 @@ def convert_to_multi_sig_user(self, authorized_users: List[str], threshold: int)
timestamp,
)

def create_convert_from_multi_sig_user_action(self, threshold: int = 0) -> Dict[str, Any]:
"""
Create an inner action for converting a multi-sig account back to a regular account.

Note: As of SDK version 0.22.0, the Hyperliquid backend may reject threshold=0
when authorizedUsers is empty, returning "Invalid multi-sig threshold". If you
encounter this error, try threshold=1 or contact Hyperliquid support.

Args:
threshold: The threshold for the multi-sig conversion. Should be 0 when
authorizedUsers is empty, but the backend may require threshold >= 1.

Returns:
A dictionary representing the inner action, ready to be used with `multi_sig()`.
"""
timestamp = get_timestamp_ms()
signers = {
"authorizedUsers": [],
"threshold": threshold,
}
is_mainnet = self.base_url == MAINNET_API_URL
chain_id = "0xa4b1" if is_mainnet else "0x66eee"
hyperliquid_chain = "Mainnet" if is_mainnet else "Testnet"
return {
"type": "convertToMultiSigUser",
"signatureChainId": chain_id,
"hyperliquidChain": hyperliquid_chain,
"signers": json.dumps(signers),
"nonce": timestamp,
}

def spot_deploy_register_token(
self, token_name: str, sz_decimals: int, wei_decimals: int, max_gas: int, full_name: str
) -> Any:
Expand Down Expand Up @@ -1078,17 +1109,18 @@ def c_validator_unregister(self) -> Any:

def multi_sig(self, multi_sig_user, inner_action, signatures, nonce, vault_address=None):
multi_sig_user = multi_sig_user.lower()
is_mainnet = self.base_url == MAINNET_API_URL
chain_id = "0xa4b1" if is_mainnet else "0x66eee"
multi_sig_action = {
"type": "multiSig",
"signatureChainId": "0x66eee",
"signatureChainId": chain_id,
"signatures": signatures,
"payload": {
"multiSigUser": multi_sig_user,
"outerSigner": self.wallet.address.lower(),
"action": inner_action,
},
}
is_mainnet = self.base_url == MAINNET_API_URL
signature = sign_multi_sig_action(
self.wallet,
multi_sig_action,
Expand Down
3 changes: 2 additions & 1 deletion hyperliquid/utils/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ def sign_l1_action(wallet, action, active_pool, nonce, expires_after, is_mainnet
def sign_user_signed_action(wallet, action, payload_types, primary_type, is_mainnet):
# signatureChainId is the chain used by the wallet to sign and can be any chain.
# hyperliquidChain determines the environment and prevents replaying an action on a different chain.
action["signatureChainId"] = "0x66eee"
if "signatureChainId" not in action:
action["signatureChainId"] = "0xa4b1" if is_mainnet else "0x66eee"
action["hyperliquidChain"] = "Mainnet" if is_mainnet else "Testnet"
data = user_signed_payload(primary_type, payload_types, action)
return sign_inner(wallet, data)
Expand Down