@nktkas/hyperliquid
Version:
Hyperliquid API SDK for all major JS runtimes, written in TypeScript.
236 lines • 10.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeL1Action = executeL1Action;
exports.executeUserSignedAction = executeUserSignedAction;
const v = __importStar(require("valibot"));
const _schemas_js_1 = require("../../../_schemas.js");
const mod_js_1 = require("../../../../signing/mod.js");
const errors_js_1 = require("./errors.js");
const _nonce_js_1 = require("./_nonce.js");
const _semaphore_js_1 = require("./_semaphore.js");
// =============================================================
// Execute L1 Action
// =============================================================
/**
* Execute an L1 action on the Hyperliquid Exchange.
* Handles both single-wallet and multi-sig signing.
*/
async function executeL1Action(config, action, options) {
const { transport } = config;
const leader = getLeader(config);
const walletAddress = await (0, mod_js_1.getWalletAddress)(leader);
// Semaphore ensures requests arrive at server in nonce order (prevents out-of-order delivery)
const key = `${walletAddress}:${transport.isTestnet}`;
return await (0, _semaphore_js_1.withLock)(key, async () => {
const nonce = await (config.nonceManager?.(walletAddress) ?? _nonce_js_1.defaultNonceManager.getNonce(key));
// Validate and resolve options
const vaultAddress = v.parse(v.optional(_schemas_js_1.Address), options?.vaultAddress ?? config.defaultVaultAddress);
const expiresAfter = v.parse(v.optional(_schemas_js_1.UnsignedInteger), options?.expiresAfter ??
(typeof config.defaultExpiresAfter === "number"
? config.defaultExpiresAfter
: await config.defaultExpiresAfter?.()));
const signal = options?.signal;
// Sign action (multi-sig or single wallet)
const [finalAction, signature] = isMultiSig(config)
? await signMultiSigL1(config, action, walletAddress, nonce, vaultAddress, expiresAfter)
: [
action,
await (0, mod_js_1.signL1Action)({
wallet: leader,
action,
nonce,
isTestnet: transport.isTestnet,
vaultAddress,
expiresAfter,
}),
];
// Send request and validate response
const response = await transport.request("exchange", {
action: finalAction,
signature,
nonce,
vaultAddress,
expiresAfter,
}, signal);
(0, errors_js_1.assertSuccessResponse)(response);
return response;
});
}
// =============================================================
// Execute User-Signed Action
// =============================================================
/** Extract nonce field name from EIP-712 types ("nonce" or "time"). */
function getNonceFieldName(types) {
const primaryType = Object.keys(types)[0];
const field = types[primaryType].find((f) => f.name === "nonce" || f.name === "time");
return field?.name ?? "nonce";
}
/**
* Execute a user-signed action (EIP-712) on the Hyperliquid Exchange.
* Handles both single-wallet and multi-sig signing.
* Automatically adds signatureChainId, hyperliquidChain, and nonce/time.
*/
async function executeUserSignedAction(config, action, types, options) {
const { transport } = config;
const leader = getLeader(config);
const walletAddress = await (0, mod_js_1.getWalletAddress)(leader);
// Semaphore ensures requests arrive at server in nonce order (prevents out-of-order delivery)
const key = `${walletAddress}:${transport.isTestnet}`;
return (0, _semaphore_js_1.withLock)(key, async () => {
const nonce = await (config.nonceManager?.(walletAddress) ?? _nonce_js_1.defaultNonceManager.getNonce(key));
const signal = options?.signal;
// Add system fields for user-signed actions
const { type, ...restAction } = action;
const nonceFieldName = getNonceFieldName(types);
const fullAction = {
type,
signatureChainId: await getSignatureChainId(config),
hyperliquidChain: transport.isTestnet ? "Testnet" : "Mainnet",
...restAction,
[nonceFieldName]: nonce,
};
// Sign action (multi-sig or single wallet)
const [finalAction, signature] = isMultiSig(config)
? await signMultiSigUserSigned(config, fullAction, types, walletAddress, nonce)
: [fullAction, await (0, mod_js_1.signUserSignedAction)({ wallet: leader, action: fullAction, types })];
// Send request and validate response
const response = await transport.request("exchange", {
action: finalAction,
signature,
nonce,
}, signal);
(0, errors_js_1.assertSuccessResponse)(response);
return response;
});
}
// =============================================================
// Multi-sig signing (private)
// =============================================================
/** Remove leading zeros from signature components (required by Hyperliquid). */
function trimSignature(sig) {
return {
r: sig.r.replace(/^0x0+/, "0x"),
s: sig.s.replace(/^0x0+/, "0x"),
v: sig.v,
};
}
/** Sign an L1 action with multi-sig. */
async function signMultiSigL1(config, action, outerSigner, nonce, vaultAddress, expiresAfter) {
const { transport: { isTestnet }, wallet: signers, multiSigUser } = config;
const multiSigUser_ = v.parse(_schemas_js_1.Address, multiSigUser);
const outerSigner_ = v.parse(_schemas_js_1.Address, outerSigner);
// Collect signatures from all signers
const signatures = await Promise.all(signers.map(async (signer) => {
const signature = await (0, mod_js_1.signL1Action)({
wallet: signer,
action: [multiSigUser_, outerSigner_, action],
nonce,
isTestnet,
vaultAddress,
expiresAfter,
});
return trimSignature(signature);
}));
// Build multi-sig action wrapper
const multiSigAction = {
type: "multiSig",
signatureChainId: await getSignatureChainId(config),
signatures,
payload: { multiSigUser: multiSigUser_, outerSigner: outerSigner_, action },
};
// Sign the wrapper with the leader
const signature = await (0, mod_js_1.signMultiSigAction)({
wallet: signers[0],
action: multiSigAction,
nonce,
isTestnet,
vaultAddress,
expiresAfter,
});
return [multiSigAction, signature];
}
/** Sign a user-signed action (EIP-712) with multi-sig. */
async function signMultiSigUserSigned(config, action, types, outerSigner, nonce) {
const { wallet: signers, multiSigUser, transport: { isTestnet } } = config;
const multiSigUser_ = v.parse(_schemas_js_1.Address, multiSigUser);
const outerSigner_ = v.parse(_schemas_js_1.Address, outerSigner);
// Collect signatures from all signers
const signatures = await Promise.all(signers.map(async (signer) => {
const signature = await (0, mod_js_1.signUserSignedAction)({
wallet: signer,
action: { payloadMultiSigUser: multiSigUser_, outerSigner: outerSigner_, ...action },
types,
});
return trimSignature(signature);
}));
// Build multi-sig action wrapper
const multiSigAction = {
type: "multiSig",
signatureChainId: await getSignatureChainId(config),
signatures,
payload: { multiSigUser: multiSigUser_, outerSigner: outerSigner_, action },
};
// Sign the wrapper with the leader
const signature = await (0, mod_js_1.signMultiSigAction)({
wallet: signers[0],
action: multiSigAction,
nonce,
isTestnet,
});
return [multiSigAction, signature];
}
// =============================================================
// Helpers (private)
// =============================================================
/** Type guard for multi-sig configuration. */
function isMultiSig(config) {
return Array.isArray(config.wallet);
}
/** Get the leader wallet (first signer for multi-sig, or the single wallet). */
function getLeader(config) {
return isMultiSig(config) ? config.wallet[0] : config.wallet;
}
/** Resolve signature chain ID from config or wallet. */
async function getSignatureChainId(config) {
if (config.signatureChainId) {
const id = typeof config.signatureChainId === "function"
? await config.signatureChainId()
: config.signatureChainId;
return v.parse(_schemas_js_1.Hex, id);
}
return (0, mod_js_1.getWalletChainId)(getLeader(config));
}
//# sourceMappingURL=execute.js.map