facinet 2.3.0 → 2.4.2

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
@@ -2,16 +2,16 @@
2
2
 
3
3
  > JavaScript SDK and CLI tool for the x402 Facilitator Network
4
4
 
5
- Make gasless USDC payments and manage facilitators on the x402 decentralized payment network. Now with **multichain support** across 4 testnets.
5
+ Make gasless USDC payments and manage facilitators on the x402 decentralized payment network. Now with **multichain support** across 4 testnets with **network-specific facilitator selection**.
6
6
 
7
7
  ## Supported Chains
8
8
 
9
- | Chain | Network ID | Gas Token | Explorer |
10
- |-------|-----------|-----------|----------|
11
- | Avalanche Fuji | `avalanche-fuji` | AVAX | testnet.snowtrace.io |
12
- | Ethereum Sepolia | `ethereum-sepolia` | ETH | sepolia.etherscan.io |
13
- | Base Sepolia | `base-sepolia` | ETH | sepolia.basescan.org |
14
- | Polygon Amoy | `polygon-amoy` | MATIC | amoy.polygonscan.com |
9
+ | Chain | Network ID | Chain ID | Gas Token | Explorer | API URL |
10
+ |-------|-----------|----------|-----------|----------|---------|
11
+ | Avalanche Fuji | `avalanche-fuji` | 43113 | AVAX | testnet.snowtrace.io | facinet.vercel.app |
12
+ | Ethereum Sepolia | `ethereum-sepolia` | 11155111 | ETH | sepolia.etherscan.io | facinet.vercel.app |
13
+ | Base Sepolia | `base-sepolia` | 84532 | ETH | sepolia.basescan.org | facinet.vercel.app |
14
+ | Polygon Amoy | `polygon-amoy` | 80002 | MATIC | amoy.polygonscan.com | facinet.vercel.app |
15
15
 
16
16
  ## Installation
17
17
 
@@ -29,14 +29,18 @@ npm install -g facinet
29
29
 
30
30
  ## SDK Usage
31
31
 
32
+ ### Network-Specific Facilitator Selection
33
+
34
+ The SDK automatically selects **active facilitators** for the **specific network** you're using. Each network has its own facilitator pool, and the SDK ensures you only get facilitators registered for that network.
35
+
32
36
  ### Browser (with MetaMask)
33
37
 
34
38
  ```typescript
35
39
  import { Facinet } from 'facinet';
36
40
 
37
- // Initialize SDK - choose your chain
41
+ // Initialize SDK for Base Sepolia network
38
42
  const facinet = new Facinet({
39
- network: 'base-sepolia', // or 'avalanche-fuji', 'ethereum-sepolia', 'polygon-amoy'
43
+ network: 'base-sepolia', // SDK will use Base Sepolia API and facilitators
40
44
  });
41
45
 
42
46
  // Make a payment (MetaMask will prompt for signature)
@@ -46,8 +50,9 @@ const result = await facinet.pay({
46
50
  });
47
51
 
48
52
  console.log('Payment successful!', result.txHash);
49
- console.log('Processed by:', result.facilitator.name);
50
- console.log('Network:', result.payment.network);
53
+ console.log('Processed by:', result.facilitator.name); // Facilitator name
54
+ console.log('Facilitator ID:', result.facilitator.id);
55
+ console.log('Network:', result.payment.network); // 'base-sepolia'
51
56
  ```
52
57
 
53
58
  ### Node.js (with Private Key)
@@ -58,7 +63,7 @@ import { Facinet } from 'facinet';
58
63
  // Initialize with private key on Polygon Amoy
59
64
  const facinet = new Facinet({
60
65
  privateKey: process.env.PRIVATE_KEY,
61
- network: 'polygon-amoy',
66
+ network: 'polygon-amoy', // SDK will use Polygon Amoy API and facilitators
62
67
  });
63
68
 
64
69
  // Make a payment
@@ -68,6 +73,7 @@ const result = await facinet.pay({
68
73
  });
69
74
 
70
75
  console.log('Transaction:', result.txHash);
76
+ console.log('Facilitator:', result.facilitator.name);
71
77
  ```
72
78
 
73
79
  ### Quick One-Liner
@@ -75,27 +81,94 @@ console.log('Transaction:', result.txHash);
75
81
  ```typescript
