@x402-crosschain/facilitator 1.0.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/Dockerfile ADDED
@@ -0,0 +1,64 @@
1
+ # Multi-stage Dockerfile for x402 Cross-Chain Facilitator
2
+ # Optimized for production deployment
3
+
4
+ # Stage 1: Dependencies
5
+ FROM node:20-alpine AS deps
6
+ WORKDIR /app
7
+
8
+ # Install pnpm
9
+ RUN npm install -g pnpm
10
+
11
+ # Copy package files
12
+ COPY package.json pnpm-lock.yaml* ./
13
+
14
+ # Install dependencies
15
+ RUN pnpm install --frozen-lockfile --prod
16
+
17
+ # Stage 2: Builder
18
+ FROM node:20-alpine AS builder
19
+ WORKDIR /app
20
+
21
+ # Install pnpm
22
+ RUN npm install -g pnpm
23
+
24
+ # Copy package files
25
+ COPY package.json pnpm-lock.yaml* tsconfig.json ./
26
+
27
+ # Install all dependencies (including devDependencies for building)
28
+ RUN pnpm install --frozen-lockfile
29
+
30
+ # Copy source code
31
+ COPY src ./src
32
+
33
+ # Build TypeScript
34
+ RUN pnpm build
35
+
36
+ # Stage 3: Runner
37
+ FROM node:20-alpine AS runner
38
+ WORKDIR /app
39
+
40
+ # Set environment to production
41
+ ENV NODE_ENV=production
42
+
43
+ # Create non-root user for security
44
+ RUN addgroup --system --gid 1001 nodejs && \
45
+ adduser --system --uid 1001 facilitator
46
+
47
+ # Copy necessary files from builder
48
+ COPY --from=deps --chown=facilitator:nodejs /app/node_modules ./node_modules
49
+ COPY --from=builder --chown=facilitator:nodejs /app/dist ./dist
50
+ COPY --chown=facilitator:nodejs package.json ./
51
+
52
+ # Switch to non-root user
53
+ USER facilitator
54
+
55
+ # Expose port
56
+ EXPOSE 3001
57
+
58
+ # Health check
59
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
60
+ CMD node -e "require('http').get('http://localhost:3001/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1); })"
61
+
62
+ # Start the application
63
+ CMD ["node", "dist/index.js"]
64
+
package/README.md ADDED
@@ -0,0 +1,641 @@
1
+ # x402 Cross-Chain Facilitator
2
+
3
+ > **Backend service for instant cross-chain payment settlement**
4
+
5
+ The facilitator handles cross-chain payment routing, bridging via Relay Network, and on-chain settlement verification. Designed for self-hosting or managed deployment.
6
+
7
+ ---
8
+
9
+ ## 🎯 **Overview**
10
+
11
+ The facilitator is the backend engine that powers cross-chain x402 payments:
12
+
13
+ 1. **Quote Generation** - Calculates cross-chain routes and costs
14
+ 2. **Bridge Coordination** - Manages Relay Network instant bridging
15
+ 3. **Settlement Verification** - Confirms on-chain payment completion
16
+ 4. **Status Tracking** - Monitors payment lifecycle
17
+
18
+ ---
19
+
20
+ ## ⚡ **Key Features**
21
+
22
+ - ⚡ **Instant Bridging** - 2-3 second settlement via Relay liquidity pools
23
+ - 🌐 **69+ Chains** - Support for all major EVM chains + Solana, Bitcoin
24
+ - 🔒 **Non-Custodial** - No funds held, only coordinates transactions
25
+ - 📊 **Status Polling** - Automatic bridge completion tracking
26
+ - 🐳 **Docker Ready** - Easy deployment with Docker/Kubernetes
27
+ - 🔧 **Configurable** - Environment-based configuration
28
+
29
+ ---
30
+
31
+ ## 📦 **Deployment Options**
32
+
33
+ ### **Option 1: Docker (Recommended)**
34
+
35
+ ```bash
36
+ # Build image
37
+ docker build -t x402-facilitator .
38
+
39
+ # Run container
40
+ docker run -d \
41
+ -p 3001:3001 \
42
+ -e BASE_RPC_URL=https://mainnet.base.org \
43
+ -e PAYMENT_SETTLEMENT_ADDRESS=0xYourContractAddress \
44
+ -e SETTLER_PRIVATE_KEY=0xYourPrivateKey \
45
+ --name x402-facilitator \
46
+ x402-facilitator
47
+ ```
48
+
49
+ ### **Option 2: Node.js**
50
+
51
+ ```bash
52
+ # Install dependencies
53
+ pnpm install
54
+
55
+ # Build
56
+ pnpm build
57
+
58
+ # Start
59
+ pnpm start
60
+ ```
61
+
62
+ ### **Option 3: Programmatic**
63
+
64
+ ```typescript
65
+ import { startFacilitator } from '@x402-crosschain/facilitator';
66
+
67
+ await startFacilitator({
68
+ port: 3001,
69
+ baseRpcUrl: 'https://mainnet.base.org',
70
+ paymentSettlementAddress: '0xYourContractAddress',
71
+ settlerPrivateKey: process.env.SETTLER_PRIVATE_KEY,
72
+ });
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 🔧 **Configuration**
78
+
79
+ ### **Environment Variables**
80
+
81
+ Create a `.env` file:
82
+
83
+ ```bash
84
+ # Required Configuration
85
+ BASE_RPC_URL=https://mainnet.base.org
86
+ PAYMENT_SETTLEMENT_ADDRESS=0xYourContractAddress
87
+ SETTLER_PRIVATE_KEY=0xYourPrivateKeyHere
88
+
89
+ # Optional Configuration
90
+ PORT=3001
91
+ POLL_INTERVAL=10000
92
+ ```
93
+
94
+ ### **Configuration Options**
95
+
96
+ | Variable | Description | Default | Required |
97
+ |----------|-------------|---------|----------|
98
+ | `BASE_RPC_URL` | Base network RPC endpoint | - | ✅ Yes |
99
+ | `PAYMENT_SETTLEMENT_ADDRESS` | Settlement contract address on Base | - | ✅ Yes |
100
+ | `SETTLER_PRIVATE_KEY` | Private key with settler role | - | ✅ Yes |
101
+ | `PORT` | HTTP server port | 3001 | ❌ No |
102
+ | `POLL_INTERVAL` | Status check interval (ms) | 10000 | ❌ No |
103
+
104
+ ---
105
+
106
+ ## 📡 **API Endpoints**
107
+
108
+ ### **POST /quote-route**
109
+
110
+ Get a cross-chain payment quote with transaction data.
111
+
112
+ **Request:**
113
+
114
+ ```typescript
115
+ POST http://localhost:3001/quote-route
116
+ Content-Type: application/json
117
+
118
+ {
119
+ "requirement": {
120
+ "chainId": 8453, // Base
121
+ "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC
122
+ "amount": "1000000" // 1 USDC (6 decimals)
123
+ },
124
+ "payer": {
125
+ "fromChainId": 42161, // Arbitrum
126
+ "fromToken": "0x0000000000000000000000000000000000000000", // Native ETH
127
+ "address": "0x742d35Cc6634C0532925a3b8D9d4DB0a2D7DD5B3"
128
+ }
129
+ }
130
+ ```
131
+
132
+ **Response:**
133
+
134
+ ```json
135
+ {
136
+ "paymentId": "0xabc123...",
137
+ "txData": {
138
+ "to": "0xa5F565650890fBA1824Ee0F21EbBbF660a179934",
139
+ "data": "0x...",
140
+ "value": "12345678901234",
141
+ "chainId": 42161
142
+ },
143
+ "route": {
144
+ "fromChainId": 42161,
145
+ "toChainId": 8453,
146
+ "fromToken": "0x0000000000000000000000000000000000000000",
147
+ "toToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
148
+ "expectedOutput": "1000000"
149
+ }
150
+ }
151
+ ```
152
+
153
+ **Response Fields:**
154
+
155
+ | Field | Type | Description |
156
+ |-------|------|-------------|
157
+ | `paymentId` | string | Unique payment identifier for tracking |
158
+ | `txData.to` | string | Relay contract address to send transaction |
159
+ | `txData.data` | string | Encoded transaction calldata |
160
+ | `txData.value` | string | ETH amount to send (in wei) |
161
+ | `txData.chainId` | number | Source chain ID |
162
+ | `route.expectedOutput` | string | Expected USDC received on Base |
163
+
164
+ ---
165
+
166
+ ### **GET /verify**
167
+
168
+ Check if a payment has been settled on-chain.
169
+
170
+ **Request:**
171
+
172
+ ```bash
173
+ GET http://localhost:3001/verify?paymentId=0xabc123...
174
+ ```
175
+
176
+ **Response:**
177
+
178
+ ```json
179
+ {
180
+ "settled": true
181
+ }
182
+ ```
183
+
184
+ **Response Fields:**
185
+
186
+ | Field | Type | Description |
187
+ |-------|------|-------------|
188
+ | `settled` | boolean | `true` if payment settled on-chain, `false` otherwise |
189
+
190
+ ---
191
+
192
+ ### **POST /submit-tx**
193
+
194
+ Submit transaction hash for tracking (optional).
195
+
196
+ **Request:**
197
+
198
+ ```typescript
199
+ POST http://localhost:3001/submit-tx
200
+ Content-Type: application/json
201
+
202
+ {
203
+ "paymentId": "0xabc123...",
204
+ "txHash": "0xdef456..."
205
+ }
206
+ ```
207
+
208
+ **Response:**
209
+
210
+ ```json
211
+ {
212
+ "success": true
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ### **GET /health**
219
+
220
+ Health check endpoint.
221
+
222
+ **Request:**
223
+
224
+ ```bash
225
+ GET http://localhost:3001/health
226
+ ```
227
+
228
+ **Response:**
229
+
230
+ ```json
231
+ {
232
+ "ok": true,
233
+ "activePayments": 3,
234
+ "timestamp": "2025-12-17T00:21:45.123Z"
235
+ }
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 🏗️ **Architecture**
241
+
242
+ ```
243
+ ┌──────────────────────────────────────────────┐
244
+ │ x402 Facilitator Service │
245
+ ├──────────────────────────────────────────────┤
246
+ │ │
247
+ │ ┌────────────┐ ┌──────────────┐ │
248
+ │ │ Express │ │ Relay │ │
249
+ │ │ API │──│ Service │ │
250
+ │ │ Server │ │ │ │
251
+ │ └────────────┘ └──────────────┘ │
252
+ │ │ │ │
253
+ │ │ │ │
254
+ │ ┌──────┴────────┐ ┌───┴──────────────┐ │
255
+ │ │ Settlement │ │ Status │ │
256
+ │ │ Service │ │ Poller │ │
257
+ │ └───────────────┘ └──────────────────┘ │
258
+ │ │ │
259
+ └─────────┼────────────────────────────────────┘
260
+
261
+
262
+ ┌───────────────┐
263
+ │ Base Network │
264
+ │ Settlement │
265
+ │ Contract │
266
+ └───────────────┘
267
+ ```
268
+
269
+ ### **Components**
270
+
271
+ 1. **Express API Server** - HTTP endpoints for quote/verify/health
272
+ 2. **Relay Service** - Integration with Relay Network for bridging
273
+ 3. **Settlement Service** - On-chain settlement contract interactions
274
+ 4. **Status Poller** - Background process monitoring bridge completion
275
+
276
+ ---
277
+
278
+ ## 🔄 **Payment Flow**
279
+
280
+ ```
281
+ 1. Client → POST /quote-route
282
+
283
+ 2. Facilitator → Relay Network (get quote)
284
+
285
+ 3. Facilitator → Register payment on-chain
286
+
287
+ 4. Facilitator → Return tx data to client
288
+
289
+ 5. Client → Submit tx on source chain (Arbitrum)
290
+
291
+ 6. Relay Network → Instant bridge (2-3 seconds)
292
+
293
+ 7. Status Poller → Check completion
294
+
295
+ 8. Facilitator → Settle payment on-chain (Base)
296
+
297
+ 9. Client → GET /verify (returns true)
298
+ ```
299
+
300
+ ---
301
+
302
+ ## 🐳 **Docker Deployment**
303
+
304
+ ### **Dockerfile**
305
+
306
+ See [`Dockerfile`](./Dockerfile) in this directory.
307
+
308
+ ### **Docker Compose**
309
+
310
+ ```yaml
311
+ version: '3.8'
312
+
313
+ services:
314
+ facilitator:
315
+ build: .
316
+ ports:
317
+ - "3001:3001"
318
+ environment:
319
+ - BASE_RPC_URL=https://mainnet.base.org
320
+ - PAYMENT_SETTLEMENT_ADDRESS=${PAYMENT_SETTLEMENT_ADDRESS}
321
+ - SETTLER_PRIVATE_KEY=${SETTLER_PRIVATE_KEY}
322
+ - PORT=3001
323
+ restart: unless-stopped
324
+ healthcheck:
325
+ test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
326
+ interval: 30s
327
+ timeout: 10s
328
+ retries: 3
329
+ ```
330
+
331
+ ### **Kubernetes Deployment**
332
+
333
+ ```yaml
334
+ apiVersion: apps/v1
335
+ kind: Deployment
336
+ metadata:
337
+ name: x402-facilitator
338
+ spec:
339
+ replicas: 2
340
+ selector:
341
+ matchLabels:
342
+ app: x402-facilitator
343
+ template:
344
+ metadata:
345
+ labels:
346
+ app: x402-facilitator
347
+ spec:
348
+ containers:
349
+ - name: facilitator
350
+ image: your-registry/x402-facilitator:latest
351
+ ports:
352
+ - containerPort: 3001
353
+ env:
354
+ - name: BASE_RPC_URL
355
+ value: "https://mainnet.base.org"
356
+ - name: PAYMENT_SETTLEMENT_ADDRESS
357
+ valueFrom:
358
+ secretKeyRef:
359
+ name: facilitator-secrets
360
+ key: contract-address
361
+ - name: SETTLER_PRIVATE_KEY
362
+ valueFrom:
363
+ secretKeyRef:
364
+ name: facilitator-secrets
365
+ key: settler-key
366
+ livenessProbe:
367
+ httpGet:
368
+ path: /health
369
+ port: 3001
370
+ initialDelaySeconds: 30
371
+ periodSeconds: 10
372
+ ---
373
+ apiVersion: v1
374
+ kind: Service
375
+ metadata:
376
+ name: x402-facilitator
377
+ spec:
378
+ selector:
379
+ app: x402-facilitator
380
+ ports:
381
+ - port: 80
382
+ targetPort: 3001
383
+ type: LoadBalancer
384
+ ```
385
+
386
+ ---
387
+
388
+ ## 🔒 **Security Considerations**
389
+
390
+ ### **Private Key Management**
391
+
392
+ ⚠️ **CRITICAL**: Never expose your settler private key!
393
+
394
+ ```bash
395
+ # ✅ Good: Use environment variables
396
+ export SETTLER_PRIVATE_KEY=0x...
397
+
398
+ # ✅ Good: Use Docker secrets
399
+ docker run --env-file .env.production x402-facilitator
400
+
401
+ # ❌ Bad: Hardcode in source
402
+ const privateKey = "0x123..." // NEVER DO THIS
403
+ ```
404
+
405
+ ### **Access Control**
406
+
407
+ ```typescript
408
+ // Add rate limiting
409
+ import rateLimit from 'express-rate-limit';
410
+
411
+ app.use('/quote-route', rateLimit({
412
+ windowMs: 15 * 60 * 1000, // 15 minutes
413
+ max: 100, // limit each IP to 100 requests per window
414
+ }));
415
+
416
+ // Add CORS for production
417
+ app.use(cors({
418
+ origin: process.env.ALLOWED_ORIGINS?.split(','),
419
+ }));
420
+ ```
421
+
422
+ ### **Monitoring**
423
+
424
+ ```bash
425
+ # Check facilitator health
426
+ curl http://localhost:3001/health
427
+
428
+ # Monitor logs
429
+ docker logs -f x402-facilitator
430
+
431
+ # Track active payments
432
+ curl http://localhost:3001/health | jq '.activePayments'
433
+ ```
434
+
435
+ ---
436
+
437
+ ## 📊 **Operational Metrics**
438
+
439
+ ### **Performance**
440
+
441
+ - **Quote Generation**: < 1 second
442
+ - **Bridge Settlement**: 2-3 seconds (via Relay)
443
+ - **On-chain Settlement**: 2-5 seconds (Base finality)
444
+ - **Total Time**: ~5-10 seconds end-to-end
445
+
446
+ ### **Reliability**
447
+
448
+ - **Uptime Target**: 99.9%
449
+ - **Success Rate**: > 99% (via Relay Network)
450
+ - **Automatic Retries**: Failed bridges automatically refunded
451
+
452
+ ### **Resource Requirements**
453
+
454
+ | Resource | Minimum | Recommended |
455
+ |----------|---------|-------------|
456
+ | **CPU** | 1 core | 2 cores |
457
+ | **RAM** | 512 MB | 1 GB |
458
+ | **Storage** | 1 GB | 5 GB |
459
+ | **Network** | 10 Mbps | 100 Mbps |
460
+
461
+ ---
462
+
463
+ ## 🧪 **Testing**
464
+
465
+ ### **Local Testing**
466
+
467
+ ```bash
468
+ # Start facilitator
469
+ pnpm dev
470
+
471
+ # Test health endpoint
472
+ curl http://localhost:3001/health
473
+
474
+ # Test quote endpoint
475
+ curl -X POST http://localhost:3001/quote-route \
476
+ -H "Content-Type: application/json" \
477
+ -d '{
478
+ "requirement": {
479
+ "chainId": 84532,
480
+ "tokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
481
+ "amount": "1000000"
482
+ },
483
+ "payer": {
484
+ "fromChainId": 421614,
485
+ "fromToken": "0x0000000000000000000000000000000000000000",
486
+ "address": "0x742d35Cc6634C0532925a3b8D9d4DB0a2D7DD5B3"
487
+ }
488
+ }'
489
+ ```
490
+
491
+ ### **Integration Testing**
492
+
493
+ See [`examples/`](../../examples/) directory for full integration tests.
494
+
495
+ ---
496
+
497
+ ## 🔧 **Troubleshooting**
498
+
499
+ ### **Common Issues**
500
+
501
+ #### 1. **"Settler not authorized"**
502
+
503
+ ```bash
504
+ # Ensure settler address matches contract deployment
505
+ # Settler is the address derived from SETTLER_PRIVATE_KEY
506
+ ```
507
+
508
+ #### 2. **"Relay quote failed"**
509
+
510
+ ```bash
511
+ # Check if route is supported
512
+ # Verify fromChainId and toChainId are valid
513
+ # Ensure amount is above minimum ($1.00 recommended)
514
+ ```
515
+
516
+ #### 3. **"Payment not settling"**
517
+
518
+ ```bash
519
+ # Check facilitator logs for poller status
520
+ # Verify Base RPC is responding
521
+ # Ensure settler wallet has gas for settlement txs
522
+ ```
523
+
524
+ #### 4. **"Connection refused"**
525
+
526
+ ```bash
527
+ # Verify facilitator is running
528
+ curl http://localhost:3001/health
529
+
530
+ # Check firewall rules
531
+ sudo ufw allow 3001
532
+
533
+ # Verify Docker port mapping
534
+ docker ps | grep facilitator
535
+ ```
536
+
537
+ ---
538
+
539
+ ## 📈 **Scaling**
540
+
541
+ ### **Horizontal Scaling**
542
+
543
+ ```yaml
544
+ # Deploy multiple instances behind load balancer
545
+ replicas: 3
546
+
547
+ # Use sticky sessions for in-memory payment tracking
548
+ # Or migrate to Redis for shared state:
549
+ ```
550
+
551
+ ### **Redis Integration (Future)**
552
+
553
+ ```typescript
554
+ // Coming soon: Shared payment state
555
+ import Redis from 'ioredis';
556
+
557
+ const redis = new Redis(process.env.REDIS_URL);
558
+ await redis.set(`payment:${paymentId}`, JSON.stringify(payment));
559
+ ```
560
+
561
+ ---
562
+
563
+ ## 🔄 **Upgrading**
564
+
565
+ ### **Version Migration**
566
+
567
+ ```bash
568
+ # Pull latest image
569
+ docker pull your-registry/x402-facilitator:latest
570
+
571
+ # Rolling update
572
+ kubectl rollout restart deployment/x402-facilitator
573
+
574
+ # Verify health
575
+ kubectl rollout status deployment/x402-facilitator
576
+ ```
577
+
578
+ ---
579
+
580
+ ## 📚 **API Client Examples**
581
+
582
+ ### **JavaScript/TypeScript**
583
+
584
+ ```typescript
585
+ // Get quote
586
+ const response = await fetch('https://facilitator.yourdomain.com/quote-route', {
587
+ method: 'POST',
588
+ headers: { 'Content-Type': 'application/json' },
589
+ body: JSON.stringify({ requirement, payer }),
590
+ });
591
+
592
+ const { paymentId, txData } = await response.json();
593
+
594
+ // Check status
595
+ const verifyResponse = await fetch(
596
+ `https://facilitator.yourdomain.com/verify?paymentId=${paymentId}`
597
+ );
598
+ const { settled } = await verifyResponse.json();
599
+ ```
600
+
601
+ ### **cURL**
602
+
603
+ ```bash
604
+ # Get quote
605
+ curl -X POST https://facilitator.yourdomain.com/quote-route \
606
+ -H "Content-Type: application/json" \
607
+ -d '{"requirement":{...},"payer":{...}}'
608
+
609
+ # Check status
610
+ curl "https://facilitator.yourdomain.com/verify?paymentId=0xabc..."
611
+
612
+ # Health check
613
+ curl https://facilitator.yourdomain.com/health
614
+ ```
615
+
616
+ ---
617
+
618
+ ## 📄 **License**
619
+
620
+ Apache 2.0 License
621
+
622
+ ---
623
+
624
+ ## 🙏 **Acknowledgments**
625
+
626
+ - [Relay Network](https://relay.link) - Instant cross-chain bridging
627
+ - [x402 Protocol](https://x402.org) - Open payment standard
628
+ - [ethers.js](https://docs.ethers.org) - Ethereum interactions
629
+
630
+ ---
631
+
632
+ ## 🆘 **Support**
633
+
634
+ - **Documentation**: https://docs.x402crosschain.com
635
+ - **GitHub Issues**: https://github.com/your-org/x402-cross-bridge-sdk/issues
636
+ - **Discord**: https://discord.gg/your-invite
637
+ - **Email**: support@x402crosschain.com
638
+
639
+ ---
640
+
641
+ **Made with ❤️ by the x402 Cross-Chain Team**
@@ -0,0 +1,8 @@
1
+ export declare const config: {
2
+ baseRpcUrl: string;
3
+ paymentSettlementAddress: string;
4
+ settlerPrivateKey: string;
5
+ port: number;
6
+ pollInterval: number;
7
+ };
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,MAAM;;;;;;CAMlB,CAAC"}