@nktkas/hyperliquid
Version:
Hyperliquid API SDK for all major JS runtimes, written in TypeScript.
142 lines • 5.88 kB
JavaScript
import { TransportError } from "../../_errors.js";
import { AbortSignal_ } from "../_polyfills.js";
/** Error thrown when an HTTP request fails. */
export class HttpRequestError extends TransportError {
/** The HTTP response that caused the error. */
response;
/** The response body text. */
body;
/**
* Creates a new HTTP request error.
* @param args - The error arguments.
* @param args.response - The HTTP response that caused the error.
* @param args.body - The response body text.
* @param options - The error options.
*/
constructor(args, options) {
const { response, body } = args ?? {};
let message;
if (response) {
message = `${response.status} ${response.statusText}`.trim();
if (body)
message += ` - ${body}`;
}
else {
message = `Unknown HTTP request error: ${options?.cause}`;
}
super(message, options);
this.name = "HttpRequestError";
this.response = response;
this.body = body;
}
}
/** Mainnet API URL. */
export const MAINNET_API_URL = "https://api.hyperliquid.xyz";
/** Testnet API URL. */
export const TESTNET_API_URL = "https://api.hyperliquid-testnet.xyz";
/** Mainnet RPC URL. */
export const MAINNET_RPC_URL = "https://rpc.hyperliquid.xyz";
/** Testnet RPC URL. */
export const TESTNET_RPC_URL = "https://rpc.hyperliquid-testnet.xyz";
/**
* HTTP transport for Hyperliquid API.
*
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint
* @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint
*/
export class HttpTransport {
isTestnet;
timeout;
apiUrl;
rpcUrl;
fetchOptions;
/**
* Creates a new HTTP transport instance.
* @param options - Configuration options for the HTTP transport layer.
*/
constructor(options) {
this.isTestnet = options?.isTestnet ?? false;
this.timeout = options?.timeout === undefined ? 10_000 : options.timeout;
this.apiUrl = options?.apiUrl ?? (this.isTestnet ? TESTNET_API_URL : MAINNET_API_URL);
this.rpcUrl = options?.rpcUrl ?? (this.isTestnet ? TESTNET_RPC_URL : MAINNET_RPC_URL);
this.fetchOptions = options?.fetchOptions ?? {};
}
/**
* Sends a request to the Hyperliquid API via [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
*
* @param endpoint - The API endpoint to send the request to.
* @param payload - The payload to send with the request.
* @param signal - [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to cancel the request.
*
* @returns A promise that resolves with parsed JSON response body.
*
* @throws {HttpRequestError} Thrown when the HTTP request fails.
*/
async request(endpoint, payload, signal) {
try {
// Construct a Request
const url = new URL(endpoint, endpoint === "explorer" ? this.rpcUrl : this.apiUrl);
const init = this._mergeRequestInit({
body: JSON.stringify(payload),
headers: {
"Accept-Encoding": "gzip, deflate, br, zstd",
"Content-Type": "application/json",
},
keepalive: true,
method: "POST",
signal: this.timeout ? AbortSignal_.timeout(this.timeout) : undefined,
}, this.fetchOptions, { signal });
// Send the Request and wait for a Response
const response = await fetch(url, init);
// Validate the Response
if (!response.ok || !response.headers.get("Content-Type")?.includes("application/json")) {
// Unload the response body to prevent memory leaks
const body = await response.text().catch(() => undefined);
throw new HttpRequestError({ response, body });
}
// Parse the response body
const body = await response.json();
// Check if the response is an error
if (body?.type === "error") {
throw new HttpRequestError({ response, body: body?.message });
}
// Return the response body
return body;
}
catch (error) {
if (error instanceof TransportError)
throw error; // Re-throw known errors
throw new HttpRequestError(undefined, { cause: error });
}
}
/** Merges multiple `HeadersInit` into one [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers). */
_mergeHeadersInit(...inits) {
const merged = new Headers();
for (const headers of inits) {
const entries = Symbol.iterator in headers ? headers : Object.entries(headers);
for (const [key, value] of entries) {
merged.set(key, value);
}
}
return merged;
}
/** Merges multiple [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) into one [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit). */
_mergeRequestInit(...inits) {
const merged = {};
const headersList = [];
const signals = [];
for (const init of inits) {
Object.assign(merged, init);
if (init.headers)
headersList.push(init.headers);
if (init.signal)
signals.push(init.signal);
}
if (headersList.length > 0)
merged.headers = this._mergeHeadersInit(...headersList);
if (signals.length > 0)
merged.signal = signals.length > 1 ? AbortSignal_.any(signals) : signals[0];
return merged;
}
}
//# sourceMappingURL=mod.js.map