76
82
  import { Facinet } from 'facinet';
77
83
 
78
- // Quick payment on any chain
84
+ // Quick payment on Ethereum Sepolia
79
85
  await Facinet.quickPay({
80
86
  amount: '1',
81
87
  recipient: '0xMerchantAddress',
82
88
  privateKey: process.env.PRIVATE_KEY,
83
- network: 'ethereum-sepolia',
89
+ network: 'ethereum-sepolia', // Uses Ethereum Sepolia facilitators
84
90
  });
85
91
  ```
86
92
 
87
- ### Get Available Facilitators
93
+ ### Get Active Facilitators for a Network
88
94
 
89
95
  ```typescript
90
- const facinet = new Facinet();
96
+ import { Facinet } from 'facinet';
91
97
 
92
- // Get all active facilitators
98
+ // Initialize for a specific network
99
+ const facinet = new Facinet({
100
+ network: 'base-sepolia', // Only gets facilitators for Base Sepolia
101
+ });
102
+
103
+ // Get all active facilitators for this network
93
104
  const facilitators = await facinet.getFacilitators();
94
- console.log(`${facilitators.length} active facilitators`);
105
+ console.log(`${facilitators.length} active facilitators on Base Sepolia`);
106
+
107
+ facilitators.forEach(fac => {
108
+ console.log(`- ${fac.name} (${fac.id})`);
109
+ console.log(` Wallet: ${fac.facilitatorWallet}`);
110
+ console.log(` Network: ${fac.network || 'N/A'}`);
111
+ console.log(` Chain ID: ${fac.chainId || 'N/A'}`);
112
+ console.log(` Status: ${fac.status}`);
113
+ });
95
114
 
96
- // Get a random facilitator
115
+ // Get a random active facilitator for this network
97
116
  const randomFacilitator = await facinet.selectRandomFacilitator();
98
117
  console.log('Selected:', randomFacilitator.name);
118
+ console.log('Network:', randomFacilitator.network);
119
+ ```
120
+
121
+ ### Network-Specific Examples
122
+
123
+ #### Avalanche Fuji
124
+
125
+ ```typescript
126
+ const facinet = new Facinet({
127
+ network: 'avalanche-fuji', // Uses Avalanche Fuji facilitators
128
+ });
129
+
130
+ const result = await facinet.pay({
131
+ amount: '1',
132
+ recipient: '0xYourMerchantAddress',
133
+ });
134
+
135
+ console.log('Facilitator:', result.facilitator.name);
136
+ console.log('Network:', result.payment.network); // 'avalanche-fuji'
137
+ ```
138
+
139
+ #### Ethereum Sepolia
140
+
141
+ ```typescript
142
+ const facinet = new Facinet({
143
+ network: 'ethereum-sepolia', // Uses Ethereum Sepolia facilitators
144
+ });
145
+
146
+ const facilitators = await facinet.getFacilitators();
147
+ // Only returns facilitators registered for Ethereum Sepolia (Chain ID: 11155111)
148
+ ```
149
+
150
+ #### Base Sepolia
151
+
152
+ ```typescript
153
+ const facinet = new Facinet({
154
+ network: 'base-sepolia', // Uses Base Sepolia facilitators
155
+ });
156
+
157
+ const facilitator = await facinet.selectRandomFacilitator();
158
+ // Only selects from facilitators registered for Base Sepolia (Chain ID: 84532)
159
+ ```
160
+
161
+ #### Polygon Amoy
162
+
163
+ ```typescript
164
+ const facinet = new Facinet({
165
+ network: 'polygon-amoy', // Uses Polygon Amoy facilitators
166
+ });
167
+
168
+ const result = await facinet.pay({
169
+ amount: '2',
170
+ recipient: '0xYourMerchantAddress',
171
+ });
99
172
  ```
100
173
 
101
174
  ### Get Supported Chains
@@ -106,7 +179,10 @@ import { Facinet } from 'facinet';
106
179
  // List all supported chains
107
180
  const chains = Facinet.getSupportedChains();
108
181
  chains.forEach(chain => {
109
- console.log(`${chain.displayName} (${chain.name}) - Chain ID: ${chain.chainId}`);
182
+ console.log(`${chain.displayName} (${chain.name})`);
183
+ console.log(` Chain ID: ${chain.chainId}`);
184
+ console.log(` Gas Token: ${chain.gasToken}`);
185
+ console.log(` USDC Address: ${chain.usdcAddress}`);
110
186
  });
111
187
 
112
188
  // Get supported network names
@@ -119,18 +195,38 @@ console.log('Networks:', networks);
119
195
 
120
196
  ```typescript
