facinet 2.2.1 → 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/README.md +71 -242
- package/dist/browser.js +2720 -27515
- package/dist/browser.js.map +4 -4
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +7 -8
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/facilitator.d.ts +4 -15
- package/dist/commands/facilitator.d.ts.map +1 -1
- package/dist/commands/facilitator.js +24 -28
- package/dist/commands/facilitator.js.map +1 -1
- package/dist/commands/pay.d.ts +1 -8
- package/dist/commands/pay.d.ts.map +1 -1
- package/dist/commands/pay.js +35 -30
- package/dist/commands/pay.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +636 -92
- package/dist/index.js.map +7 -1
- package/dist/sdk/Facinet.d.ts +19 -3
- package/dist/sdk/Facinet.d.ts.map +1 -1
- package/dist/sdk/Facinet.js +113 -52
- package/dist/sdk/Facinet.js.map +1 -1
- package/dist/sdk/types.d.ts +8 -2
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +346 -25
- package/dist/sdk.js.map +7 -1
- package/dist/sdk.mjs +93 -32
- package/dist/sdk.mjs.map +2 -2
- package/dist/utils/api.d.ts +13 -12
- package/dist/utils/api.d.ts.map +1 -1
- package/dist/utils/api.js +4 -32
- package/dist/utils/api.js.map +1 -1
- package/dist/utils/config.d.ts +2 -9
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +0 -1
- package/dist/utils/config.js.map +1 -1
- package/package.json +4 -2
package/dist/sdk.js
CHANGED
|
@@ -1,26 +1,347 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
{
|
|
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: "
|
|
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:
|
|
70
|
+
network: resolvedNetwork,
|
|
21
71
|
rpcUrl: config.rpcUrl || ""
|
|
22
72
|
};
|
|
23
|
-
this.chain = CHAINS[
|
|
73
|
+
this.chain = CHAINS[resolvedNetwork];
|
|
24
74
|
if (!this.chain) {
|
|
25
|
-
throw new Error(
|
|
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:
|
|
82
|
-
version:
|
|
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,10 +184,14 @@ 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: {
|
|
189
|
+
EIP712Domain: [
|
|
190
|
+
{ name: "name", type: "string" },
|
|
191
|
+
{ name: "version", type: "string" },
|
|
192
|
+
{ name: "chainId", type: "uint256" },
|
|
193
|
+
{ name: "verifyingContract", type: "address" }
|
|
194
|
+
],
|
|
123
195
|
TransferWithAuthorization: types.TransferWithAuthorization
|
|
124
196
|
},
|
|
125
197
|
domain,
|
|
@@ -130,28 +202,14 @@ var Facinet = class _Facinet {
|
|
|
130
202
|
method: "eth_signTypedData_v4",
|
|
131
203
|
params: [payerAddress, typedDataPayload]
|
|
132
204
|
});
|
|
133
|
-
console.log("\u{1F510} Signature:", signature, "Length:", signature.length);
|
|
134
205
|
} else {
|
|
135
206
|
throw new Error("No signing method available");
|
|
136
207
|
}
|
|
137
|
-
console.log("\u{1F4DD} Final authorization:", {
|
|
138
|
-
from: payerAddress,
|
|
139
|
-
// Checksummed
|
|
140
|
-
to: recipientAddress,
|
|
141
|
-
// Checksummed
|
|
142
|
-
value: amount.toString(),
|
|
143
|
-
validAfter: validAfter.toString(),
|
|
144
|
-
validBefore: validBefore.toString(),
|
|
145
|
-
nonce,
|
|
146
|
-
signature: signature?.slice(0, 20) + "..."
|
|
147
|
-
});
|
|
148
208
|
const paymentPayload = {
|
|
149
209
|
signature,
|
|
150
210
|
authorization: {
|
|
151
211
|
from: payerAddress,
|
|
152
|
-
// Checksummed
|
|
153
212
|
to: recipientAddress,
|
|
154
|
-
// Checksummed
|
|
155
213
|
value: amount.toString(),
|
|
156
214
|
validAfter: validAfter.toString(),
|
|
157
215
|
validBefore: validBefore.toString(),
|
|
@@ -167,7 +225,9 @@ var Facinet = class _Facinet {
|
|
|
167
225
|
}
|
|
168
226
|
);
|
|
169
227
|
if (!response.data.success) {
|
|
170
|
-
throw new Error(
|
|
228
|
+
throw new Error(
|
|
229
|
+
response.data.error || response.data.message || "Payment failed"
|
|
230
|
+
);
|
|
171
231
|
}
|
|
172
232
|
return {
|
|
173
233
|
success: true,
|
|
@@ -179,9 +239,7 @@ var Facinet = class _Facinet {
|
|
|
179
239
|
},
|
|
180
240
|
payment: {
|
|
181
241
|
from: payerAddress,
|
|
182
|
-
// Checksummed
|
|
183
242
|
to: recipientAddress,
|
|
184
|
-
// Checksummed
|
|
185
243
|
amount: params.amount,
|
|
186
244
|
network: this.config.network
|
|
187
245
|
}
|
|
@@ -224,16 +282,19 @@ var Facinet = class _Facinet {
|
|
|
224
282
|
*
|
|
225
283
|
* @example
|
|
226
284
|
* ```typescript
|
|
285
|
+
* // Pay on Base Sepolia
|
|
227
286
|
* await Facinet.quickPay({
|
|
228
287
|
* amount: '1',
|
|
229
288
|
* recipient: '0xMerchantAddress',
|
|
230
|
-
* privateKey: process.env.PRIVATE_KEY
|
|
289
|
+
* privateKey: process.env.PRIVATE_KEY,
|
|
290
|
+
* network: 'base-sepolia'
|
|
231
291
|
* });
|
|
232
292
|
* ```
|
|
233
293
|
*/
|
|
234
294
|
static async quickPay(params) {
|
|
235
295
|
const facinet = new _Facinet({
|
|
236
|
-
privateKey: params.privateKey
|
|
296
|
+
privateKey: params.privateKey,
|
|
297
|
+
network: params.network
|
|
237
298
|
});
|
|
238
299
|
return facinet.pay({
|
|
239
300
|
amount: params.amount,
|