UNPKG

@deeeed/hyperliquid-node20

Version:

Unofficial Hyperliquid API SDK for all major JS runtimes, written in TypeScript. Fork with Node.js 20.18.0+ compatibility.

151 lines (150 loc) 6.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiSignClient = void 0; const sha3_1 = require("@noble/hashes/sha3"); const secp256k1_1 = require("@noble/secp256k1"); const mod_js_1 = require("../signing/mod.js"); const exchange_js_1 = require("./exchange.js"); /** * Multi-signature exchange client for interacting with the Hyperliquid API. * @typeParam T The transport used to connect to the Hyperliquid API. * @typeParam S Array of wallets where the first wallet acts as the leader. */ class MultiSignClient extends exchange_js_1.ExchangeClient { multiSignAddress; signers; /** * Initialises a new multi-signature client instance. * @param args - The parameters for the multi-signature client. * * @example * ```ts * import * as hl from "@nktkas/hyperliquid"; * * const multiSignAddress = "0x..."; * const signers = [ * "0x...", // Private key; or any other wallet libraries * ] as const; * * const transport = new hl.HttpTransport(); * const multiSignClient = new hl.MultiSignClient({ transport, multiSignAddress, signers }); * ``` */ constructor(args) { super({ ...args, wallet: args.signers[0] }); this.multiSignAddress = args.multiSignAddress; this.signers = args.signers; Object.defineProperty(this, "wallet", { get() { return this.signers[0]; }, set(value) { this.signers[0] = value; }, enumerable: true, configurable: true, }); } async _executeAction(args, signal) { const { action, vaultAddress, expiresAfter, multiSigNonce } = args; if (action.type === "multiSig") { // Multi-signature action return await super._executeAction({ action, vaultAddress, expiresAfter, multiSigNonce, }, signal); } // Sign an action // deno-lint-ignore no-explicit-any const sortedAction = mod_js_1.actionSorter[action.type](action); // TypeScript cannot infer a type from a dynamic function call let nonce; if ("signatureChainId" in sortedAction) { // User-signed action nonce = "nonce" in sortedAction ? sortedAction.nonce : sortedAction.time; } else { // L1 action nonce = await this.nonceManager(); } const outerSigner = await this._getWalletAddress(this.signers[0]); let signatures; if ("signatureChainId" in sortedAction) { // User-signed action signatures = await Promise.all(this.signers.map(async (signer) => { const types = structuredClone(mod_js_1.userSignedActionEip712Types[sortedAction.type]); // for safe mutation Object.values(types)[0].splice(// array mutation 1, // after `hyperliquidChain` 0, // do not remove any elements { name: "payloadMultiSigUser", type: "address" }, { name: "outerSigner", type: "address" }); return await (0, mod_js_1.signUserSignedAction)({ wallet: signer, action: { payloadMultiSigUser: this.multiSignAddress, outerSigner, ...sortedAction, }, types, }); })); if ("agentName" in sortedAction && sortedAction.agentName === "") sortedAction.agentName = null; } else { // L1 action signatures = await Promise.all(this.signers.map(async (signer) => { return await (0, mod_js_1.signL1Action)({ wallet: signer, action: [this.multiSignAddress.toLowerCase(), outerSigner.toLowerCase(), sortedAction], nonce, isTestnet: this.isTestnet, vaultAddress, expiresAfter, }); })); } // Send a multi-signature action return await super.multiSig({ signatures, payload: { multiSigUser: this.multiSignAddress, outerSigner, action: sortedAction, }, nonce, vaultAddress, expiresAfter, }, signal); } /** Extracts the wallet address from different wallet types. */ async _getWalletAddress(wallet) { if ((0, mod_js_1.isValidPrivateKey)(wallet)) { return privateKeyToAddress(wallet); } else if ((0, mod_js_1.isAbstractViemWalletClient)(wallet)) { return wallet.address; } else if ((0, mod_js_1.isAbstractEthersSigner)(wallet) || (0, mod_js_1.isAbstractEthersV5Signer)(wallet)) { return await wallet.getAddress(); } else if ((0, mod_js_1.isAbstractWindowEthereum)(wallet)) { return await getWindowEthereumAddress(wallet); } else { throw new Error("Unsupported wallet for getting address"); } } } exports.MultiSignClient = MultiSignClient; function privateKeyToAddress(privateKey) { const cleanPrivKey = privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey; const publicKey = (0, secp256k1_1.getPublicKey)(cleanPrivKey, false); const publicKeyWithoutPrefix = publicKey.slice(1); const hash = (0, sha3_1.keccak_256)(publicKeyWithoutPrefix); const addressBytes = hash.slice(-20); const address = secp256k1_1.etc.bytesToHex(addressBytes); return `0x${address}`; } async function getWindowEthereumAddress(ethereum) { const accounts = await ethereum.request({ method: "eth_requestAccounts", params: [] }); if (!Array.isArray(accounts) || accounts.length === 0) { throw new Error("No Ethereum accounts available"); } return accounts[0]; }