All articles
Hyperliquid·May 29, 2026·6 min read

Hyperliquid Sub-Accounts & Agent Wallets for Bot Security

A practical guide to using a Hyperliquid agent wallet and sub-accounts to isolate strategies and keep your master key cold on a live production bot.

A leaked API key on most exchanges means someone can withdraw your funds. On Hyperliquid, the equivalent mistake is running your bot with the private key that owns the account — and the good news is you almost never have to. Hyperliquid gives you two primitives that, used together, let a production bot sign thousands of orders a day without ever touching the key that controls your money: agent wallets (API wallets) and sub-accounts. Get the wiring right once and a full server compromise costs you open positions, not your balance.

I've deployed this pattern across market-making and perps desks, and the details matter. Here's the architecture I actually ship.

What an agent wallet can and can't do

An agent wallet is a separate keypair that you approve to trade on behalf of a master account. You generate it locally, send an approveAgent action signed by the master key, and from then on the agent key signs orders. The critical property: an agent wallet can place and cancel orders, but it cannot withdraw funds, cannot transfer between accounts, and cannot approve other agents. Those actions require the master signature.

So the threat model shifts. If your trading server is popped and the attacker exfiltrates the agent key, they can open and close positions — annoying, potentially costly if they cross the spread on purpose — but they cannot move a single USDC off the account. Your master key never lived on that box.

Two more mechanics worth burning into memory:

  • Approved agents expire. When you approve one you can set a validity window; unnamed agents default to roughly 180 days. Name your agents (the approveAgent action takes a name) so you can see and revoke them individually.
  • You can have multiple named agents per account. This is the hook for per-strategy isolation, which I'll get to.

If you're still deciding on your signing stack, the Python vs. Rust breakdown in our API bot guide covers how the SDKs handle agent approval and nonce management, which is where most people trip on the first day.

Sub-accounts: separate books, one master

A sub-account is a distinct trading account with its own balance, positions, and margin, all owned by your master address. You create them with a createSubAccount action, fund them by transferring USDC from the master, and each one shows up under a derived address.

The reason this matters for a bot: positions in one sub-account don't touch the margin of another. If your liquidation-hunting strategy blows through its allocation and gets liquidated, your market-making inventory in a different sub-account is untouched. You're partitioning risk at the protocol level instead of trusting your own code to not cross-contaminate.

The combination is what you want:

master key (cold, hardware wallet / offline signer)
│
├── funds & controls everything, signs rarely
│
├── sub-account: MM        ← agent key A (hot, on MM server)
├── sub-account: funding   ← agent key B (hot, on arb server)
└── sub-account: liq-hunt  ← agent key C (hot, on liq server)

Each strategy runs on its own box with its own hot agent key, scoped to its own sub-account. A compromise of the funding-arb server leaks agent key B, which can trade the funding sub-account and nothing else. The blast radius is one book.

This maps cleanly onto how we structure client deployments for a Hyperliquid market maker versus a funding-rate arbitrage bot — different risk profiles, different sub-accounts, different keys, so an incident in one strategy never cascades into another.

The approval flow, concretely

Here's the shape of it with the Python SDK. The master signs once to bring an agent online; after that the agent object signs orders.

from hyperliquid.exchange import Exchange
from eth_account import Account

# master key — used ONCE here, then goes back in cold storage
master = Account.from_key(MASTER_PRIVKEY)
exch = Exchange(master, base_url=MAINNET)

# generate a fresh agent keypair locally
agent_account, agent_key = exch.approve_agent(name="mm-server-1")
# persist agent_key to the MM server's secret store — NOT the master key

# on the trading server, sign orders with the agent:
agent = Account.from_key(agent_key)
bot = Exchange(agent, base_url=MAINNET, account_address=MASTER_ADDRESS)
bot.order("ETH", is_buy=True, sz=0.1, limit_px=3000, order_type={"limit": {"tif": "Gtc"}})

Note account_address=MASTER_ADDRESS on the trading Exchange. The agent signs, but orders act on the master (or sub-account) address. Get this wrong and you'll see confusing "insufficient balance" errors because you're trading an empty agent address.

For sub-account trading you point the same pattern at the sub-account address and fund it via sub_account_transfer from the master. One gotcha: the nonce/rate-limit budget is tied to the address you're acting as. Hammer one sub-account with a tight quoting loop and you can starve your own order throughput there without affecting the others — which, honestly, is another argument for splitting high-frequency strategies off on their own.

Operational rules I don't break

A few hard-won conventions:

  1. The master key signs from an offline machine, never the server. Approving an agent, creating a sub-account, and periodic withdrawals are batch operations you can do from a laptop with a hardware wallet. Your CI/CD and your running bots never see it.
  2. One agent per host, named after the host. When something looks wrong at 3am, "revoke mm-server-1" is a clean, targeted action. A shared agent across five servers is a shared blast radius.
  3. Rotate on a schedule, not just on incident. Because agents expire anyway, bake re-approval into a monthly job. If you're rotating monthly, a stolen key has a short shelf life even if you never notice the theft.
  4. Keep a kill switch on the master, not the agent. Position-flattening logic that yanks everything to flat should be a master-signed script you can run from cold storage, independent of whatever state the compromised bot is in.
  5. Withdrawals go through a separate, reviewed path. Since agents can't withdraw, there's no reason your trading code even imports withdrawal logic. Keep it in a different repo with its own approvals.

There's a subtlety for delta-neutral desks: if you run the funding-rate arbitrage across a CEX and Hyperliquid, your CEX side has its own key hygiene problem that agent wallets don't solve. Treat the two legs as separate security domains and don't let one server hold credentials for both.

Where this leaves your dashboard

One consequence of splitting into sub-accounts: your PnL is now fragmented across several addresses. The Hyperliquid UI shows sub-accounts, but for a real desk you'll want an aggregated view that rolls up positions, margin usage, and per-strategy PnL into one place — which is exactly the problem a purpose-built trading dashboard solves. If you're allocating capital to the HLP vault alongside your own strategies, fold that position into the same view so you're looking at true net exposure, not three separate screens.

None of this is exotic. It's the same principle as running services under least-privilege users, applied to a signing key: the thing that touches the internet all day should be able to do the least amount of damage possible. Agent wallets and sub-accounts give you the primitives; the discipline is in never, ever putting the master key where the bot can reach it.

If you'd rather have this partitioned architecture built and audited from the start, that's the default posture we ship on every Hyperliquid perps bot we deploy.

Need a bot like this built?

We design, build and run trading bots on Solana, Hyperliquid and Polymarket.

Start a project
#Hyperliquid#agent wallet#bot security#sub-accounts#key management