• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nktkas / hyperliquid / 29445874293

15 Jul 2026 07:47PM UTC coverage: 96.203% (+0.7%) from 95.46%
29445874293

push

github

web-flow
build(deps): bump actions/setup-node from 6 to 7 (#166)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

1065 of 1238 branches covered (86.03%)

Branch coverage included in aggregate %.

8944 of 9166 relevant lines covered (97.58%)

435.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.43
/src/api/exchange/_methods/_base/execute.ts
1
/**
2
 * Execute helpers for L1 and user-signed Exchange API actions.
3
 * @module
4
 */
5

6
import * as v from "@valibot/valibot";
168✔
7
import { HyperliquidError, parse } from "../../../../_base.ts";
168✔
8
import {
168✔
9
  getWalletChainId,
168✔
10
  signL1Action,
168✔
11
  signMultiSigL1,
168✔
12
  signMultiSigUserSigned,
168✔
13
  signUserSignedAction,
168✔
14
} from "../../../../signing/mod.ts";
168✔
15
import { Address, Hex, UnsignedInteger } from "../../../_schemas.ts";
168✔
16
import type { ExchangeConfig } from "./_config.ts";
17
import { executeWithShell } from "./_shell.ts";
168✔
18

19
// ============================================================
20
// Execute L1 Action
21
// ============================================================
22

23
/**
24
 * Execute an L1 action on the Hyperliquid Exchange.
25
 *
26
 * Handles both single-wallet and multi-sig signing.
27
 *
28
 * @param config Exchange API configuration.
29
 * @param action Action payload to execute.
30
 * @param options Additional options for the request.
31
 * @return API response.
32
 *
33
 * @throws {ValidationError} If the request options fail validation.
34
 * @throws {ApiRequestError} If the API returns an error response.
35
 */
36
export async function executeL1Action<T>(
168✔
37
  config: ExchangeConfig,
168✔
38
  action: Record<string, unknown>,
168✔
39
  options?: {
168✔
40
    vaultAddress?: string;
41
    expiresAfter?: string | number;
42
    signal?: AbortSignal;
43
  },
168✔
44
): Promise<T> {
45
  // Validate options before acquiring the lock.
46
  const vaultAddress = parse(
315✔
47
    v.optional(Address),
315✔
48
    options?.vaultAddress ?? config.defaultVaultAddress,
315✔
49
  );
50
  const expiresAfter = parse(
315✔
51
    v.optional(UnsignedInteger),
315✔
52
    options?.expiresAfter ??
315✔
53
      (typeof config.defaultExpiresAfter === "function"
315!
54
        ? await config.defaultExpiresAfter()
315✔
55
        : config.defaultExpiresAfter),
315✔
56
  );
57

58
  return executeWithShell<T>(config, async (nonce) => {
315✔
59
    if ("wallet" in config) {
315✔
60
      const signature = await signL1Action({
164✔
61
        wallet: config.wallet,
164✔
62
        action,
164✔
63
        nonce,
164✔
64
        isTestnet: config.transport.isTestnet,
164✔
65
        vaultAddress,
164✔
66
        expiresAfter,
164✔
67
      });
164✔
68
      return { action, signature, extras: { vaultAddress, expiresAfter } };
164✔
69
    } else {
164✔
70
      const { action: wrapper, signature } = await signMultiSigL1({
151✔
71
        signers: config.signers,
151✔
72
        multiSigUser: config.multiSigUser,
151✔
73
        signatureChainId: await resolveSignatureChainId(config),
151✔
74
        action,
151✔
75
        nonce,
151✔
76
        isTestnet: config.transport.isTestnet,
151✔
77
        vaultAddress,
151✔
78
        expiresAfter,
151✔
79
      });
151✔
80
      return { action: wrapper, signature, extras: { vaultAddress, expiresAfter } };
151✔
81
    }
151✔
82
  }, options?.signal);
315✔
83
}
315✔
84

85
// ============================================================
86
// Execute User-Signed Action
87
// ============================================================
88

89
/**
90
 * Execute a user-signed action (EIP-712) on the Hyperliquid Exchange.
91
 *
92
 * Handles both single-wallet and multi-sig signing.
93
 *
94
 * @param config Exchange API configuration.
95
 * @param action Action payload to execute.
96
 * @param types EIP-712 type definitions for signing.
97
 * @param options Additional options for the request.
98
 * @return API response.
99
 *
100
 * @throws {ValidationError} If the request options fail validation.
101
 * @throws {ApiRequestError} If the API returns an error response.
102
 */
103
export function executeUserSignedAction<T>(
168✔
104
  config: ExchangeConfig,
168✔
105
  action: Record<string, unknown>,
168✔
106
  types: Record<string, readonly { name: string; type: string }[]>,
168✔
107
  options?: {
168✔
108
    /** Transforms the completed action into its outer multi-sig payload representation. */
109
    toMultiSigPayloadAction?: (action: Readonly<Record<string, unknown>>) => Record<string, unknown>;
110
    signal?: AbortSignal;
111
  },
168✔
112
): Promise<T> {
113
  return executeWithShell<T>(config, async (nonce) => {
417✔
114
    // --- Construct full action (type, system fields, user fields, nonce/time)
115
    const { type, ...restAction } = action;
417✔
116
    const nonceFieldName = extractNonceFieldName(types);
417✔
117
    const baseFields = {
417✔
118
      type,
417✔
119
      signatureChainId: await resolveSignatureChainId(config),
417✔
120
      hyperliquidChain: config.transport.isTestnet ? "Testnet" : "Mainnet",
×
121
    } as const;
417✔
122
    const fullAction = nonceFieldName === "nonce"
417✔
123
      ? { ...baseFields, ...restAction, nonce }
417✔
124
      : { ...baseFields, ...restAction, time: nonce };
417✔
125

126
    // --- Sign (single-wallet or multi-sig) -------------------
127
    if ("wallet" in config) {
417✔
128
      const signature = await signUserSignedAction({
330✔
129
        wallet: config.wallet,
330✔
130
        action: fullAction,
330✔
131
        types,
330✔
132
      });
330✔
133
      return { action: fullAction, signature };
330✔
134
    } else {
417✔
135
      const payloadAction = options?.toMultiSigPayloadAction?.(fullAction) ?? fullAction;
87✔
136
      const { action: wrapper, signature } = await signMultiSigUserSigned({
87✔
137
        signers: config.signers,
87✔
138
        multiSigUser: config.multiSigUser,
87✔
139
        action: fullAction,
87✔
140
        payloadAction,
87✔
141
        types,
87✔
142
      });
87✔
143
      return { action: wrapper, signature };
87✔
144
    }
87✔
145
  }, options?.signal);
417✔
146
}
417✔
147

148
// ============================================================
149
// Helpers
150
// ============================================================
151

152
/** Extracts the nonce field name ("nonce" or "time") from EIP-712 type definitions. */
153
function extractNonceFieldName(types: Record<string, readonly { name: string; type: string }[]>): "nonce" | "time" {
168✔
154
  const primaryType = Object.keys(types)[0];
417✔
155
  const field = types[primaryType].find((f) => f.name === "nonce" || f.name === "time");
417✔
156
  if (!field) {
417!
157
    throw new HyperliquidError(`EIP-712 types must contain a "nonce" or "time" field in "${primaryType}"`);
×
158
  }
×
159
  return field.name as "nonce" | "time";
417✔
160
}
417✔
161

162
/** Resolves signature chain ID from config, or falls back to the leader wallet's chain ID. */
163
async function resolveSignatureChainId(config: ExchangeConfig): Promise<`0x${string}`> {
168✔
164
  if (config.signatureChainId) {
568!
165
    const id = typeof config.signatureChainId === "function"
×
166
      ? await config.signatureChainId()
×
167
      : config.signatureChainId;
×
168
    return parse(Hex, id);
×
169
  }
×
170
  const leader = "wallet" in config ? config.wallet : config.signers[0];
568✔
171
  return await getWalletChainId(leader);
568✔
172
}
568✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc