facinet 2.2.2 → 2.3.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/dist/sdk.js CHANGED
@@ -1,26 +1,347 @@
1
1
  "use strict";
2
- /**
3
- * Facinet SDK - Main Export
4
- *
5
- * Use this when installing as a library:
6
- * npm install facinet
7
- *
8
- * @example
9
- * ```typescript
10
- * import { Facinet } from 'facinet';
11
- *
12
- * const facinet = new Facinet();
13
- * await facinet.pay({
14
- * amount: '1',
15
- * recipient: '0xYourAddress'
16
- * });
17
- * ```
18
- */
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.default = exports.Facinet = void 0;
21
- var Facinet_1 = require("./sdk/Facinet");
22
- Object.defineProperty(exports, "Facinet", { enumerable: true, get: function () { return Facinet_1.Facinet; } });
23
- // Re-export for convenience
24
- var Facinet_2 = require("./sdk/Facinet");
25
- Object.defineProperty(exports, "default", { enumerable: true, get: function () { return Facinet_2.Facinet; } });
26
- //# sourceMappingURL=sdk.js.map
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/sdk.ts
31
+ var sdk_exports = {};
32
+ __export(sdk_exports, {
33
+ Facinet: () => Facinet,
34
+ default: () => Facinet
35
+ });
36
+ module.exports = __toCommonJS(sdk_exports);
37
+
38
+ // src/sdk/Facinet.ts
39
+ var import_axios = __toESM(require("axios"));
40
+ var import_ethers = require("ethers");
41
+ var CHAINS = {
42
+ "avalanche-fuji": {
43
+ name: "avalanche-fuji",
44
+ displayName: "Avalanche Fuji",
45
+ chainId: 43113,
46
+ rpcUrl: "https://api.avax-test.network/ext/bc/C/rpc",
47
+ usdcAddress: "0x5425890298aed601595a70AB815c96711a31Bc65",
48
+ usdcDecimals: 6,
49
+ gasToken: "AVAX",
50
+ blockExplorer: "https://testnet.snowtrace.io",
51
+ erc3009DomainName: "USD Coin",
52
+ erc3009DomainVersion: "2"
53
+ },
54
+ "ethereum-sepolia": {
55
+ name: "ethereum-sepolia",
56
+ displayName: "Ethereum Sepolia",
57
+ chainId: 11155111,
58
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
59
+ usdcAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
60
+ usdcDecimals: 6,
61
+ gasToken: "ETH",
62
+ blockExplorer: "https://sepolia.etherscan.io",
63
+ erc3009DomainName: "USDC",
64
+ erc3009DomainVersion: "2"
65
+ },
66
+ "base-sepolia": {
67
+ name: "base-sepolia",
68
+ displayName: "Base Sepolia",
69
+ chainId: 84532,
70
+ rpcUrl: "https://sepolia.base.org",
71
+ usdcAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
72
+ usdcDecimals: 6,
73
+ gasToken: "ETH",
74
+ blockExplorer: "https://sepolia.basescan.org",
75
+ erc3009DomainName: "USDC",
76
+ erc3009DomainVersion: "2"
77
+ },
78
+ "polygon-amoy": {
79
+ name: "polygon-amoy",
80
+ displayName: "Polygon Amoy",
81
+ chainId: 80002,
82
+ rpcUrl: "https://rpc-amoy.polygon.technology",
83
+ usdcAddress: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
84
+ usdcDecimals: 6,
85
+ gasToken: "MATIC",
86
+ blockExplorer: "https://amoy.polygonscan.com",
87
+ erc3009DomainName: "USDC",
88
+ erc3009DomainVersion: "2"
89
+ }
90
+ };
91
+ var NETWORK_ALIASES = {
92
+ "avalanche": "avalanche-fuji",
93
+ "ethereum": "ethereum-sepolia",
94
+ "polygon": "polygon-amoy",
95
+ "base": "base-sepolia"
96
+ };
97
+ function resolveNetwork(network) {
98
+ return NETWORK_ALIASES[network] || network;
99
+ }
100
+ var Facinet = class _Facinet {
101
+ constructor(config = {}) {
102
+ const apiUrl = (config.apiUrl || "https://x402-avalanche-chi.vercel.app").replace(/\/$/, "");
103
+ const resolvedNetwork = resolveNetwork(config.network || "avalanche-fuji");
104
+ this.config = {
105
+ apiUrl,
106
+ privateKey: config.privateKey || "",
107
+ network: resolvedNetwork,
108
+ rpcUrl: config.rpcUrl || ""
109
+ };
110
+ this.chain = CHAINS[resolvedNetwork];
111
+ if (!this.chain) {
112
+ throw new Error(
113
+ `Unsupported network: ${config.network}. Supported networks: ${Object.keys(CHAINS).join(", ")}`
114
+ );
115
+ }
116
+ if (this.config.privateKey) {
117
+ const provider = new import_ethers.JsonRpcProvider(
118
+ this.config.rpcUrl || this.chain.rpcUrl
119
+ );
120
+ this.wallet = new import_ethers.Wallet(this.config.privateKey, provider);
121
+ }
122
+ }
123
+ /**
124
+ * Get the current chain configuration
125
+ */
126
+ getChain() {
127
+ return { ...this.chain };
128
+ }
129
+ /**
130
+ * Get all supported chains
131
+ */
132
+ static getSupportedChains() {
133
+ return Object.values(CHAINS);
134
+ }
135
+ /**
136
+ * Get supported network names
137
+ */
138
+ static getSupportedNetworks() {
139
+ return Object.keys(CHAINS);
140
+ }
141
+ /**
142
+ * Make a payment via x402 facilitator network
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * const facinet = new Facinet({ network: 'base-sepolia' });
147
+ * const result = await facinet.pay({
148
+ * amount: '1',
149
+ * recipient: '0xMerchantAddress',
150
+ * payerAddress: '0xCustomerAddress'
151
+ * });
152
+ * console.log('Payment successful!', result.txHash);
153
+ * ```
154
+ */
155
+ async pay(params) {
156
+ if (!params.amount || parseFloat(params.amount) <= 0) {
157
+ throw new Error("Invalid amount");
158
+ }
159
+ if (!params.recipient.match(/^0x[a-fA-F0-9]{40}$/)) {
160
+ throw new Error("Invalid recipient address");
161
+ }
162
+ let payerAddress = params.payerAddress || "";
163
+ if (!payerAddress) {
164
+ if (this.wallet) {
165
+ payerAddress = this.wallet.address;
166
+ } else if (typeof window !== "undefined" && window.ethereum) {
167
+ const accounts = await window.ethereum.request({
168
+ method: "eth_requestAccounts"
169
+ });
170
+ payerAddress = accounts[0];
171
+ } else {
172
+ throw new Error(
173
+ "No payer address provided and no wallet available. Provide payerAddress or privateKey in config."
174
+ );
175
+ }
176
+ }
177
+ payerAddress = payerAddress.toLowerCase();
178
+ const recipientAddress = params.recipient.toLowerCase();
179
+ const facilitator = await this.selectRandomFacilitator();
180
+ const amount = BigInt(parseFloat(params.amount) * 1e6);
181
+ const validAfter = Math.floor(Date.now() / 1e3) - 60;
182
+ const validBefore = validAfter + 3600;
183
+ const nonce = "0x" + Array.from(
184
+ { length: 64 },
185
+ () => Math.floor(Math.random() * 16).toString(16)
186
+ ).join("");
187
+ const domain = {
188
+ name: this.chain.erc3009DomainName,
189
+ version: this.chain.erc3009DomainVersion,
190
+ chainId: this.chain.chainId,
191
+ verifyingContract: this.chain.usdcAddress
192
+ };
193
+ const types = {
194
+ TransferWithAuthorization: [
195
+ { name: "from", type: "address" },
196
+ { name: "to", type: "address" },
197
+ { name: "value", type: "uint256" },
198
+ { name: "validAfter", type: "uint256" },
199
+ { name: "validBefore", type: "uint256" },
200
+ { name: "nonce", type: "bytes32" }
201
+ ]
202
+ };
203
+ const value = {
204
+ from: payerAddress,
205
+ to: recipientAddress,
206
+ value: amount,
207
+ validAfter,
208
+ validBefore,
209
+ nonce
210
+ };
211
+ let signature;
212
+ if (this.wallet) {
213
+ signature = await this.wallet.signTypedData(domain, types, value);
214
+ } else if (typeof window !== "undefined" && window.ethereum) {
215
+ const { TypedDataEncoder } = await import("ethers");
216
+ const messageForSigning = {
217
+ from: payerAddress,
218
+ to: recipientAddress,
219
+ value: amount.toString(),
220
+ validAfter: validAfter.toString(),
221
+ validBefore: validBefore.toString(),
222
+ nonce
223
+ };
224
+ const typedDataPayload = JSON.stringify({
225
+ types: {
226
+ EIP712Domain: [
227
+ { name: "name", type: "string" },
228
+ { name: "version", type: "string" },
229
+ { name: "chainId", type: "uint256" },
230
+ { name: "verifyingContract", type: "address" }
231
+ ],
232
+ TransferWithAuthorization: types.TransferWithAuthorization
233
+ },
234
+ domain,
235
+ primaryType: "TransferWithAuthorization",
236
+ message: messageForSigning
237
+ });
238
+ signature = await window.ethereum.request({
239
+ method: "eth_signTypedData_v4",
240
+ params: [payerAddress, typedDataPayload]
241
+ });
242
+ } else {
243
+ throw new Error("No signing method available");
244
+ }
245
+ const paymentPayload = {
246
+ signature,
247
+ authorization: {
248
+ from: payerAddress,
249
+ to: recipientAddress,
250
+ value: amount.toString(),
251
+ validAfter: validAfter.toString(),
252
+ validBefore: validBefore.toString(),
253
+ nonce
254
+ }
255
+ };
256
+ try {
257
+ const response = await import_axios.default.post(
258
+ `${this.config.apiUrl}/api/x402/settle-custom`,
259
+ {
260
+ facilitatorId: facilitator.id,
261
+ paymentPayload
262
+ }
263
+ );
264
+ if (!response.data.success) {
265
+ throw new Error(
266
+ response.data.error || response.data.message || "Payment failed"
267
+ );
268
+ }
269
+ return {
270
+ success: true,
271
+ txHash: response.data.txHash,
272
+ facilitator: {
273
+ id: facilitator.id,
274
+ name: facilitator.name,
275
+ wallet: facilitator.facilitatorWallet
276
+ },
277
+ payment: {
278
+ from: payerAddress,
279
+ to: recipientAddress,
280
+ amount: params.amount,
281
+ network: this.config.network
282
+ }
283
+ };
284
+ } catch (error) {
285
+ if (error.response?.data) {
286
+ const backendError = error.response.data.message || error.response.data.error;
287
+ throw new Error(`Payment failed: ${backendError}`);
288
+ }
289
+ throw error;
290
+ }
291
+ }
292
+ /**
293
+ * Get all active facilitators
294
+ */
295
+ async getFacilitators() {
296
+ const response = await import_axios.default.get(
297
+ `${this.config.apiUrl}/api/facilitator/list`
298
+ );
299
+ if (response.data.success) {
300
+ return response.data.facilitators.filter(
301
+ (f) => f.status === "active"
302
+ );
303
+ }
304
+ return [];
305
+ }
306
+ /**
307
+ * Select a random active facilitator
308
+ */
309
+ async selectRandomFacilitator() {
310
+ const facilitators = await this.getFacilitators();
311
+ if (facilitators.length === 0) {
312
+ throw new Error("No active facilitators available");
313
+ }
314
+ const randomIndex = Math.floor(Math.random() * facilitators.length);
315
+ return facilitators[randomIndex];
316
+ }
317
+ /**
318
+ * Quick payment helper (static method)
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * // Pay on Base Sepolia
323
+ * await Facinet.quickPay({
324
+ * amount: '1',
325
+ * recipient: '0xMerchantAddress',
326
+ * privateKey: process.env.PRIVATE_KEY,
327
+ * network: 'base-sepolia'
328
+ * });
329
+ * ```
330
+ */
331
+ static async quickPay(params) {
332
+ const facinet = new _Facinet({
333
+ privateKey: params.privateKey,
334
+ network: params.network
335
+ });
336
+ return facinet.pay({
337
+ amount: params.amount,
338
+ recipient: params.recipient,
339
+ payerAddress: params.payerAddress
340
+ });
341
+ }
342
+ };
343
+ // Annotate the CommonJS export names for ESM import in node:
344
+ 0 && (module.exports = {
345
+ Facinet
346
+ });
347
+ //# sourceMappingURL=sdk.js.map
package/dist/sdk.js.map CHANGED
@@ -1 +1,7 @@
1
- {"version":3,"file":"sdk.js","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAEH,yCAAwC;AAA/B,kGAAA,OAAO,OAAA;AAShB,4BAA4B;AAC5B,yCAAmD;AAA1C,kGAAA,OAAO,OAAW"}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/sdk.ts", "../src/sdk/Facinet.ts"],
4
+ "sourcesContent": ["/**\n * Facinet SDK - Main Export\n *\n * Use this when installing as a library:\n * npm install facinet\n *\n * @example\n * ```typescript\n * import { Facinet } from 'facinet';\n *\n * const facinet = new Facinet({ network: 'base-sepolia' });\n * await facinet.pay({\n * amount: '1',\n * recipient: '0xYourAddress'\n * });\n * ```\n */\n\nexport { Facinet } from './sdk/Facinet';\nexport type {\n FacinetConfig,\n PaymentParams,\n PaymentResult,\n Facilitator,\n ChainConfig,\n} from './sdk/types';\nexport { Facinet as default } from './sdk/Facinet';\n", "/**\n * Facinet SDK - Main Class\n *\n * JavaScript/TypeScript SDK for integrating x402 payments\n * Supports multichain: Avalanche Fuji, Ethereum Sepolia, Base Sepolia, Polygon Amoy\n */\n\nimport axios from 'axios';\nimport { Wallet, JsonRpcProvider } from 'ethers';\nimport type { FacinetConfig, PaymentParams, PaymentResult, Facilitator, ChainConfig } from './types';\n\n// All 4 supported chains\nconst CHAINS: Record<string, ChainConfig> = {\n 'avalanche-fuji': {\n name: 'avalanche-fuji',\n displayName: 'Avalanche Fuji',\n chainId: 43113,\n rpcUrl: 'https://api.avax-test.network/ext/bc/C/rpc',\n usdcAddress: '0x5425890298aed601595a70AB815c96711a31Bc65',\n usdcDecimals: 6,\n gasToken: 'AVAX',\n blockExplorer: 'https://testnet.snowtrace.io',\n erc3009DomainName: 'USD Coin',\n erc3009DomainVersion: '2',\n },\n 'ethereum-sepolia': {\n name: 'ethereum-sepolia',\n displayName: 'Ethereum Sepolia',\n chainId: 11155111,\n rpcUrl: 'https://ethereum-sepolia-rpc.publicnode.com',\n usdcAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n usdcDecimals: 6,\n gasToken: 'ETH',\n blockExplorer: 'https://sepolia.etherscan.io',\n erc3009DomainName: 'USDC',\n erc3009DomainVersion: '2',\n },\n 'base-sepolia': {\n name: 'base-sepolia',\n displayName: 'Base Sepolia',\n chainId: 84532,\n rpcUrl: 'https://sepolia.base.org',\n usdcAddress: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n usdcDecimals: 6,\n gasToken: 'ETH',\n blockExplorer: 'https://sepolia.basescan.org',\n erc3009DomainName: 'USDC',\n erc3009DomainVersion: '2',\n },\n 'polygon-amoy': {\n name: 'polygon-amoy',\n displayName: 'Polygon Amoy',\n chainId: 80002,\n rpcUrl: 'https://rpc-amoy.polygon.technology',\n usdcAddress: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582',\n usdcDecimals: 6,\n gasToken: 'MATIC',\n blockExplorer: 'https://amoy.polygonscan.com',\n erc3009DomainName: 'USDC',\n erc3009DomainVersion: '2',\n },\n};\n\n// Legacy aliases for backwards compatibility\nconst NETWORK_ALIASES: Record<string, string> = {\n 'avalanche': 'avalanche-fuji',\n 'ethereum': 'ethereum-sepolia',\n 'polygon': 'polygon-amoy',\n 'base': 'base-sepolia',\n};\n\n/**\n * Resolve network name (handles legacy aliases)\n */\nfunction resolveNetwork(network: string): string {\n return NETWORK_ALIASES[network] || network;\n}\n\nexport class Facinet {\n private config: Required<Pick<FacinetConfig, 'apiUrl' | 'privateKey' | 'network' | 'rpcUrl'>>;\n private chain: ChainConfig;\n private wallet?: InstanceType<typeof Wallet>;\n\n constructor(config: FacinetConfig = {}) {\n const apiUrl = (config.apiUrl || 'https://x402-avalanche-chi.vercel.app').replace(/\\/$/, '');\n const resolvedNetwork = resolveNetwork(config.network || 'avalanche-fuji');\n\n this.config = {\n apiUrl,\n privateKey: config.privateKey || '',\n network: resolvedNetwork as any,\n rpcUrl: config.rpcUrl || '',\n };\n\n this.chain = CHAINS[resolvedNetwork];\n if (!this.chain) {\n throw new Error(\n `Unsupported network: ${config.network}. Supported networks: ${Object.keys(CHAINS).join(', ')}`\n );\n }\n\n if (this.config.privateKey) {\n const provider = new JsonRpcProvider(\n this.config.rpcUrl || this.chain.rpcUrl\n );\n this.wallet = new Wallet(this.config.privateKey, provider);\n }\n }\n\n /**\n * Get the current chain configuration\n */\n getChain(): ChainConfig {\n return { ...this.chain };\n }\n\n /**\n * Get all supported chains\n */\n static getSupportedChains(): ChainConfig[] {\n return Object.values(CHAINS);\n }\n\n /**\n * Get supported network names\n */\n static getSupportedNetworks(): string[] {\n return Object.keys(CHAINS);\n }\n\n /**\n * Make a payment via x402 facilitator network\n *\n * @example\n * ```typescript\n * const facinet = new Facinet({ network: 'base-sepolia' });\n * const result = await facinet.pay({\n * amount: '1',\n * recipient: '0xMerchantAddress',\n * payerAddress: '0xCustomerAddress'\n * });\n * console.log('Payment successful!', result.txHash);\n * ```\n */\n async pay(params: PaymentParams): Promise<PaymentResult> {\n if (!params.amount || parseFloat(params.amount) <= 0) {\n throw new Error('Invalid amount');\n }\n\n if (!params.recipient.match(/^0x[a-fA-F0-9]{40}$/)) {\n throw new Error('Invalid recipient address');\n }\n\n // Resolve payer address\n let payerAddress = params.payerAddress || '';\n if (!payerAddress) {\n if (this.wallet) {\n payerAddress = this.wallet.address as `0x${string}`;\n } else if (typeof window !== 'undefined' && (window as any).ethereum) {\n const accounts = await (window as any).ethereum.request({\n method: 'eth_requestAccounts',\n });\n payerAddress = accounts[0];\n } else {\n throw new Error(\n 'No payer address provided and no wallet available. Provide payerAddress or privateKey in config.'\n );\n }\n }\n\n payerAddress = payerAddress.toLowerCase() as `0x${string}`;\n const recipientAddress = params.recipient.toLowerCase();\n\n // Select random facilitator\n const facilitator = await this.selectRandomFacilitator();\n\n // Create ERC-3009 authorization\n const amount = BigInt(parseFloat(params.amount) * 1e6); // 6 decimals for USDC\n const validAfter = Math.floor(Date.now() / 1000) - 60;\n const validBefore = validAfter + 3600; // 1 hour validity\n const nonce =\n '0x' +\n Array.from({ length: 64 }, () =>\n Math.floor(Math.random() * 16).toString(16)\n ).join('');\n\n // EIP-712 Domain - uses chain-specific domain name\n const domain = {\n name: this.chain.erc3009DomainName,\n version: this.chain.erc3009DomainVersion,\n chainId: this.chain.chainId,\n verifyingContract: this.chain.usdcAddress,\n };\n\n const types = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n };\n\n const value = {\n from: payerAddress,\n to: recipientAddress,\n value: amount,\n validAfter,\n validBefore,\n nonce,\n };\n\n // Sign the authorization\n let signature: string;\n\n if (this.wallet) {\n signature = await this.wallet.signTypedData(domain, types, value);\n } else if (typeof window !== 'undefined' && (window as any).ethereum) {\n const { TypedDataEncoder } = await import('ethers');\n\n const messageForSigning = {\n from: payerAddress,\n to: recipientAddress,\n value: amount.toString(),\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n };\n\n const typedDataPayload = JSON.stringify({\n types: {\n EIP712Domain: [\n { name: 'name', type: 'string' },\n { name: 'version', type: 'string' },\n { name: 'chainId', type: 'uint256' },\n { name: 'verifyingContract', type: 'address' },\n ],\n TransferWithAuthorization: types.TransferWithAuthorization,\n },\n domain,\n primaryType: 'TransferWithAuthorization',\n message: messageForSigning,\n });\n\n signature = await (window as any).ethereum.request({\n method: 'eth_signTypedData_v4',\n params: [payerAddress, typedDataPayload],\n });\n } else {\n throw new Error('No signing method available');\n }\n\n // Build payment payload\n const paymentPayload = {\n signature,\n authorization: {\n from: payerAddress,\n to: recipientAddress,\n value: amount.toString(),\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n };\n\n // Submit to facilitator\n try {\n const response = await axios.post(\n `${this.config.apiUrl}/api/x402/settle-custom`,\n {\n facilitatorId: facilitator.id,\n paymentPayload,\n }\n );\n\n if (!response.data.success) {\n throw new Error(\n response.data.error || response.data.message || 'Payment failed'\n );\n }\n\n return {\n success: true,\n txHash: response.data.txHash,\n facilitator: {\n id: facilitator.id,\n name: facilitator.name,\n wallet: facilitator.facilitatorWallet,\n },\n payment: {\n from: payerAddress,\n to: recipientAddress,\n amount: params.amount,\n network: this.config.network,\n },\n };\n } catch (error: any) {\n if (error.response?.data) {\n const backendError =\n error.response.data.message || error.response.data.error;\n throw new Error(`Payment failed: ${backendError}`);\n }\n throw error;\n }\n }\n\n /**\n * Get all active facilitators\n */\n async getFacilitators(): Promise<Facilitator[]> {\n const response = await axios.get(\n `${this.config.apiUrl}/api/facilitator/list`\n );\n\n if (response.data.success) {\n return response.data.facilitators.filter(\n (f: Facilitator) => f.status === 'active'\n );\n }\n\n return [];\n }\n\n /**\n * Select a random active facilitator\n */\n async selectRandomFacilitator(): Promise<Facilitator> {\n const facilitators = await this.getFacilitators();\n\n if (facilitators.length === 0) {\n throw new Error('No active facilitators available');\n }\n\n const randomIndex = Math.floor(Math.random() * facilitators.length);\n return facilitators[randomIndex];\n }\n\n /**\n * Quick payment helper (static method)\n *\n * @example\n * ```typescript\n * // Pay on Base Sepolia\n * await Facinet.quickPay({\n * amount: '1',\n * recipient: '0xMerchantAddress',\n * privateKey: process.env.PRIVATE_KEY,\n * network: 'base-sepolia'\n * });\n * ```\n */\n static async quickPay(\n params: PaymentParams & { privateKey?: string; network?: FacinetConfig['network'] }\n ): Promise<PaymentResult> {\n const facinet = new Facinet({\n privateKey: params.privateKey,\n network: params.network,\n });\n\n return facinet.pay({\n amount: params.amount,\n recipient: params.recipient,\n payerAddress: params.payerAddress,\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["axios"]
7
+ }
package/dist/sdk.mjs CHANGED
@@ -2,27 +2,79 @@
2
2
  import axios from "axios";
