create-mantle-facilitator 0.3.5 → 0.4.1

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 CHANGED
@@ -37,6 +37,7 @@ After creation, edit the `.env` file:
37
37
  # Required
38
38
  FACILITATOR_PRIVATE_KEY=0x... # Wallet that pays gas fees
39
39
  RPC_URL=https://rpc.mantle.xyz # Mantle RPC endpoint
40
+ FACILITATOR_SECRET=fac_xxx... # Auto-generated security secret
40
41
 
41
42
  # Optional
42
43
  USDC_ADDRESS=0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9
@@ -51,8 +52,28 @@ TELEMETRY_PROJECT_KEY=pk_xxx # Get from dashboard
51
52
  ### Important Notes
52
53
 
53
54
  - **FACILITATOR_PRIVATE_KEY**: This wallet will pay gas fees for all settlements. Make sure it has MNT for gas.
55
+ - **FACILITATOR_SECRET**: Auto-generated during setup. **Copy this to your backend's environment variables** to enable secure authentication.
54
56
  - **Security**: Never commit `.env` to version control. The generated `.gitignore` already excludes it.
55
57
 
58
+ ### Connecting to Your Backend
59
+
60
+ Add `FACILITATOR_SECRET` to your backend and configure the SDK:
61
+
62
+ ```typescript
63
+ import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/express';
64
+
65
+ // Self-hosted facilitator (requires facilitatorUrl + facilitatorSecret)
66
+ const pay = mantlePaywall({
67
+ priceUsd: 0.01,
68
+ payTo: '0xYourWallet',
69
+ facilitatorUrl: 'https://your-facilitator.com', // Required for self-hosted
70
+ facilitatorSecret: process.env.FACILITATOR_SECRET!, // Required for self-hosted
71
+ // projectKey: process.env.PROJECT_KEY, // Optional: for analytics
72
+ });
73
+ ```
74
+
75
+ **Note:** When using `facilitatorSecret` (self-hosted mode), `facilitatorUrl` is required. The URL is automatically passed to clients via 402 responses, so frontend code doesn't need to configure it.
76
+
56
77
  ## Running Locally
57
78
 
58
79
  ```bash
@@ -221,7 +242,7 @@ curl -X POST http://localhost:8080/verify \
221
242
 
222
243
  ### POST /settle
223
244
 
224
- Executes the USDC transfer on-chain.
245
+ Executes the USDC transfer on-chain. Requires a valid `settleToken` for authentication.
225
246
 
226
247
  ```bash
227
248
  curl -X POST http://localhost:8080/settle \
@@ -229,7 +250,8 @@ curl -X POST http://localhost:8080/settle \
229
250
  -d '{
230
251
  "x402Version": 1,
231
252
  "paymentHeader": "base64...",
232
- "paymentRequirements": {...}
253
+ "paymentRequirements": {...},
254
+ "settleToken": "base64..."
233
255
  }'
234
256
  ```
235
257
 
@@ -240,6 +262,8 @@ curl -X POST http://localhost:8080/settle \
240
262
  }
241
263
  ```
242
264
 
265
+ **Note:** The `settleToken` is automatically generated by the SDK when `facilitatorSecret` is configured, and passed through the client to authenticate with your facilitator.
266
+
243
267
  ---
244
268
 
245
269
  ## How It Works
@@ -298,17 +322,23 @@ Telemetry errors never affect payment processing — if analytics backend is dow
298
322
 
299
323
  ## Security Considerations
300
324
 
301
- 1. **Private Key Security**
325
+ 1. **Facilitator Secret (REQUIRED)**
326
+ - `FACILITATOR_SECRET` prevents unauthorized usage of your facilitator
327
+ - Without it, anyone could use your facilitator URL to settle their payments and drain your gas wallet
328
+ - The secret creates a secure token flow: Backend → Client → Facilitator
329
+ - Always set `facilitatorSecret` in your backend SDK configuration
330
+
331
+ 2. **Private Key Security**
302
332
  - Never commit `.env` to git
303
333
  - Use secret management in production (Railway secrets, Fly secrets, etc.)
304
334
  - Consider using hardware wallets for high-value facilitators
