Skip to main content
Trading on Hyperliquid uses a two-part signing scheme. Two different keys sign two different classes of action, and understanding the split is the key to understanding why trades don’t trigger a wallet popup.
  • The user’s L1 wallet — any EVM-compatible signer the user connects. It owns the funds.
  • An SDK-managed agent — a separate keypair the SDK creates and the user authorizes once.
The user approves the agent a single time; from then on the agent signs every trade, so there is no wallet prompt per order. Provisioning the agent is part of the one-time Setup flow — this page explains what the two signatures are and why, not the step-by-step API (which Setup covers).

The L1 (user) wallet signature

This is a signature from the user’s own EVM-compatible signer — MetaMask, a smart-contract wallet, a backend signer, anything that can produce an EVM signature. It is the key that ultimately owns the account and its balance. You hand it to the SDK via userWallet at construction (createPerpsClient) or later via setUserWallet(). These user-level messages are signed as EIP-712 typed data with the signature chain set to Arbitrum — Hyperliquid’s L1 signature chain. The signer is just an EVM key; it is not tied to Ethereum the chain, protocol, or asset. On Hyperliquid the L1 wallet is asked to sign only the few actions the protocol requires the account owner to authorize directly:
  • APPROVE_AGENT — authorizes the agent keypair (the one-time bootstrap that makes popup-free trading possible).
  • APPROVE_BUILDER_FEE — authorizes LI.FI’s per-trade builder fee.
  • WITHDRAWAL — moves funds off Hyperliquid. Agents cannot withdraw.
  • SEND_ASSET — transfers balance between sub-accounts / venues. Agents cannot move assets.
These all either delegate authority or move real funds, so the protocol requires the account owner’s own signature for them — they are mandatory and cannot be delegated to the agent.

The agent signature

An agent (Hyperliquid also calls it an “API wallet” or “approved wallet”) is a key the account owner has authorized to place orders on its behalf — but which cannot withdraw or move funds. That asymmetry is what makes it safe to keep an agent key on the client: the worst it can do is trade. In @lifi/perps-sdk the agent is a standard secp256k1 keypair (the same curve EVM wallets use), generated and held by the SDK — distinct from the user’s wallet. It signs every trading action as EIP-712 typed data with no wallet prompt: placeOrder, placeTriggerOrder, cancelOrders, modifyOrders, updateLeverage, and updatePositionMargin.

How the agent is created

The SDK generates the agent keypair client-side, the first time an agent is needed (during the APPROVE_AGENT setup step). The agent’s public address is what the user’s wallet signs to approve; the private key never leaves the client. The LI.FI backend and Hyperliquid only ever see the agent’s address, never its private key.

How the agent is stored

The keypair is persisted through a pluggable StorageAdapter. The default is the browser’s localStorage; pass your own adapter (via the provider/client config) for non-browser environments such as a server or a secure enclave.
  • Namespace: lifi-perps-agent:<l1-address>:hyperliquid — one agent per L1 address per provider.
  • Scope: client-side only. The agent private key is never transmitted to the LI.FI backend.

Expiry and re-provisioning

An agent approval can lapse (Hyperliquid policy, or an optional TTL set at approval time), and a user on a new device won’t have the stored key. When that happens, checkSetup() re-surfaces APPROVE_AGENT and the user re-approves once. Agents are not silently re-provisioned mid-trade — a trading call with no valid agent fails and points you back through Setup, so a re-approval is always an explicit, user-visible step.

Who signs what

ActionSigner
placeOrder, placeTriggerOrder, cancelOrders, modifyOrders, updateLeverage, updatePositionMarginSDK-managed agent (no popup)
APPROVE_AGENT, APPROVE_BUILDER_FEEUser’s L1 wallet (one-time setup)
WITHDRAWAL, SEND_ASSETUser’s L1 wallet (always — agents cannot move funds)
Approved agents appear in the account’s config.agents array — see Account Abstraction / Account Configuration.