@nktkas/hyperliquid 0.22.0 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -27
- package/esm/mod.d.ts +1 -1
- package/esm/mod.d.ts.map +1 -1
- package/esm/src/clients/exchange.d.ts +136 -166
- package/esm/src/clients/exchange.d.ts.map +1 -1
- package/esm/src/clients/exchange.js +122 -147
- package/esm/src/clients/multiSign.d.ts +129 -282
- package/esm/src/clients/multiSign.d.ts.map +1 -1
- package/esm/src/clients/multiSign.js +125 -246
- package/esm/src/signing/_ethers.d.ts +33 -0
- package/esm/src/signing/_ethers.d.ts.map +1 -0
- package/esm/src/signing/_ethers.js +12 -0
- package/esm/src/signing/_private_key.d.ts +22 -0
- package/esm/src/signing/_private_key.d.ts.map +1 -0
- package/esm/src/signing/_private_key.js +124 -0
- package/esm/src/signing/_sorter.d.ts +154 -0
- package/esm/src/signing/_sorter.d.ts.map +1 -0
- package/esm/src/{signing.js → signing/_sorter.js} +1 -401
- package/esm/src/signing/_viem.d.ts +23 -0
- package/esm/src/signing/_viem.d.ts.map +1 -0
- package/esm/src/signing/_viem.js +6 -0
- package/esm/src/signing/_window.d.ts +23 -0
- package/esm/src/signing/_window.d.ts.map +1 -0
- package/esm/src/signing/_window.js +29 -0
- package/esm/src/signing/mod.d.ts +251 -0
- package/esm/src/signing/mod.d.ts.map +1 -0
- package/esm/src/signing/mod.js +352 -0
- package/esm/src/types/exchange/requests.d.ts +1 -1
- package/esm/src/types/exchange/requests.d.ts.map +1 -1
- package/package.json +6 -5
- package/script/mod.d.ts +1 -1
- package/script/mod.d.ts.map +1 -1
- package/script/src/clients/exchange.d.ts +136 -166
- package/script/src/clients/exchange.d.ts.map +1 -1
- package/script/src/clients/exchange.js +206 -231
- package/script/src/clients/multiSign.d.ts +129 -282
- package/script/src/clients/multiSign.d.ts.map +1 -1
- package/script/src/clients/multiSign.js +170 -291
- package/script/src/signing/_ethers.d.ts +33 -0
- package/script/src/signing/_ethers.d.ts.map +1 -0
- package/script/src/signing/_ethers.js +26 -0
- package/script/src/signing/_private_key.d.ts +22 -0
- package/script/src/signing/_private_key.d.ts.map +1 -0
- package/script/src/signing/_private_key.js +138 -0
- package/script/src/signing/_sorter.d.ts +154 -0
- package/script/src/signing/_sorter.d.ts.map +1 -0
- package/script/src/{signing.js → signing/_sorter.js} +2 -410
- package/script/src/signing/_viem.d.ts +23 -0
- package/script/src/signing/_viem.d.ts.map +1 -0
- package/script/src/signing/_viem.js +19 -0
- package/script/src/signing/_window.d.ts +23 -0
- package/script/src/signing/_window.d.ts.map +1 -0
- package/script/src/signing/_window.js +43 -0
- package/script/src/signing/mod.d.ts +251 -0
- package/script/src/signing/mod.d.ts.map +1 -0
- package/script/src/signing/mod.js +387 -0
- package/script/src/types/exchange/requests.d.ts +1 -1
- package/script/src/types/exchange/requests.d.ts.map +1 -1
- package/esm/src/signing.d.ts +0 -463
- package/esm/src/signing.d.ts.map +0 -1
- package/script/src/signing.d.ts +0 -463
- package/script/src/signing.d.ts.map +0 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module contains functions for generating Hyperliquid transaction signatures
|
|
3
|
+
* and interfaces to various wallet implementations.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { signL1Action } from "@nktkas/hyperliquid/signing";
|
|
8
|
+
*
|
|
9
|
+
* const action = {
|
|
10
|
+
* type: "cancel",
|
|
11
|
+
* cancels: [{ a: 0, o: 12345 }],
|
|
12
|
+
* };
|
|
13
|
+
* const nonce = Date.now();
|
|
14
|
+
*
|
|
15
|
+
* const signature = await signL1Action({
|
|
16
|
+
* wallet,
|
|
17
|
+
* action,
|
|
18
|
+
* nonce,
|
|
19
|
+
* isTestnet: true, // Change to false for mainnet
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { signUserSignedAction } from "@nktkas/hyperliquid/signing";
|
|
25
|
+
*
|
|
26
|
+
* const action = {
|
|
27
|
+
* type: "approveAgent",
|
|
28
|
+
* hyperliquidChain: "Testnet", // "Mainnet" or "Testnet"
|
|
29
|
+
* signatureChainId: "0x66eee",
|
|
30
|
+
* nonce: Date.now(),
|
|
31
|
+
* agentAddress: "0x...",
|
|
32
|
+
* agentName: "Agent",
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const signature = await signUserSignedAction({
|
|
36
|
+
* wallet,
|
|
37
|
+
* action,
|
|
38
|
+
* types: {
|
|
39
|
+
* "HyperliquidTransaction:ApproveAgent": [
|
|
40
|
+
* { name: "hyperliquidChain", type: "string" },
|
|
41
|
+
* { name: "agentAddress", type: "address" },
|
|
42
|
+
* { name: "agentName", type: "string" },
|
|
43
|
+
* { name: "nonce", type: "uint64" },
|
|
44
|
+
* ],
|
|
45
|
+
* },
|
|
46
|
+
* chainId: parseInt(action.signatureChainId, 16),
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @module
|
|
51
|
+
*/
|
|
52
|
+
import { type ValueMap, type ValueType } from "../../deps/jsr.io/@std/msgpack/1.0.3/encode.js";
|
|
53
|
+
import { type AbstractEthersSigner, type AbstractEthersV5Signer, isAbstractEthersSigner, isAbstractEthersV5Signer } from "./_ethers.js";
|
|
54
|
+
import { isValidPrivateKey } from "./_private_key.js";
|
|
55
|
+
import { type AbstractViemWalletClient, isAbstractViemWalletClient } from "./_viem.js";
|
|
56
|
+
import { type AbstractWindowEthereum, isAbstractWindowEthereum } from "./_window.js";
|
|
57
|
+
import type { Hex } from "../base.js";
|
|
58
|
+
export { type AbstractEthersSigner, type AbstractEthersV5Signer, type AbstractViemWalletClient, type AbstractWindowEthereum, type Hex, isAbstractEthersSigner, isAbstractEthersV5Signer, isAbstractViemWalletClient, isAbstractWindowEthereum, isValidPrivateKey, type ValueMap, type ValueType, };
|
|
59
|
+
export * from "./_sorter.js";
|
|
60
|
+
/** Abstract interface for a wallet that can sign typed data. */
|
|
61
|
+
export type AbstractWallet = Hex | AbstractViemWalletClient | AbstractEthersSigner | AbstractEthersV5Signer | AbstractWindowEthereum;
|
|
62
|
+
export interface Signature {
|
|
63
|
+
r: Hex;
|
|
64
|
+
s: Hex;
|
|
65
|
+
v: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a hash of the L1 action.
|
|
69
|
+
*
|
|
70
|
+
* Note: Hash generation depends on the order of the action keys.
|
|
71
|
+
*
|
|
72
|
+
* @param action - The action to be hashed.
|
|
73
|
+
* @param nonce - Unique request identifier (recommended current timestamp in ms).
|
|
74
|
+
* @param vaultAddress - Optional vault address used in the action.
|
|
75
|
+
* @param expiresAfter - Optional expiration time of the action in milliseconds since the epoch.
|
|
76
|
+
* @returns The hash of the action.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createL1ActionHash(action: ValueType, nonce: number, vaultAddress?: Hex, expiresAfter?: number): Hex;
|
|
79
|
+
/**
|
|
80
|
+
* Sign an L1 action.
|
|
81
|
+
*
|
|
82
|
+
* Note: Signature generation depends on the order of the action keys.
|
|
83
|
+
* @param args - Arguments for signing the action.
|
|
84
|
+
* @returns The signature components r, s, and v.
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { signL1Action } from "@nktkas/hyperliquid/signing";
|
|
88
|
+
*
|
|
89
|
+
* const privateKey = "0x..."; // or `viem`, `ethers`
|
|
90
|
+
*
|
|
91
|
+
* const action = {
|
|
92
|
+
* type: "cancel",
|
|
93
|
+
* cancels: [
|
|
94
|
+
* { a: 0, o: 12345 }, // Asset index and order ID
|
|
95
|
+
* ],
|
|
96
|
+
* };
|
|
97
|
+
* const nonce = Date.now();
|
|
98
|
+
*
|
|
99
|
+
* const signature = await signL1Action({
|
|
100
|
+
* wallet: privateKey,
|
|
101
|
+
* action,
|
|
102
|
+
* nonce,
|
|
103
|
+
* isTestnet: true, // Change to false for mainnet
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* const response = await fetch("https://api.hyperliquid-testnet.xyz/exchange", {
|
|
107
|
+
* method: "POST",
|
|
108
|
+
* headers: { "Content-Type": "application/json" },
|
|
109
|
+
* body: JSON.stringify({ action, signature, nonce }),
|
|
110
|
+
* });
|
|
111
|
+
* const body = await response.json();
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function signL1Action(args: {
|
|
115
|
+
/** Wallet to sign the action. */
|
|
116
|
+
wallet: AbstractWallet;
|
|
117
|
+
/** The action to be signed. */
|
|
118
|
+
action: ValueType;
|
|
119
|
+
/** Unique request identifier (recommended current timestamp in ms). */
|
|
120
|
+
nonce: number;
|
|
121
|
+
/** Indicates if the action is for the testnet. Default is `false`. */
|
|
122
|
+
isTestnet?: boolean;
|
|
123
|
+
/** Optional vault address used in the action. */
|
|
124
|
+
vaultAddress?: Hex;
|
|
125
|
+
/** Optional expiration time of the action in milliseconds since the epoch. */
|
|
126
|
+
expiresAfter?: number;
|
|
127
|
+
}): Promise<Signature>;
|
|
128
|
+
/**
|
|
129
|
+
* Sign a user-signed action.
|
|
130
|
+
*
|
|
131
|
+
* Note: Signature generation depends on the order of types.
|
|
132
|
+
*
|
|
133
|
+
* @param args - Arguments for signing the action.
|
|
134
|
+
* @returns The signature components r, s, and v.
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* import { signUserSignedAction } from "@nktkas/hyperliquid/signing";
|
|
138
|
+
*
|
|
139
|
+
* const privateKey = "0x..."; // or `viem`, `ethers`
|
|
140
|
+
*
|
|
141
|
+
* const action = {
|
|
142
|
+
* type: "approveAgent",
|
|
143
|
+
* hyperliquidChain: "Testnet", // "Mainnet" or "Testnet"
|
|
144
|
+
* signatureChainId: "0x66eee",
|
|
145
|
+
* nonce: Date.now(),
|
|
146
|
+
* agentAddress: "0x...", // Change to your agent address
|
|
147
|
+
* agentName: "Agent",
|
|
148
|
+
* };
|
|
149
|
+
*
|
|
150
|
+
* const signature = await signUserSignedAction({
|
|
151
|
+
* wallet: privateKey,
|
|
152
|
+
* action,
|
|
153
|
+
* types: {
|
|
154
|
+
* "HyperliquidTransaction:ApproveAgent": [
|
|
155
|
+
* { name: "hyperliquidChain", type: "string" },
|
|
156
|
+
* { name: "agentAddress", type: "address" },
|
|
157
|
+
* { name: "agentName", type: "string" },
|
|
158
|
+
* { name: "nonce", type: "uint64" },
|
|
159
|
+
* ],
|
|
160
|
+
* },
|
|
161
|
+
* chainId: parseInt(action.signatureChainId, 16),
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* const response = await fetch("https://api.hyperliquid-testnet.xyz/exchange", {
|
|
165
|
+
* method: "POST",
|
|
166
|
+
* headers: { "Content-Type": "application/json" },
|
|
167
|
+
* body: JSON.stringify({ action, signature, nonce: action.nonce }),
|
|
168
|
+
* });
|
|
169
|
+
* const body = await response.json();
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export declare function signUserSignedAction(args: {
|
|
173
|
+
/** Wallet to sign the action. */
|
|
174
|
+
wallet: AbstractWallet;
|
|
175
|
+
/** The action to be signed. */
|
|
176
|
+
action: Record<string, unknown>;
|
|
177
|
+
/** The types of the action. */
|
|
178
|
+
types: {
|
|
179
|
+
[key: string]: {
|
|
180
|
+
name: string;
|
|
181
|
+
type: string;
|
|
182
|
+
}[];
|
|
183
|
+
};
|
|
184
|
+
/** The chain ID. */
|
|
185
|
+
chainId: number;
|
|
186
|
+
}): Promise<Signature>;
|
|
187
|
+
/**
|
|
188
|
+
* Sign a multi-signature action.
|
|
189
|
+
*
|
|
190
|
+
* Note: Signature generation depends on the order of the action keys.
|
|
191
|
+
*
|
|
192
|
+
* @param args - Arguments for signing the action.
|
|
193
|
+
* @returns The signature components r, s, and v.
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* import { signL1Action, signMultiSigAction } from "@nktkas/hyperliquid/signing";
|
|
197
|
+
* import { privateKeyToAccount } from "viem/accounts";
|
|
198
|
+
*
|
|
199
|
+
* const wallet = privateKeyToAccount("0x...");
|
|
200
|
+
* const multiSigUser = "0x..."; // Multi-sig user address
|
|
201
|
+
*
|
|
202
|
+
* const nonce = Date.now();
|
|
203
|
+
* const action = { // Example action
|
|
204
|
+
* type: "scheduleCancel",
|
|
205
|
+
* time: Date.now() + 10000
|
|
206
|
+
* };
|
|
207
|
+
*
|
|
208
|
+
* // First, create signature from one of the authorized signers
|
|
209
|
+
* const signature = await signL1Action({
|
|
210
|
+
* wallet,
|
|
211
|
+
* action: [multiSigUser.toLowerCase(), wallet.address.toLowerCase(), action],
|
|
212
|
+
* nonce,
|
|
213
|
+
* isTestnet: true,
|
|
214
|
+
* });
|
|
215
|
+
*
|
|
216
|
+
* // Then use it in the multi-sig action
|
|
217
|
+
* const multiSigSignature = await signMultiSigAction({
|
|
218
|
+
* wallet,
|
|
219
|
+
* action: {
|
|
220
|
+
* type: "multiSig",
|
|
221
|
+
* signatureChainId: "0x66eee",
|
|
222
|
+
* signatures: [signature],
|
|
223
|
+
* payload: {
|
|
224
|
+
* multiSigUser,
|
|
225
|
+
* outerSigner: wallet.address,
|
|
226
|
+
* action,
|
|
227
|
+
* }
|
|
228
|
+
* },
|
|
229
|
+
* nonce,
|
|
230
|
+
* hyperliquidChain: "Testnet",
|
|
231
|
+
* signatureChainId: "0x66eee",
|
|
232
|
+
* });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export declare function signMultiSigAction(args: {
|
|
236
|
+
/** Wallet to sign the action. */
|
|
237
|
+
wallet: AbstractWallet;
|
|
238
|
+
/** The action to be signed. */
|
|
239
|
+
action: ValueMap;
|
|
240
|
+
/** Unique request identifier (recommended current timestamp in ms). */
|
|
241
|
+
nonce: number;
|
|
242
|
+
/** Optional vault address used in the action. */
|
|
243
|
+
vaultAddress?: Hex;
|
|
244
|
+
/** Optional expiration time of the action in milliseconds since the epoch. */
|
|
245
|
+
expiresAfter?: number;
|
|
246
|
+
/** HyperLiquid network ("Mainnet" or "Testnet"). */
|
|
247
|
+
hyperliquidChain: "Mainnet" | "Testnet";
|
|
248
|
+
/** Chain ID used for signing. */
|
|
249
|
+
signatureChainId: Hex;
|
|
250
|
+
}): Promise<Signature>;
|
|
251
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/src/signing/mod.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module contains functions for generating Hyperliquid transaction signatures
|
|
3
|
+
* and interfaces to various wallet implementations.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { signL1Action } from "@nktkas/hyperliquid/signing";
|
|
8
|
+
*
|
|
9
|
+
* const action = {
|
|
10
|
+
* type: "cancel",
|
|
11
|
+
* cancels: [{ a: 0, o: 12345 }],
|
|
12
|
+
* };
|
|
13
|
+
* const nonce = Date.now();
|
|
14
|
+
*
|
|
15
|
+
* const signature = await signL1Action({
|
|
16
|
+
* wallet,
|
|
17
|
+
* action,
|
|
18
|
+
* nonce,
|
|
19
|
+
* isTestnet: true, // Change to false for mainnet
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { signUserSignedAction } from "@nktkas/hyperliquid/signing";
|
|
25
|
+
*
|
|
26
|
+
* const action = {
|
|
27
|
+
* type: "approveAgent",
|
|
28
|
+
* hyperliquidChain: "Testnet", // "Mainnet" or "Testnet"
|
|
29
|
+
* signatureChainId: "0x66eee",
|
|
30
|
+
* nonce: Date.now(),
|
|
31
|
+
* agentAddress: "0x...",
|
|
32
|
+
* agentName: "Agent",
|
|
33
|
+
* };
|
|
34
|
+
*
|
|
35
|
+
* const signature = await signUserSignedAction({
|
|
36
|
+
* wallet,
|
|
37
|
+
* action,
|
|
38
|
+
* types: {
|
|
39
|
+
* "HyperliquidTransaction:ApproveAgent": [
|
|
40
|
+
* { name: "hyperliquidChain", type: "string" },
|
|
41
|
+
* { name: "agentAddress", type: "address" },
|
|
42
|
+
* { name: "agentName", type: "string" },
|
|
43
|
+
* { name: "nonce", type: "uint64" },
|
|
44
|
+
* ],
|
|
45
|
+
* },
|
|
46
|
+
* chainId: parseInt(action.signatureChainId, 16),
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @module
|
|
51
|
+
*/
|
|
52
|
+
import { keccak_256 } from "@noble/hashes/sha3";
|
|
53
|
+
import { encode as encodeMsgpack } from "../../deps/jsr.io/@std/msgpack/1.0.3/encode.js";
|
|
54
|
+
import { decodeHex, encodeHex } from "../../deps/jsr.io/@std/encoding/1.0.10/hex.js";
|
|
55
|
+
import { concat as concatBytes } from "../../deps/jsr.io/@std/bytes/1.0.6/concat.js";
|
|
56
|
+
import { isAbstractEthersSigner, isAbstractEthersV5Signer, } from "./_ethers.js";
|
|
57
|
+
import { isValidPrivateKey, signTypedDataWithPrivateKey } from "./_private_key.js";
|
|
58
|
+
import { isAbstractViemWalletClient } from "./_viem.js";
|
|
59
|
+
import { isAbstractWindowEthereum, signTypedDataWithWindowEthereum } from "./_window.js";
|
|
60
|
+
export { isAbstractEthersSigner, isAbstractEthersV5Signer, isAbstractViemWalletClient, isAbstractWindowEthereum, isValidPrivateKey, };
|
|
61
|
+
export * from "./_sorter.js";
|
|
62
|
+
/**
|
|
63
|
+
* Create a hash of the L1 action.
|
|
64
|
+
*
|
|
65
|
+
* Note: Hash generation depends on the order of the action keys.
|
|
66
|
+
*
|
|
67
|
+
* @param action - The action to be hashed.
|
|
68
|
+
* @param nonce - Unique request identifier (recommended current timestamp in ms).
|
|
69
|
+
* @param vaultAddress - Optional vault address used in the action.
|
|
70
|
+
* @param expiresAfter - Optional expiration time of the action in milliseconds since the epoch.
|
|
71
|
+
* @returns The hash of the action.
|
|
72
|
+
*/
|
|
73
|
+
export function createL1ActionHash(action, nonce, vaultAddress, expiresAfter) {
|
|
74
|
+
// 1. Action
|
|
75
|
+
const actionBytes = encodeMsgpack(normalizeIntegersForMsgPack(action));
|
|
76
|
+
// 2. Nonce
|
|
77
|
+
const nonceBytes = new Uint8Array(8);
|
|
78
|
+
new DataView(nonceBytes.buffer).setBigUint64(0, BigInt(nonce));
|
|
79
|
+
// 3. Vault address
|
|
80
|
+
const vaultMarker = vaultAddress ? Uint8Array.of(1) : Uint8Array.of(0);
|
|
81
|
+
const vaultBytes = vaultAddress ? decodeHex(vaultAddress.slice(2)) : new Uint8Array();
|
|
82
|
+
// 4. Expires after
|
|
83
|
+
let expiresMarker;
|
|
84
|
+
let expiresBytes;
|
|
85
|
+
if (expiresAfter !== undefined) {
|
|
86
|
+
expiresMarker = Uint8Array.of(0);
|
|
87
|
+
expiresBytes = new Uint8Array(8);
|
|
88
|
+
new DataView(expiresBytes.buffer).setBigUint64(0, BigInt(expiresAfter));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
expiresMarker = new Uint8Array();
|
|
92
|
+
expiresBytes = new Uint8Array();
|
|
93
|
+
}
|
|
94
|
+
// Create a keccak256 hash
|
|
95
|
+
const bytes = concatBytes([
|
|
96
|
+
actionBytes,
|
|
97
|
+
nonceBytes,
|
|
98
|
+
vaultMarker,
|
|
99
|
+
vaultBytes,
|
|
100
|
+
expiresMarker,
|
|
101
|
+
expiresBytes,
|
|
102
|
+
]);
|
|
103
|
+
const hash = keccak_256(bytes);
|
|
104
|
+
return `0x${encodeHex(hash)}`;
|
|
105
|
+
}
|
|
106
|
+
/** Layer to make {@link https://jsr.io/@std/msgpack | @std/msgpack} compatible with {@link https://github.com/msgpack/msgpack-javascript | @msgpack/msgpack}. */
|
|
107
|
+
function normalizeIntegersForMsgPack(obj) {
|
|
108
|
+
const THIRTY_ONE_BITS = 2147483648;
|
|
109
|
+
const THIRTY_TWO_BITS = 4294967296;
|
|
110
|
+
if (typeof obj === "number" && Number.isInteger(obj) &&
|
|
111
|
+
obj <= Number.MAX_SAFE_INTEGER && obj >= Number.MIN_SAFE_INTEGER &&
|
|
112
|
+
(obj >= THIRTY_TWO_BITS || obj < -THIRTY_ONE_BITS)) {
|
|
113
|
+
return BigInt(obj);
|
|
114
|
+
}
|
|
115
|
+
if (Array.isArray(obj)) {
|
|
116
|
+
return obj.map(normalizeIntegersForMsgPack);
|
|
117
|
+
}
|
|
118
|
+
if (obj && typeof obj === "object" && obj !== null) {
|
|
119
|
+
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, normalizeIntegersForMsgPack(value)]));
|
|
120
|
+
}
|
|
121
|
+
return obj;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Sign an L1 action.
|
|
125
|
+
*
|
|
126
|
+
* Note: Signature generation depends on the order of the action keys.
|
|
127
|
+
* @param args - Arguments for signing the action.
|
|
128
|
+
* @returns The signature components r, s, and v.
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* import { signL1Action } from "@nktkas/hyperliquid/signing";
|
|
132
|
+
*
|
|
133
|
+
* const privateKey = "0x..."; // or `viem`, `ethers`
|
|
134
|
+
*
|
|
135
|
+
* const action = {
|
|
136
|
+
* type: "cancel",
|
|
137
|
+
* cancels: [
|
|
138
|
+
* { a: 0, o: 12345 }, // Asset index and order ID
|
|
139
|
+
* ],
|
|
140
|
+
* };
|
|
141
|
+
* const nonce = Date.now();
|
|
142
|
+
*
|
|
143
|
+
* const signature = await signL1Action({
|
|
144
|
+
* wallet: privateKey,
|
|
145
|
+
* action,
|
|
146
|
+
* nonce,
|
|
147
|
+
* isTestnet: true, // Change to false for mainnet
|
|
148
|
+
* });
|
|
149
|
+
*
|
|
150
|
+
* const response = await fetch("https://api.hyperliquid-testnet.xyz/exchange", {
|
|
151
|
+
* method: "POST",
|
|
152
|
+
* headers: { "Content-Type": "application/json" },
|
|
153
|
+
* body: JSON.stringify({ action, signature, nonce }),
|
|
154
|
+
* });
|
|
155
|
+
* const body = await response.json();
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export async function signL1Action(args) {
|
|
159
|
+
const { wallet, action, nonce, isTestnet = false, vaultAddress, expiresAfter, } = args;
|
|
160
|
+
const domain = {
|
|
161
|
+
name: "Exchange",
|
|
162
|
+
version: "1",
|
|
163
|
+
chainId: 1337, // hyperliquid requires a fixed chain
|
|
164
|
+
verifyingContract: "0x0000000000000000000000000000000000000000",
|
|
165
|
+
};
|
|
166
|
+
const types = {
|
|
167
|
+
Agent: [
|
|
168
|
+
{ name: "source", type: "string" },
|
|
169
|
+
{ name: "connectionId", type: "bytes32" },
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
const actionHash = createL1ActionHash(action, nonce, vaultAddress, expiresAfter);
|
|
173
|
+
const message = {
|
|
174
|
+
source: isTestnet ? "b" : "a",
|
|
175
|
+
connectionId: actionHash,
|
|
176
|
+
};
|
|
177
|
+
const signature = await abstractSignTypedData({ wallet, domain, types, message });
|
|
178
|
+
return splitSignature(signature);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Sign a user-signed action.
|
|
182
|
+
*
|
|
183
|
+
* Note: Signature generation depends on the order of types.
|
|
184
|
+
*
|
|
185
|
+
* @param args - Arguments for signing the action.
|
|
186
|
+
* @returns The signature components r, s, and v.
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* import { signUserSignedAction } from "@nktkas/hyperliquid/signing";
|
|
190
|
+
*
|
|
191
|
+
* const privateKey = "0x..."; // or `viem`, `ethers`
|
|
192
|
+
*
|
|
193
|
+
* const action = {
|
|
194
|
+
* type: "approveAgent",
|
|
195
|
+
* hyperliquidChain: "Testnet", // "Mainnet" or "Testnet"
|
|
196
|
+
* signatureChainId: "0x66eee",
|
|
197
|
+
* nonce: Date.now(),
|
|
198
|
+
* agentAddress: "0x...", // Change to your agent address
|
|
199
|
+
* agentName: "Agent",
|
|
200
|
+
* };
|
|
201
|
+
*
|
|
202
|
+
* const signature = await signUserSignedAction({
|
|
203
|
+
* wallet: privateKey,
|
|
204
|
+
* action,
|
|
205
|
+
* types: {
|
|
206
|
+
* "HyperliquidTransaction:ApproveAgent": [
|
|
207
|
+
* { name: "hyperliquidChain", type: "string" },
|
|
208
|
+
* { name: "agentAddress", type: "address" },
|
|
209
|
+
* { name: "agentName", type: "string" },
|
|
210
|
+
* { name: "nonce", type: "uint64" },
|
|
211
|
+
* ],
|
|
212
|
+
* },
|
|
213
|
+
* chainId: parseInt(action.signatureChainId, 16),
|
|
214
|
+
* });
|
|
215
|
+
*
|
|
216
|
+
* const response = await fetch("https://api.hyperliquid-testnet.xyz/exchange", {
|
|
217
|
+
* method: "POST",
|
|
218
|
+
* headers: { "Content-Type": "application/json" },
|
|
219
|
+
* body: JSON.stringify({ action, signature, nonce: action.nonce }),
|
|
220
|
+
* });
|
|
221
|
+
* const body = await response.json();
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
export async function signUserSignedAction(args) {
|
|
225
|
+
const { wallet, action, types, chainId } = args;
|
|
226
|
+
const domain = {
|
|
227
|
+
name: "HyperliquidSignTransaction",
|
|
228
|
+
version: "1",
|
|
229
|
+
chainId,
|
|
230
|
+
verifyingContract: "0x0000000000000000000000000000000000000000",
|
|
231
|
+
};
|
|
232
|
+
const signature = await abstractSignTypedData({ wallet, domain, types, message: action });
|
|
233
|
+
return splitSignature(signature);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Sign a multi-signature action.
|
|
237
|
+
*
|
|
238
|
+
* Note: Signature generation depends on the order of the action keys.
|
|
239
|
+
*
|
|
240
|
+
* @param args - Arguments for signing the action.
|
|
241
|
+
* @returns The signature components r, s, and v.
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* import { signL1Action, signMultiSigAction } from "@nktkas/hyperliquid/signing";
|
|
245
|
+
* import { privateKeyToAccount } from "viem/accounts";
|
|
246
|
+
*
|
|
247
|
+
* const wallet = privateKeyToAccount("0x...");
|
|
248
|
+
* const multiSigUser = "0x..."; // Multi-sig user address
|
|
249
|
+
*
|
|
250
|
+
* const nonce = Date.now();
|
|
251
|
+
* const action = { // Example action
|
|
252
|
+
* type: "scheduleCancel",
|
|
253
|
+
* time: Date.now() + 10000
|
|
254
|
+
* };
|
|
255
|
+
*
|
|
256
|
+
* // First, create signature from one of the authorized signers
|
|
257
|
+
* const signature = await signL1Action({
|
|
258
|
+
* wallet,
|
|
259
|
+
* action: [multiSigUser.toLowerCase(), wallet.address.toLowerCase(), action],
|
|
260
|
+
* nonce,
|
|
261
|
+
* isTestnet: true,
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* // Then use it in the multi-sig action
|
|
265
|
+
* const multiSigSignature = await signMultiSigAction({
|
|
266
|
+
* wallet,
|
|
267
|
+
* action: {
|
|
268
|
+
* type: "multiSig",
|
|
269
|
+
* signatureChainId: "0x66eee",
|
|
270
|
+
* signatures: [signature],
|
|
271
|
+
* payload: {
|
|
272
|
+
* multiSigUser,
|
|
273
|
+
* outerSigner: wallet.address,
|
|
274
|
+
* action,
|
|
275
|
+
* }
|
|
276
|
+
* },
|
|
277
|
+
* nonce,
|
|
278
|
+
* hyperliquidChain: "Testnet",
|
|
279
|
+
* signatureChainId: "0x66eee",
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
export async function signMultiSigAction(args) {
|
|
284
|
+
const { wallet, action, nonce, hyperliquidChain, signatureChainId, vaultAddress, expiresAfter, } = args;
|
|
285
|
+
const multiSigActionHash = createL1ActionHash(action, nonce, vaultAddress, expiresAfter);
|
|
286
|
+
const message = {
|
|
287
|
+
multiSigActionHash,
|
|
288
|
+
hyperliquidChain,
|
|
289
|
+
signatureChainId,
|
|
290
|
+
nonce,
|
|
291
|
+
};
|
|
292
|
+
return await signUserSignedAction({
|
|
293
|
+
wallet,
|
|
294
|
+
action: message,
|
|
295
|
+
types: {
|
|
296
|
+
"HyperliquidTransaction:SendMultiSig": [
|
|
297
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
298
|
+
{ name: "multiSigActionHash", type: "bytes32" },
|
|
299
|
+
{ name: "nonce", type: "uint64" },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
chainId: parseInt(signatureChainId, 16),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/** Signs typed data with the provided wallet using EIP-712. */
|
|
306
|
+
async function abstractSignTypedData(args) {
|
|
307
|
+
const { wallet, domain, types, message } = args;
|
|
308
|
+
if (isValidPrivateKey(wallet)) {
|
|
309
|
+
return await signTypedDataWithPrivateKey({
|
|
310
|
+
privateKey: wallet,
|
|
311
|
+
domain,
|
|
312
|
+
types,
|
|
313
|
+
primaryType: Object.keys(types)[0],
|
|
314
|
+
message,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
else if (isAbstractViemWalletClient(wallet)) {
|
|
318
|
+
return await wallet.signTypedData({
|
|
319
|
+
domain,
|
|
320
|
+
types: {
|
|
321
|
+
EIP712Domain: [
|
|
322
|
+
{ name: "name", type: "string" },
|
|
323
|
+
{ name: "version", type: "string" },
|
|
324
|
+
{ name: "chainId", type: "uint256" },
|
|
325
|
+
{ name: "verifyingContract", type: "address" },
|
|
326
|
+
],
|
|
327
|
+
...types,
|
|
328
|
+
},
|
|
329
|
+
primaryType: Object.keys(types)[0],
|
|
330
|
+
message,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
else if (isAbstractEthersSigner(wallet)) {
|
|
334
|
+
return await wallet.signTypedData(domain, types, message);
|
|
335
|
+
}
|
|
336
|
+
else if (isAbstractEthersV5Signer(wallet)) {
|
|
337
|
+
return await wallet._signTypedData(domain, types, message);
|
|
338
|
+
}
|
|
339
|
+
else if (isAbstractWindowEthereum(wallet)) {
|
|
340
|
+
return await signTypedDataWithWindowEthereum(wallet, domain, types, message);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
throw new Error("Unsupported wallet for signing typed data");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/** Splits a signature hexadecimal string into its components. */
|
|
347
|
+
function splitSignature(signature) {
|
|
348
|
+
const r = `0x${signature.slice(2, 66)}`;
|
|
349
|
+
const s = `0x${signature.slice(66, 130)}`;
|
|
350
|
+
const v = parseInt(signature.slice(130, 132), 16);
|
|
351
|
+
return { r, s, v };
|
|
352
|
+
}
|
|
@@ -70,7 +70,7 @@ export interface ApproveAgentRequest extends BaseExchangeRequest {
|
|
|
70
70
|
/** Agent address. */
|
|
71
71
|
agentAddress: Hex;
|
|
72
72
|
/** Agent name or undefined for unnamed agent. */
|
|
73
|
-
agentName?: string;
|
|
73
|
+
agentName?: string | null;
|
|
74
74
|
/** Unique request identifier (current timestamp in ms). */
|
|
75
75
|
nonce: number;
|
|
76
76
|
};
|