create-mantle-facilitator 0.3.1 → 0.3.4

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/ascii-art.txt ADDED
@@ -0,0 +1,11 @@
1
+ _____ ______ __ __ __
2
+ / ___/___ / / __/ / /_ ____ _____/ /____ ____/ /
3
+ \__ \/ _ \/ / /_______/ __ \/ __ \/ ___/ __/ _ \/ __ /
4
+ ___/ / __/ / __/_____/ / / / /_/ (__ ) /_/ __/ /_/ /
5
+ /____/\___/_/_/ /_/ /_/\____/____/\__/\___/\__,_/
6
+ __ ___ __ __ ____ _ ___ __ __
7
+ / |/ /___ _____ / /_/ /__ / __/___ ______(_) (_) /_____ _/ /_____ _____
8
+ / /|_/ / __ `/ __ \/ __/ / _ \ / /_/ __ `/ ___/ / / / __/ __ `/ __/ __ \/ ___/
9
+ / / / / /_/ / / / / /_/ / __/ / __/ /_/ / /__/ / / / /_/ /_/ / /_/ /_/ / /
10
+ /_/ /_/\__,_/_/ /_/\__/_/\___/ /_/ \__,_/\___/_/_/_/\__/\__,_/\__/\____/_/
11
+
package/dist/index.mjs CHANGED
@@ -5,30 +5,181 @@ import path from "path";
5
5
  import fs from "fs";
6
6
  import { fileURLToPath } from "url";
7
7
  import fse from "fs-extra";
8
- function log(msg) {
9
- console.log(msg);
8
+ import prompts from "prompts";
9
+ function getAsciiArt() {
10
+ const asciiArtPath = path.join(
11
+ path.dirname(fileURLToPath(import.meta.url)),
12
+ "..",
13
+ "ascii-art.txt"
14
+ );
15
+ try {
16
+ return fs.readFileSync(asciiArtPath, "utf-8");
17
+ } catch (error) {
18
+ return "Mantle Facilitator";
19
+ }
10
20
  }
11
- function fail(msg) {
12
- console.error(msg);
13
- process.exit(1);
21
+ async function promptSetup() {
22
+ const questions = [
23
+ {
24
+ type: "text",
25
+ name: "projectName",
26
+ message: "Where should we create the facilitator?",
27
+ initial: "my-facilitator",
28
+ validate: (value) => {
29
+ if (!value) return "Project name is required";
30
+ if (value !== "." && !/^[a-zA-Z0-9-_]+$/.test(value)) {
31
+ return "Name can only contain letters, numbers, dashes, and underscores";
32
+ }
33
+ const targetDir = value === "." ? process.cwd() : path.join(process.cwd(), value);
34
+ if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
35
+ return `Directory ${value} already exists and is not empty`;
36
+ }
37
+ return true;
38
+ }
39
+ },
40
+ {
41
+ type: "text",
42
+ name: "rpcUrl",
43
+ message: "RPC URL for Mantle network:",
44
+ initial: "https://rpc.mantle.xyz",
45
+ validate: (value) => {
46
+ if (!value.startsWith("http://") && !value.startsWith("https://")) {
47
+ return "RPC URL must start with http:// or https://";
48
+ }
49
+ return true;
50
+ }
51
+ },
52
+ {
53
+ type: "password",
54
+ name: "privateKey",
55
+ message: "Facilitator wallet private key (optional):",
56
+ validate: (value) => {
57
+ if (!value) return true;
58
+ if (!value.startsWith("0x")) return "Private key must start with 0x";
59
+ if (value.length !== 66) return "Private key must be 66 characters (0x + 64 hex)";
60
+ if (!/^0x[0-9a-fA-F]{64}$/.test(value)) return "Invalid hex format";
61
+ return true;
62
+ }
63
+ },
64
+ {
65
+ type: "text",
66
+ name: "usdcAddress",
67
+ message: "USDC contract address:",
68
+ initial: "0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9",
69
+ validate: (value) => {
70
+ if (!value.startsWith("0x")) return "Address must start with 0x";
71
+ if (value.length !== 42) return "Address must be 42 characters";
72
+ if (!/^0x[0-9a-fA-F]{40}$/.test(value)) return "Invalid hex format";
73
+ return true;
74
+ }
75
+ },
76
+ {
77
+ type: "confirm",
78
+ name: "enableTelemetry",
79
+ message: "Enable telemetry? (Send anonymous usage stats to improve the SDK)",
80
+ initial: false
81
+ },
82
+ {
83
+ type: (prev) => prev ? "text" : null,
84
+ name: "telemetryKey",
85
+ message: "Enter PROJECT_KEY from https://x402mantlesdk.xyz/dashboard (optional):",
86
+ initial: ""
87
+ }
88
+ ];
89
+ return await prompts(questions, {
90
+ onCancel: () => {
91
+ console.log("\nSetup cancelled.");
92
+ process.exit(0);
93
+ }
94
+ });
14
95
  }
15
- function cleanName(name) {
16
- return name.replace(/[^a-zA-Z0-9-_]/g, "");
96
+ function generateEnvFile(answers) {
97
+ const env = [
98
+ `# Server Configuration`,
99
+ `PORT=8080`,
100
+ ``,
101
+ `# Blockchain Configuration`,
102
+ `NETWORK_ID=mantle-mainnet`,
103
+ `CHAIN_ID=5000`,
104
+ `RPC_URL=${answers.rpcUrl}`,
105
+ ``,
106
+ `# USDC Token Configuration`,
107
+ `USDC_ADDRESS=${answers.usdcAddress}`,
108
+ `USDC_DECIMALS=6`,
109
+ ``,
110
+ `# Facilitator Wallet (keeps private key safe)`,
111
+ answers.privateKey ? `FACILITATOR_PRIVATE_KEY=${answers.privateKey}` : `# FACILITATOR_PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE`,
112
+ ``,
113
+ `# Logging`,
114
+ `LOG_LEVEL=info`,
115
+ ``
116
+ ];
117
+ if (answers.enableTelemetry && answers.telemetryKey) {
118
+ env.push(`# Telemetry (Optional - for analytics)`);
119
+ env.push(`TELEMETRY_PROJECT_KEY=${answers.telemetryKey}`);
120
+ env.push(``);
121
+ } else if (answers.enableTelemetry) {
122
+ env.push(`# Telemetry (Optional - for analytics)`);
123
+ env.push(`# TELEMETRY_PROJECT_KEY=your_project_key_here`);
124
+ env.push(`# Get your key from https://x402mantlesdk.xyz/dashboard`);
125
+ env.push(``);
126
+ }
127
+ return env.join("\n");
17
128
  }