121
197
  interface FacinetConfig {
122
- apiUrl?: string; // Default: 'https://x402-avalanche-chi.vercel.app'
198
+ apiUrl?: string; // Optional: Override API URL (default: 'https://facinet.vercel.app')
123
199
  privateKey?: string; // For Node.js (not needed in browser)
124
200
  network?: 'avalanche-fuji' | 'ethereum-sepolia' | 'base-sepolia' | 'polygon-amoy';
125
201
  rpcUrl?: string; // Custom RPC URL (optional)
126
202
  }
127
203
  ```
128
204
 
205
+ **Unified API URL** (used for all networks):
206
+ - All networks → `https://facinet.vercel.app`
207
+
129
208
  **Legacy aliases** are supported for backwards compatibility:
130
- - `'avalanche'` -> `'avalanche-fuji'`
131
- - `'ethereum'` -> `'ethereum-sepolia'`
132
- - `'polygon'` -> `'polygon-amoy'`
133
- - `'base'` -> `'base-sepolia'`
209
+ - `'avalanche'` `'avalanche-fuji'`
210
+ - `'ethereum'` `'ethereum-sepolia'`
211
+ - `'polygon'` `'polygon-amoy'`
212
+ - `'base'` `'base-sepolia'`
213
+
214
+ ### Facilitator Filtering
215
+
216
+ The SDK automatically filters facilitators by:
217
+
218
+ 1. **Status**: Only `'active'` facilitators are selected
219
+ 2. **Network**: Only facilitators registered for the selected network
220
+ 3. **Chain ID**: Only facilitators matching the chain ID
221
+
222
+ ```typescript
223
+ const facinet = new Facinet({ network: 'base-sepolia' });
224
+
225
+ // This will only return facilitators that are:
226
+ // - Status: 'active'
227
+ // - Network: 'base-sepolia' OR Chain ID: 84532
228
+ const facilitators = await facinet.getFacilitators();
229
+ ```
134
230
 
135
231
  ### TypeScript Support
136
232
 
@@ -153,6 +249,12 @@ const params: PaymentParams = {
153
249
  amount: '1',
154
250
  recipient: '0x...',
155
251
  };
