Signing
Every exchange action on Hyperliquid requires an EIP-712 signature. ExchangeClient handles this automatically. The following functions are for custom integrations or actions not yet supported by ExchangeClient.
How signing works
Hyperliquid has two signing flows depending on the action type:
Examples
Trading and position management
Fund movements and account security
EIP-712 domain
Exchange, chain ID 1337
HyperliquidSignTransaction, user's chain ID
What gets signed
Action hash as connectionId
Action fields directly
L1 action
The action is never signed directly. Instead, a phantom agent is constructed:
Msgpack-encode the action object (field order matters β the expected order varies by action type)
Append the nonce as uint64 big-endian (8 bytes)
Append a vault marker:
0x01+ 20-byte vault address, or0x00if noneIf
expiresAfteris set, append0x00+ the timestamp as uint64 big-endianKeccak-256 hash the concatenated bytes. This is the
connectionIdSign an EIP-712 message with:
Domain:
{ name: "Exchange", version: "1", chainId: 1337, verifyingContract: 0x0...0 }Type:
Agent { source: string, connectionId: bytes32 }Message:
{ source: "a" (mainnet) or "b" (testnet), connectionId }whereconnectionIdis the hash from step 5
Send
{ action, signature: { r, s, v }, nonce }to the exchange endpoint
Chain ID 1337 is hardcoded and doesn't depend on the wallet's network. The phantom agent construct means the validator recovers the signer from the Agent message, then verifies that the connectionId matches the action hash.
User-signed action
The action fields are placed directly into the EIP-712 message, with no hashing or phantom agent:
Each action type defines its own typed data structure (for example,
HyperliquidTransaction:ApproveAgent)Sign an EIP-712 message with:
Domain:
{ name: "HyperliquidSignTransaction", version: "1", chainId: <signatureChainId>, verifyingContract: 0x0...0 }Type and message: defined per action
Send
{ action, signature: { r, s, v }, nonce }to the exchange endpoint
The signatureChainId field in the action (hex, such as "0x66eee") sets the EIP-712 domain chain ID.
Common rules
Both flows share:
Nonce: current timestamp in milliseconds. Hyperliquid stores the 100 highest nonces per signer and rejects duplicates. See Nonces.
Signature: ECDSA
{ r, s, v }wherevis27or28Hex strings: lowercase all hex values before signing
The following functions implement these flows. Each accepts any compatible wallet.
L1 actions
signL1Action signs an L1 action and returns an ECDSA signature.
Use isTestnet: true when signing for the testnet β this changes the EIP-712 source from "a" to "b".
User-signed actions
signUserSignedAction signs a user-signed action and returns an ECDSA signature.
Each action type has its own EIP-712 types, exported from @nktkas/hyperliquid/api/exchange using the convention PascalCase(actionType) + "Types" (for example, Withdraw3Types for withdraw3, ApproveAgentTypes for approveAgent).
Action hashing
createL1ActionHash produces the keccak256 hash used as connectionId in L1 signing. Use it to verify that your action serialization matches what the SDK produces:
The hash depends on key order in the action object. The expected order varies by action type.
Optional parameters vaultAddress and expiresAfter are included in the hash when present.
Multi-sig actions
signMultiSigAction signs the multi-sig wrapper after all signers have signed the inner action. The inner signing differs for L1 and user-signed actions. Use isTestnet: true when signing for the testnet, as with signL1Action.
L1 actions
Each signer signs the [multiSigUser, outerSigner, action] tuple via signL1Action. The leader (first signer) then signs the wrapper via signMultiSigAction:
User-signed
Each signer signs the action with embedded payloadMultiSigUser and outerSigner fields via signUserSignedAction. The leader then signs the wrapper via signMultiSigAction:
Wallet compatibility
All signing functions accept AbstractWallet β a union of supported wallet interfaces:
signTypedData
Address
Chain ID
Any object matching one of these interfaces works. See the Custom tab in signing examples.
Helpers
These functions work with any supported wallet type:
getWalletAddressβ returns the wallet address, always lowercasegetWalletChainIdβ returns the wallet chain ID as hex, falls back to"0x1"for local wallets without a provider
Last updated