18
- async function main() {
19
- const args = process.argv.slice(2);
20
- const rawProjectName = args[0];
21
- if (!rawProjectName) {
22
- fail("Usage: create-mantle-facilitator <project-name>");
129
+ async function createProjectInteractive() {
130
+ console.log(getAsciiArt());
131
+ console.log("\nWelcome to Mantle Facilitator Setup!\n");
132
+ const answers = await promptSetup();
133
+ const targetDir = answers.projectName === "." ? process.cwd() : path.join(process.cwd(), answers.projectName);
134
+ console.log(`
135
+ Creating facilitator in ${targetDir}...`);
136
+ const templateDir = path.join(
137
+ path.dirname(fileURLToPath(import.meta.url)),
138
+ "..",
139
+ "template"
140
+ );
141
+ if (!fs.existsSync(templateDir)) {
142
+ console.error("ERROR: Template folder not found in CLI package.");
143
+ process.exit(1);
144
+ }
145
+ await fse.copy(templateDir, targetDir);
146
+ console.log("\u2713 Template files copied");
147
+ const packageJsonPath = path.join(targetDir, "package.json");
148
+ if (fs.existsSync(packageJsonPath)) {
149
+ const packageJson = JSON.parse(await fse.readFile(packageJsonPath, "utf-8"));
150
+ packageJson.name = answers.projectName === "." ? path.basename(process.cwd()) : answers.projectName;
151
+ if (packageJson.private === true) delete packageJson.private;
152
+ await fse.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
153
+ console.log("\u2713 Package configured");
154
+ }
155
+ const envContent = generateEnvFile(answers);
156
+ await fse.writeFile(path.join(targetDir, ".env"), envContent);
157
+ console.log("\u2713 Environment variables configured");
158
+ console.log("\n\u2713 Done! Your facilitator is ready.\n");
159
+ console.log("Next steps:");
160
+ if (answers.projectName !== ".") {
161
+ console.log(` cd ${answers.projectName}`);
162
+ }
163
+ console.log(" npm install");
164
+ console.log(" npm run dev");
165
+ console.log("\nFacilitator will start on http://localhost:8080");
166
+ const reminders = [];
167
+ if (!answers.privateKey) {
168
+ reminders.push("IMPORTANT: Set FACILITATOR_PRIVATE_KEY in .env before starting");
169
+ }
170
+ if (answers.enableTelemetry && !answers.telemetryKey) {
171
+ reminders.push("Telemetry enabled: Get PROJECT_KEY from https://x402mantlesdk.xyz/dashboard");
23
172
  }
24
- const projectName = cleanName(rawProjectName);
25
- if (!projectName) {
26
- fail("Invalid project name.");
173
+ if (reminders.length > 0) {
174
+ console.log("\n" + reminders.join("\n"));
27
175
  }
28
- const cwd = process.cwd();
29
- const targetDir = path.join(cwd, projectName);
176
+ }
177
+ async function createProjectNonInteractive(projectName) {
178
+ console.log(`Creating facilitator: ${projectName}`);
179
+ const targetDir = path.join(process.cwd(), projectName);
30
180
  if (fs.existsSync(targetDir)) {
31
- fail(`Target directory already exists: ${targetDir}`);
181
+ console.error(`ERROR: Target directory already exists: ${targetDir}`);
182
+ process.exit(1);
32
183
  }
33
184
  const templateDir = path.join(
34
185
  path.dirname(fileURLToPath(import.meta.url)),
@@ -36,27 +187,45 @@ async function main() {
36
187
  "template"
37
188
  );
38
189
  if (!fs.existsSync(templateDir)) {
39
- fail("Template folder not found in CLI package.");
190
+ console.error("ERROR: Template folder not found in CLI package.");
191
+ process.exit(1);
40
192
  }
41
193
  await fse.copy(templateDir, targetDir);
42
- const pkgPath = path.join(targetDir, "package.json");
43
- if (fs.existsSync(pkgPath)) {
44
- const pkg = JSON.parse(await fse.readFile(pkgPath, "utf8"));
45
- pkg.name = projectName;
46
- if (pkg.private === true) delete pkg.private;
47
- await fse.writeFile(pkgPath, JSON.stringify(pkg, null, 2), "utf8");
48
- }
49
- log("");
50
- log("\u2705 Mantle facilitator scaffolded!");
51
- log("");
52
- log("Next steps:");
53
- log(` cd ${projectName}`);
54
- log(" cp .env.example .env");
55
- log(" npm install");
56
- log(" npm run dev");
57
- log("");
194
+ const packageJsonPath = path.join(targetDir, "package.json");
195
+ if (fs.existsSync(packageJsonPath)) {
196
+ const packageJson = JSON.parse(await fse.readFile(packageJsonPath, "utf-8"));
197
+ packageJson.name = projectName;
198
+ if (packageJson.private === true) delete packageJson.private;
199
+ await fse.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
200
+ }
201
+ const envDefaults = {
202
+ projectName,
203
+ rpcUrl: "https://rpc.mantle.xyz",
204
+ privateKey: "",
205
+ usdcAddress: "0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9",
206
+ enableTelemetry: false,
207
+ telemetryKey: ""
208
+ };
209
+ const envContent = generateEnvFile(envDefaults);
210
+ await fse.writeFile(path.join(targetDir, ".env"), envContent);
211
+ console.log("\n\u2713 Done!\n");
212
+ console.log("IMPORTANT: Edit .env file and set FACILITATOR_PRIVATE_KEY");
213
+ console.log(`Then run: cd ${projectName} && npm install && npm run dev`);
214
+ }
215
+ async function main() {
216
+ const args = process.argv.slice(2);
217
+ if (args.length > 0) {
218
+ const projectName = args[0];
219
+ if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
220
+ console.error("ERROR: Project name can only contain letters, numbers, dashes, and underscores");
221
+ process.exit(1);
222
+ }
223
+ await createProjectNonInteractive(projectName);
224
+ return;
225
+ }
226
+ await createProjectInteractive();
58
227
  }
59
228
  main().catch((err) => {
60
- console.error(err);
229
+ console.error("Setup failed:", err);
61
230
  process.exit(1);
62
231
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mantle-facilitator",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "template"
11
+ "template",
12
+ "ascii-art.txt"
12
13
  ],
13
14
  "scripts": {
14
15
  "build": "tsup",
@@ -16,11 +17,13 @@
16
17
  "prepublishOnly": "npm run build"
17
18
  },
18
19
  "dependencies": {
19
- "fs-extra": "^11.2.0"
20
+ "fs-extra": "^11.2.0",
21
+ "prompts": "^2.4.2"
20
22
  },
21
23
  "devDependencies": {
22
24
  "@types/fs-extra": "^11.0.4",
23
25
  "@types/node": "^22.0.0",
26
+ "@types/prompts": "^2.4.9",
24
27
  "tsup": "^8.0.0",
25
28
  "typescript": "^5.6.0"
26
29
  }
@@ -16,12 +16,15 @@ This facilitator:
16
16
 
17
17
  ## Quickstart
18
18
 
19
+ This template is generated by `create-mantle-facilitator`. The CLI automatically creates a `.env` file with all configuration.
20
+
21
+ If you need to modify configuration:
22
+ 1. Edit `.env` file
23
+ 2. Set `FACILITATOR_PRIVATE_KEY` if not already set
24
+ 3. Adjust `RPC_URL` if using a custom RPC endpoint
25
+
19
26
  ```bash
20
- git clone <this-repo>
21
- cd mantle-x402-facilitator-template
22
27
  npm install
23
- cp .env.example .env
24
- # Fill RPC_URL and FACILITATOR_PRIVATE_KEY
25
28
  npm start
26
29
  ```
27
30
 
@@ -183,7 +183,7 @@ export function verifyPayment(
183
183
  expected: authorization.from,
184
184
  message: 'This may indicate EIP-712 domain/types mismatch between SDK and facilitator'
185
185
  });
186
- // ⚠️ DO NOT return error - let USDC contract validate on-chain
186
+ // WARNING: DO NOT return error - let USDC contract validate on-chain
187
187
  } else {
188
188
  console.log('[FACILITATOR] Signature verification passed (preflight check)');
189
189
  }
@@ -191,7 +191,7 @@ export function verifyPayment(
191
191
  console.warn('[FACILITATOR WARNING] Signature verification failed (will let USDC contract validate on-chain)', {
192
192
  error: err instanceof Error ? err.message : "Unknown error"
193
193
  });
194
- // ⚠️ DO NOT return error - let USDC contract validate on-chain
194
+ // WARNING: DO NOT return error - let USDC contract validate on-chain
195
195
  }
196
196
 
197
197
  return { isValid: true, invalidReason: null };
@@ -1,25 +0,0 @@
1
- # Server
2
- PORT=8080
3
-
4
- # Network
5
- NETWORK_ID=mantle-mainnet
6
- CHAIN_ID=5000
7
- RPC_URL=https://rpc.mantle.xyz
8
-
9
- # Asset (USDC on Mantle)
10
- USDC_ADDRESS=0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9
11
- USDC_DECIMALS=6
12
-
13
- # Facilitator signer (pays gas for transferWithAuthorization)
14
- FACILITATOR_PRIVATE_KEY=0xYOUR_PRIVATE_KEY
15
-
16
- # Optional: enable verbose logs
17
- LOG_LEVEL=debug
18
-
19
- # =============================================================================
20
- # Optional: Analytics & Telemetry
21
- # =============================================================================
22
- # Uncomment to send usage metrics to analytics backend
23
- # This helps improve the x402 ecosystem and provides you with payment analytics
24
-
25
- # TELEMETRY_PROJECT_KEY=your_project_key_here