Unofficial Hyperliquid API SDK for all major JS runtimes, written in TypeScript.
- 🖋️ Typed: Source code is 100% TypeScript.
- 🧪 Tested: Good code coverage and type relevance.
- 📦 Minimal dependencies: A few small trusted dependencies.
- 🌐 Cross-Environment Support: Compatible with all major JS runtimes.
- 🔧 Integratable: Easy to use with wallet providers (viem or ethers).
- 📚 Documented: JSDoc annotations with usage examples.
- 🌲 Tree-shaking friendly: Easily import only the API calls you use for minimal bundle size.
Note
While this library is in TypeScript, it can also be used in JavaScript and supports ESM/CommonJS.
npm i @nktkas/hyperliquid
pnpm add @nktkas/hyperliquid
yarn add @nktkas/hyperliquid
If you are using a version of Node.js lower than v22 and intend to use WebSocketTransport
,
you need to install the ws
package and pass the WebSocket
class to the
constructor:
import WebSocket from "ws"; // install `ws` package
import * as hl from "@nktkas/hyperliquid";
const transport = new hl.WebSocketTransport({
reconnect: {
WebSocket, // pass `WebSocket` class from `ws` package
},
});
deno add jsr:@nktkas/hyperliquid
The SDK is fully browser-compatible; integrate it via CDN or bundle it with your application.
<script type="module">
import * as hl from "https://esm.sh/jsr/@nktkas/hyperliquid";
</script>
For React Native, you need to import polyfills before importing the SDK:
import "fast-text-encoding"; // `TextEncoder` (utf-8)
import "event-target-polyfill"; // `EventTarget`, `Event`
import * as hl from "@nktkas/hyperliquid";
The SDK includes a command-line interface for quick interactions with Hyperliquid API without writing code.
npx @nktkas/hyperliquid --help
// 1. Import module
import * as hl from "@nktkas/hyperliquid";
// 2. Set up client with transport
const infoClient = new hl.InfoClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
});
// 3. Query data
const openOrders = await infoClient.openOrders({ user: "0x..." });
// 1. Import module
import * as hl from "@nktkas/hyperliquid";
// 2. Set up client with wallet and transport
const exchClient = new hl.ExchangeClient({
wallet: "0x...", // `viem`, `ethers`, or private key directly
transport: new hl.HttpTransport(), // or `WebSocketTransport`
});
// 3. Execute an action
const result = await exchClient.order({
orders: [{
a: 0,
b: true,
p: "30000",
s: "0.1",
r: false,
t: {
limit: {
tif: "Gtc",
},
},
}],
grouping: "na",
});
// 1. Import module
import * as hl from "@nktkas/hyperliquid";
// 2. Set up client with transport
const subsClient = new hl.SubscriptionClient({
transport: new hl.WebSocketTransport(),
});
// 3. Subscribe to events
const sub = await subsClient.allMids((event) => {
console.log(event);
});
await sub.unsubscribe();
// 1. Import module
import * as hl from "@nktkas/hyperliquid";
// 2. Set up client with transport, multi-sign address, and signers
const multiSignClient = new hl.MultiSignClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
multiSigUser: "0x...",
signers: [
"0x...", // `viem`, `ethers`, or private key directly
// ... (more signers if needed)
],
});
// 3. Execute an action (same as `ExchangeClient`)
await multiSignClient.approveAgent({ agentAddress: "0x..." });
First, choose and configure your transport layer (more details in the Transports):
import * as hl from "@nktkas/hyperliquid";
// 1. HTTP Transport: suitable for one-time requests or serverless environments
const httpTransport = new hl.HttpTransport({ ... }); // Accepts optional parameters (e.g. isTestnet, timeout, etc.)
// 2. WebSocket Transport: has better network latency than HTTP transport
const wsTransport = new hl.WebSocketTransport({ ... }); // Accepts optional parameters (e.g. isTestnet, timeout, reconnect, etc.)
Next, initialize a client with the transport layer (more details in the Clients):
Create InfoClient
import * as hl from "@nktkas/hyperliquid";
const infoClient = new hl.InfoClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
});
Create ExchangeClient
import * as hl from "@nktkas/hyperliquid";
import { createWalletClient, custom } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { ethers } from "ethers";
const transport = new hl.HttpTransport(); // or `WebSocketTransport`
// 1. Using private key directly
const privateKey = "0x...";
const exchClient = new hl.ExchangeClient({ wallet: privateKey, transport });
// 2. Using Viem
const viemAccount = privateKeyToAccount("0x...");
const exchClient = new hl.ExchangeClient({ wallet: viemAccount, transport });
// 3. Using Ethers
const ethersWallet = new ethers.Wallet("0x...");
const exchClient = new hl.ExchangeClient({ wallet: ethersWallet, transport });
// 4. Using external wallet (e.g. MetaMask) via Viem
const [account] = await window.ethereum.request({ method: "eth_requestAccounts" }) as `0x${string}`[];
const externalWallet = createWalletClient({ account, transport: custom(window.ethereum) });
const exchClient = new hl.ExchangeClient({ wallet: externalWallet, transport });
Create SubscriptionClient
import * as hl from "@nktkas/hyperliquid";
const subsClient = new hl.SubscriptionClient({
transport: new hl.WebSocketTransport(),
});
Create MultiSignClient
import * as hl from "@nktkas/hyperliquid";
import { privateKeyToAccount } from "viem/accounts";
import { ethers } from "ethers";
const multiSignClient = new hl.MultiSignClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
multiSigUser: "0x...",
signers: [
privateKeyToAccount("0x..."), // leader (first signer), signs the transaction twice
new ethers.Wallet("0x..."),
{ // can be a custom async wallet
async signTypedData(params: {
domain: {
name: string;
version: string;
chainId: number;
verifyingContract: Hex;
};
types: {
[key: string]: {
name: string;
type: string;
}[];
};
primaryType: string;
message: Record<string, unknown>;
}): Promise<Hex> {
// Custom signer logic
return "0x..."; // return hex signature
},
},
"0x...", // private key directly
// ... more signers
],
});
Finally, use client methods to interact with the Hyperliquid API (more details in the API Reference):
Example of using an InfoClient
import * as hl from "@nktkas/hyperliquid";
const infoClient = new hl.InfoClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
});
// L2 Book
const l2Book = await infoClient.l2Book({ coin: "ETH" });
// User clearinghouse state
const clearinghouseState = await infoClient.clearinghouseState({ user: "0x..." });
// User open orders
const openOrders = await infoClient.openOrders({ user: "0x..." });
Example of using an ExchangeClient
import * as hl from "@nktkas/hyperliquid";
const exchClient = new hl.ExchangeClient({
wallet: "0x...", // `viem`, `ethers`, or private key directly
transport: new hl.HttpTransport(), // or `WebSocketTransport`
});
// Place an order
const result = await exchClient.order({
orders: [{
a: 0,
b: true,
p: "30000",
s: "0.1",
r: false,
t: {
limit: {
tif: "Gtc",
},
},
}],
grouping: "na",
});
// Approve an agent
const result = await exchClient.approveAgent({ agentAddress: "0x..." });
// Withdraw funds
const result = await exchClient.withdraw3({ destination: "0x...", amount: "100" });
Example of using a SubscriptionClient
import * as hl from "@nktkas/hyperliquid";
const subsClient = new hl.SubscriptionClient({
transport: new hl.WebSocketTransport(),
});
// L2 Book updates
await subsClient.l2Book({ coin: "ETH" }, (data) => {
console.log(data);
});
// User fills
await subsClient.userFills({ user: "0x..." }, (data) => {
console.log(data);
});
// Candle updates
await subsClient.candle({ coin: "ETH", interval: "1h" }, (data) => {
console.log(data);
});
Example of using a MultiSignClient
import * as hl from "@nktkas/hyperliquid";
const multiSignClient = new hl.MultiSignClient({
transport: new hl.HttpTransport(), // or `WebSocketTransport`
multiSigUser: "0x...",
signers: [
"0x...", // `viem`, `ethers`, or private key directly
// ... (more signers if needed)
],
});
// Interaction is the same as with `ExchangeClient`
A client is an interface through which you can interact with the Hyperliquid API.
class InfoClient {
constructor(args: {
transport: HttpTransport | WebSocketTransport;
});
// Market
allMids(params?: AllMidsParameters): Promise<AllMidsResponse>;
candleSnapshot(params: CandleSnapshotParameters): Promise<CandleSnapshotResponse>;
fundingHistory(params: FundingHistoryParameters): Promise<FundingHistoryResponse>;
l2Book(params: L2BookParameters): Promise<L2BookResponse>;
liquidatable(): Promise<LiquidatableResponse>;
marginTable(params: MarginTableParameters): Promise<MarginTableResponse>;
maxMarketOrderNtls(): Promise<MaxMarketOrderNtlsResponse>;
meta(params?: MetaParameters): Promise<MetaResponse>;
metaAndAssetCtxs(params?: MetaAndAssetCtxsParameters): Promise<MetaAndAssetCtxsResponse>;
perpDeployAuctionStatus(): Promise<PerpDeployAuctionStatusResponse>;
perpDexLimits(params: PerpDexLimitsParameters): Promise<PerpDexLimitsResponse>;
perpDexs(): Promise<PerpDexsResponse>;
perpsAtOpenInterestCap(params?: PerpsAtOpenInterestCapParameters): Promise<PerpsAtOpenInterestCapResponse>;
predictedFundings(): Promise<PredictedFundingsResponse>;
recentTrades(params: RecentTradesParameters): Promise<RecentTradesResponse>;
spotDeployState(params: SpotDeployStateParameters): Promise<SpotDeployStateResponse>;
spotMeta(): Promise<SpotMetaResponse>;
spotMetaAndAssetCtxs(): Promise<SpotMetaAndAssetCtxsResponse>;
spotPairDeployAuctionStatus(): Promise<SpotPairDeployAuctionStatusResponse>;
tokenDetails(params: TokenDetailsParameters): Promise<TokenDetailsResponse>;
// Account
activeAssetData(params: ActiveAssetDataParameters): Promise<ActiveAssetDataResponse>;
clearinghouseState(params: ClearinghouseStateParameters): Promise<ClearinghouseStateResponse>;
extraAgents(params: ExtraAgentsParameters): Promise<ExtraAgentsResponse>;
isVip(params: IsVipParameters): Promise<IsVipResponse>;
legalCheck(params: LegalCheckParameters): Promise<LegalCheckResponse>;
maxBuilderFee(params: MaxBuilderFeeParameters): Promise<MaxBuilderFeeResponse>;
portfolio(params: PortfolioParameters): Promise<PortfolioResponse>;
preTransferCheck(params: PreTransferCheckParameters): Promise<PreTransferCheckResponse>;
referral(params: ReferralParameters): Promise<ReferralResponse>;
spotClearinghouseState(params: SpotClearinghouseStateParameters): Promise<SpotClearinghouseStateResponse>;
subAccounts(params: SubAccountsParameters): Promise<SubAccountsResponse>;
userFees(params: UserFeesParameters): Promise<UserFeesResponse>;
userFunding(params: UserFundingParameters): Promise<UserFundingResponse>;
userNonFundingLedgerUpdates(
params: UserNonFundingLedgerUpdatesParameters,
): Promise<UserNonFundingLedgerUpdatesResponse>;
userRateLimit(params: UserRateLimitParameters): Promise<UserRateLimitResponse>;
userRole(params: UserRoleParameters): Promise<UserRoleResponse>;
userToMultiSigSigners(params: UserToMultiSigSignersParameters): Promise<UserToMultiSigSignersResponse>;
webData2(params: WebData2Parameters): Promise<WebData2Response>;
// Order
frontendOpenOrders(params: FrontendOpenOrdersParameters): Promise<FrontendOpenOrdersResponse>;
historicalOrders(params: HistoricalOrdersParameters): Promise<HistoricalOrdersResponse>;
openOrders(params: OpenOrdersParameters): Promise<OpenOrdersResponse>;
orderStatus(params: OrderStatusParameters): Promise<OrderStatusResponse>;
twapHistory(params: TwapHistoryParameters): Promise<TwapHistoryResponse>;
userFills(params: UserFillsParameters): Promise<UserFillsResponse>;
userFillsByTime(params: UserFillsByTimeParameters): Promise<UserFillsByTimeResponse>;
userTwapSliceFills(params: UserTwapSliceFillsParameters): Promise<UserTwapSliceFillsResponse>;
userTwapSliceFillsByTime(params: UserTwapSliceFillsByTimeParameters): Promise<UserTwapSliceFillsByTimeResponse>;
// Validator
gossipRootIps(): Promise<GossipRootIpsResponse>;
delegations(params: DelegationsParameters): Promise<DelegationsResponse>;
delegatorHistory(params: DelegatorHistoryParameters): Promise<DelegatorHistoryResponse>;
delegatorRewards(params: DelegatorRewardsParameters): Promise<DelegatorRewardsResponse>;
delegatorSummary(params: DelegatorSummaryParameters): Promise<DelegatorSummaryResponse>;
validatorL1Votes(): Promise<ValidatorL1VotesResponse>;
validatorSummaries(): Promise<ValidatorSummariesResponse>;
// Vault
leadingVaults(params: LeadingVaultsParameters): Promise<LeadingVaultsResponse>;
userVaultEquities(params: UserVaultEquitiesParameters): Promise<UserVaultEquitiesResponse>;
vaultDetails(params: VaultDetailsParameters): Promise<VaultDetailsResponse>;
vaultSummaries(): Promise<VaultSummariesResponse>;
// Server
exchangeStatus(): Promise<ExchangeStatusResponse>;
// Explorer (RPC endpoint)
blockDetails(params: BlockDetailsParameters): Promise<BlockDetailsResponse>;
txDetails(params: TxDetailsParameters): Promise<TxDetailsResponse>;
userDetails(params: UserDetailsParameters): Promise<UserDetailsResponse>;
}
class ExchangeClient {
constructor(args: {
transport: HttpTransport | WebSocketTransport;
wallet: AbstractWallet | Hex; // viem, ethers or private key directly
defaultVaultAddress?: Hex; // Vault address used by default if not provided in method call
signatureChainId?: Hex | (() => MaybePromise<Hex>); // Chain ID used for signing (default: get chain id from wallet otherwise `0x1`)
nonceManager?: () => MaybePromise<number>; // Function to get the next nonce (default: monotonically incrementing `Date.now()`)
});
// Order
batchModify(params: BatchModifyParameters): Promise<OrderResponseSuccess>;
cancel(params: CancelParameters): Promise<CancelResponseSuccess>;
cancelByCloid(params: CancelByCloidParameters): Promise<CancelResponseSuccess>;
modify(params: ModifyParameters): Promise<SuccessResponse>;
order(params: OrderParameters): Promise<OrderResponseSuccess>;
scheduleCancel(params?: ScheduleCancelParameters): Promise<SuccessResponse>;
twapCancel(params: TwapCancelParameters): Promise<TwapCancelResponseSuccess>;
twapOrder(params: TwapOrderParameters): Promise<TwapOrderResponseSuccess>;
updateIsolatedMargin(params: UpdateIsolatedMarginParameters): Promise<SuccessResponse>;
updateLeverage(params: UpdateLeverageParameters): Promise<SuccessResponse>;
// Account
approveAgent(params: ApproveAgentParameters): Promise<SuccessResponse>;
approveBuilderFee(params: ApproveBuilderFeeParameters): Promise<SuccessResponse>;
claimRewards(): Promise<SuccessResponse>;
createSubAccount(params: CreateSubAccountParameters): Promise<CreateSubAccountResponse>;
evmUserModify(params: EvmUserModifyParameters): Promise<SuccessResponse>;
noop(): Promise<SuccessResponse>;
registerReferrer(params: RegisterReferrerParameters): Promise<SuccessResponse>;
reserveRequestWeight(params: ReserveRequestWeightParameters): Promise<SuccessResponse>;
setDisplayName(params: SetDisplayNameParameters): Promise<SuccessResponse>;
setReferrer(params: SetReferrerParameters): Promise<SuccessResponse>;
subAccountModify(params: SubAccountModifyParameters): Promise<SuccessResponse>;
spotUser(params: SpotUserParameters): Promise<SuccessResponse>;
// Transfer
sendAsset(params: SendAssetParameters): Promise<SuccessResponse>;
spotSend(params: SpotSendParameters): Promise<SuccessResponse>;
subAccountSpotTransfer(params: SubAccountSpotTransferParameters): Promise<SuccessResponse>;
subAccountTransfer(params: SubAccountTransferParameters): Promise<SuccessResponse>;
usdClassTransfer(params: UsdClassTransferParameters): Promise<SuccessResponse>;
usdSend(params: UsdSendParameters): Promise<SuccessResponse>;
withdraw3(params: Withdraw3Parameters): Promise<SuccessResponse>;
// Staking
cDeposit(params: CDepositParameters): Promise<SuccessResponse>;
cWithdraw(params: CWithdrawParameters): Promise<SuccessResponse>;
tokenDelegate(params: TokenDelegateParameters): Promise<SuccessResponse>;
// Market
perpDeploy(params: PerpDeployParameters): Promise<SuccessResponse>;
spotDeploy(params: SpotDeployParameters): Promise<SuccessResponse>;
// Vault
createVault(params: CreateVaultParameters): Promise<CreateVaultResponse>;
vaultDistribute(params: VaultDistributeParameters): Promise<SuccessResponse>;
vaultModify(params: VaultModifyParameters): Promise<SuccessResponse>;
vaultTransfer(params: VaultTransferParameters): Promise<SuccessResponse>;
// Multi-Sign
convertToMultiSigUser(params: ConvertToMultiSigUserParameters): Promise<SuccessResponse>;
multiSig(params: MultiSigParameters): Promise<
| SuccessResponse
| CancelSuccessResponse
| CreateSubAccountResponse
| CreateVaultResponse
| OrderSuccessResponse
| TwapOrderSuccessResponse
| TwapCancelSuccessResponse
>;
// Validator
cSignerAction(params: CSignerActionParameters): Promise<SuccessResponse>;
cValidatorAction(params: CValidatorActionParameters): Promise<SuccessResponse>;
}
class SubscriptionClient {
constructor(args: {
transport: WebSocketTransport;
});
// Market
activeAssetCtx(params: WsActiveAssetCtxParameters, listener: (data: WsActiveAssetCtxEvent) => void);
allMids(params?: WsAllMidsParameters, listener: (data: WsAllMidsEvent) => void);
assetCtxs(params?: WsAssetCtxsParameters, listener: (data: WsAssetCtxsEvent) => void);
bbo(params: WsBboParameters, listener: (data: WsBboEvent) => void);
candle(params: WsCandleParameters, listener: (data: WsCandleEvent) => void);
l2Book(params: WsL2BookParameters, listener: (data: WsL2BookEvent) => void);
trades(params: WsTradesParameters, listener: (data: WsTradesEvent) => void);
// Account
activeAssetData(params: WsActiveAssetDataParameters, listener: (data: WsActiveAssetDataEvent) => void);
clearinghouseState(params: WsClearinghouseStateParameters, listener: (data: WsClearinghouseStateEvent) => void);
notification(params: WsNotificationParameters, listener: (data: WsNotificationEvent) => void);
userEvents(params: WsUserEventsParameters, listener: (data: WsUserEventsEvent) => void);
userFundings(params: WsUserFundingsParameters, listener: (data: WsUserFundingsEvent) => void);
userNonFundingLedgerUpdates(
params: WsUserNonFundingLedgerUpdatesParameters,
listener: (data: WsUserNonFundingLedgerUpdatesEvent) => void,
);
webData2(params: WsWebData2Parameters, listener: (data: WsWebData2Event) => void);
// Order
openOrders(params: WsOpenOrdersParameters, listener: (data: WsOpenOrdersEvent) => void);
orderUpdates(params: WsOrderUpdatesParameters, listener: (data: WsOrderUpdatesEvent) => void);
userFills(params: WsUserFillsParameters, listener: (data: WsUserFillsEvent) => void);
userTwapHistory(params: WsUserTwapHistoryParameters, listener: (data: WsUserTwapHistoryEvent) => void);
userTwapSliceFills(params: WsUserTwapSliceFillsParameters, listener: (data: WsUserTwapSliceFillsEvent) => void);
// Explorer (RPC endpoint)
explorerBlock(listener: (data: WsExplorerBlockEvent) => void);
explorerTxs(listener: (data: WsExplorerTxsEvent) => void);
}
class MultiSignClient extends ExchangeClient {
constructor(
args: {
// ... same params as in `ExchangeClient` except `wallet`
// and
multiSigUser: Hex;
signers: [
AbstractWallet | Hex, // leader (first signer), signs the transaction twice
...(AbstractWallet | Hex)[], // ... more signers
];
},
);
// Same methods as `ExchangeClient`
}
Transport is a layer between class/function requests and Hyperliquid servers.
Uses fetch for requests. Supports Info / Exchange / Explorer endpoints.
Features:
- Can change any settings in the created instance without creating a new instance (e.g. switch between mainnet/testnet).
- Intercepting and modifying requests/responses/errors using on* functions.
class HttpTransport {
constructor(options?: {
/** Whether to use testnet url (default: false) */
isTestnet?: boolean;
/** Request timeout in ms (default: 10_000) */
timeout?: number;
/** Custom server URLs */
server?: {
mainnet?: { api?: string | URL; rpc?: string | URL };
testnet?: { api?: string | URL; rpc?: string | URL };
};
/** Custom fetch options */
fetchOptions?: RequestInit;
/** Callback before request is sent */
onRequest?: (request: Request) => MaybePromise<Request | void | null | undefined>;
/** Callback after response is received */
onResponse?: (response: Response) => MaybePromise<Response | void | null | undefined>;
/** Callback on error during fetching */
onError?: (error: unknown) => MaybePromise<Error | void | null | undefined>;
});
}
Uses WebSocket for requests. Supports subscriptions and post requests.
Features:
- Automatically restores connection after loss and resubscribes to previous subscriptions.
- Smart keep alive (pings only when idle).
- Lazy initialization with message buffering during connection establishment.
Limitations:
- 1 instance = 1 immutable endpoint. Cannot mix api/explorer endpoints or mainnet/testnet in single connection. Need to create separate instances for different endpoints.
- Cannot send explorer post-requests, only explorer subscriptions.
class WebSocketTransport {
constructor(options?: {
/** Indicates this transport uses testnet endpoint (default: false) */
isTestnet?: boolean;
/**
* Custom WebSocket endpoint for API and Subscription requests.
* (default: `wss://api.hyperliquid.xyz` for `isTestnet` = false, `wss://api.testnet.hyperliquid.xyz` for `isTestnet` = true)
*/
url?: string | URL;
/** Timeout for requests in ms (default: 10_000) */
timeout?: number;
/** Interval between sending ping messages in ms (default: 30_000) */
keepAliveInterval?: number;
/** Reconnection policy configuration for closed connections */
reconnect?: {
/** Custom WebSocket constructor (default: global WebSocket) */
WebSocket?: new (url: string | URL) => WebSocket;
/** Maximum number of reconnection attempts (default: 3) */
maxRetries?: number;
/** Maximum time in ms to wait for a connection to open (default: 10_000) */
connectionTimeout?: number;
/** Delay before reconnection in ms (default: Exponential backoff (max 10s)) */
reconnectionDelay?: number | ((attempt: number) => number);
};
/** Enable automatic re-subscription to Hyperliquid subscription after reconnection (default: true) */
resubscribe?: boolean;
});
ready(signal?: AbortSignal): Promise<void>;
close(signal?: AbortSignal): Promise<void>;
}
All SDK errors extend from HyperliquidError
base class for unified error handling:
import { ApiRequestError, HyperliquidError, SchemaError, TransportError } from "@nktkas/hyperliquid";
try {
await exchClient.order({ ... });
} catch (error) {
if (error instanceof SchemaError) {
// Invalid data format (before sending request)
} else if (error instanceof ApiRequestError) {
// API returned error (e.g., insufficient funds)
} else if (error instanceof TransportError) {
// Network/connection failure (e.g., timeout)
} else if (error instanceof HyperliquidError) {
// Some other Hyperliquid SDK error
}
}
This module contains functions for generating Hyperliquid transaction signatures.
import { signL1Action } from "@nktkas/hyperliquid/signing";
import { CancelRequest, parser } from "@nktkas/hyperliquid/api/exchange";
import { privateKeyToAccount } from "viem/accounts";
const wallet = privateKeyToAccount("0x..."); // viem or ethers
const action = parser(CancelRequest.entries.action)({ // for correct signature generation
type: "cancel",
cancels: [
{ a: 0, o: 12345 },
],
});
const nonce = Date.now();
const signature = await signL1Action({ wallet, action, nonce });
// Send the signed action to the Hyperliquid API
const response = await fetch("https://api.hyperliquid.xyz/exchange", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action, signature, nonce }),
});
const body = await response.json();
import { signUserSignedAction } from "@nktkas/hyperliquid/signing";
import { ApproveAgentRequest, ApproveAgentTypes, parser } from "@nktkas/hyperliquid/api/exchange";
import { privateKeyToAccount } from "viem/accounts";
const wallet = privateKeyToAccount("0x..."); // viem or ethers
const action = parser(ApproveAgentRequest.entries.action)({ // for correct signature generation
type: "approveAgent",
signatureChainId: "0x66eee",
hyperliquidChain: "Mainnet",
agentAddress: "0x...",
agentName: "Agent",
nonce: Date.now(),
});
const signature = await signUserSignedAction({ wallet, action, types: ApproveAgentTypes });
// Send the signed action to the Hyperliquid API
const response = await fetch("https://api.hyperliquid.xyz/exchange", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action, signature, nonce: action.nonce }),
});
const body = await response.json();
This module contains raw functions for interacting with the Hyperliquid API and valibot schemas for API requests/responses.
You can use raw functions to maximize tree-shaking in your app. This is useful if you are pedantic about bundle size and want to only include the API calls you use.
import { HttpTransport } from "@nktkas/hyperliquid";
import { clearinghouseState } from "@nktkas/hyperliquid/api/info";
// ^^^^^^^^^^^^^^^^^^
// same name as in `InfoClient`
const transport = new HttpTransport(); // or `WebSocketTransport`
const data = await clearinghouseState(
{ transport }, // same params as in `InfoClient`
{ user: "0x..." },
);
import { HttpTransport } from "@nktkas/hyperliquid";
import { privateKeyToAccount } from "viem/accounts";
import { order } from "@nktkas/hyperliquid/api/exchange";
// ^^^^^
// same name as in `ExchangeClient`
const wallet = privateKeyToAccount("0x..."); // viem or ethers
const transport = new HttpTransport(); // or `WebSocketTransport`
const data = await order(
{ transport, wallet }, // same params as in `ExchangeClient` or `MultiSignClient`
{
orders: [
{
a: 0,
b: true,
p: "30000",
s: "0.1",
r: false,
t: { limit: { tif: "Gtc" } },
c: "0x...",
},
],
grouping: "na",
},
);
import { WebSocketTransport } from "@nktkas/hyperliquid";
import { candle } from "@nktkas/hyperliquid/api/subscription";
// ^^^^^^
// same name as in `SubscriptionClient`
const transport = new WebSocketTransport();
const sub = await candle(
{ transport }, // same params as in `SubscriptionClient`
{ coin: "ETH", interval: "1h" },
(data) => console.log(data),
);
Another way to use this module is to access valibot schemas for API requests/responses. For example, to validate data and sort object keys before creating a signature.
import { OrderRequest, parser } from "@nktkas/hyperliquid/api/exchange";
// ^^^^^^^^^^^^
// both a valibot schema and a typescript type
const action = {
type: "order",
orders: [{
a: 0,
b: true,
p: "50000",
s: "0.1",
r: false,
t: { limit: { tif: "Gtc" } },
}],
grouping: "na",
} satisfies OrderRequest["action"]; // can be used as type
// or as valibot schema
// ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
const validatedAction = parser(OrderRequest.entries.action)(action);
// ^^^^^^
// validates, formats, sorts object keys for correct signature generation
// and returns typed data
Also valibot schema can be converted to JSON Schema (e.g. for generating OpenAPI spec):
import { OrderRequest } from "@nktkas/hyperliquid/api/exchange";
import { toJsonSchema } from "@valibot/to-json-schema";
const schema = toJsonSchema(OrderRequest, { errorMode: "ignore", typeMode: "output" });
console.log(JSON.stringify(schema, null, 2));
// {
// "$schema": "http://json-schema.org/draft-07/schema#",
// "type": "object",
// "properties": {
// "action": {
// "type": "object",
// "properties": {
// "type": { "const": "order" },
// "orders": { "type": "array", "items": {...} },
// "grouping": { "anyOf": [...] },
// "builder": { "type": "object", ... }
// },
// "required": ["type", "orders", "grouping"]
// },
// "nonce": { "type": "number" },
// "signature": {
// "type": "object",
// "properties": {
// "r": { "type": "string", ... },
// "s": { "type": "string", ... },
// "v": { "anyOf": [{"const": 27}, {"const": 28}] }
// },
// "required": ["r", "s", "v"]
// },
// "vaultAddress": { "type": "string", ... },
// "expiresAfter": { "type": "number" }
// },
// "required": ["action", "nonce", "signature"]
// }
Hyperliquid requires chain 1337
for L1 action signatures. To handle this with external wallets:
- (recommended) Create an Agent Wallet and execute all L1 actions through it
- Change a user's chain to
1337
, however, the user will sign unreadable data
Hyperliquid doesn't have traditional market orders, but you can achieve market-like execution by placing limit order
with tif: "Ioc"
and price that guarantee immediate execution:
- For buys: set limit price >= current best ask
- For sells: set limit price <= current best bid
How to use the Agent Wallet?
Use agent's private key instead of master account's private key.
How to use the Vault / Sub-Account?
Pass vault or sub-account address via vaultAddress
options to method or set defaultVaultAddress
in client options.
Set isTestnet: true
in transport options.
We appreciate your help! To contribute, please read the contributing instructions.