252
+
253
+ // PaymentResult includes facilitator information
254
+ const result: PaymentResult = await facinet.pay(params);
255
+ console.log(result.facilitator.name); // Facilitator name
256
+ console.log(result.facilitator.id); // Facilitator ID
257
+ console.log(result.facilitator.wallet); // Facilitator wallet address
156
258
  ```
157
259
 
158
260
  ### React Example
@@ -164,6 +266,7 @@ import { Facinet } from 'facinet';
164
266
  function PaymentButton() {
165
267
  const [loading, setLoading] = useState(false);
166
268
  const [txHash, setTxHash] = useState('');
269
+ const [facilitatorName, setFacilitatorName] = useState('');
167
270
  const [network] = useState('base-sepolia');
168
271
 
169
272
  const handlePayment = async () => {
@@ -177,7 +280,8 @@ function PaymentButton() {
177
280
  });
178
281
 
179
282
  setTxHash(result.txHash);
180
- alert('Payment successful!');
283
+ setFacilitatorName(result.facilitator.name);
284
+ alert(`Payment successful! Processed by ${result.facilitator.name}`);
181
285
  } catch (error) {
182
286
  console.error('Payment failed:', error);
183
287
  alert('Payment failed');
@@ -191,7 +295,13 @@ function PaymentButton() {
191
295
  <button onClick={handlePayment} disabled={loading}>
192
296
  {loading ? 'Processing...' : 'Pay 1 USDC'}
193
297
  </button>
194
- {txHash && <p>Transaction: {txHash.slice(0, 10)}...</p>}
298
+ {txHash && (
299
+ <div>
300
+ <p>Transaction: {txHash.slice(0, 10)}...</p>
301
+ <p>Facilitator: {facilitatorName}</p>
302
+ <p>Network: {network}</p>
303
+ </div>
304
+ )}
195
305
  </div>
196
306
  );
197
307
  }
@@ -199,11 +309,31 @@ function PaymentButton() {
199
309
 
200
310
  ### How SDK Payments Work
201
311
 
202
- 1. **Initialize SDK** - Create Facinet instance with your config and chain
203
- 2. **Random Facilitator** - SDK automatically picks random active facilitator
204
- 3. **Sign Authorization** - User signs ERC-3009 authorization (gasless!)
205
- 4. **Facilitator Executes** - Facilitator submits transaction and pays gas
206
- 5. **Payment Complete** - USDC transferred to YOUR merchant address
312
+ 1. **Initialize SDK** - Create Facinet instance with your network choice
313
+ 2. **Unified API** - SDK uses the unified API endpoint (`facinet.vercel.app`) for all networks
314
+ 3. **Active Facilitator Selection** - SDK picks a random **active** facilitator **for your network**
315
+ 4. **Sign Authorization** - User signs ERC-3009 authorization (gasless!)
316
+ 5. **Facilitator Executes** - Selected facilitator submits transaction and pays gas
317
+ 6. **Payment Complete** - USDC transferred to YOUR merchant address
318
+
319
+ ### Error Handling
320
+
321
+ ```typescript
322
+ try {
323
+ const facinet = new Facinet({ network: 'base-sepolia' });
324
+ const result = await facinet.pay({
325
+ amount: '1',
326
+ recipient: '0xYourMerchantAddress',
327
+ });
328
+ } catch (error) {
329
+ if (error.message.includes('No active facilitators available')) {
330
+ console.error('No active facilitators found for Base Sepolia');
331
+ console.error('Please check that facilitators are registered for this network');
332
+ } else {
333
+ console.error('Payment failed:', error.message);
334
+ }
335
+ }
336
+ ```
207
337
 
208
338
  ## CLI Usage
209
339
 
@@ -218,23 +348,33 @@ facinet connect
218
348
  ### 2. Make a Payment
219
349
 
220
350
  ```bash
221
- # Pay on Avalanche (default)
351
+ # Pay on Avalanche Fuji (default)
222
352
  facinet pay --amount 1 --to 0xRecipientAddress
223
353
 
224
- # Pay on Base Sepolia
354
+ # Pay on Base Sepolia (uses Base Sepolia facilitators)
225
355
  facinet pay --amount 1 --to 0xRecipientAddress --chain base
226
356
 
227
- # Pay on Polygon Amoy
357
+ # Pay on Polygon Amoy (uses Polygon Amoy facilitators)
228
358
  facinet pay --amount 1 --to 0xRecipientAddress --chain polygon
229
359
 
230
- # Pay on Ethereum Sepolia
360
+ # Pay on Ethereum Sepolia (uses Ethereum Sepolia facilitators)
231
361
  facinet pay --amount 1 --to 0xRecipientAddress --chain ethereum
232
362
  ```
233
363
 
234
364
  ### 3. List Active Facilitators
235
365
 
236
366
  ```bash
367
+ # List all active facilitators (for the configured network)
237
368
  facinet facilitator list
369
+
370
+ # The output shows:
371
+ # - Facilitator name
372
+ # - Facilitator ID
373
+ # - Wallet address
374
+ # - Status (active/inactive/needs_funding)
375
+ # - Network (if available)
376
+ # - Chain ID (if available)
377
+ # - Total payments processed
238
378
  ```
239
379
 
240
380
  ### 4. Check Facilitator Status
@@ -243,6 +383,12 @@ facinet facilitator list
243
383
  facinet facilitator status fac_xyz123
244
384
  ```
245
385
 
386
+ ### 5. Check Facilitator Balance
387
+
388
+ ```bash
389
+ facinet facilitator balance fac_xyz123
390
+ ```
391
+
246
392
  ## Configuration
247
393
 
248
394
  Facinet stores configuration in `~/.facinet/config.json`:
@@ -252,13 +398,28 @@ Facinet stores configuration in `~/.facinet/config.json`:
252
398
  "privateKey": "0x...",
