From cf6fb27a00554003d1398d0c70590fe2bb4d6bdc Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:11:59 +0000 Subject: [PATCH 1/3] Add userDetails API method and example - Add user_details() method to Info class in hyperliquid/info.py - Method retrieves array of user transaction details via POST /info - Supports all transaction types: orders, cancels, leverage updates, vault transfers, withdrawals, deposits, etc. - Add comprehensive example in examples/basic_user_details.py - Example demonstrates fetching and displaying transaction details with proper formatting Co-authored-by: Stenton Mayne --- examples/basic_user_details.py | 46 ++++++++++++++++++++++++++++++++++ hyperliquid/info.py | 37 +++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 examples/basic_user_details.py diff --git a/examples/basic_user_details.py b/examples/basic_user_details.py new file mode 100644 index 00000000..f75eeb9a --- /dev/null +++ b/examples/basic_user_details.py @@ -0,0 +1,46 @@ +import json + +import example_utils + +from hyperliquid.utils import constants + + +def main(): + address, info, exchange = example_utils.setup(base_url=constants.MAINNET_API_URL, skip_ws=True) + + # Retrieve user transaction details + print(f"Fetching transaction details for address: {address}") + user_details = info.user_details(address) + + # Print the response type + print(f"\nResponse type: {user_details.get('type')}") + print(f"Total transactions: {len(user_details.get('txs', []))}") + + # Print details of the first few transactions + txs = user_details.get("txs", []) + if len(txs) > 0: + print("\nFirst 5 transactions:") + for i, tx in enumerate(txs[:5]): + print(f"\n--- Transaction {i + 1} ---") + print(f" Time: {tx.get('time')}") + print(f" User: {tx.get('user')}") + print(f" Block: {tx.get('block')}") + print(f" Hash: {tx.get('hash')}") + print(f" Error: {tx.get('error')}") + + # Print action details + action = tx.get("action") + if isinstance(action, dict): + print(f" Action Type: {action.get('type')}") + print(f" Action Details: {json.dumps(action, indent=4)}") + elif isinstance(action, list): + print(f" Action: {action}") + else: + print(f" Action: {action}") + else: + print("No transactions found for this user") + + +if __name__ == "__main__": + main() + diff --git a/hyperliquid/info.py b/hyperliquid/info.py index 5898386b..161d8e20 100644 --- a/hyperliquid/info.py +++ b/hyperliquid/info.py @@ -754,6 +754,43 @@ def extra_agents(self, user: str) -> Any: """ return self.post("/info", {"type": "extraAgents", "user": user}) + def user_details(self, user: str) -> Any: + """Retrieve array of user transaction details. + + POST /info + + Args: + user (str): Onchain address in 42-character hexadecimal format; + e.g. 0x0000000000000000000000000000000000000000. + + Returns: + { + "type": "userDetails", + "txs": [ + { + "time": int, # Transaction creation timestamp + "user": str, # Creator's address + "action": dict, # Action performed in transaction (with "type" field) + "block": int, # Block number where transaction was included + "hash": str, # Transaction hash (66 character hex string) + "error": Optional[str] # Error message if transaction failed + }, + ... + ] + } + + Note: + The action field can contain various transaction types including: + - orders (type: "order") + - cancels (type: "cancel") + - leverage updates (type: "updateLeverage") + - vault transfers (type: "vaultTransfer") + - withdrawals (type: "withdraw2") + - deposits and other EVM transactions (type: "evmRawTx") + - and many other action types + """ + return self.post("/info", {"type": "userDetails", "user": user}) + def _remap_coin_subscription(self, subscription: Subscription) -> None: if ( subscription["type"] == "l2Book" From a8085ce9d87ea51bdae8ac5f579760761419896d Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:23:23 +0000 Subject: [PATCH 2/3] Fix userDetails API endpoint - Change from POST /info to POST /explorer endpoint - This matches the TypeScript SDK implementation which uses the explorer endpoint - Fixes 422 error: 'Failed to deserialize the JSON body into the target type' Co-authored-by: Stenton Mayne --- hyperliquid/info.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hyperliquid/info.py b/hyperliquid/info.py index 161d8e20..f880c3b4 100644 --- a/hyperliquid/info.py +++ b/hyperliquid/info.py @@ -757,7 +757,7 @@ def extra_agents(self, user: str) -> Any: def user_details(self, user: str) -> Any: """Retrieve array of user transaction details. - POST /info + POST /explorer Args: user (str): Onchain address in 42-character hexadecimal format; @@ -789,7 +789,7 @@ def user_details(self, user: str) -> Any: - deposits and other EVM transactions (type: "evmRawTx") - and many other action types """ - return self.post("/info", {"type": "userDetails", "user": user}) + return self.post("/explorer", {"type": "userDetails", "user": user}) def _remap_coin_subscription(self, subscription: Subscription) -> None: if ( From d67de4ac1fdd3e5a765d80365510fd2537c2a680 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:13:24 +0000 Subject: [PATCH 3/3] Fix userDetails to use RPC endpoint instead of API endpoint - Add MAINNET_RPC_URL and TESTNET_RPC_URL constants - Update user_details() to use RPC URL (rpc.hyperliquid.xyz) instead of API URL - The explorer endpoint is only available on RPC, not API - Automatically detect testnet vs mainnet based on base_url - Tested and confirmed working with testnet address Co-authored-by: Stenton Mayne --- hyperliquid/info.py | 22 ++++++++++++++++++++-- hyperliquid/utils/constants.py | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hyperliquid/info.py b/hyperliquid/info.py index f880c3b4..da233373 100644 --- a/hyperliquid/info.py +++ b/hyperliquid/info.py @@ -1,4 +1,5 @@ from hyperliquid.api import API +from hyperliquid.utils import constants from hyperliquid.utils.types import ( Any, Callable, @@ -757,7 +758,7 @@ def extra_agents(self, user: str) -> Any: def user_details(self, user: str) -> Any: """Retrieve array of user transaction details. - POST /explorer + POST /explorer (via RPC endpoint) Args: user (str): Onchain address in 42-character hexadecimal format; @@ -789,7 +790,24 @@ def user_details(self, user: str) -> Any: - deposits and other EVM transactions (type: "evmRawTx") - and many other action types """ - return self.post("/explorer", {"type": "userDetails", "user": user}) + # The explorer endpoint requires the RPC URL, not the API URL + # Determine which RPC URL to use based on the current base_url + if "testnet" in self.base_url: + rpc_url = constants.TESTNET_RPC_URL + else: + rpc_url = constants.MAINNET_RPC_URL + + # Make request directly to RPC endpoint + import requests + + response = requests.post( + f"{rpc_url}/explorer", + json={"type": "userDetails", "user": user}, + headers={"Content-Type": "application/json"}, + timeout=self.timeout, + ) + self._handle_exception(response) + return response.json() def _remap_coin_subscription(self, subscription: Subscription) -> None: if ( diff --git a/hyperliquid/utils/constants.py b/hyperliquid/utils/constants.py index 1b2bf974..36d6d9dd 100644 --- a/hyperliquid/utils/constants.py +++ b/hyperliquid/utils/constants.py @@ -1,3 +1,7 @@ MAINNET_API_URL = "https://api.hyperliquid.xyz" TESTNET_API_URL = "https://api.hyperliquid-testnet.xyz" LOCAL_API_URL = "http://localhost:3001" + +# RPC URLs for explorer endpoints +MAINNET_RPC_URL = "https://rpc.hyperliquid.xyz" +TESTNET_RPC_URL = "https://rpc.hyperliquid-testnet.xyz"