@nktkas/hyperliquid
Version:
Hyperliquid API SDK for all major JS runtimes, written in TypeScript.
233 lines (232 loc) • 7.81 kB
TypeScript
/**
* Multi-sig wrapper construction and outer signing.
* @module
*/
/// <amd-module name="file:///home/runner/work/hyperliquid/hyperliquid/src/signing/_multiSig.ts" />
import { type AbstractWallet, type Signature } from "./_abstractWallet.js";
/** A multi-sig wrapper as it appears on the wire. */
interface MultiSigAction {
/** Wire-level discriminator. */
type: "multiSig";
/** Chain ID in hex format for [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signing. */
signatureChainId: `0x${string}`;
/** Trimmed inner signatures from the authorized signers. */
signatures: readonly Signature[];
/** The inner authorization payload. */
payload: {
/** The multi-sig account address (lowercased). */
multiSigUser: `0x${string}`;
/** The leader address (lowercased). */
outerSigner: `0x${string}`;
/** The action being authorized. */
action: Record<string, unknown> | readonly unknown[];
};
}
/**
* Signs an L1 action with multi-sig orchestration.
*
* Collects inner signatures from each signer over `[multiSigUser, outerSigner, action]`,
* builds the multi-sig wrapper, and signs the wrapper with the leader (first signer).
*
* @param args The signers, action, and signing parameters.
* @return The wrapper action (modified) and the leader's signature.
*
* @throws {AbstractWalletError} If signing fails.
*
* @example
* ```ts
* import { signMultiSigL1 } from "@nktkas/hyperliquid/signing";
* import { privateKeyToAccount } from "npm:viem/accounts";
*
* const signers = [
* privateKeyToAccount("0x..."),
* privateKeyToAccount("0x..."),
* // ...more signers if needed
* ] as const;
*
* const action = { type: "cancel", cancels: [{ a: 0, o: 12345 }] };
* const nonce = Date.now();
*
* const { action: wrapper, signature } = await signMultiSigL1({
* signers,
* multiSigUser: "0x...",
* signatureChainId: "0x66eee",
* action,
* nonce,
* });
* ```
*
* @example
* \- Full cycle of signing and sending a multi-sig L1 action to the Hyperliquid API
* ```ts
* import { canonicalize, signMultiSigL1 } from "@nktkas/hyperliquid/signing";
* import { CancelRequest } from "@nktkas/hyperliquid/api/exchange";
* import { privateKeyToAccount } from "npm:viem/accounts";
*
* const signers = [
* privateKeyToAccount("0x..."),
* privateKeyToAccount("0x..."),
* // ...more signers if needed
* ] as const;
*
* // For correct hashing, keys in the L1 action must be in
* // the same order as in the schema definition
* // ⌄⌄⌄⌄⌄⌄⌄⌄⌄
* const action = canonicalize(CancelRequest.entries.action, {
* type: "cancel",
* cancels: [{ a: 0, o: 12345 }],
* });
* const nonce = Date.now();
*
* const { action: wrapper, signature } = await signMultiSigL1({
* signers,
* multiSigUser: "0x...",
* signatureChainId: "0x66eee",
* action,
* nonce,
* });
*
* // Send the multi-sig wrapper to the Hyperliquid API
* const response = await fetch("https://api.hyperliquid.xyz/exchange", {
* method: "POST",
* headers: { "Content-Type": "application/json" },
* body: JSON.stringify({ action: wrapper, signature, nonce }),
* });
* const body = await response.json();
* ```
*/
export declare function signMultiSigL1(args: {
/** Array of wallets for multi-sig. First wallet is the leader. */
signers: readonly [AbstractWallet, ...AbstractWallet[]];
/** The multi-signature account address. */
multiSigUser: `0x${string}`;
/** Chain ID in hex format for [EIP-712](https://eips.ethereum.org/EIPS/eip-712) signing. */
signatureChainId: `0x${string}`;
/** The action payload. */
action: Record<string, unknown> | unknown[];
/** The current timestamp in ms. */
nonce: number;
/**
* Indicates if the action is for the testnet.
*
* Default: `false`
*/
isTestnet?: boolean;
/** Optional vault address used in the action. */
vaultAddress?: `0x${string}`;
/** Optional expiration time of the action in ms since the epoch. */
expiresAfter?: number;
}): Promise<{
action: MultiSigAction;
signature: Signature;
}>;
/**
* Signs a user-signed action with multi-sig orchestration.
*
* Collects inner signatures from each signer over the action with multi-sig
* fields injected into its [EIP-712](https://eips.ethereum.org/EIPS/eip-712) type, builds the wrapper, and signs the
* wrapper with the leader.
*
* @param args The signers, action, types, and signing parameters.
* @return The wrapper action and the leader's signature.
*
* @throws {AbstractWalletError} If signing fails.
*
* @example
* ```ts
* import { signMultiSigUserSigned } from "@nktkas/hyperliquid/signing";
* import { ApproveAgentTypes } from "@nktkas/hyperliquid/api/exchange";
* import { privateKeyToAccount } from "npm:viem/accounts";
*
* const signers = [
* privateKeyToAccount("0x..."),
* privateKeyToAccount("0x..."),
* // ...more signers if needed
* ] as const;
*
* const types = ApproveAgentTypes; // or custom EIP-712 types matching the action
* const action = {
* type: "approveAgent",
* signatureChainId: "0x66eee" as const,
* hyperliquidChain: "Mainnet" as const,
* agentAddress: "0x...",
* agentName: "Agent",
* nonce: Date.now(),
* };
*
* const { action: wrapper, signature } = await signMultiSigUserSigned({
* signers,
* multiSigUser: "0x...",
* action,
* types,
* });
* ```
*
* @example
* \- Full cycle of signing and sending a multi-sig user-signed action to the Hyperliquid API
* ```ts
* import { signMultiSigUserSigned } from "@nktkas/hyperliquid/signing";
* import { ApproveAgentTypes } from "@nktkas/hyperliquid/api/exchange";
* import { privateKeyToAccount } from "npm:viem/accounts";
*
* const signers = [
* privateKeyToAccount("0x..."),
* privateKeyToAccount("0x..."),
* // ...more signers if needed
* ] as const;
*
* const types = ApproveAgentTypes; // or custom EIP-712 types matching the action
* const action = {
* type: "approveAgent",
* signatureChainId: "0x66eee" as const,
* hyperliquidChain: "Mainnet" as const,
* agentAddress: "0x...",
* agentName: "Agent",
* nonce: Date.now(),
* };
*
* const { action: wrapper, signature } = await signMultiSigUserSigned({
* signers,
* multiSigUser: "0x...",
* action,
* types,
* });
*
* // Send the multi-sig wrapper to the Hyperliquid API
* const response = await fetch("https://api.hyperliquid.xyz/exchange", {
* method: "POST",
* headers: { "Content-Type": "application/json" },
* body: JSON.stringify({ action: wrapper, signature, nonce: action.nonce }),
* });
* const body = await response.json();
* ```
*/
export declare function signMultiSigUserSigned(args: {
/** Array of wallets for multi-sig. First wallet is the leader. */
signers: readonly [AbstractWallet, ...AbstractWallet[]];
/** The multi-signature account address. */
multiSigUser: `0x${string}`;
/** The action payload (must include `signatureChainId`, `hyperliquidChain`, and `nonce` or `time`). */
action: {
signatureChainId: `0x${string}`;
hyperliquidChain: "Mainnet" | "Testnet";
[key: string]: unknown;
} & ({
nonce: number;
time?: never;
} | {
time: number;
nonce?: never;
});
/** [EIP-712](https://eips.ethereum.org/EIPS/eip-712) type definitions. */
types: Record<string, readonly {
name: string;
type: string;
}[]>;
}): Promise<{
action: MultiSigAction;
signature: Signature;
}>;
/** Removes leading zeros from signature `r` and `s` (required for multi-sig signatures). */
export declare function trimSignature(sig: Signature): Signature;
export {};