3
3
  import { Wallet, JsonRpcProvider } from "ethers";
4
4
  var CHAINS = {
5
- avalanche: {
6
- name: "Avalanche Fuji",
5
+ "avalanche-fuji": {
6
+ name: "avalanche-fuji",
7
+ displayName: "Avalanche Fuji",
7
8
  chainId: 43113,
8
9
  rpcUrl: "https://api.avax-test.network/ext/bc/C/rpc",
9
10
  usdcAddress: "0x5425890298aed601595a70AB815c96711a31Bc65",
11
+ usdcDecimals: 6,
10
12
  gasToken: "AVAX",
11
- blockExplorer: "https://testnet.snowtrace.io"
13
+ blockExplorer: "https://testnet.snowtrace.io",
14
+ erc3009DomainName: "USD Coin",
15
+ erc3009DomainVersion: "2"
16
+ },
17
+ "ethereum-sepolia": {
18
+ name: "ethereum-sepolia",
19
+ displayName: "Ethereum Sepolia",
20
+ chainId: 11155111,
21
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
22
+ usdcAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
23
+ usdcDecimals: 6,
24
+ gasToken: "ETH",
25
+ blockExplorer: "https://sepolia.etherscan.io",
26
+ erc3009DomainName: "USDC",
27
+ erc3009DomainVersion: "2"
28
+ },
29
+ "base-sepolia": {
30
+ name: "base-sepolia",
31
+ displayName: "Base Sepolia",
32
+ chainId: 84532,
33
+ rpcUrl: "https://sepolia.base.org",
34
+ usdcAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
35
+ usdcDecimals: 6,
36
+ gasToken: "ETH",
37
+ blockExplorer: "https://sepolia.basescan.org",
38
+ erc3009DomainName: "USDC",
39
+ erc3009DomainVersion: "2"
40
+ },
41
+ "polygon-amoy": {
42
+ name: "polygon-amoy",
43
+ displayName: "Polygon Amoy",
44
+ chainId: 80002,
45
+ rpcUrl: "https://rpc-amoy.polygon.technology",
46
+ usdcAddress: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
47
+ usdcDecimals: 6,
48
+ gasToken: "MATIC",
49
+ blockExplorer: "https://amoy.polygonscan.com",
50
+ erc3009DomainName: "USDC",
51
+ erc3009DomainVersion: "2"
12
52
  }
13
53
  };
54
+ var NETWORK_ALIASES = {
55
+ "avalanche": "avalanche-fuji",
56
+ "ethereum": "ethereum-sepolia",
57
+ "polygon": "polygon-amoy",
58
+ "base": "base-sepolia"
59
+ };
60
+ function resolveNetwork(network) {
61
+ return NETWORK_ALIASES[network] || network;
62
+ }
14
63
  var Facinet = class _Facinet {
15
64
  constructor(config = {}) {
16
65
  const apiUrl = (config.apiUrl || "https://x402-avalanche-chi.vercel.app").replace(/\/$/, "");
66
+ const resolvedNetwork = resolveNetwork(config.network || "avalanche-fuji");
17
67
  this.config = {
18
68
  apiUrl,
19
69
  privateKey: config.privateKey || "",
20
- network: config.network || "avalanche",
70
+ network: resolvedNetwork,
21
71
  rpcUrl: config.rpcUrl || ""
22
72
  };
23
- this.chain = CHAINS[this.config.network];
73
+ this.chain = CHAINS[resolvedNetwork];
24
74
  if (!this.chain) {
25
- throw new Error(`Unsupported network: ${this.config.network}`);
75
+ throw new Error(
76
+ `Unsupported network: ${config.network}. Supported networks: ${Object.keys(CHAINS).join(", ")}`
77
+ );
26
78
  }
27
79
  if (this.config.privateKey) {
28
80
  const provider = new JsonRpcProvider(
@@ -31,12 +83,30 @@ var Facinet = class _Facinet {
31
83
  this.wallet = new Wallet(this.config.privateKey, provider);
32
84
  }
33
85
  }
86
+ /**
87
+ * Get the current chain configuration
88
+ */
89
+ getChain() {
90
+ return { ...this.chain };
91
+ }
92
+ /**
93
+ * Get all supported chains
94
+ */
95
+ static getSupportedChains() {
96
+ return Object.values(CHAINS);
97
+ }
98
+ /**
99
+ * Get supported network names
100
+ */
101
+ static getSupportedNetworks() {
102
+ return Object.keys(CHAINS);
103
+ }
34
104
  /**
35
105
  * Make a payment via x402 facilitator network
36
106
  *
37
107
  * @example
38
108
  * ```typescript
39
- * const facinet = new Facinet();
109
+ * const facinet = new Facinet({ network: 'base-sepolia' });
40
110
  * const result = await facinet.pay({
41
111
  * amount: '1',
42
112
  * recipient: '0xMerchantAddress',
@@ -78,8 +148,8 @@ var Facinet = class _Facinet {
78
148
  () => Math.floor(Math.random() * 16).toString(16)
79
149
  ).join("");
80
150
  const domain = {
81
- name: "USD Coin",
82
- version: "2",
151
+ name: this.chain.erc3009DomainName,
152
+ version: this.chain.erc3009DomainVersion,
83
153
  chainId: this.chain.chainId,
84
154
  verifyingContract: this.chain.usdcAddress
85
155
  };
@@ -95,9 +165,7 @@ var Facinet = class _Facinet {
95
165
  };
96
166
  const value = {
97
167
  from: payerAddress,
98
- // Checksummed
99
168
  to: recipientAddress,
100
- // Checksummed - Payment goes to merchant's address!
101
169
  value: amount,
102
170
  validAfter,
103
171
  validBefore,
@@ -116,8 +184,6 @@ var Facinet = class _Facinet {
116
184
  validBefore: validBefore.toString(),
117
185
  nonce
118
186
  };
119
- const hash = TypedDataEncoder.hash(domain, types, messageForSigning);
120
- console.log("\u{1F510} Signing hash:", hash);
121
187
  const typedDataPayload = JSON.stringify({
122
188
  types: {
123
189
  EIP712Domain: [
@@ -136,28 +202,14 @@ var Facinet = class _Facinet {
136
202
  method: "eth_signTypedData_v4",
137
203
  params: [payerAddress, typedDataPayload]
138
204
  });
139
- console.log("\u{1F510} Signature:", signature, "Length:", signature.length);
140
205
  } else {
141
206
  throw new Error("No signing method available");
142
207
  }
143
- console.log("\u{1F4DD} Final authorization:", {
144
- from: payerAddress,
145
- // Checksummed
146
- to: recipientAddress,
147
- // Checksummed
148
- value: amount.toString(),
149
- validAfter: validAfter.toString(),
150
- validBefore: validBefore.toString(),
151
- nonce,
152
- signature: signature?.slice(0, 20) + "..."
153
- });
154
208
  const paymentPayload = {
155
209
  signature,
156
210
  authorization: {
157
211
  from: payerAddress,
158
- // Checksummed
159
212
  to: recipientAddress,
160
- // Checksummed
161
213
  value: amount.toString(),
162
214
  validAfter: validAfter.toString(),
163
215
  validBefore: validBefore.toString(),
@@ -173,7 +225,9 @@ var Facinet = class _Facinet {
173
225
  }
174
226
  );
175
227
  if (!response.data.success) {
176
- throw new Error(response.data.error || response.data.message || "Payment failed");
228
+ throw new Error(
229
+ response.data.error || response.data.message || "Payment failed"
230
+ );
177
231
  }
178
232
  return {
179
233
  success: true,
@@ -185,9 +239,7 @@ var Facinet = class _Facinet {
185
239
  },
186
240
  payment: {
187
241
  from: payerAddress,
188
- // Checksummed
189
242
  to: recipientAddress,
190
- // Checksummed
191
243
  amount: params.amount,
192
244
  network: this.config.network
193
245
  }
@@ -230,16 +282,19 @@ var Facinet = class _Facinet {
230
282
  *
231
283
  * @example
232
284
  * ```typescript
285
+ * // Pay on Base Sepolia
233
286
  * await Facinet.quickPay({
234
287
  * amount: '1',
235
288
  * recipient: '0xMerchantAddress',
236
- * privateKey: process.env.PRIVATE_KEY
289
+ * privateKey: process.env.PRIVATE_KEY,
290
+ * network: 'base-sepolia'
237
291
  * });
238
292
  * ```
239
293
  */
240
294
  static async quickPay(params) {
241
295
  const facinet = new _Facinet({
242
- privateKey: params.privateKey
296
+ privateKey: params.privateKey,
297
+ network: params.network
243
298
  });
244
299
  return facinet.pay({
245
300
  amount: params.amount,