# SDK-006 — HL Wallet Validation

**Status:** Complete
**Date:** 2026-03-10
**Branch:** `sdk-006-hl-wallet-validation` (off `main`)
**Location:** `hyperscaled-sdk/`

---

## Summary

Implemented Hyperliquid wallet validation end-to-end for the current SDK and CLI surfaces:

- one shared strict HL/EVM-style wallet validator using `^0x[a-fA-F0-9]{40}$`
- `client.account.validate_wallet(address) -> bool`
- reuse of that same helper by config validation
- working `hyperscaled account setup <wallet>`
- registration CLI preflight validation before later purchase/submit logic
- focused tests for valid, invalid, mixed-case, malformed, empty, and CLI failure-path inputs

---

## What Was Built

### New Files

```
hyperscaled-sdk/
├── planning/
│   └── reports/
│       └── SDK-006.md         # This report
└── tests/
    ├── test_account.py        # SDK-006 wallet validation + account CLI tests
    └── test_register.py       # SDK-006 register CLI validation tests
```

### Modified Files

| File | Change |
|------|--------|
| `hyperscaled/sdk/config.py` | Promoted the wallet regex helper into shared `is_valid_hl_address()` and reused it for config validation |
| `hyperscaled/sdk/account.py` | Replaced stub with `AccountClient` and `validate_wallet(address)` |
| `hyperscaled/sdk/client.py` | Wired `client.account` as a real lazy-loaded sub-client |
| `hyperscaled/cli/account.py` | Implemented `account setup <wallet_address>` with fail-fast validation and config persistence |
| `hyperscaled/cli/register.py` | Implemented registration wallet-validation preflight for `hyperscaled register --miner ... --size ...` and `register purchase` |
| `tests/test_client.py` | Updated lazy sub-client coverage now that `client.account` is implemented |
| `tests/test_account.py` | Added CLI tests for successful wallet setup and invalid-input rejection |
| `tests/test_register.py` | Added CLI tests for invalid overrides, missing configured wallet, and valid registration preflight paths |

---

## Details

### 1. Shared wallet validation helper

The repo already had the correct strict wallet format rule in the config layer. `SDK-006` now exposes that rule as a reusable helper:

```python
def is_valid_hl_address(value: str) -> bool:
    return bool(_HL_ADDRESS_RE.match(value))
```

This keeps validation behavior centralized and ensures the SDK and config layer use the exact same rule.

### 2. `AccountClient.validate_wallet()`

Added the public SDK surface reserved in the sprint doc:

```python
class AccountClient:
    def validate_wallet(self, address: str) -> bool:
        return is_valid_hl_address(address)
```

The method is intentionally pure and local:

- no backend round-trip
- no checksum logic
- no trimming or normalization
- returns only `True` / `False`

This matches the sprint guidance that user-facing CLI flows should be responsible for turning failed validation into errors.

### 3. `client.account` wiring

`HyperscaledClient.account` is now a real lazy-loaded sub-client instead of a stubbed `NotImplementedError`.

That means the planned public API now works:

```python
from hyperscaled import HyperscaledClient

client = HyperscaledClient()
client.account.validate_wallet("0x...")
```

### 4. Test coverage

Added dedicated tests covering:

- valid lowercase addresses
- valid mixed-case addresses
- malformed addresses
- missing `0x` prefix
- invalid hex characters
- empty strings
- leading/trailing whitespace rejection

Also updated client tests so `client.account` is verified as lazy-loaded and cacheable.

### 5. CLI wiring

Implemented the two missing CLI validation surfaces called out by the sprint ticket.

`hyperscaled account setup <wallet_address>` now:

- validates via `client.account.validate_wallet()`
- fails fast with a clear error and non-zero exit code on invalid input
- writes `wallet.hl_address` only after validation succeeds

`hyperscaled register --miner ... --size ...` now:

- accepts an optional `--hl-wallet` override
- otherwise uses the configured `wallet.hl_address`
- rejects missing wallet configuration before any later purchase/submit logic
- rejects invalid wallet input before any later purchase/submit logic

The legacy `register purchase` shape also uses the same validation gate so there is no duplicate regex logic between entry points.

---

## Acceptance Criteria Status

| Criterion | Status |
|-----------|--------|
| `client.account.validate_wallet(address)` returns `True`/`False` with no network dependency | Pass |
| Invalid HL wallet addresses are rejected by `config set wallet.hl_address` | Pass |
| Invalid HL wallet addresses are rejected in the registration CLI flow before payment or submission | Pass |
| SDK and CLI use one shared validation implementation, not duplicated regexes | Pass |
| Unit tests cover valid, invalid, mixed-case, malformed, and empty-string inputs | Pass |

---

## Verification

Verification was run in the repo virtual environment at `hyperscaled-sdk/.venv`.

```bash
source .venv/bin/activate
ruff format hyperscaled/sdk/account.py hyperscaled/sdk/client.py hyperscaled/sdk/config.py hyperscaled/cli/account.py hyperscaled/cli/register.py tests/test_account.py tests/test_register.py
ruff check hyperscaled/sdk/account.py hyperscaled/sdk/client.py hyperscaled/sdk/config.py hyperscaled/cli/account.py hyperscaled/cli/register.py tests/test_account.py tests/test_register.py
pytest -q tests/test_account.py tests/test_register.py tests/test_client.py tests/test_config.py
```

### Results

```
ruff format ...   → 7 files left unchanged
ruff check ...    → All checks passed!
pytest -q ...     → 68 passed, 1 warning in 0.96s
```

### Warning observed

`pytest` still reports one existing runtime warning in `tests/test_client.py`:

- `TestSyncHelpers.test_run_sync_raises_in_running_loop`
- warning: `coroutine 'sleep' was never awaited`

This warning was not introduced by the `SDK-006` changes.

---

## What's Next

`SDK-006` now cleanly unblocks:

- `SDK-007` — HL account setup & balance check
- `SDK-008` — funded account purchase and registration flow
