@ampvaleo/x402-hyperliquid-client 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Valeo Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @ampvaleo/x402-hyperliquid-client
2
+
3
+ Sign `sendAsset` payments and perform automatic x402 retries via `x402Fetch`.
4
+
5
+ ```ts
6
+ import { x402Fetch } from "@ampvaleo/x402-hyperliquid-client";
7
+ import { privateKeyToAccount } from "viem/accounts";
8
+
9
+ const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
10
+ const wallet = {
11
+ getAddress: async () => account.address,
12
+ signTypedData: (a) => account.signTypedData(a),
13
+ };
14
+
15
+ const res = await x402Fetch("https://api.example.com/paid", {}, wallet);
16
+ ```
17
+
18
+ Optional **ethers v6** `Signer` helper: `createEthersWallet(signer)`.
19
+
20
+ See the [root README](../../README.md).
package/dist/index.cjs ADDED
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createEthersWallet: () => createEthersWallet,
24
+ createViemWallet: () => createViemWallet,
25
+ signSendAsset: () => signSendAsset,
26
+ signatureHexToComponents: () => signatureHexToComponents,
27
+ x402Fetch: () => x402Fetch
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/fetch.ts
32
+ var import_x402_hyperliquid_core3 = require("@ampvaleo/x402-hyperliquid-core");
33
+
34
+ // src/sign.ts
35
+ var import_x402_hyperliquid_core = require("@ampvaleo/x402-hyperliquid-core");
36
+ var import_x402_hyperliquid_core2 = require("@ampvaleo/x402-hyperliquid-core");
37
+ var import_viem = require("viem");
38
+ function signatureHexToComponents(sig) {
39
+ const bytes = (0, import_viem.hexToBytes)(sig);
40
+ if (bytes.length !== 65) {
41
+ throw new Error(`Expected 65-byte signature, got ${bytes.length}`);
42
+ }
43
+ const r = (0, import_viem.bytesToHex)(bytes.slice(0, 32));
44
+ const s = (0, import_viem.bytesToHex)(bytes.slice(32, 64));
45
+ let v = bytes[64];
46
+ if (v === 0 || v === 1) {
47
+ v += 27;
48
+ }
49
+ return { r, s, v };
50
+ }
51
+ async function signSendAsset(wallet, requirements, options = {}) {
52
+ if (requirements.scheme !== import_x402_hyperliquid_core.HYPERLIQUID_SENDASSET_SCHEME) {
53
+ throw new Error(`Unsupported scheme: ${requirements.scheme}`);
54
+ }
55
+ const extra = requirements.extra;
56
+ const chainName = extra.chain;
57
+ const chain = (0, import_x402_hyperliquid_core.getChainConfig)(chainName);
58
+ const amount = options.amount ?? requirements.maxAmountRequired;
59
+ const nonceMs = options.nonceMs ?? Date.now();
60
+ const nonce = BigInt(nonceMs);
61
+ const message = {
62
+ hyperliquidChain: chain.hyperliquidLabel,
63
+ destination: requirements.payTo,
64
+ sourceDex: extra.sourceDex,
65
+ destinationDex: extra.destinationDex,
66
+ token: extra.token,
67
+ amount,
68
+ fromSubAccount: "",
69
+ nonce
70
+ };
71
+ const typed = (0, import_x402_hyperliquid_core.buildSendAssetTypedData)({
72
+ chainId: chain.chainId,
73
+ message
74
+ });
75
+ const sigHex = await wallet.signTypedData({
76
+ domain: typed.domain,
77
+ types: import_x402_hyperliquid_core2.VIEM_SEND_ASSET_TYPES,
78
+ primaryType: typed.primaryType,
79
+ message: typed.message
80
+ });
81
+ const { r, s, v } = signatureHexToComponents(sigHex);
82
+ const action = {
83
+ type: "sendAsset",
84
+ signatureChainId: chain.signatureChainId,
85
+ hyperliquidChain: chain.hyperliquidLabel,
86
+ destination: requirements.payTo,
87
+ sourceDex: extra.sourceDex,
88
+ destinationDex: extra.destinationDex,
89
+ token: extra.token,
90
+ amount,
91
+ fromSubAccount: "",
92
+ nonce: nonceMs
93
+ };
94
+ return {
95
+ scheme: import_x402_hyperliquid_core.HYPERLIQUID_SENDASSET_SCHEME,
96
+ network: chain.network,
97
+ nonce: nonceMs,
98
+ signatureChainId: chain.signatureChainId,
99
+ signature: { r, s, v },
100
+ action,
101
+ accepted: {
102
+ ...requirements,
103
+ maxAmountRequired: requirements.maxAmountRequired
104
+ }
105
+ };
106
+ }
107
+
108
+ // src/fetch.ts
109
+ async function x402Fetch(input, init, wallet, options = {}) {
110
+ const first = await fetch(input, init);
111
+ if (first.status !== 402) {
112
+ return first;
113
+ }
114
+ let body;
115
+ try {
116
+ body = await first.json();
117
+ } catch {
118
+ return first;
119
+ }
120
+ const accepts = body.accepts;
121
+ if (!accepts?.length) {
122
+ return first;
123
+ }
124
+ const chosen = options.selectRequirement?.(accepts) ?? accepts.find((a) => a.scheme === import_x402_hyperliquid_core3.HYPERLIQUID_SENDASSET_SCHEME);
125
+ if (!chosen) {
126
+ return first;
127
+ }
128
+ const payload = await signSendAsset(wallet, chosen);
129
+ const payment = (0, import_x402_hyperliquid_core3.encodePaymentHeader)(payload);
130
+ const headers = new Headers(init?.headers ?? void 0);
131
+ headers.set("X-PAYMENT", payment);
132
+ return fetch(input, {
133
+ ...init,
134
+ headers
135
+ });
136
+ }
137
+
138
+ // src/wallet.ts
139
+ function createViemWallet(client, account) {
140
+ return {
141
+ getAddress: async () => account,
142
+ signTypedData: (args) => client.signTypedData({
143
+ account,
144
+ domain: args.domain,
145
+ types: args.types,
146
+ primaryType: args.primaryType,
147
+ message: {
148
+ ...args.message,
149
+ nonce: args.message.nonce
150
+ }
151
+ })
152
+ };
153
+ }
154
+ async function createEthersWallet(signer) {
155
+ const address = await signer.getAddress();
156
+ return {
157
+ getAddress: async () => address,
158
+ signTypedData: async (args) => {
159
+ const sig = await signer.signTypedData(
160
+ args.domain,
161
+ args.types,
162
+ args.message
163
+ );
164
+ return sig;
165
+ }
166
+ };
167
+ }
168
+ // Annotate the CommonJS export names for ESM import in node:
169
+ 0 && (module.exports = {
170
+ createEthersWallet,
171
+ createViemWallet,
172
+ signSendAsset,
173
+ signatureHexToComponents,
174
+ x402Fetch
175
+ });
176
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/fetch.ts","../src/sign.ts","../src/wallet.ts"],"sourcesContent":["export { x402Fetch, type X402FetchOptions } from \"./fetch.js\";\nexport { signSendAsset, signatureHexToComponents, type SignSendAssetOptions } from \"./sign.js\";\nexport {\n createEthersWallet,\n createViemWallet,\n type HyperliquidWallet,\n} from \"./wallet.js\";\n","import {\n HYPERLIQUID_SENDASSET_SCHEME,\n encodePaymentHeader,\n type PaymentRequirements,\n} from \"@ampvaleo/x402-hyperliquid-core\";\n\nimport { signSendAsset } from \"./sign.js\";\nimport type { HyperliquidWallet } from \"./wallet.js\";\n\nexport interface X402FetchOptions {\n /** Choose which accepted requirement to pay; defaults to the first Hyperliquid sendAsset entry */\n selectRequirement?: (\n accepts: PaymentRequirements[],\n ) => PaymentRequirements | undefined;\n}\n\n/**\n * Drop-in `fetch` that automatically pays Hyperliquid x402 challenges (402 + `accepts`).\n */\nexport async function x402Fetch(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n wallet: HyperliquidWallet,\n options: X402FetchOptions = {},\n): Promise<Response> {\n const first = await fetch(input, init);\n if (first.status !== 402) {\n return first;\n }\n\n let body: unknown;\n try {\n body = await first.json();\n } catch {\n return first;\n }\n\n const accepts = (body as { accepts?: PaymentRequirements[] }).accepts;\n if (!accepts?.length) {\n return first;\n }\n\n const chosen =\n options.selectRequirement?.(accepts) ??\n accepts.find((a) => a.scheme === HYPERLIQUID_SENDASSET_SCHEME);\n\n if (!chosen) {\n return first;\n }\n\n const payload = await signSendAsset(wallet, chosen);\n const payment = encodePaymentHeader(payload);\n\n const headers = new Headers(init?.headers ?? undefined);\n headers.set(\"X-PAYMENT\", payment);\n\n return fetch(input, {\n ...init,\n headers,\n });\n}\n","import type { PaymentPayload, PaymentRequirements } from \"@ampvaleo/x402-hyperliquid-core\";\nimport {\n HYPERLIQUID_SENDASSET_SCHEME,\n buildSendAssetTypedData,\n getChainConfig,\n type HyperliquidChainName,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport { VIEM_SEND_ASSET_TYPES } from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Hex } from \"viem\";\nimport { bytesToHex, hexToBytes } from \"viem\";\n\nimport type { HyperliquidWallet } from \"./wallet.js\";\n\nexport interface SignSendAssetOptions {\n /** Defaults to `requirements.maxAmountRequired` */\n amount?: string;\n /** Millisecond nonce; defaults to `Date.now()` */\n nonceMs?: number;\n}\n\nexport function signatureHexToComponents(sig: Hex): {\n r: Hex;\n s: Hex;\n v: number;\n} {\n const bytes = hexToBytes(sig);\n if (bytes.length !== 65) {\n throw new Error(`Expected 65-byte signature, got ${bytes.length}`);\n }\n const r = bytesToHex(bytes.slice(0, 32));\n const s = bytesToHex(bytes.slice(32, 64));\n let v = bytes[64]!;\n if (v === 0 || v === 1) {\n v += 27;\n }\n return { r, s, v };\n}\n\n/**\n * Build and sign a Hyperliquid sendAsset payment for the given x402 requirements.\n */\nexport async function signSendAsset(\n wallet: HyperliquidWallet,\n requirements: PaymentRequirements,\n options: SignSendAssetOptions = {},\n): Promise<PaymentPayload> {\n if (requirements.scheme !== HYPERLIQUID_SENDASSET_SCHEME) {\n throw new Error(`Unsupported scheme: ${requirements.scheme}`);\n }\n const extra = requirements.extra;\n const chainName = extra.chain as HyperliquidChainName;\n const chain = getChainConfig(chainName);\n const amount = options.amount ?? requirements.maxAmountRequired;\n const nonceMs = options.nonceMs ?? Date.now();\n const nonce = BigInt(nonceMs);\n\n const message = {\n hyperliquidChain: chain.hyperliquidLabel,\n destination: requirements.payTo,\n sourceDex: extra.sourceDex,\n destinationDex: extra.destinationDex,\n token: extra.token,\n amount,\n fromSubAccount: \"\",\n nonce,\n };\n\n const typed = buildSendAssetTypedData({\n chainId: chain.chainId,\n message,\n });\n\n const sigHex = await wallet.signTypedData({\n domain: typed.domain,\n types: VIEM_SEND_ASSET_TYPES,\n primaryType: typed.primaryType,\n message: typed.message,\n });\n\n const { r, s, v } = signatureHexToComponents(sigHex);\n\n const action = {\n type: \"sendAsset\" as const,\n signatureChainId: chain.signatureChainId,\n hyperliquidChain: chain.hyperliquidLabel,\n destination: requirements.payTo,\n sourceDex: extra.sourceDex,\n destinationDex: extra.destinationDex,\n token: extra.token,\n amount,\n fromSubAccount: \"\",\n nonce: nonceMs,\n };\n\n return {\n scheme: HYPERLIQUID_SENDASSET_SCHEME,\n network: chain.network,\n nonce: nonceMs,\n signatureChainId: chain.signatureChainId,\n signature: { r, s, v },\n action,\n accepted: {\n ...requirements,\n maxAmountRequired: requirements.maxAmountRequired,\n },\n };\n}\n","import type {\n PaymentRequirements,\n SendAssetEip712Message,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport {\n SEND_ASSET_PRIMARY_TYPE,\n VIEM_SEND_ASSET_TYPES,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Address, Hex, TypedDataDomain } from \"viem\";\nimport type { WalletClient } from \"viem\";\n\n/** Minimal wallet surface for Hyperliquid EIP-712 sendAsset */\nexport interface HyperliquidWallet {\n getAddress(): Promise<Address>;\n signTypedData(args: {\n domain: TypedDataDomain;\n types: typeof VIEM_SEND_ASSET_TYPES;\n primaryType: typeof SEND_ASSET_PRIMARY_TYPE;\n message: SendAssetEip712Message;\n }): Promise<Hex>;\n}\n\nexport function createViemWallet(\n client: WalletClient,\n account: Address,\n): HyperliquidWallet {\n return {\n getAddress: async () => account,\n signTypedData: (args) =>\n client.signTypedData({\n account,\n domain: args.domain,\n types: args.types,\n primaryType: args.primaryType,\n message: {\n ...args.message,\n nonce: args.message.nonce,\n },\n }),\n };\n}\n\n/** ethers v6 Signer — optional peer dependency */\nexport async function createEthersWallet(\n signer: import(\"ethers\").Signer,\n): Promise<HyperliquidWallet> {\n const address = (await signer.getAddress()) as Address;\n return {\n getAddress: async () => address,\n signTypedData: async (args) => {\n const sig = await signer.signTypedData(\n args.domain,\n args.types as Record<string, import(\"ethers\").TypedDataField[]>,\n args.message as unknown as Record<string, unknown>,\n );\n return sig as Hex;\n },\n };\n}\n\nexport type { PaymentRequirements };\n"],"mappings":"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","names":["import_x402_hyperliquid_core","import_x402_hyperliquid_core"]}
@@ -0,0 +1,44 @@
1
+ import { VIEM_SEND_ASSET_TYPES, SEND_ASSET_PRIMARY_TYPE, SendAssetEip712Message, PaymentRequirements, PaymentPayload } from '@ampvaleo/x402-hyperliquid-core';
2
+ import * as ethers from 'ethers';
3
+ import { Address, TypedDataDomain, Hex, WalletClient } from 'viem';
4
+
5
+ /** Minimal wallet surface for Hyperliquid EIP-712 sendAsset */
6
+ interface HyperliquidWallet {
7
+ getAddress(): Promise<Address>;
8
+ signTypedData(args: {
9
+ domain: TypedDataDomain;
10
+ types: typeof VIEM_SEND_ASSET_TYPES;
11
+ primaryType: typeof SEND_ASSET_PRIMARY_TYPE;
12
+ message: SendAssetEip712Message;
13
+ }): Promise<Hex>;
14
+ }
15
+ declare function createViemWallet(client: WalletClient, account: Address): HyperliquidWallet;
16
+ /** ethers v6 Signer — optional peer dependency */
17
+ declare function createEthersWallet(signer: ethers.Signer): Promise<HyperliquidWallet>;
18
+
19
+ interface X402FetchOptions {
20
+ /** Choose which accepted requirement to pay; defaults to the first Hyperliquid sendAsset entry */
21
+ selectRequirement?: (accepts: PaymentRequirements[]) => PaymentRequirements | undefined;
22
+ }
23
+ /**
24
+ * Drop-in `fetch` that automatically pays Hyperliquid x402 challenges (402 + `accepts`).
25
+ */
26
+ declare function x402Fetch(input: RequestInfo | URL, init: RequestInit | undefined, wallet: HyperliquidWallet, options?: X402FetchOptions): Promise<Response>;
27
+
28
+ interface SignSendAssetOptions {
29
+ /** Defaults to `requirements.maxAmountRequired` */
30
+ amount?: string;
31
+ /** Millisecond nonce; defaults to `Date.now()` */
32
+ nonceMs?: number;
33
+ }
34
+ declare function signatureHexToComponents(sig: Hex): {
35
+ r: Hex;
36
+ s: Hex;
37
+ v: number;
38
+ };
39
+ /**
40
+ * Build and sign a Hyperliquid sendAsset payment for the given x402 requirements.
41
+ */
42
+ declare function signSendAsset(wallet: HyperliquidWallet, requirements: PaymentRequirements, options?: SignSendAssetOptions): Promise<PaymentPayload>;
43
+
44
+ export { type HyperliquidWallet, type SignSendAssetOptions, type X402FetchOptions, createEthersWallet, createViemWallet, signSendAsset, signatureHexToComponents, x402Fetch };
@@ -0,0 +1,44 @@
1
+ import { VIEM_SEND_ASSET_TYPES, SEND_ASSET_PRIMARY_TYPE, SendAssetEip712Message, PaymentRequirements, PaymentPayload } from '@ampvaleo/x402-hyperliquid-core';
2
+ import * as ethers from 'ethers';
3
+ import { Address, TypedDataDomain, Hex, WalletClient } from 'viem';
4
+
5
+ /** Minimal wallet surface for Hyperliquid EIP-712 sendAsset */
6
+ interface HyperliquidWallet {
7
+ getAddress(): Promise<Address>;
8
+ signTypedData(args: {
9
+ domain: TypedDataDomain;
10
+ types: typeof VIEM_SEND_ASSET_TYPES;
11
+ primaryType: typeof SEND_ASSET_PRIMARY_TYPE;
12
+ message: SendAssetEip712Message;
13
+ }): Promise<Hex>;
14
+ }
15
+ declare function createViemWallet(client: WalletClient, account: Address): HyperliquidWallet;
16
+ /** ethers v6 Signer — optional peer dependency */
17
+ declare function createEthersWallet(signer: ethers.Signer): Promise<HyperliquidWallet>;
18
+
19
+ interface X402FetchOptions {
20
+ /** Choose which accepted requirement to pay; defaults to the first Hyperliquid sendAsset entry */
21
+ selectRequirement?: (accepts: PaymentRequirements[]) => PaymentRequirements | undefined;
22
+ }
23
+ /**
24
+ * Drop-in `fetch` that automatically pays Hyperliquid x402 challenges (402 + `accepts`).
25
+ */
26
+ declare function x402Fetch(input: RequestInfo | URL, init: RequestInit | undefined, wallet: HyperliquidWallet, options?: X402FetchOptions): Promise<Response>;
27
+
28
+ interface SignSendAssetOptions {
29
+ /** Defaults to `requirements.maxAmountRequired` */
30
+ amount?: string;
31
+ /** Millisecond nonce; defaults to `Date.now()` */
32
+ nonceMs?: number;
33
+ }
34
+ declare function signatureHexToComponents(sig: Hex): {
35
+ r: Hex;
36
+ s: Hex;
37
+ v: number;
38
+ };
39
+ /**
40
+ * Build and sign a Hyperliquid sendAsset payment for the given x402 requirements.
41
+ */
42
+ declare function signSendAsset(wallet: HyperliquidWallet, requirements: PaymentRequirements, options?: SignSendAssetOptions): Promise<PaymentPayload>;
43
+
44
+ export { type HyperliquidWallet, type SignSendAssetOptions, type X402FetchOptions, createEthersWallet, createViemWallet, signSendAsset, signatureHexToComponents, x402Fetch };
package/dist/index.js ADDED
@@ -0,0 +1,152 @@
1
+ // src/fetch.ts
2
+ import {
3
+ HYPERLIQUID_SENDASSET_SCHEME as HYPERLIQUID_SENDASSET_SCHEME2,
4
+ encodePaymentHeader
5
+ } from "@ampvaleo/x402-hyperliquid-core";
6
+
7
+ // src/sign.ts
8
+ import {
9
+ HYPERLIQUID_SENDASSET_SCHEME,
10
+ buildSendAssetTypedData,
11
+ getChainConfig
12
+ } from "@ampvaleo/x402-hyperliquid-core";
13
+ import { VIEM_SEND_ASSET_TYPES } from "@ampvaleo/x402-hyperliquid-core";
14
+ import { bytesToHex, hexToBytes } from "viem";
15
+ function signatureHexToComponents(sig) {
16
+ const bytes = hexToBytes(sig);
17
+ if (bytes.length !== 65) {
18
+ throw new Error(`Expected 65-byte signature, got ${bytes.length}`);
19
+ }
20
+ const r = bytesToHex(bytes.slice(0, 32));
21
+ const s = bytesToHex(bytes.slice(32, 64));
22
+ let v = bytes[64];
23
+ if (v === 0 || v === 1) {
24
+ v += 27;
25
+ }
26
+ return { r, s, v };
27
+ }
28
+ async function signSendAsset(wallet, requirements, options = {}) {
29
+ if (requirements.scheme !== HYPERLIQUID_SENDASSET_SCHEME) {
30
+ throw new Error(`Unsupported scheme: ${requirements.scheme}`);
31
+ }
32
+ const extra = requirements.extra;
33
+ const chainName = extra.chain;
34
+ const chain = getChainConfig(chainName);
35
+ const amount = options.amount ?? requirements.maxAmountRequired;
36
+ const nonceMs = options.nonceMs ?? Date.now();
37
+ const nonce = BigInt(nonceMs);
38
+ const message = {
39
+ hyperliquidChain: chain.hyperliquidLabel,
40
+ destination: requirements.payTo,
41
+ sourceDex: extra.sourceDex,
42
+ destinationDex: extra.destinationDex,
43
+ token: extra.token,
44
+ amount,
45
+ fromSubAccount: "",
46
+ nonce
47
+ };
48
+ const typed = buildSendAssetTypedData({
49
+ chainId: chain.chainId,
50
+ message
51
+ });
52
+ const sigHex = await wallet.signTypedData({
53
+ domain: typed.domain,
54
+ types: VIEM_SEND_ASSET_TYPES,
55
+ primaryType: typed.primaryType,
56
+ message: typed.message
57
+ });
58
+ const { r, s, v } = signatureHexToComponents(sigHex);
59
+ const action = {
60
+ type: "sendAsset",
61
+ signatureChainId: chain.signatureChainId,
62
+ hyperliquidChain: chain.hyperliquidLabel,
63
+ destination: requirements.payTo,
64
+ sourceDex: extra.sourceDex,
65
+ destinationDex: extra.destinationDex,
66
+ token: extra.token,
67
+ amount,
68
+ fromSubAccount: "",
69
+ nonce: nonceMs
70
+ };
71
+ return {
72
+ scheme: HYPERLIQUID_SENDASSET_SCHEME,
73
+ network: chain.network,
74
+ nonce: nonceMs,
75
+ signatureChainId: chain.signatureChainId,
76
+ signature: { r, s, v },
77
+ action,
78
+ accepted: {
79
+ ...requirements,
80
+ maxAmountRequired: requirements.maxAmountRequired
81
+ }
82
+ };
83
+ }
84
+
85
+ // src/fetch.ts
86
+ async function x402Fetch(input, init, wallet, options = {}) {
87
+ const first = await fetch(input, init);
88
+ if (first.status !== 402) {
89
+ return first;
90
+ }
91
+ let body;
92
+ try {
93
+ body = await first.json();
94
+ } catch {
95
+ return first;
96
+ }
97
+ const accepts = body.accepts;
98
+ if (!accepts?.length) {
99
+ return first;
100
+ }
101
+ const chosen = options.selectRequirement?.(accepts) ?? accepts.find((a) => a.scheme === HYPERLIQUID_SENDASSET_SCHEME2);
102
+ if (!chosen) {
103
+ return first;
104
+ }
105
+ const payload = await signSendAsset(wallet, chosen);
106
+ const payment = encodePaymentHeader(payload);
107
+ const headers = new Headers(init?.headers ?? void 0);
108
+ headers.set("X-PAYMENT", payment);
109
+ return fetch(input, {
110
+ ...init,
111
+ headers
112
+ });
113
+ }
114
+
115
+ // src/wallet.ts
116
+ function createViemWallet(client, account) {
117
+ return {
118
+ getAddress: async () => account,
119
+ signTypedData: (args) => client.signTypedData({
120
+ account,
121
+ domain: args.domain,
122
+ types: args.types,
123
+ primaryType: args.primaryType,
124
+ message: {
125
+ ...args.message,
126
+ nonce: args.message.nonce
127
+ }
128
+ })
129
+ };
130
+ }
131
+ async function createEthersWallet(signer) {
132
+ const address = await signer.getAddress();
133
+ return {
134
+ getAddress: async () => address,
135
+ signTypedData: async (args) => {
136
+ const sig = await signer.signTypedData(
137
+ args.domain,
138
+ args.types,
139
+ args.message
140
+ );
141
+ return sig;
142
+ }
143
+ };
144
+ }
145
+ export {
146
+ createEthersWallet,
147
+ createViemWallet,
148
+ signSendAsset,
149
+ signatureHexToComponents,
150
+ x402Fetch
151
+ };
152
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fetch.ts","../src/sign.ts","../src/wallet.ts"],"sourcesContent":["import {\n HYPERLIQUID_SENDASSET_SCHEME,\n encodePaymentHeader,\n type PaymentRequirements,\n} from \"@ampvaleo/x402-hyperliquid-core\";\n\nimport { signSendAsset } from \"./sign.js\";\nimport type { HyperliquidWallet } from \"./wallet.js\";\n\nexport interface X402FetchOptions {\n /** Choose which accepted requirement to pay; defaults to the first Hyperliquid sendAsset entry */\n selectRequirement?: (\n accepts: PaymentRequirements[],\n ) => PaymentRequirements | undefined;\n}\n\n/**\n * Drop-in `fetch` that automatically pays Hyperliquid x402 challenges (402 + `accepts`).\n */\nexport async function x402Fetch(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n wallet: HyperliquidWallet,\n options: X402FetchOptions = {},\n): Promise<Response> {\n const first = await fetch(input, init);\n if (first.status !== 402) {\n return first;\n }\n\n let body: unknown;\n try {\n body = await first.json();\n } catch {\n return first;\n }\n\n const accepts = (body as { accepts?: PaymentRequirements[] }).accepts;\n if (!accepts?.length) {\n return first;\n }\n\n const chosen =\n options.selectRequirement?.(accepts) ??\n accepts.find((a) => a.scheme === HYPERLIQUID_SENDASSET_SCHEME);\n\n if (!chosen) {\n return first;\n }\n\n const payload = await signSendAsset(wallet, chosen);\n const payment = encodePaymentHeader(payload);\n\n const headers = new Headers(init?.headers ?? undefined);\n headers.set(\"X-PAYMENT\", payment);\n\n return fetch(input, {\n ...init,\n headers,\n });\n}\n","import type { PaymentPayload, PaymentRequirements } from \"@ampvaleo/x402-hyperliquid-core\";\nimport {\n HYPERLIQUID_SENDASSET_SCHEME,\n buildSendAssetTypedData,\n getChainConfig,\n type HyperliquidChainName,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport { VIEM_SEND_ASSET_TYPES } from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Hex } from \"viem\";\nimport { bytesToHex, hexToBytes } from \"viem\";\n\nimport type { HyperliquidWallet } from \"./wallet.js\";\n\nexport interface SignSendAssetOptions {\n /** Defaults to `requirements.maxAmountRequired` */\n amount?: string;\n /** Millisecond nonce; defaults to `Date.now()` */\n nonceMs?: number;\n}\n\nexport function signatureHexToComponents(sig: Hex): {\n r: Hex;\n s: Hex;\n v: number;\n} {\n const bytes = hexToBytes(sig);\n if (bytes.length !== 65) {\n throw new Error(`Expected 65-byte signature, got ${bytes.length}`);\n }\n const r = bytesToHex(bytes.slice(0, 32));\n const s = bytesToHex(bytes.slice(32, 64));\n let v = bytes[64]!;\n if (v === 0 || v === 1) {\n v += 27;\n }\n return { r, s, v };\n}\n\n/**\n * Build and sign a Hyperliquid sendAsset payment for the given x402 requirements.\n */\nexport async function signSendAsset(\n wallet: HyperliquidWallet,\n requirements: PaymentRequirements,\n options: SignSendAssetOptions = {},\n): Promise<PaymentPayload> {\n if (requirements.scheme !== HYPERLIQUID_SENDASSET_SCHEME) {\n throw new Error(`Unsupported scheme: ${requirements.scheme}`);\n }\n const extra = requirements.extra;\n const chainName = extra.chain as HyperliquidChainName;\n const chain = getChainConfig(chainName);\n const amount = options.amount ?? requirements.maxAmountRequired;\n const nonceMs = options.nonceMs ?? Date.now();\n const nonce = BigInt(nonceMs);\n\n const message = {\n hyperliquidChain: chain.hyperliquidLabel,\n destination: requirements.payTo,\n sourceDex: extra.sourceDex,\n destinationDex: extra.destinationDex,\n token: extra.token,\n amount,\n fromSubAccount: \"\",\n nonce,\n };\n\n const typed = buildSendAssetTypedData({\n chainId: chain.chainId,\n message,\n });\n\n const sigHex = await wallet.signTypedData({\n domain: typed.domain,\n types: VIEM_SEND_ASSET_TYPES,\n primaryType: typed.primaryType,\n message: typed.message,\n });\n\n const { r, s, v } = signatureHexToComponents(sigHex);\n\n const action = {\n type: \"sendAsset\" as const,\n signatureChainId: chain.signatureChainId,\n hyperliquidChain: chain.hyperliquidLabel,\n destination: requirements.payTo,\n sourceDex: extra.sourceDex,\n destinationDex: extra.destinationDex,\n token: extra.token,\n amount,\n fromSubAccount: \"\",\n nonce: nonceMs,\n };\n\n return {\n scheme: HYPERLIQUID_SENDASSET_SCHEME,\n network: chain.network,\n nonce: nonceMs,\n signatureChainId: chain.signatureChainId,\n signature: { r, s, v },\n action,\n accepted: {\n ...requirements,\n maxAmountRequired: requirements.maxAmountRequired,\n },\n };\n}\n","import type {\n PaymentRequirements,\n SendAssetEip712Message,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport {\n SEND_ASSET_PRIMARY_TYPE,\n VIEM_SEND_ASSET_TYPES,\n} from \"@ampvaleo/x402-hyperliquid-core\";\nimport type { Address, Hex, TypedDataDomain } from \"viem\";\nimport type { WalletClient } from \"viem\";\n\n/** Minimal wallet surface for Hyperliquid EIP-712 sendAsset */\nexport interface HyperliquidWallet {\n getAddress(): Promise<Address>;\n signTypedData(args: {\n domain: TypedDataDomain;\n types: typeof VIEM_SEND_ASSET_TYPES;\n primaryType: typeof SEND_ASSET_PRIMARY_TYPE;\n message: SendAssetEip712Message;\n }): Promise<Hex>;\n}\n\nexport function createViemWallet(\n client: WalletClient,\n account: Address,\n): HyperliquidWallet {\n return {\n getAddress: async () => account,\n signTypedData: (args) =>\n client.signTypedData({\n account,\n domain: args.domain,\n types: args.types,\n primaryType: args.primaryType,\n message: {\n ...args.message,\n nonce: args.message.nonce,\n },\n }),\n };\n}\n\n/** ethers v6 Signer — optional peer dependency */\nexport async function createEthersWallet(\n signer: import(\"ethers\").Signer,\n): Promise<HyperliquidWallet> {\n const address = (await signer.getAddress()) as Address;\n return {\n getAddress: async () => address,\n signTypedData: async (args) => {\n const sig = await signer.signTypedData(\n args.domain,\n args.types as Record<string, import(\"ethers\").TypedDataField[]>,\n args.message as unknown as Record<string, unknown>,\n );\n return sig as Hex;\n },\n };\n}\n\nexport type { PaymentRequirements };\n"],"mappings":"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","names":["HYPERLIQUID_SENDASSET_SCHEME","HYPERLIQUID_SENDASSET_SCHEME"]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@ampvaleo/x402-hyperliquid-client",
3
+ "version": "0.1.0",
4
+ "description": "Sign and fetch helpers for x402 on Hyperliquid HyperCore",
5
+ "author": "Valeo Protocol",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/valeo-cash/x402-hyperliquid.git",
10
+ "directory": "packages/client"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.cjs",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js",
23
+ "require": "./dist/index.cjs"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "dependencies": {
31
+ "viem": "^2.21.54",
32
+ "@ampvaleo/x402-hyperliquid-core": "0.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "ethers": "^6.0.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "ethers": {
39
+ "optional": true
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.10.2",
44
+ "tsup": "^8.3.5",
45
+ "typescript": "^5.7.2",
46
+ "vitest": "^2.1.8"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "test": "vitest run"
51
+ }
52
+ }