vault_usd_transfer, sub_account_transfer, or sub_account_spot_transfer fails with a misleading User or API Wallet ... does not exist error, a reported cause is an EIP-55 checksummed address being signed in a form the node does not expect.
A public GitHub issue reports that the node uses the lowercase address form when computing the L1 action hash, while the Python SDK signs the address exactly as passed. Passing a checksummed address can therefore produce a signature that the node rejects.
Reported issue
Issue #339, opened on Nov 12, 2026 and still open as of Nov 15, 2026, reports the problem inexchange.py:
vault_usd_transfersub_account_transfersub_account_spot_transfer
spot_deploy_user_genesis and spot_deploy_freeze_user, which do call .lower() on addresses.
The issue author also reports that eth_account returns checksummed addresses by default, so a natural call such as ex.vault_usd_transfer(checksummed_vault, ...) may sign a checksummed vault address. The node then rejects the signature and returns a confusing error that mentions a garbage address.
Reported testnet observations
The issue describes testnet tests against api.hyperliquid-testnet.xyz, using the node's signer-recovery error as an oracle. The recovered address in the error indicates whether the signature verifies.
| Test | Recovered address | Reported verdict |
|---|---|---|
subAccountTransfer, user address lowercase | master address | Signature verifies |
subAccountTransfer, user address checksummed | garbage (0x65a0d3b3...) | Signature fails |
vaultTransfer, vault address checksummed (0xdFc24B077bc1425A03F8D9810F644F1CBFDfD5e5) | garbage (0x196ec5a6...) | Signature fails |
vaultTransfer and subAccountTransfer: the node hashes the lowercase form, so a checksummed address in the signed payload never verifies. The reported impact is that the call fails and no funds move.
Amount check for a rejected transfer
Because the issue reports that the rejected call does not move funds, the amount moved is 0.
For an attempted transfer of 1,000 USDC:
intended amount - amount moved = 1,000 - 0 = 1,000 USDC
That means 1,000 USDC remains untransferred if the signature is rejected.
Like what you're reading? Try it yourself โ this link supports ChartedTrader at no cost to you.
Create a Hyperliquid account โReference workaround
The suggested fix in the issue is to lowercase the address arguments before signing, matching the .lower() calls already used in the spot deploy methods.
A reference workaround is:
# Lowercase address arguments before passing them to the SDK.
vault_address = "0x...".lower()
user_address = "0x...".lower()
# Use the actual SDK method signature in your version.
# ex.vault_usd_transfer(vault_address, ...)
# ex.sub_account_transfer(user_address, ...)
# ex.sub_account_spot_transfer(user_address, ...)
This is a user-reported workaround, not an official confirmed SDK fix. As of Nov 15, 2026, the issue remains open.
Vault context
Vault deposits and withdrawals can also be performed from the vault page in the Hyperliquid UI. According to the Hyperliquid vaults documentation, HLP has a lock-up period of 4 days, while user vaults have a lock-up period of 1 day.
For a broader SDK walkthrough, see Hyperliquid Python SDK Tutorial: Build a Bot. For vault lock-up timing, see Hyperliquid Vault Lockup & Withdrawal Schedule.
Sources
- Hyperliquid Python SDK issue #339: vault_usd_transfer / sub_account_transfer signatures rejected when addresses are checksummed
- Hyperliquid docs: For vault depositors (legacy)
Continue with Hyperliquid
Browse the Hyperliquid guide hub for the complete user journey.