253
399
  "address": "0x...",
254
400
  "network": "avalanche-fuji",
255
- "apiUrl": "https://x402-avalanche-chi.vercel.app"
401
+ "apiUrl": "https://facinet.vercel.app"
256
402
  }
257
403
  ```
258
404
 
405
+ **Note**: The `apiUrl` defaults to `https://facinet.vercel.app` for all networks. You can override it if needed.
406
+
259
407
  ## How It Works
260
408
 
261
- 1. **Random Selection** - SDK/CLI picks a random active facilitator from the network
409
+ ### Network-Specific Facilitator Selection
410
+
411
+ 1. **Network Selection** - You specify a network (e.g., `'base-sepolia'`)
412
+ 2. **Unified API** - SDK uses the unified API endpoint (`facinet.vercel.app`) for all networks
413
+ 3. **Facilitator Filtering** - SDK fetches facilitators and filters by:
414
+ - Status: Must be `'active'`
415
+ - Network: Must match your selected network (if facilitator has `network` property)
416
+ - Chain ID: Must match your chain ID (if facilitator has `chainId` property)
417
+ 4. **Random Selection** - SDK picks a random facilitator from the filtered list
418
+ 5. **Payment Processing** - Selected facilitator processes your payment
419
+
420
+ ### Payment Flow
421
+
422
+ 1. **Random Selection** - SDK/CLI picks a random active facilitator from the **selected network**
262
423
  2. **Sign Authorization** - You sign an ERC-3009 payment authorization (gasless for you!)
263
424
  3. **Facilitator Executes** - The facilitator submits the transaction and pays gas
264
425
  4. **Payment Complete** - USDC transferred on-chain
@@ -289,7 +450,7 @@ facinet --help
289
450
  facinet-sdk/
290
451
  ├── src/
291
452
  │ ├── sdk/
292
- │ │ ├── Facinet.ts # Main SDK class (multichain)
453
+ │ │ ├── Facinet.ts # Main SDK class (multichain with network-specific facilitators)
293
454
  │ │ └── types.ts # TypeScript types
294
455
  │ ├── commands/
295
456
  │ │ ├── connect.ts # Wallet connection
@@ -297,7 +458,7 @@ facinet-sdk/
297
458
  │ │ └── facilitator.ts # Facilitator management
298
459
  │ ├── utils/
299
460
  │ │ ├── config.ts # Configuration management
300
- │ │ └── api.ts # API client
461
+ │ │ └── api.ts # API client with network filtering
301
462
  │ ├── sdk.ts # SDK exports
302
463
  │ └── index.ts # CLI entry point
303
464
  ├── dist/ # Compiled JavaScript
@@ -307,7 +468,18 @@ facinet-sdk/
307
468
  └── README.md
