Contributor Guide¤
Installation and Setup¤
Prerequisites¤
First, set up uv
if you haven't already. uv
is a fast Python package manager that we use for dependency management:
# Install uv (choose one method)
# Via pip
pip install uv
# Via homebrew (macOS)
brew install uv
# Via curl (Unix)
curl -LsSf https://astral.sh/uv/install.sh | sh
Project Setup¤
Now clone the GitHub repository and set up the development environment:
git clone git@github.com:papicapital/hl-sdk.git
cd hl-sdk/
# Install all dependencies including dev dependencies
uv sync --all-groups
# Run tests to verify everything is working
uv run pytest tests/
Architecture Overview¤
The SDK is organized into several key modules:
hl.api
: Main entry point providing unified access to all endpointshl.account
: Authentication credentials and account managementhl.info
: Read-only information retrieval (market data, user state, etc.)hl.exchange
: Write operations (placing orders, transfers, etc.)hl.ws
: WebSocket client for real-time data connectionshl.subscriptions
: WebSocket subscription methods for real-time feedshl.signer
: Cryptographic signing for authenticated requestshl.types
: Type definitions for all API requests and responseshl.transport
: HTTP and WebSocket transport layershl.universe
: Asset metadata and ID/name resolutionhl.errors
: Error handling and exception definitionshl.result
: Result type for explicit error handlinghl.constants
: Predefined constants for orders, sides, and groupingshl.cloid
: Client order ID generation and managementhl.validator
: Request and response validationhl.network
: Network configuration (mainnet/testnet)hl._lib
: Utility functions
Testing Framework¤
Test Structure¤
Tests are organized in the tests/
folder and follow a specific pattern for API testing:
tests/
├── fixtures/ # Captured API responses
│ ├── http/ # HTTP endpoint fixtures
│ └── ws/ # WebSocket fixtures
├── test_info.py # Info endpoint tests
├── test_exchange.py # Exchange endpoint tests
├── test_ws.py # WebSocket tests
├── test_subscription.py # Subscription tests
├── mock_http_transport.py # HTTP mocking infrastructure
├── mock_ws_transport.py # WebSocket mocking infrastructure
└── conftest.py # Test configuration and fixtures
Capture-Replay System¤
The SDK uses a sophisticated capture-replay system for testing:
- First run: Tests make live requests against the testnet API
- Capture: Request/response payloads are saved to fixture files
- Subsequent runs: Tests replay captured responses without live requests
This approach provides: - Fast test execution (no network calls after first run) - Consistent test results (no dependency on live market conditions) - Offline development capability
Setting Up Environment Variables for Capture Mode¤
To run tests in capture mode (making live API calls), you need to provide authentication credentials via environment variables. Create a .env
file in the project root:
# .env file
HL_ADDRESS=0x1234567890123456789012345678901234567890
HL_SECRET_KEY=0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
# Optional: Additional addresses for specific test scenarios
SUB_ACCOUNT_ADDRESS=0x...
VAULT_ADDRESS=0x...
AGENT_ADDRESS=0x...
SECOND_ADDRESS=0x...
VALIDATOR_ADDRESS=0x...
Important: Never commit the .env
file to version control. Use testnet credentials only.
Running Tests in Capture Mode¤
There are several ways to run tests with environment variables:
# Method 1: Using uv with --env-file
uv run --env-file=.env pytest tests/
# Method 2: Export variables in your shell
export HL_ADDRESS=0x1234567890123456789012345678901234567890
export HL_SECRET_KEY=0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
uv run pytest tests/
# Method 3: Set variables inline (one-time usage)
HL_ADDRESS=0x... HL_SECRET_KEY=0x... uv run pytest tests/
When credentials are provided: - Tests will make live API calls to the Hyperliquid testnet - New fixture files will be created for any missing test scenarios - Existing fixtures will be used for replay
When credentials are missing: - Tests will only run in replay mode using existing fixtures - Tests without fixtures will be skipped or fail
Creating New Tests¤
Basic Test Structure¤
async def test_all_mids(info_client: Info) -> None:
"""Test fetching all mids."""
result = await info_client.all_mids()
assert result.is_ok()
response = result.unwrap()
assert isinstance(response, dict)
# Add specific assertions for your use case
Test Fixtures¤
The test system provides several fixtures automatically:
info_client
: Pre-configuredInfo
instance with capture enabledexchange_client
: Pre-configuredExchange
instance with capture enabledws_client
: Pre-configured WebSocket client for real-time testssubscriptions_client
: Pre-configuredSubscriptions
instance for WebSocket subscriptions
Capture File Management¤
Capture files follow the naming convention: {module_name}-{method_name}.json
Examples:
- test_info-test_all_mids.json
- test_exchange-test_place_order.json
Important: Renaming test modules or methods without renaming the corresponding capture file will result in new capture files being created on the next run.
Testing Authenticated Endpoints¤
For endpoints requiring authentication, ensure your test environment has proper credentials:
async def test_user_state(info_client: Info) -> None:
"""Test fetching user state."""
# This will use the test account configured in the fixture
result = await info_client.user_state()
assert result.is_ok()
response = result.unwrap()
assert "marginSummary" in response
assert "crossMarginSummary" in response
Error Testing¤
Create tests for common error scenarios:
async def test_invalid_asset(info_client: Info) -> None:
"""Test error handling for invalid asset."""
result = await info_client.l2_book(asset="INVALID")
assert result.is_err()
error = result.unwrap_err()
assert "not found" in str(error).lower()
Running Tests¤
# Run all tests (replay mode if fixtures exist)
uv run pytest
# Run all tests in capture mode
uv run --env-file=.env pytest
# Run specific test file
uv run pytest tests/test_info.py
# Run specific test method
uv run pytest tests/test_info.py::test_all_mids
# Run with verbose output
uv run pytest -v
# Clear cache and run fresh
uv run pytest --cache-clear
Test Coverage¤
Aim for comprehensive test coverage:
- Happy path: At least one test for every public method
- Error cases: Common error scenarios (invalid inputs, network errors)
- Edge cases: Boundary conditions and special parameters
- Integration: End-to-end workflows combining multiple methods
Code Quality¤
Type Safety¤
The SDK is fully typed using Python's type system:
- All public methods have complete type annotations
- Use
Result[T, ApiError]
for all API calls to handle errors explicitly - Leverage the
hl.types
module for request/response types
Error Handling¤
Follow the Result pattern consistently:
# Good: Explicit error handling
result = await info.all_mids()
if result.is_err():
print(f"Error: {result.unwrap_err()}")
return
mids = result.unwrap()
# Bad: Assuming success
mids = await info.all_mids() # This won't work!
Documentation¤
- Add docstrings to all public methods
- Include parameter descriptions and examples
- Document any non-obvious behavior or limitations
Pre-commit Hooks¤
The project uses pre-commit hooks to ensure code quality and consistency. These hooks automatically run before each commit to catch issues early.
Installation¤
Then install the hooks for this repository:
# Install the pre-commit hooks
uv run pre-commit install
Configured Hooks¤
The project includes the following pre-commit hooks:
- mypy: Type checking with strict mode enabled
- ruff-check: Linting to catch code quality issues
- ruff-format: Code formatting for consistent style
- pytest: Running the test suite to ensure functionality
Running Pre-commit¤
Pre-commit hooks will automatically run when you make a commit. However, you can also run them manually:
# Run all hooks on all files
uv run pre-commit run --all-files
# Run all hooks on staged files only
uv run pre-commit run
Manual Quality Checks¤
You can also run the quality checks manually using uv:
# Type checking
uv run mypy hl tests
# Linting
uv run ruff check .
# Code formatting
uv run ruff format .
# Tests
uv run pytest --tb=short -v
Fixing Issues¤
If pre-commit hooks fail:
- Formatting issues: Run
uv run ruff format .
to auto-fix formatting - Linting issues: Run
uv run ruff check . --fix
to auto-fix where possible - Type issues: Address mypy errors by adding proper type annotations
- Test failures: Fix the failing tests before committing
Bypassing Pre-commit (Not Recommended)¤
In rare cases, you may need to bypass pre-commit hooks:
# Skip pre-commit hooks (use sparingly)
git commit --no-verify -m "your commit message"
Note: Only bypass hooks when absolutely necessary, as they help maintain code quality and prevent issues from reaching the main branch.
Undocumented Methods¤
We implement some API methods which are not documented in Hyperliquid's official API docs. This enables more complete functionality and better integration with other documented methods.
These methods are reverse-engineered from: - Hyperliquid's frontend application - The official Python reference SDK - Discord community discussions
Method Name | Status | Source | Description | Notes |
---|---|---|---|---|
transfer_usd |
✅ | Reference SDK | Transfer USDC between spot/perp wallets | Previously usd_class_transfer |
set_referrer |
✅ | Reference SDK | Set referrer code for fee discounts | One-time operation |
transfer_account_funds |
✅ | Reference SDK | Transfer between main/sub accounts | Previously sub_account_transfer |
vault_usd_transfer |
❌ | Reference SDK | Transfer USDC to/from vaults | Planned implementation |
vault_summaries |
❌ | Discord | Get vault performance summaries | Community requested |
vault_details |
✅ | Discord | Get detailed vault information | Recently implemented |
convert_to_multi_sig_user |
❌ | Reference SDK | Convert account to multi-signature | Advanced feature |
multi_sig |
❌ | Reference SDK | Multi-signature operations | Advanced feature |
Deprecated: user_spot_transfer
has been replaced by transfer_usd
following the API's migration from spotUser
to usdClassTransfer
.
Contributing Undocumented Methods¤
When implementing undocumented methods:
- Verify the source: Ensure the method exists in the reference implementation or has community validation
- Add comprehensive tests: Since these aren't officially documented, thorough testing is critical
- Document limitations: Note any known issues or incomplete functionality
Development Workflow¤
- Create feature branch:
git checkout -b feature/your-feature-name
- Implement changes: Follow the existing patterns and add comprehensive tests
- Run tests: Ensure all tests pass with
uv run pytest
- Update documentation: Add/update docstrings and examples
- Submit PR: Create a pull request with clear description of changes
Getting Help¤
- API Documentation: Hyperliquid Docs
- GitHub Issues: Report bugs or request features via GitHub issues
- Discord Community: Join Papi's Pit for real-time support and community discussions