305
335
 
306
- 2. **Gas Management**
336
+ 3. **Gas Management**
307
337
  - Monitor MNT balance regularly
308
338
  - Set up alerts for low balance
309
339
  - Facilitator needs ~0.001 MNT per settlement
310
340
 
311
- 3. **Network Security**
341
+ 4. **Network Security**
312
342
  - Use HTTPS in production
313
343
  - Consider rate limiting
314
344
  - Monitor for unusual patterns
package/dist/index.mjs CHANGED
@@ -3,9 +3,13 @@
3
3
  // src/index.ts
4
4
  import path from "path";
5
5
  import fs from "fs";
6
+ import crypto from "crypto";
6
7
  import { fileURLToPath } from "url";
7
8
  import fse from "fs-extra";
8
9
  import prompts from "prompts";
10
+ function generateFacilitatorSecret() {
11
+ return "fac_" + crypto.randomBytes(32).toString("hex");
12
+ }
9
13
  function getAsciiArt() {
10
14
  const asciiArtPath = path.join(
11
15
  path.dirname(fileURLToPath(import.meta.url)),
@@ -94,6 +98,7 @@ async function promptSetup() {
94
98
  });
95
99
  }
96
100
  function generateEnvFile(answers) {
101
+ const facilitatorSecret = generateFacilitatorSecret();
97
102
  const env = [
98
103
  `# Server Configuration`,
99
104
  `PORT=8080`,
@@ -110,6 +115,10 @@ function generateEnvFile(answers) {
110
115
  `# Facilitator Wallet (keeps private key safe)`,
111
116
  answers.privateKey ? `FACILITATOR_PRIVATE_KEY=${answers.privateKey}` : `# FACILITATOR_PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE`,
112
117
  ``,
118
+ `# Security: Shared secret between your backend and this facilitator`,
119
+ `# IMPORTANT: Copy this value to your backend's facilitatorSecret config`,
120
+ `FACILITATOR_SECRET=${facilitatorSecret}`,
121
+ ``,
113
122
  `# Logging`,
114
123
  `LOG_LEVEL=info`,
115
124
  ``
@@ -167,6 +176,7 @@ Creating facilitator in ${targetDir}...`);
167
176
  if (!answers.privateKey) {
168
177
  reminders.push("IMPORTANT: Set FACILITATOR_PRIVATE_KEY in .env before starting");
169
178
  }
179
+ reminders.push("IMPORTANT: Copy FACILITATOR_SECRET from .env to your backend configuration");
170
180
  if (answers.enableTelemetry && !answers.telemetryKey) {
171
181
  reminders.push("Telemetry enabled: Get PROJECT_KEY from https://x402mantlesdk.xyz/dashboard");
172
182
  }
@@ -210,6 +220,7 @@ async function createProjectNonInteractive(projectName) {
210
220
  await fse.writeFile(path.join(targetDir, ".env"), envContent);
211
221
  console.log("\n\u2713 Done!\n");
212
222
  console.log("IMPORTANT: Edit .env file and set FACILITATOR_PRIVATE_KEY");
223
+ console.log("IMPORTANT: Copy FACILITATOR_SECRET from .env to your backend configuration");
213
224
  console.log(`Then run: cd ${projectName} && npm install && npm run dev`);
214
225
  }
215
226
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mantle-facilitator",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,6 +24,7 @@ Edit `.env` file:
24
24
  # Required
25
25
  FACILITATOR_PRIVATE_KEY=0x... # Wallet that pays gas
26
26
  RPC_URL=https://rpc.mantle.xyz
27
+ FACILITATOR_SECRET=fac_xxx... # Auto-generated, share with your backend
27
28
 
28
29
  # Optional
29
30
  PORT=8080
@@ -33,7 +34,36 @@ USDC_ADDRESS=0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9
33
34
  TELEMETRY_PROJECT_KEY=pk_xxx # Get from https://x402mantlesdk.xyz/dashboard
34
35
  ```
35
36
 
36
- **Important:** The facilitator wallet needs MNT for gas fees. Check balance at `/health`.
37
+ **Important:**
38
+ - The facilitator wallet needs MNT for gas fees. Check balance at `/health`.
39
+ - Copy `FACILITATOR_SECRET` to your backend's environment variables.
40
+
41
+ ## Security
42
+
43
+ The `FACILITATOR_SECRET` protects your facilitator from unauthorized usage:
44
+
45
+ 1. Your backend generates a `settleToken` using this secret when returning 402 responses
46
+ 2. The client passes this token to your facilitator's `/settle` endpoint
47
+ 3. The facilitator verifies the token matches before executing the transaction
48
+
49
+ This prevents third parties from using your facilitator to settle their payments and drain your gas wallet.
50
+
51
+ **Backend configuration:**
52
+
53
+ ```typescript
54
+ import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/express';
55
+
56
+ // Self-hosted facilitator (requires facilitatorUrl + facilitatorSecret)
57
+ const pay = mantlePaywall({
58
+ priceUsd: 0.01,
59
+ payTo: '0xYourWallet',
60
+ facilitatorUrl: 'https://your-facilitator.com', // Required for self-hosted
61
+ facilitatorSecret: process.env.FACILITATOR_SECRET!, // Required for self-hosted
62
+ // projectKey: process.env.PROJECT_KEY, // Optional: for analytics
63
+ });
64
+ ```
65
+
66
+ **Note:** When using `facilitatorSecret`, `facilitatorUrl` is required. The URL is automatically passed to clients via 402 responses.
37
67
 
38
68
  ## Endpoints
39
69
 
@@ -1,24 +1,136 @@
1
1
  // src/config.ts
2
2
  import "dotenv/config";
3
+ import { ethers } from "ethers";
3
4
  import { DEFAULT_TELEMETRY_ENDPOINT } from "./constants";
4
5
 
5
- function required(name: string): string {
6
- const v = process.env[name];
7
- if (!v) throw new Error(`Missing env var: ${name}`);
8
- return v;
6
+ // ============================================
7
+ // Validation types and helpers
8
+ // ============================================
9
+
10
+ interface ConfigError {
11
+ field: string;
12
+ message: string;
13
+ howToFix: string[];
14
+ }
15
+
16
+ function validateConfig(): ConfigError[] {
17
+ const errors: ConfigError[] = [];
18
+
19
+ // Required: FACILITATOR_PRIVATE_KEY
20
+ const privateKey = process.env.FACILITATOR_PRIVATE_KEY;
21
+ if (!privateKey) {
22
+ errors.push({
23
+ field: "FACILITATOR_PRIVATE_KEY",
24
+ message: "This is the wallet that will pay gas fees for settlements.",
25
+ howToFix: [
26
+ "Open .env file",
27
+ "Set FACILITATOR_PRIVATE_KEY=0xYourPrivateKey",
28
+ "Need a wallet? Create one at https://metamask.io",
29
+ "Make sure it has MNT for gas fees.",
30
+ ],
31
+ });
32
+ } else if (!privateKey.startsWith("0x") || privateKey.length !== 66) {
33
+ errors.push({
34
+ field: "FACILITATOR_PRIVATE_KEY",
35
+ message: "Invalid private key format.",
36
+ howToFix: [
37
+ "Private key must start with 0x",
38
+ "Private key must be 66 characters (0x + 64 hex digits)",
39
+ `Current length: ${privateKey.length} characters`,
40
+ ],
41
+ });
42
+ }
43
+
44
+ // Required: FACILITATOR_SECRET
45
+ const secret = process.env.FACILITATOR_SECRET;
46
+ if (!secret) {
47
+ errors.push({
48
+ field: "FACILITATOR_SECRET",
49
+ message: "This secret authenticates your backend with the facilitator.",
50
+ howToFix: [
51
+ "Open .env file",
52
+ "Set FACILITATOR_SECRET=fac_YOUR_SECRET_HERE",
53
+ "Then copy this value to your backend's facilitatorSecret config",
54
+ ],
55
+ });
56
+ }
57
+
58
+ // Required: RPC_URL
59
+ if (!process.env.RPC_URL) {
60
+ errors.push({
61
+ field: "RPC_URL",
62
+ message: "RPC URL for connecting to Mantle network.",
63
+ howToFix: [
64
+ "Open .env file",
65
+ "Set RPC_URL=https://rpc.mantle.xyz",
66
+ ],
67
+ });
68
+ }
69
+
70
+ // Required: USDC_ADDRESS
71
+ if (!process.env.USDC_ADDRESS) {
72
+ errors.push({
73
+ field: "USDC_ADDRESS",
74
+ message: "USDC contract address on Mantle.",
75
+ howToFix: [
76
+ "Open .env file",
77
+ "Set USDC_ADDRESS=0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9",
78
+ ],
79
+ });
80
+ }
81
+
82
+ return errors;
83
+ }
84
+
85
+ function printConfigErrors(errors: ConfigError[]): void {
86
+ console.error("");
87
+ console.error("CONFIGURATION ERROR");
88
+ console.error("=".repeat(50));
89
+
90
+ for (const error of errors) {
91
+ console.error("");
92
+ console.error(`Missing: ${error.field}`);
93
+ console.error(`${error.message}`);
94
+ console.error("");
95
+ console.error("To fix:");
96
+ error.howToFix.forEach((step, i) => {
97
+ console.error(` ${i + 1}. ${step}`);
98
+ });
99
+ }
100
+
101
+ console.error("");
102
+ console.error("=".repeat(50));
103
+ console.error("");
104
+ }
105
+
106
+ // ============================================
107
+ // Run validation at startup
108
+ // ============================================
109
+
110
+ const configErrors = validateConfig();
111
+ if (configErrors.length > 0) {
112
+ printConfigErrors(configErrors);
113
+ process.exit(1);
9
114
  }
10
115
 
116
+ // ============================================
117
+ // Build config (all required fields are validated)
118
+ // ============================================
119
+
11
120
  export const CONFIG = {
12
121
  port: Number(process.env.PORT ?? 8080),
13
122
 
14
123
  networkId: process.env.NETWORK_ID ?? "mantle-mainnet",
15
124
  chainId: Number(process.env.CHAIN_ID ?? 5000),
16
- rpcUrl: required("RPC_URL"),
125
+ rpcUrl: process.env.RPC_URL!,
17
126
 
18
- usdcAddress: required("USDC_ADDRESS"),
127
+ usdcAddress: process.env.USDC_ADDRESS!,
19
128
  usdcDecimals: Number(process.env.USDC_DECIMALS ?? 6),
20
129
 
21
- facilitatorPrivateKey: required("FACILITATOR_PRIVATE_KEY"),
130
+ facilitatorPrivateKey: process.env.FACILITATOR_PRIVATE_KEY!,
131
+
132
+ // Secret for settle token verification (required for security)
133
+ facilitatorSecret: process.env.FACILITATOR_SECRET!,
22
134
 
23
135
  logLevel: process.env.LOG_LEVEL ?? "info",
24
136
 
@@ -30,3 +142,12 @@ export const CONFIG = {
30
142
  }
31
143
  : undefined,
32
144
  } as const;
145
+
146
+ // ============================================
147
+ // Derive facilitator address from private key
148
+ // ============================================
149
+
150
+ export function getFacilitatorAddress(): string {
151
+ const wallet = new ethers.Wallet(CONFIG.facilitatorPrivateKey);
152
+ return wallet.address;
153
+ }
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import express from "express";
3
3
  import cors from "cors";
4
- import { CONFIG } from "./config";
4
+ import { CONFIG, getFacilitatorAddress } from "./config";
5
5
 
6
6
  import { healthRoute } from "./routes/health";
7
7
  import { supportedRoute } from "./routes/supported";
@@ -25,7 +25,36 @@ app.get("/supported", supportedRoute);
25
25
  app.post("/verify", verifyRoute);
26
26
  app.post("/settle", settleRoute);
27
27
 
28
+ // ============================================
29
+ // Startup banner
30
+ // ============================================
31
+
32
+ function printStartupBanner(): void {
33
+ const address = getFacilitatorAddress();
34
+ const shortAddress = `${address.slice(0, 6)}...${address.slice(-4)}`;
35
+ const telemetryStatus = CONFIG.telemetry ? "enabled" : "disabled";
36
+
37
+ console.log("");
38
+ console.log("+".padEnd(50, "-") + "+");
39
+ console.log("| Mantle x402 Facilitator".padEnd(50) + "|");
40
+ console.log("+".padEnd(50, "-") + "+");
41
+ console.log(`| Network: ${CONFIG.networkId} (${CONFIG.chainId})`.padEnd(50) + "|");
42
+ console.log(`| Address: ${shortAddress}`.padEnd(50) + "|");
43
+ console.log(`| Port: ${CONFIG.port}`.padEnd(50) + "|");
44
+ console.log(`| Telemetry: ${telemetryStatus}`.padEnd(50) + "|");
45
+ console.log("+".padEnd(50, "-") + "+");
46
+ console.log("");
47
+
48
+ // Telemetry warning
49
+ if (!CONFIG.telemetry) {
50
+ console.log("Note: Telemetry is disabled.");
51
+ console.log(" To enable analytics, set TELEMETRY_PROJECT_KEY in .env");
52
+ console.log(" Get your key at https://x402mantlesdk.xyz/dashboard");
53
+ console.log("");
54
+ }
55
+ }
56
+
28
57
  app.listen(CONFIG.port, () => {
29
- console.log(`Facilitator server listening on http://localhost:${CONFIG.port}`);
30
- console.log(`Network: ${CONFIG.networkId} (chainId=${CONFIG.chainId})`);
58
+ printStartupBanner();
59
+ console.log(`Server listening on http://localhost:${CONFIG.port}`);
31
60
  });
@@ -4,6 +4,7 @@ import { ethers } from "ethers";
4
4
  import { decodePaymentHeader, verifyPayment, type PaymentRequirements } from "../x402";
5
5
  import { getFacilitatorSigner, getUsdcContract } from "../blockchain";
6
6
  import { CONFIG } from "../config";
7
+ import { verifySettleToken } from "../security";
7
8
 
8
9
  function signatureToVRS(signature: string) {
9
10
  const sig = ethers.Signature.from(signature);
@@ -15,10 +16,11 @@ export async function settleRoute(req: Request, res: Response) {
15
16
  const raw = req.body ?? {};
16
17
 
17
18
  try {
18
- const { x402Version, paymentHeader, paymentRequirements } = raw as {
19
+ const { x402Version, paymentHeader, paymentRequirements, settleToken } = raw as {
19
20
  x402Version?: number;
20
21
  paymentHeader?: string;
21
22
  paymentRequirements?: PaymentRequirements;
23
+ settleToken?: string;
22
24
  };
23
25
  const projectKey = req.header("X-Project-Key"); // Optional: for hosted facilitator billing
24
26
 
@@ -47,6 +49,18 @@ export async function settleRoute(req: Request, res: Response) {
47
49
  return;
48
50
  }
49
51
 
52
+ // Verify settle token (required for self-hosted facilitator security)
53
+ const tokenVerification = verifySettleToken(settleToken, paymentRequirements, CONFIG.facilitatorSecret);
54
+ if (!tokenVerification.valid) {
55
+ res.status(401).json({
56
+ success: false,
57
+ error: tokenVerification.error || "Invalid settle token",
58
+ txHash: null,
59
+ networkId: paymentRequirements.network,
60
+ });
61
+ return;
62
+ }
63
+
50
64
  const headerObj = decodePaymentHeader(paymentHeader);
51
65
  const verify = verifyPayment(headerObj, paymentRequirements);
52
66
 
@@ -0,0 +1,126 @@
1
+ // src/security.ts
2
+ import crypto from "crypto";
3
+
4
+ export interface PaymentRequirementsForToken {
5
+ payTo: string;
6
+ maxAmountRequired: string;
7
+ asset: string;
8
+ network: string;
9
+ }
10
+
11
+ interface SettleTokenPayload {
12
+ payTo: string;
13
+ amount: string;
14
+ asset: string;
15
+ network: string;
16
+ timestamp: number;
17
+ expires: number;
18
+ }
19
+
20
+ interface DecodedSettleToken {
21
+ payload: SettleTokenPayload;
22
+ signature: string;
23
+ }
24
+
25
+ /**
26
+ * Creates a settle token that authorizes a specific payment.
27
+ * The token is signed with HMAC-SHA256 and includes expiration.
28
+ *
29
+ * @param paymentRequirements - The payment requirements to authorize
30
+ * @param secret - The FACILITATOR_SECRET
31
+ * @param expiresInSeconds - Token validity period (default: 600 seconds = 10 minutes)
32
+ * @returns Base64-encoded settle token
33
+ */
34
+ export function createSettleToken(
35
+ paymentRequirements: PaymentRequirementsForToken,
36
+ secret: string,
37
+ expiresInSeconds: number = 600
38
+ ): string {
39
+ const timestamp = Math.floor(Date.now() / 1000);
40
+
41
+ const payload: SettleTokenPayload = {
42
+ payTo: paymentRequirements.payTo.toLowerCase(),
43
+ amount: paymentRequirements.maxAmountRequired,
44
+ asset: paymentRequirements.asset.toLowerCase(),
45
+ network: paymentRequirements.network,
46
+ timestamp,
47
+ expires: timestamp + expiresInSeconds,
48
+ };
49
+
50
+ const data = JSON.stringify(payload);
51
+ const signature = crypto.createHmac("sha256", secret).update(data).digest("hex");
52
+
53
+ return Buffer.from(JSON.stringify({ payload, signature })).toString("base64");
54
+ }
55
+
56
+ /**
57
+ * Verifies a settle token against payment requirements.
58
+ * Checks signature, expiration, and that the token matches the requirements.
59
+ *
60
+ * @param token - Base64-encoded settle token
61
+ * @param paymentRequirements - The payment requirements to verify against
62
+ * @param secret - The FACILITATOR_SECRET
63
+ * @returns true if token is valid, false otherwise
64
+ */
65
+ export function verifySettleToken(
66
+ token: string | undefined,
67
+ paymentRequirements: PaymentRequirementsForToken,
68
+ secret: string
69
+ ): { valid: boolean; error?: string } {
70
+ if (!token) {
71
+ return { valid: false, error: "Missing settle token" };
72
+ }
73
+
74
+ try {
75
+ const decoded: DecodedSettleToken = JSON.parse(
76
+ Buffer.from(token, "base64").toString("utf-8")
77
+ );
78
+ const { payload, signature } = decoded;
79
+
80
+ // Check expiration
81
+ const now = Math.floor(Date.now() / 1000);
82
+ if (payload.expires < now) {
83
+ return { valid: false, error: "Settle token expired" };
84
+ }
85
+
86
+ // Check that token matches payment requirements (case-insensitive for addresses)
87
+ if (payload.payTo.toLowerCase() !== paymentRequirements.payTo.toLowerCase()) {
88
+ return { valid: false, error: "PayTo address mismatch" };
89
+ }
90
+
91
+ if (payload.amount !== paymentRequirements.maxAmountRequired) {
92
+ return { valid: false, error: "Amount mismatch" };
93
+ }
94
+
95
+ if (payload.asset.toLowerCase() !== paymentRequirements.asset.toLowerCase()) {
96
+ return { valid: false, error: "Asset mismatch" };
97
+ }
98
+
99
+ if (payload.network !== paymentRequirements.network) {
100
+ return { valid: false, error: "Network mismatch" };
101
+ }
102
+
103
+ // Verify signature
104
+ const data = JSON.stringify(payload);
105
+ const expectedSignature = crypto
106
+ .createHmac("sha256", secret)
107
+ .update(data)
108
+ .digest("hex");
109
+
110
+ if (signature !== expectedSignature) {
111
+ return { valid: false, error: "Invalid signature" };
112
+ }
113
+
114
+ return { valid: true };
115
+ } catch (err) {
116
+ return { valid: false, error: "Invalid token format" };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Generates a random secret for FACILITATOR_SECRET.
122
+ * Format: fac_<64 hex characters>
123
+ */
124
+ export function generateFacilitatorSecret(): string {
125
+ return "fac_" + crypto.randomBytes(32).toString("hex");
126
+ }