308
469
  ```
309
470
 
310
- ## What's New in v2.3.0
471
+ ## What's New in v2.4.0
472
+
473
+ - **Network-Specific Facilitator Selection** - SDK automatically filters facilitators by network/chainId
474
+ - **Unified API Endpoint** - All networks use the unified API endpoint (`facinet.vercel.app`)
475
+ - **Enhanced Facilitator Filtering** - Only active facilitators for the selected network are returned
476
+ - **Better Error Messages** - Error messages now include network information
477
+ - **Facilitator Name Display** - Facilitator names are properly displayed in all results
478
+ - **Improved Documentation** - Comprehensive examples for each network
479
+
480
+ ## Previous Versions
481
+
482
+ ### v2.3.0
311
483
 
312
484
  - **Multichain Support** - Now supports 4 chains: Avalanche Fuji, Ethereum Sepolia, Base Sepolia, Polygon Amoy
313
485
  - **Chain-specific ERC-3009 domains** - Correct EIP-712 domain names per chain (USD Coin vs USDC)
package/dist/browser.js CHANGED
@@ -2715,13 +2715,20 @@ var FacinetSDK = (() => {
2715
2715
  "polygon": "polygon-amoy",
2716
2716
  "base": "base-sepolia"
2717
2717
  };
2718
+ var DEFAULT_API_URL = "https://facinet.vercel.app";
2718
2719
  function resolveNetwork(network) {
2719
2720
  return NETWORK_ALIASES[network] || network;
2720
2721
  }
2722
+ function getApiUrlForNetwork(network, customApiUrl) {
2723
+ if (customApiUrl) {
2724
+ return customApiUrl.replace(/\/$/, "");
2725
+ }
2726
+ return DEFAULT_API_URL;
2727
+ }
2721
2728
  var Facinet = class _Facinet {
2722
2729
  constructor(config = {}) {
2723
- const apiUrl = (config.apiUrl || "https://x402-avalanche-chi.vercel.app").replace(/\/$/, "");
2724
2730
  const resolvedNetwork = resolveNetwork(config.network || "avalanche-fuji");
2731
+ const apiUrl = getApiUrlForNetwork(resolvedNetwork, config.apiUrl);
2725
2732
  this.config = {
2726
2733
  apiUrl,
2727
2734
  privateKey: config.privateKey || "",
@@ -2892,7 +2899,7 @@ var FacinetSDK = (() => {
2892
2899
  txHash: response.data.txHash,
2893
2900
  facilitator: {
2894
2901
  id: facilitator.id,
2895
- name: facilitator.name,
2902
+ name: facilitator.name || `Facilitator ${facilitator.id.slice(0, 8)}`,
2896
2903
  wallet: facilitator.facilitatorWallet
2897
2904
  },
2898
2905
  payment: {
@@ -2911,29 +2918,56 @@ var FacinetSDK = (() => {
2911
2918
  }
2912
2919
  }
2913
2920
  /**
2914
- * Get all active facilitators
2921
+ * Get all active facilitators for the current network
2915
2922
  */
2916
2923
  async getFacilitators() {
2917
- const response = await axios_default.get(
2918
- `${this.config.apiUrl}/api/facilitator/list`
2919
- );
2920
- if (response.data.success) {
2921
- return response.data.facilitators.filter(
2922
- (f) => f.status === "active"
2924
+ try {
2925
+ const response = await axios_default.get(
2926
+ `${this.config.apiUrl}/api/facilitator/list`,
2927
+ {
2928
+ timeout: 1e4,
2929
+ headers: {
2930
+ "User-Agent": "Facinet-SDK/2.3.0"
2931
+ }
2932
+ }
2933
+ );
2934
+ if (response.data.success && Array.isArray(response.data.facilitators)) {
2935
+ return response.data.facilitators.filter((f) => {
2936
+ if (f.status !== "active") {
2937
+ return false;
2938
+ }
2939
+ if (f.network) {
2940
+ return f.network === this.config.network;
2941
+ }
2942
+ if (f.chainId !== void 0) {
2943
+ return f.chainId === this.chain.chainId;
2944
+ }
2945
+ return true;
2946
+ });
2947
+ }
2948
+ return [];
2949
+ } catch (error) {
2950
+ throw new Error(
2951
+ `Failed to fetch facilitators for ${this.chain.displayName} (${this.config.network}): ${error.message || "Unknown error"}`
2923
2952
  );
2924
2953
  }
2925
- return [];
2926
2954
  }
2927
2955
  /**
2928
- * Select a random active facilitator
2956
+ * Select a random active facilitator for the current network
2929
2957
  */
2930
2958
  async selectRandomFacilitator() {
2931
2959
  const facilitators = await this.getFacilitators();
2932
2960
  if (facilitators.length === 0) {
2933
- throw new Error("No active facilitators available");
2961
+ throw new Error(
2962
+ `No active facilitators available for ${this.chain.displayName} (${this.config.network}, Chain ID: ${this.chain.chainId}). Please check that facilitators are registered for this network. API URL: ${this.config.apiUrl}`
2963
+ );
2934
2964
  }
2935
2965
  const randomIndex = Math.floor(Math.random() * facilitators.length);
2936
- return facilitators[randomIndex];
2966
+ const selectedFacilitator = facilitators[randomIndex];
2967
+ if (!selectedFacilitator.name) {
2968
+ selectedFacilitator.name = `Facilitator ${selectedFacilitator.id.slice(0, 8)}`;
2969
+ }
2970
+ return selectedFacilitator;
2937
2971
  }
2938
2972
  /**
2939
2973
  * Quick payment helper (static method)