@alchemy/x402 0.2.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/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # @alchemy/x402
2
+
3
+ CLI and library for Alchemy x402 authentication and payments. Handles SIWE (Sign-In With Ethereum) auth and x402 per-request payments for the Alchemy agentic gateway.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @alchemy/x402
9
+ ```
10
+
11
+ ## CLI
12
+
13
+ ```bash
14
+ # Generate a new wallet
15
+ npx alchemy-x402 wallet generate
16
+
17
+ # Import an existing wallet (accepts key or file path)
18
+ npx alchemy-x402 wallet import --private-key <key>
19
+
20
+ # Generate a SIWE token
21
+ npx alchemy-x402 sign-siwe --private-key <key> --expires-after 1h
22
+
23
+ # Create an x402 payment from a PAYMENT-REQUIRED header
24
+ npx alchemy-x402 pay --private-key <key> --payment-required <header>
25
+ ```
26
+
27
+ ## Library
28
+
29
+ ```ts
30
+ import { signSiwe, generateWallet, getWalletAddress, createPayment, buildX402Client } from "@alchemy/x402";
31
+ ```
32
+
33
+ ### Generate a wallet
34
+
35
+ ```ts
36
+ const wallet = generateWallet();
37
+ // { privateKey: "0x...", address: "0x..." }
38
+
39
+ const address = getWalletAddress("0x<private-key>");
40
+ ```
41
+
42
+ ### Sign a SIWE token
43
+
44
+ ```ts
45
+ const token = await signSiwe({
46
+ privateKey: "0x<private-key>",
47
+ expiresAfter: "1h", // optional, default "1h"
48
+ });
49
+ ```
50
+
51
+ ### Create an x402 payment
52
+
53
+ ```ts
54
+ const paymentHeader = await createPayment({
55
+ privateKey: "0x<private-key>",
56
+ paymentRequiredHeader: "<raw PAYMENT-REQUIRED header value>",
57
+ });
58
+ ```
59
+
60
+ ### Use with @x402/fetch
61
+
62
+ For full request orchestration with automatic 402 payment handling, use `buildX402Client` with `@x402/fetch`:
63
+
64
+ ```ts
65
+ import { buildX402Client, signSiwe } from "@alchemy/x402";
66
+ import { wrapFetchWithPayment } from "@x402/fetch";
67
+
68
+ const privateKey = "0x<private-key>";
69
+ const client = buildX402Client(privateKey);
70
+ const siweToken = await signSiwe({ privateKey });
71
+
72
+ // Wrap fetch with SIWE auth
73
+ const authedFetch: typeof fetch = async (input, init) => {
74
+ const headers = new Headers(init?.headers);
75
+ headers.set("Authorization", `SIWE ${siweToken}`);
76
+ return fetch(input, { ...init, headers });
77
+ };
78
+
79
+ // Wrap with automatic x402 payment handling
80
+ const paymentFetch = wrapFetchWithPayment(authedFetch, client);
81
+
82
+ const response = await paymentFetch("https://x402.alchemy.com/...");
83
+ ```
84
+
85
+ ## Private key input
86
+
87
+ All commands and functions accept private keys as:
88
+
89
+ - Hex string with `0x` prefix: `0xac09...`
90
+ - Raw hex string: `ac09...`
91
+ - File path: `/path/to/keyfile`
92
+
93
+ ## Development
94
+
95
+ ### Prerequisites
96
+
97
+ - Node.js >= 20
98
+ - [pnpm](https://pnpm.io/)
99
+
100
+ **Option A** — [mise](https://mise.jdx.dev/) (recommended, installs both Node and pnpm from `.tool-versions`):
101
+
102
+ ```bash
103
+ mise install
104
+ ```
105
+
106
+ **Option B** — [corepack](https://nodejs.org/api/corepack.html) (ships with Node, reads `packageManager` from package.json):
107
+
108
+ ```bash
109
+ corepack enable
110
+ ```
111
+
112
+ ### Getting started
113
+
114
+ ```bash
115
+ git clone git@github.com:alchemyplatform/alchemy-x402.git
116
+ cd alchemy-x402
117
+ pnpm install
118
+ pnpm run build
119
+ pnpm run typecheck
120
+ pnpm test
121
+ ```
122
+
123
+ ## For maintainers
124
+
125
+ ### Adding a changeset
126
+
127
+ This project uses [Changesets](https://github.com/changesets/changesets) for versioning and npm publishing.
128
+
129
+ When your PR includes a user-facing change, add a changeset:
130
+
131
+ ```bash
132
+ pnpm run changeset
133
+ ```
134
+
135
+ Select the semver bump type (patch/minor/major) and describe the change. Commit the generated `.changeset/*.md` file with your PR.
136
+
137
+ ### Release flow
138
+
139
+ 1. Merge PRs with changeset files to `main`
140
+ 2. CI automatically opens a "Version Packages" PR that bumps the version and updates `CHANGELOG.md`
141
+ 3. Merge the version PR to publish to npm
142
+
143
+ ### Secrets
144
+
145
+ Add an `NPM_PUBLISH_TOKEN` secret to the repo (**Settings > Secrets and variables > Actions**) with a token from [npmjs.com](https://www.npmjs.com/settings/~/tokens).
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "@commander-js/extra-typings";
3
+ import { randomBytes } from "crypto";
4
+ import ms from "ms";
5
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
6
+ import { existsSync, readFileSync } from "fs";
7
+ import { createPublicClient, http } from "viem";
8
+ import { base } from "viem/chains";
9
+ import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm";
10
+ import { x402Client } from "@x402/core/client";
11
+ import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from "@x402/core/http";
12
+
13
+ //#region src/lib/wallet.ts
14
+ const RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;
15
+ function resolvePrivateKey(keyOrPath) {
16
+ if (keyOrPath.startsWith("0x") || RAW_HEX_RE.test(keyOrPath)) return keyOrPath;
17
+ if (existsSync(keyOrPath)) return readFileSync(keyOrPath, "utf-8").trim();
18
+ return keyOrPath;
19
+ }
20
+ function normalizePrivateKey(key) {
21
+ const resolved = resolvePrivateKey(key);
22
+ return resolved.startsWith("0x") ? resolved : `0x${resolved}`;
23
+ }
24
+ function generateWallet() {
25
+ const privateKey = generatePrivateKey();
26
+ return {
27
+ privateKey,
28
+ address: privateKeyToAccount(privateKey).address
29
+ };
30
+ }
31
+ function getWalletAddress(privateKey) {
32
+ return privateKeyToAccount(normalizePrivateKey(privateKey)).address;
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/lib/siwe.ts
37
+ function generateNonce() {
38
+ return randomBytes(16).toString("hex");
39
+ }
40
+ async function signSiwe(opts) {
41
+ const account = privateKeyToAccount(normalizePrivateKey(opts.privateKey));
42
+ const issuedAt = opts.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
43
+ const nonce = opts.nonce ?? generateNonce();
44
+ const duration = ms(opts.expiresAfter ?? "1h");
45
+ if (duration === void 0) throw new Error(`Invalid duration: ${opts.expiresAfter}`);
46
+ const expirationTime = new Date(new Date(issuedAt).getTime() + duration).toISOString();
47
+ const message = [
48
+ "x402.alchemy.com wants you to sign in with your Ethereum account:",
49
+ account.address,
50
+ "",
51
+ "Sign in to Alchemy Gateway",
52
+ "",
53
+ "URI: https://x402.alchemy.com",
54
+ "Version: 1",
55
+ "Chain ID: 8453",
56
+ `Nonce: ${nonce}`,
57
+ `Issued At: ${issuedAt}`,
58
+ `Expiration Time: ${expirationTime}`
59
+ ].join("\n");
60
+ const signature = await account.signMessage({ message });
61
+ return `${Buffer.from(message).toString("base64url")}.${signature}`;
62
+ }
63
+
64
+ //#endregion
65
+ //#region src/cli/commands/sign-siwe.ts
66
+ const signSiweCommand = new Command("sign-siwe").description("Generate a SIWE authentication token").requiredOption("--private-key <key>", "Wallet private key").option("--expires-after <duration>", "Token expiration duration (e.g. 1h, 30m, 7d)", "1h").action(async (opts) => {
67
+ const token = await signSiwe({
68
+ privateKey: opts.privateKey,
69
+ expiresAfter: opts.expiresAfter
70
+ });
71
+ process.stdout.write(token);
72
+ });
73
+
74
+ //#endregion
75
+ //#region src/cli/commands/wallet.ts
76
+ const walletCommand = new Command("wallet").description("Wallet management commands");
77
+ walletCommand.command("generate").description("Generate a new wallet").action(() => {
78
+ const wallet = generateWallet();
79
+ console.log(JSON.stringify(wallet, null, 2));
80
+ });
81
+ walletCommand.command("import").description("Import an existing wallet and display its address").requiredOption("--private-key <key>", "Wallet private key").action((opts) => {
82
+ const address = getWalletAddress(opts.privateKey);
83
+ console.log(JSON.stringify({ address }, null, 2));
84
+ });
85
+
86
+ //#endregion
87
+ //#region src/lib/payment.ts
88
+ function buildX402Client(privateKey) {
89
+ const account = privateKeyToAccount(normalizePrivateKey(privateKey));
90
+ const publicClient = createPublicClient({
91
+ chain: base,
92
+ transport: http()
93
+ });
94
+ const scheme = new ExactEvmScheme(toClientEvmSigner({
95
+ address: account.address,
96
+ signTypedData: async (params) => {
97
+ return account.signTypedData(params);
98
+ },
99
+ readContract: async (params) => {
100
+ return publicClient.readContract(params);
101
+ }
102
+ }));
103
+ const client = new x402Client();
104
+ client.register("eip155:8453", scheme);
105
+ client.register("eip155:84532", scheme);
106
+ return client;
107
+ }
108
+ async function createPayment(opts) {
109
+ const client = buildX402Client(opts.privateKey);
110
+ const paymentRequired = decodePaymentRequiredHeader(opts.paymentRequiredHeader);
111
+ return encodePaymentSignatureHeader(await client.createPaymentPayload(paymentRequired));
112
+ }
113
+
114
+ //#endregion
115
+ //#region src/cli/commands/pay.ts
116
+ const payCommand = new Command("pay").description("Create an x402 payment from a PAYMENT-REQUIRED header").requiredOption("--private-key <key>", "Wallet private key").requiredOption("--payment-required <header>", "Raw PAYMENT-REQUIRED header value").action(async (opts) => {
117
+ const paymentHeader = await createPayment({
118
+ privateKey: opts.privateKey,
119
+ paymentRequiredHeader: opts.paymentRequired
120
+ });
121
+ process.stdout.write(paymentHeader);
122
+ });
123
+
124
+ //#endregion
125
+ //#region src/cli/index.ts
126
+ const program = new Command().name("alchemy-x402").description("CLI for Alchemy x402 authentication and payments").version("0.1.0");
127
+ program.addCommand(signSiweCommand);
128
+ program.addCommand(walletCommand);
129
+ program.addCommand(payCommand);
130
+ program.parseAsync().catch((err) => {
131
+ console.error(err);
132
+ process.exit(1);
133
+ });
134
+
135
+ //#endregion
136
+ export { };
package/dist/index.cjs ADDED
@@ -0,0 +1,124 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let crypto = require("crypto");
30
+ let ms = require("ms");
31
+ ms = __toESM(ms);
32
+ let viem_accounts = require("viem/accounts");
33
+ let fs = require("fs");
34
+ let viem = require("viem");
35
+ let viem_chains = require("viem/chains");
36
+ let _x402_evm = require("@x402/evm");
37
+ let _x402_core_client = require("@x402/core/client");
38
+ let _x402_core_http = require("@x402/core/http");
39
+
40
+ //#region src/lib/wallet.ts
41
+ const RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;
42
+ function resolvePrivateKey(keyOrPath) {
43
+ if (keyOrPath.startsWith("0x") || RAW_HEX_RE.test(keyOrPath)) return keyOrPath;
44
+ if ((0, fs.existsSync)(keyOrPath)) return (0, fs.readFileSync)(keyOrPath, "utf-8").trim();
45
+ return keyOrPath;
46
+ }
47
+ function normalizePrivateKey(key) {
48
+ const resolved = resolvePrivateKey(key);
49
+ return resolved.startsWith("0x") ? resolved : `0x${resolved}`;
50
+ }
51
+ function generateWallet() {
52
+ const privateKey = (0, viem_accounts.generatePrivateKey)();
53
+ return {
54
+ privateKey,
55
+ address: (0, viem_accounts.privateKeyToAccount)(privateKey).address
56
+ };
57
+ }
58
+ function getWalletAddress(privateKey) {
59
+ return (0, viem_accounts.privateKeyToAccount)(normalizePrivateKey(privateKey)).address;
60
+ }
61
+
62
+ //#endregion
63
+ //#region src/lib/siwe.ts
64
+ function generateNonce() {
65
+ return (0, crypto.randomBytes)(16).toString("hex");
66
+ }
67
+ async function signSiwe(opts) {
68
+ const account = (0, viem_accounts.privateKeyToAccount)(normalizePrivateKey(opts.privateKey));
69
+ const issuedAt = opts.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
70
+ const nonce = opts.nonce ?? generateNonce();
71
+ const duration = (0, ms.default)(opts.expiresAfter ?? "1h");
72
+ if (duration === void 0) throw new Error(`Invalid duration: ${opts.expiresAfter}`);
73
+ const expirationTime = new Date(new Date(issuedAt).getTime() + duration).toISOString();
74
+ const message = [
75
+ "x402.alchemy.com wants you to sign in with your Ethereum account:",
76
+ account.address,
77
+ "",
78
+ "Sign in to Alchemy Gateway",
79
+ "",
80
+ "URI: https://x402.alchemy.com",
81
+ "Version: 1",
82
+ "Chain ID: 8453",
83
+ `Nonce: ${nonce}`,
84
+ `Issued At: ${issuedAt}`,
85
+ `Expiration Time: ${expirationTime}`
86
+ ].join("\n");
87
+ const signature = await account.signMessage({ message });
88
+ return `${Buffer.from(message).toString("base64url")}.${signature}`;
89
+ }
90
+
91
+ //#endregion
92
+ //#region src/lib/payment.ts
93
+ function buildX402Client(privateKey) {
94
+ const account = (0, viem_accounts.privateKeyToAccount)(normalizePrivateKey(privateKey));
95
+ const publicClient = (0, viem.createPublicClient)({
96
+ chain: viem_chains.base,
97
+ transport: (0, viem.http)()
98
+ });
99
+ const scheme = new _x402_evm.ExactEvmScheme((0, _x402_evm.toClientEvmSigner)({
100
+ address: account.address,
101
+ signTypedData: async (params) => {
102
+ return account.signTypedData(params);
103
+ },
104
+ readContract: async (params) => {
105
+ return publicClient.readContract(params);
106
+ }
107
+ }));
108
+ const client = new _x402_core_client.x402Client();
109
+ client.register("eip155:8453", scheme);
110
+ client.register("eip155:84532", scheme);
111
+ return client;
112
+ }
113
+ async function createPayment(opts) {
114
+ const client = buildX402Client(opts.privateKey);
115
+ const paymentRequired = (0, _x402_core_http.decodePaymentRequiredHeader)(opts.paymentRequiredHeader);
116
+ return (0, _x402_core_http.encodePaymentSignatureHeader)(await client.createPaymentPayload(paymentRequired));
117
+ }
118
+
119
+ //#endregion
120
+ exports.buildX402Client = buildX402Client;
121
+ exports.createPayment = createPayment;
122
+ exports.generateWallet = generateWallet;
123
+ exports.getWalletAddress = getWalletAddress;
124
+ exports.signSiwe = signSiwe;
@@ -0,0 +1,32 @@
1
+ import { x402Client } from "@x402/core/client";
2
+
3
+ //#region src/types.d.ts
4
+ type Hex = `0x${string}`;
5
+ interface WalletInfo {
6
+ privateKey: Hex;
7
+ address: Hex;
8
+ }
9
+ interface SignSiweOptions {
10
+ privateKey: string;
11
+ expiresAfter?: string;
12
+ nonce?: string;
13
+ issuedAt?: string;
14
+ }
15
+ interface CreatePaymentOptions {
16
+ privateKey: string;
17
+ paymentRequiredHeader: string;
18
+ }
19
+ //#endregion
20
+ //#region src/lib/siwe.d.ts
21
+ declare function signSiwe(opts: SignSiweOptions): Promise<string>;
22
+ //#endregion
23
+ //#region src/lib/wallet.d.ts
24
+ declare function generateWallet(): WalletInfo;
25
+ declare function getWalletAddress(privateKey: string): Hex;
26
+ //#endregion
27
+ //#region src/lib/payment.d.ts
28
+ declare function buildX402Client(privateKey: string): x402Client;
29
+ declare function createPayment(opts: CreatePaymentOptions): Promise<string>;
30
+ //#endregion
31
+ export { type CreatePaymentOptions, type Hex, type SignSiweOptions, type WalletInfo, buildX402Client, createPayment, generateWallet, getWalletAddress, signSiwe };
32
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;iBCWvC,cAAA,CAAA,GAAkB,UAAA;AAAA,iBAMlB,gBAAA,CAAiB,UAAA,WAAqB,GAAA;;;iBClBtC,eAAA,CAAgB,UAAA,WAAkB,UAAA;AAAA,iBA0B5B,aAAA,CAAc,IAAA,EAAM,oBAAA,GAAuB,OAAA"}
@@ -0,0 +1,32 @@
1
+ import { x402Client } from "@x402/core/client";
2
+
3
+ //#region src/types.d.ts
4
+ type Hex = `0x${string}`;
5
+ interface WalletInfo {
6
+ privateKey: Hex;
7
+ address: Hex;
8
+ }
9
+ interface SignSiweOptions {
10
+ privateKey: string;
11
+ expiresAfter?: string;
12
+ nonce?: string;
13
+ issuedAt?: string;
14
+ }
15
+ interface CreatePaymentOptions {
16
+ privateKey: string;
17
+ paymentRequiredHeader: string;
18
+ }
19
+ //#endregion
20
+ //#region src/lib/siwe.d.ts
21
+ declare function signSiwe(opts: SignSiweOptions): Promise<string>;
22
+ //#endregion
23
+ //#region src/lib/wallet.d.ts
24
+ declare function generateWallet(): WalletInfo;
25
+ declare function getWalletAddress(privateKey: string): Hex;
26
+ //#endregion
27
+ //#region src/lib/payment.d.ts
28
+ declare function buildX402Client(privateKey: string): x402Client;
29
+ declare function createPayment(opts: CreatePaymentOptions): Promise<string>;
30
+ //#endregion
31
+ export { type CreatePaymentOptions, type Hex, type SignSiweOptions, type WalletInfo, buildX402Client, createPayment, generateWallet, getWalletAddress, signSiwe };
32
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;iBCWvC,cAAA,CAAA,GAAkB,UAAA;AAAA,iBAMlB,gBAAA,CAAiB,UAAA,WAAqB,GAAA;;;iBClBtC,eAAA,CAAgB,UAAA,WAAkB,UAAA;AAAA,iBA0B5B,aAAA,CAAc,IAAA,EAAM,oBAAA,GAAuB,OAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,92 @@
1
+ import { randomBytes } from "crypto";
2
+ import ms from "ms";
3
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { createPublicClient, http } from "viem";
6
+ import { base } from "viem/chains";
7
+ import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm";
8
+ import { x402Client } from "@x402/core/client";
9
+ import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from "@x402/core/http";
10
+
11
+ //#region src/lib/wallet.ts
12
+ const RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;
13
+ function resolvePrivateKey(keyOrPath) {
14
+ if (keyOrPath.startsWith("0x") || RAW_HEX_RE.test(keyOrPath)) return keyOrPath;
15
+ if (existsSync(keyOrPath)) return readFileSync(keyOrPath, "utf-8").trim();
16
+ return keyOrPath;
17
+ }
18
+ function normalizePrivateKey(key) {
19
+ const resolved = resolvePrivateKey(key);
20
+ return resolved.startsWith("0x") ? resolved : `0x${resolved}`;
21
+ }
22
+ function generateWallet() {
23
+ const privateKey = generatePrivateKey();
24
+ return {
25
+ privateKey,
26
+ address: privateKeyToAccount(privateKey).address
27
+ };
28
+ }
29
+ function getWalletAddress(privateKey) {
30
+ return privateKeyToAccount(normalizePrivateKey(privateKey)).address;
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/lib/siwe.ts
35
+ function generateNonce() {
36
+ return randomBytes(16).toString("hex");
37
+ }
38
+ async function signSiwe(opts) {
39
+ const account = privateKeyToAccount(normalizePrivateKey(opts.privateKey));
40
+ const issuedAt = opts.issuedAt ?? (/* @__PURE__ */ new Date()).toISOString();
41
+ const nonce = opts.nonce ?? generateNonce();
42
+ const duration = ms(opts.expiresAfter ?? "1h");
43
+ if (duration === void 0) throw new Error(`Invalid duration: ${opts.expiresAfter}`);
44
+ const expirationTime = new Date(new Date(issuedAt).getTime() + duration).toISOString();
45
+ const message = [
46
+ "x402.alchemy.com wants you to sign in with your Ethereum account:",
47
+ account.address,
48
+ "",
49
+ "Sign in to Alchemy Gateway",
50
+ "",
51
+ "URI: https://x402.alchemy.com",
52
+ "Version: 1",
53
+ "Chain ID: 8453",
54
+ `Nonce: ${nonce}`,
55
+ `Issued At: ${issuedAt}`,
56
+ `Expiration Time: ${expirationTime}`
57
+ ].join("\n");
58
+ const signature = await account.signMessage({ message });
59
+ return `${Buffer.from(message).toString("base64url")}.${signature}`;
60
+ }
61
+
62
+ //#endregion
63
+ //#region src/lib/payment.ts
64
+ function buildX402Client(privateKey) {
65
+ const account = privateKeyToAccount(normalizePrivateKey(privateKey));
66
+ const publicClient = createPublicClient({
67
+ chain: base,
68
+ transport: http()
69
+ });
70
+ const scheme = new ExactEvmScheme(toClientEvmSigner({
71
+ address: account.address,
72
+ signTypedData: async (params) => {
73
+ return account.signTypedData(params);
74
+ },
75
+ readContract: async (params) => {
76
+ return publicClient.readContract(params);
77
+ }
78
+ }));
79
+ const client = new x402Client();
80
+ client.register("eip155:8453", scheme);
81
+ client.register("eip155:84532", scheme);
82
+ return client;
83
+ }
84
+ async function createPayment(opts) {
85
+ const client = buildX402Client(opts.privateKey);
86
+ const paymentRequired = decodePaymentRequiredHeader(opts.paymentRequiredHeader);
87
+ return encodePaymentSignatureHeader(await client.createPaymentPayload(paymentRequired));
88
+ }
89
+
90
+ //#endregion
91
+ export { buildX402Client, createPayment, generateWallet, getWalletAddress, signSiwe };
92
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/wallet.ts","../src/lib/siwe.ts","../src/lib/payment.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex, WalletInfo } from \"../types.js\";\n\nconst RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;\n\nexport function resolvePrivateKey(keyOrPath: string): string {\n if (keyOrPath.startsWith(\"0x\") || RAW_HEX_RE.test(keyOrPath)) {\n return keyOrPath;\n }\n if (existsSync(keyOrPath)) {\n return readFileSync(keyOrPath, \"utf-8\").trim();\n }\n return keyOrPath;\n}\n\nexport function normalizePrivateKey(key: string): Hex {\n const resolved = resolvePrivateKey(key);\n return resolved.startsWith(\"0x\") ? (resolved as Hex) : (`0x${resolved}` as Hex);\n}\n\nexport function generateWallet(): WalletInfo {\n const privateKey = generatePrivateKey();\n const account = privateKeyToAccount(privateKey);\n return { privateKey, address: account.address };\n}\n\nexport function getWalletAddress(privateKey: string): Hex {\n const normalized = normalizePrivateKey(privateKey);\n const account = privateKeyToAccount(normalized);\n return account.address;\n}\n","import { randomBytes } from \"crypto\";\nimport ms from \"ms\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { SignSiweOptions } from \"../types.js\";\nimport { normalizePrivateKey } from \"./wallet.js\";\n\nfunction generateNonce(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\nexport async function signSiwe(opts: SignSiweOptions): Promise<string> {\n const normalized = normalizePrivateKey(opts.privateKey);\n const account = privateKeyToAccount(normalized);\n\n const issuedAt = opts.issuedAt ?? new Date().toISOString();\n const nonce = opts.nonce ?? generateNonce();\n\n const duration = ms((opts.expiresAfter ?? \"1h\") as Parameters<typeof ms>[0]);\n if (duration === undefined) {\n throw new Error(`Invalid duration: ${opts.expiresAfter}`);\n }\n const expirationTime = new Date(new Date(issuedAt).getTime() + duration).toISOString();\n\n const message = [\n \"x402.alchemy.com wants you to sign in with your Ethereum account:\",\n account.address,\n \"\",\n \"Sign in to Alchemy Gateway\",\n \"\",\n \"URI: https://x402.alchemy.com\",\n \"Version: 1\",\n \"Chain ID: 8453\",\n `Nonce: ${nonce}`,\n `Issued At: ${issuedAt}`,\n `Expiration Time: ${expirationTime}`,\n ].join(\"\\n\");\n\n const signature = await account.signMessage({ message });\n\n const encodedMessage = Buffer.from(message).toString(\"base64url\");\n return `${encodedMessage}.${signature}`;\n}\n","import { createPublicClient, http } from \"viem\";\nimport { base } from \"viem/chains\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { ExactEvmScheme, toClientEvmSigner } from \"@x402/evm\";\nimport { x402Client } from \"@x402/core/client\";\nimport { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from \"@x402/core/http\";\nimport type { CreatePaymentOptions } from \"../types.js\";\nimport { normalizePrivateKey } from \"./wallet.js\";\n\nexport function buildX402Client(privateKey: string) {\n const normalized = normalizePrivateKey(privateKey);\n const account = privateKeyToAccount(normalized);\n\n const publicClient = createPublicClient({\n chain: base,\n transport: http(),\n });\n\n const signer = toClientEvmSigner({\n address: account.address,\n signTypedData: async (params) => {\n return account.signTypedData(params as Parameters<typeof account.signTypedData>[0]);\n },\n readContract: async (params) => {\n return publicClient.readContract(params as Parameters<typeof publicClient.readContract>[0]);\n },\n });\n\n const scheme = new ExactEvmScheme(signer);\n const client = new x402Client();\n client.register(\"eip155:8453\", scheme);\n client.register(\"eip155:84532\", scheme);\n return client;\n}\n\nexport async function createPayment(opts: CreatePaymentOptions): Promise<string> {\n const client = buildX402Client(opts.privateKey);\n const paymentRequired = decodePaymentRequiredHeader(opts.paymentRequiredHeader);\n const payload = await client.createPaymentPayload(paymentRequired);\n return encodePaymentSignatureHeader(payload);\n}\n"],"mappings":";;;;;;;;;;;AAIA,MAAM,aAAa;AAEnB,SAAgB,kBAAkB,WAA2B;AAC3D,KAAI,UAAU,WAAW,KAAK,IAAI,WAAW,KAAK,UAAU,CAC1D,QAAO;AAET,KAAI,WAAW,UAAU,CACvB,QAAO,aAAa,WAAW,QAAQ,CAAC,MAAM;AAEhD,QAAO;;AAGT,SAAgB,oBAAoB,KAAkB;CACpD,MAAM,WAAW,kBAAkB,IAAI;AACvC,QAAO,SAAS,WAAW,KAAK,GAAI,WAAoB,KAAK;;AAG/D,SAAgB,iBAA6B;CAC3C,MAAM,aAAa,oBAAoB;AAEvC,QAAO;EAAE;EAAY,SADL,oBAAoB,WAAW,CACT;EAAS;;AAGjD,SAAgB,iBAAiB,YAAyB;AAGxD,QADgB,oBADG,oBAAoB,WAAW,CACH,CAChC;;;;;ACxBjB,SAAS,gBAAwB;AAC/B,QAAO,YAAY,GAAG,CAAC,SAAS,MAAM;;AAGxC,eAAsB,SAAS,MAAwC;CAErE,MAAM,UAAU,oBADG,oBAAoB,KAAK,WAAW,CACR;CAE/C,MAAM,WAAW,KAAK,6BAAY,IAAI,MAAM,EAAC,aAAa;CAC1D,MAAM,QAAQ,KAAK,SAAS,eAAe;CAE3C,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAkC;AAC5E,KAAI,aAAa,OACf,OAAM,IAAI,MAAM,qBAAqB,KAAK,eAAe;CAE3D,MAAM,iBAAiB,IAAI,KAAK,IAAI,KAAK,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,aAAa;CAEtF,MAAM,UAAU;EACd;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;EACV,cAAc;EACd,oBAAoB;EACrB,CAAC,KAAK,KAAK;CAEZ,MAAM,YAAY,MAAM,QAAQ,YAAY,EAAE,SAAS,CAAC;AAGxD,QAAO,GADgB,OAAO,KAAK,QAAQ,CAAC,SAAS,YAAY,CACxC,GAAG;;;;;AC/B9B,SAAgB,gBAAgB,YAAoB;CAElD,MAAM,UAAU,oBADG,oBAAoB,WAAW,CACH;CAE/C,MAAM,eAAe,mBAAmB;EACtC,OAAO;EACP,WAAW,MAAM;EAClB,CAAC;CAYF,MAAM,SAAS,IAAI,eAVJ,kBAAkB;EAC/B,SAAS,QAAQ;EACjB,eAAe,OAAO,WAAW;AAC/B,UAAO,QAAQ,cAAc,OAAsD;;EAErF,cAAc,OAAO,WAAW;AAC9B,UAAO,aAAa,aAAa,OAA0D;;EAE9F,CAAC,CAEuC;CACzC,MAAM,SAAS,IAAI,YAAY;AAC/B,QAAO,SAAS,eAAe,OAAO;AACtC,QAAO,SAAS,gBAAgB,OAAO;AACvC,QAAO;;AAGT,eAAsB,cAAc,MAA6C;CAC/E,MAAM,SAAS,gBAAgB,KAAK,WAAW;CAC/C,MAAM,kBAAkB,4BAA4B,KAAK,sBAAsB;AAE/E,QAAO,6BADS,MAAM,OAAO,qBAAqB,gBAAgB,CACtB"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@alchemy/x402",
3
+ "version": "0.2.0",
4
+ "description": "CLI and library for Alchemy x402 authentication and payments",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": {
9
+ "types": "./dist/index.d.mts",
10
+ "default": "./dist/index.mjs"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
16
+ }
17
+ },
18
+ "bin": {
19
+ "alchemy-x402": "./dist/cli/index.mjs"
20
+ },
21
+ "dependencies": {
22
+ "@commander-js/extra-typings": "^14.0.0",
23
+ "@x402/core": "^2.4.0",
24
+ "@x402/evm": "^2.4.0",
25
+ "@x402/fetch": "^2.4.0",
26
+ "commander": "^14.0.3",
27
+ "ms": "^2.1.3",
28
+ "viem": "^2.46.3"
29
+ },
30
+ "devDependencies": {
31
+ "@changesets/changelog-github": "^0.5.2",
32
+ "@changesets/cli": "^2.29.8",
33
+ "@types/ms": "^2.1.0",
34
+ "@types/node": "^25.3.0",
35
+ "tsdown": "^0.20.3",
36
+ "typescript": "^5.9.3",
37
+ "vitest": "^4.0.18"
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "engines": {
43
+ "node": ">=20.0.0"
44
+ },
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "tsdown",
51
+ "typecheck": "tsc --noEmit",
52
+ "test": "vitest run",
53
+ "changeset": "changeset",
54
+ "version-packages": "changeset version",
55
+ "release": "pnpm run build && changeset publish"
56
+ }
57
+ }