moltspay 0.8.14 → 0.9.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 +156 -504
- package/dist/cli/index.js +553 -171
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +555 -173
- package/dist/cli/index.mjs.map +1 -1
- package/dist/facilitators/index.d.mts +61 -0
- package/dist/facilitators/index.d.ts +61 -0
- package/dist/facilitators/index.js +502 -0
- package/dist/facilitators/index.js.map +1 -0
- package/dist/facilitators/index.mjs +461 -0
- package/dist/facilitators/index.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +583 -190
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +580 -192
- package/dist/index.mjs.map +1 -1
- package/dist/registry-OsEO2dOu.d.mts +233 -0
- package/dist/registry-OsEO2dOu.d.ts +233 -0
- package/dist/server/index.d.mts +19 -15
- package/dist/server/index.d.ts +19 -15
- package/dist/server/index.js +514 -145
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +514 -145
- package/dist/server/index.mjs.map +1 -1
- package/package.json +7 -2
- package/schemas/moltspay.services.schema.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2905,7 +2905,7 @@ function alphabet(letters) {
|
|
|
2905
2905
|
};
|
|
2906
2906
|
}
|
|
2907
2907
|
// @__NO_SIDE_EFFECTS__
|
|
2908
|
-
function
|
|
2908
|
+
function join5(separator = "") {
|
|
2909
2909
|
astr("join", separator);
|
|
2910
2910
|
return {
|
|
2911
2911
|
encode: (from) => {
|
|
@@ -3112,10 +3112,10 @@ var init_esm = __esm({
|
|
|
3112
3112
|
convertRadix2,
|
|
3113
3113
|
radix,
|
|
3114
3114
|
radix2,
|
|
3115
|
-
join:
|
|
3115
|
+
join: join5,
|
|
3116
3116
|
padding
|
|
3117
3117
|
};
|
|
3118
|
-
genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */
|
|
3118
|
+
genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */ join5(""));
|
|
3119
3119
|
base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
|
|
3120
3120
|
createBase58check = (sha2566) => /* @__PURE__ */ chain(checksum(4, (data) => sha2566(sha2566(data))), base58);
|
|
3121
3121
|
}
|
|
@@ -3263,14 +3263,14 @@ var init_esm2 = __esm({
|
|
|
3263
3263
|
}
|
|
3264
3264
|
this.pubHash = hash160(this.pubKey);
|
|
3265
3265
|
}
|
|
3266
|
-
derive(
|
|
3267
|
-
if (!/^[mM]'?/.test(
|
|
3266
|
+
derive(path4) {
|
|
3267
|
+
if (!/^[mM]'?/.test(path4)) {
|
|
3268
3268
|
throw new Error('Path must start with "m" or "M"');
|
|
3269
3269
|
}
|
|
3270
|
-
if (/^[mM]'?$/.test(
|
|
3270
|
+
if (/^[mM]'?$/.test(path4)) {
|
|
3271
3271
|
return this;
|
|
3272
3272
|
}
|
|
3273
|
-
const parts =
|
|
3273
|
+
const parts = path4.replace(/^[mM]'?\//, "").split("/");
|
|
3274
3274
|
let child = this;
|
|
3275
3275
|
for (const c of parts) {
|
|
3276
3276
|
const m = /^(\d+)('?)$/.exec(c);
|
|
@@ -9631,8 +9631,8 @@ var init_privateKeyToAccount = __esm({
|
|
|
9631
9631
|
});
|
|
9632
9632
|
|
|
9633
9633
|
// node_modules/viem/_esm/accounts/hdKeyToAccount.js
|
|
9634
|
-
function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path:
|
|
9635
|
-
const hdKey = hdKey_.derive(
|
|
9634
|
+
function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path: path4, ...options } = {}) {
|
|
9635
|
+
const hdKey = hdKey_.derive(path4 || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
|
|
9636
9636
|
const account = privateKeyToAccount(toHex(hdKey.privateKey), options);
|
|
9637
9637
|
return {
|
|
9638
9638
|
...account,
|
|
@@ -30342,15 +30342,20 @@ var init_accounts = __esm({
|
|
|
30342
30342
|
// src/index.ts
|
|
30343
30343
|
var index_exports = {};
|
|
30344
30344
|
__export(index_exports, {
|
|
30345
|
+
BaseFacilitator: () => BaseFacilitator,
|
|
30346
|
+
CDPFacilitator: () => CDPFacilitator,
|
|
30345
30347
|
CDPWallet: () => CDPWallet,
|
|
30346
30348
|
CHAINS: () => CHAINS,
|
|
30347
30349
|
ERC20_ABI: () => ERC20_ABI,
|
|
30350
|
+
FacilitatorRegistry: () => FacilitatorRegistry,
|
|
30348
30351
|
MoltsPayClient: () => MoltsPayClient,
|
|
30349
30352
|
MoltsPayServer: () => MoltsPayServer,
|
|
30353
|
+
createRegistry: () => createRegistry,
|
|
30350
30354
|
createWallet: () => createWallet,
|
|
30351
30355
|
getCDPWalletAddress: () => getCDPWalletAddress,
|
|
30352
30356
|
getChain: () => getChain,
|
|
30353
30357
|
getChainById: () => getChainById,
|
|
30358
|
+
getDefaultRegistry: () => getDefaultRegistry,
|
|
30354
30359
|
getTransactionStatus: () => getTransactionStatus,
|
|
30355
30360
|
getWalletAddress: () => getWalletAddress,
|
|
30356
30361
|
initCDPWallet: () => initCDPWallet,
|
|
@@ -30367,20 +30372,478 @@ init_cjs_shims();
|
|
|
30367
30372
|
|
|
30368
30373
|
// src/server/index.ts
|
|
30369
30374
|
init_cjs_shims();
|
|
30370
|
-
var
|
|
30375
|
+
var import_fs2 = require("fs");
|
|
30371
30376
|
var import_http = require("http");
|
|
30377
|
+
var path2 = __toESM(require("path"));
|
|
30378
|
+
|
|
30379
|
+
// src/facilitators/index.ts
|
|
30380
|
+
init_cjs_shims();
|
|
30381
|
+
|
|
30382
|
+
// src/facilitators/interface.ts
|
|
30383
|
+
init_cjs_shims();
|
|
30384
|
+
var BaseFacilitator = class {
|
|
30385
|
+
supportsNetwork(network) {
|
|
30386
|
+
return this.supportedNetworks.includes(network);
|
|
30387
|
+
}
|
|
30388
|
+
};
|
|
30389
|
+
|
|
30390
|
+
// src/facilitators/cdp.ts
|
|
30391
|
+
init_cjs_shims();
|
|
30392
|
+
var import_fs = require("fs");
|
|
30372
30393
|
var path = __toESM(require("path"));
|
|
30394
|
+
var X402_VERSION = 2;
|
|
30395
|
+
var CDP_MAINNET_URL = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
30396
|
+
var CDP_TESTNET_URL = "https://www.x402.org/facilitator";
|
|
30397
|
+
function loadEnvFile() {
|
|
30398
|
+
const envPaths = [
|
|
30399
|
+
path.join(process.cwd(), ".env"),
|
|
30400
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
30401
|
+
];
|
|
30402
|
+
for (const envPath of envPaths) {
|
|
30403
|
+
if ((0, import_fs.existsSync)(envPath)) {
|
|
30404
|
+
try {
|
|
30405
|
+
const content = (0, import_fs.readFileSync)(envPath, "utf-8");
|
|
30406
|
+
for (const line of content.split("\n")) {
|
|
30407
|
+
const trimmed = line.trim();
|
|
30408
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
30409
|
+
const eqIndex = trimmed.indexOf("=");
|
|
30410
|
+
if (eqIndex === -1) continue;
|
|
30411
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
30412
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
30413
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
30414
|
+
value = value.slice(1, -1);
|
|
30415
|
+
}
|
|
30416
|
+
if (!process.env[key]) {
|
|
30417
|
+
process.env[key] = value;
|
|
30418
|
+
}
|
|
30419
|
+
}
|
|
30420
|
+
break;
|
|
30421
|
+
} catch {
|
|
30422
|
+
}
|
|
30423
|
+
}
|
|
30424
|
+
}
|
|
30425
|
+
}
|
|
30426
|
+
var CDPFacilitator = class extends BaseFacilitator {
|
|
30427
|
+
name = "cdp";
|
|
30428
|
+
displayName = "Coinbase CDP";
|
|
30429
|
+
supportedNetworks;
|
|
30430
|
+
endpoint;
|
|
30431
|
+
useMainnet;
|
|
30432
|
+
apiKeyId;
|
|
30433
|
+
apiKeySecret;
|
|
30434
|
+
constructor(config = {}) {
|
|
30435
|
+
super();
|
|
30436
|
+
loadEnvFile();
|
|
30437
|
+
this.useMainnet = config.useMainnet ?? process.env.USE_MAINNET?.toLowerCase() === "true";
|
|
30438
|
+
this.apiKeyId = config.apiKeyId || process.env.CDP_API_KEY_ID;
|
|
30439
|
+
this.apiKeySecret = config.apiKeySecret || process.env.CDP_API_KEY_SECRET;
|
|
30440
|
+
this.endpoint = this.useMainnet ? CDP_MAINNET_URL : CDP_TESTNET_URL;
|
|
30441
|
+
this.supportedNetworks = this.useMainnet ? ["eip155:8453"] : ["eip155:8453", "eip155:84532"];
|
|
30442
|
+
if (this.useMainnet && (!this.apiKeyId || !this.apiKeySecret)) {
|
|
30443
|
+
console.warn("[CDPFacilitator] WARNING: Mainnet mode but missing CDP credentials!");
|
|
30444
|
+
console.warn("[CDPFacilitator] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET");
|
|
30445
|
+
}
|
|
30446
|
+
}
|
|
30447
|
+
/**
|
|
30448
|
+
* Get auth headers for CDP API requests
|
|
30449
|
+
*/
|
|
30450
|
+
async getAuthHeaders(method, urlPath, body) {
|
|
30451
|
+
if (!this.useMainnet) {
|
|
30452
|
+
return {};
|
|
30453
|
+
}
|
|
30454
|
+
if (!this.apiKeyId || !this.apiKeySecret) {
|
|
30455
|
+
throw new Error("CDP credentials required for mainnet");
|
|
30456
|
+
}
|
|
30457
|
+
try {
|
|
30458
|
+
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
30459
|
+
return await getAuthHeaders({
|
|
30460
|
+
apiKeyId: this.apiKeyId,
|
|
30461
|
+
apiKeySecret: this.apiKeySecret,
|
|
30462
|
+
requestMethod: method,
|
|
30463
|
+
requestHost: "api.cdp.coinbase.com",
|
|
30464
|
+
requestPath: urlPath,
|
|
30465
|
+
requestBody: body
|
|
30466
|
+
});
|
|
30467
|
+
} catch (err) {
|
|
30468
|
+
throw new Error(`Failed to generate CDP auth: ${err.message}`);
|
|
30469
|
+
}
|
|
30470
|
+
}
|
|
30471
|
+
/**
|
|
30472
|
+
* Health check - verify facilitator is reachable
|
|
30473
|
+
*/
|
|
30474
|
+
async healthCheck() {
|
|
30475
|
+
const start = Date.now();
|
|
30476
|
+
try {
|
|
30477
|
+
const controller = new AbortController();
|
|
30478
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
30479
|
+
const response = await fetch(this.endpoint.replace("/x402", ""), {
|
|
30480
|
+
method: "HEAD",
|
|
30481
|
+
signal: controller.signal
|
|
30482
|
+
}).catch(() => null);
|
|
30483
|
+
clearTimeout(timeout);
|
|
30484
|
+
const latencyMs = Date.now() - start;
|
|
30485
|
+
return {
|
|
30486
|
+
healthy: response !== null,
|
|
30487
|
+
latencyMs
|
|
30488
|
+
};
|
|
30489
|
+
} catch (err) {
|
|
30490
|
+
return {
|
|
30491
|
+
healthy: false,
|
|
30492
|
+
error: err.message,
|
|
30493
|
+
latencyMs: Date.now() - start
|
|
30494
|
+
};
|
|
30495
|
+
}
|
|
30496
|
+
}
|
|
30497
|
+
/**
|
|
30498
|
+
* Verify payment signature with facilitator
|
|
30499
|
+
*/
|
|
30500
|
+
async verify(paymentPayload, requirements) {
|
|
30501
|
+
try {
|
|
30502
|
+
const requestBody = {
|
|
30503
|
+
x402Version: X402_VERSION,
|
|
30504
|
+
paymentPayload,
|
|
30505
|
+
paymentRequirements: requirements
|
|
30506
|
+
};
|
|
30507
|
+
const headers = {
|
|
30508
|
+
"Content-Type": "application/json"
|
|
30509
|
+
};
|
|
30510
|
+
if (this.useMainnet) {
|
|
30511
|
+
const authHeaders = await this.getAuthHeaders(
|
|
30512
|
+
"POST",
|
|
30513
|
+
"/platform/v2/x402/verify",
|
|
30514
|
+
requestBody
|
|
30515
|
+
);
|
|
30516
|
+
Object.assign(headers, authHeaders);
|
|
30517
|
+
}
|
|
30518
|
+
const response = await fetch(`${this.endpoint}/verify`, {
|
|
30519
|
+
method: "POST",
|
|
30520
|
+
headers,
|
|
30521
|
+
body: JSON.stringify(requestBody)
|
|
30522
|
+
});
|
|
30523
|
+
const result = await response.json();
|
|
30524
|
+
if (!response.ok || !result.isValid) {
|
|
30525
|
+
return {
|
|
30526
|
+
valid: false,
|
|
30527
|
+
error: result.invalidReason || result.error || "Verification failed",
|
|
30528
|
+
details: result
|
|
30529
|
+
};
|
|
30530
|
+
}
|
|
30531
|
+
return { valid: true, details: result };
|
|
30532
|
+
} catch (err) {
|
|
30533
|
+
return {
|
|
30534
|
+
valid: false,
|
|
30535
|
+
error: `Facilitator error: ${err.message}`
|
|
30536
|
+
};
|
|
30537
|
+
}
|
|
30538
|
+
}
|
|
30539
|
+
/**
|
|
30540
|
+
* Settle payment on-chain via facilitator
|
|
30541
|
+
*/
|
|
30542
|
+
async settle(paymentPayload, requirements) {
|
|
30543
|
+
try {
|
|
30544
|
+
const requestBody = {
|
|
30545
|
+
x402Version: X402_VERSION,
|
|
30546
|
+
paymentPayload,
|
|
30547
|
+
paymentRequirements: requirements
|
|
30548
|
+
};
|
|
30549
|
+
const headers = {
|
|
30550
|
+
"Content-Type": "application/json"
|
|
30551
|
+
};
|
|
30552
|
+
if (this.useMainnet) {
|
|
30553
|
+
const authHeaders = await this.getAuthHeaders(
|
|
30554
|
+
"POST",
|
|
30555
|
+
"/platform/v2/x402/settle",
|
|
30556
|
+
requestBody
|
|
30557
|
+
);
|
|
30558
|
+
Object.assign(headers, authHeaders);
|
|
30559
|
+
}
|
|
30560
|
+
const response = await fetch(`${this.endpoint}/settle`, {
|
|
30561
|
+
method: "POST",
|
|
30562
|
+
headers,
|
|
30563
|
+
body: JSON.stringify(requestBody)
|
|
30564
|
+
});
|
|
30565
|
+
const result = await response.json();
|
|
30566
|
+
if (!response.ok || !result.success) {
|
|
30567
|
+
return {
|
|
30568
|
+
success: false,
|
|
30569
|
+
error: result.error || result.errorReason || "Settlement failed"
|
|
30570
|
+
};
|
|
30571
|
+
}
|
|
30572
|
+
return {
|
|
30573
|
+
success: true,
|
|
30574
|
+
transaction: result.transaction,
|
|
30575
|
+
status: result.status || "settled"
|
|
30576
|
+
};
|
|
30577
|
+
} catch (err) {
|
|
30578
|
+
return {
|
|
30579
|
+
success: false,
|
|
30580
|
+
error: `Settlement error: ${err.message}`
|
|
30581
|
+
};
|
|
30582
|
+
}
|
|
30583
|
+
}
|
|
30584
|
+
/**
|
|
30585
|
+
* Get CDP fee information
|
|
30586
|
+
*/
|
|
30587
|
+
async getFee() {
|
|
30588
|
+
return {
|
|
30589
|
+
perTx: 1e-3,
|
|
30590
|
+
currency: "USD",
|
|
30591
|
+
freeQuota: 1e3
|
|
30592
|
+
};
|
|
30593
|
+
}
|
|
30594
|
+
/**
|
|
30595
|
+
* Get configuration summary (for logging)
|
|
30596
|
+
*/
|
|
30597
|
+
getConfigSummary() {
|
|
30598
|
+
const mode = this.useMainnet ? "mainnet" : "testnet";
|
|
30599
|
+
const hasCredentials = !!(this.apiKeyId && this.apiKeySecret);
|
|
30600
|
+
return `CDP Facilitator (${mode}, credentials: ${hasCredentials ? "yes" : "no"})`;
|
|
30601
|
+
}
|
|
30602
|
+
};
|
|
30603
|
+
|
|
30604
|
+
// src/facilitators/registry.ts
|
|
30605
|
+
init_cjs_shims();
|
|
30606
|
+
var FacilitatorRegistry = class {
|
|
30607
|
+
factories = /* @__PURE__ */ new Map();
|
|
30608
|
+
instances = /* @__PURE__ */ new Map();
|
|
30609
|
+
selection;
|
|
30610
|
+
roundRobinIndex = 0;
|
|
30611
|
+
constructor(selection) {
|
|
30612
|
+
this.registerFactory("cdp", (config) => new CDPFacilitator(config));
|
|
30613
|
+
this.selection = selection || { primary: "cdp", strategy: "failover" };
|
|
30614
|
+
}
|
|
30615
|
+
/**
|
|
30616
|
+
* Register a new facilitator factory
|
|
30617
|
+
*/
|
|
30618
|
+
registerFactory(name, factory) {
|
|
30619
|
+
this.factories.set(name, factory);
|
|
30620
|
+
}
|
|
30621
|
+
/**
|
|
30622
|
+
* Get or create a facilitator instance
|
|
30623
|
+
*/
|
|
30624
|
+
get(name, config) {
|
|
30625
|
+
if (this.instances.has(name)) {
|
|
30626
|
+
return this.instances.get(name);
|
|
30627
|
+
}
|
|
30628
|
+
const factory = this.factories.get(name);
|
|
30629
|
+
if (!factory) {
|
|
30630
|
+
throw new Error(`Unknown facilitator: ${name}. Available: ${Array.from(this.factories.keys()).join(", ")}`);
|
|
30631
|
+
}
|
|
30632
|
+
const mergedConfig = {
|
|
30633
|
+
...this.selection.config?.[name],
|
|
30634
|
+
...config
|
|
30635
|
+
};
|
|
30636
|
+
const instance = factory(mergedConfig);
|
|
30637
|
+
this.instances.set(name, instance);
|
|
30638
|
+
return instance;
|
|
30639
|
+
}
|
|
30640
|
+
/**
|
|
30641
|
+
* Get all configured facilitator names
|
|
30642
|
+
*/
|
|
30643
|
+
getConfiguredNames() {
|
|
30644
|
+
const names = [this.selection.primary];
|
|
30645
|
+
if (this.selection.fallback) {
|
|
30646
|
+
names.push(...this.selection.fallback);
|
|
30647
|
+
}
|
|
30648
|
+
return names;
|
|
30649
|
+
}
|
|
30650
|
+
/**
|
|
30651
|
+
* Get list of facilitators based on selection strategy
|
|
30652
|
+
*/
|
|
30653
|
+
async getOrderedFacilitators(network) {
|
|
30654
|
+
const names = this.getConfiguredNames();
|
|
30655
|
+
const facilitators = [];
|
|
30656
|
+
for (const name of names) {
|
|
30657
|
+
try {
|
|
30658
|
+
const f = this.get(name);
|
|
30659
|
+
if (f.supportsNetwork(network)) {
|
|
30660
|
+
facilitators.push(f);
|
|
30661
|
+
}
|
|
30662
|
+
} catch (err) {
|
|
30663
|
+
console.warn(`[Registry] Failed to get facilitator ${name}:`, err);
|
|
30664
|
+
}
|
|
30665
|
+
}
|
|
30666
|
+
if (facilitators.length === 0) {
|
|
30667
|
+
throw new Error(`No facilitators available for network: ${network}`);
|
|
30668
|
+
}
|
|
30669
|
+
switch (this.selection.strategy) {
|
|
30670
|
+
case "random":
|
|
30671
|
+
return this.shuffle(facilitators);
|
|
30672
|
+
case "roundrobin":
|
|
30673
|
+
this.roundRobinIndex = (this.roundRobinIndex + 1) % facilitators.length;
|
|
30674
|
+
return [
|
|
30675
|
+
...facilitators.slice(this.roundRobinIndex),
|
|
30676
|
+
...facilitators.slice(0, this.roundRobinIndex)
|
|
30677
|
+
];
|
|
30678
|
+
case "cheapest":
|
|
30679
|
+
return this.sortByCheapest(facilitators);
|
|
30680
|
+
case "fastest":
|
|
30681
|
+
return this.sortByFastest(facilitators);
|
|
30682
|
+
case "failover":
|
|
30683
|
+
default:
|
|
30684
|
+
return facilitators;
|
|
30685
|
+
}
|
|
30686
|
+
}
|
|
30687
|
+
shuffle(array) {
|
|
30688
|
+
const result = [...array];
|
|
30689
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
30690
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
30691
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
30692
|
+
}
|
|
30693
|
+
return result;
|
|
30694
|
+
}
|
|
30695
|
+
async sortByCheapest(facilitators) {
|
|
30696
|
+
const withFees = await Promise.all(
|
|
30697
|
+
facilitators.map(async (f) => {
|
|
30698
|
+
try {
|
|
30699
|
+
const fee = await f.getFee?.();
|
|
30700
|
+
return { facilitator: f, perTx: fee?.perTx ?? Infinity };
|
|
30701
|
+
} catch {
|
|
30702
|
+
return { facilitator: f, perTx: Infinity };
|
|
30703
|
+
}
|
|
30704
|
+
})
|
|
30705
|
+
);
|
|
30706
|
+
withFees.sort((a, b) => a.perTx - b.perTx);
|
|
30707
|
+
return withFees.map((w) => w.facilitator);
|
|
30708
|
+
}
|
|
30709
|
+
async sortByFastest(facilitators) {
|
|
30710
|
+
const withLatency = await Promise.all(
|
|
30711
|
+
facilitators.map(async (f) => {
|
|
30712
|
+
try {
|
|
30713
|
+
const health = await f.healthCheck();
|
|
30714
|
+
return { facilitator: f, latency: health.latencyMs ?? Infinity };
|
|
30715
|
+
} catch {
|
|
30716
|
+
return { facilitator: f, latency: Infinity };
|
|
30717
|
+
}
|
|
30718
|
+
})
|
|
30719
|
+
);
|
|
30720
|
+
withLatency.sort((a, b) => a.latency - b.latency);
|
|
30721
|
+
return withLatency.map((w) => w.facilitator);
|
|
30722
|
+
}
|
|
30723
|
+
/**
|
|
30724
|
+
* Verify payment using configured facilitators
|
|
30725
|
+
*/
|
|
30726
|
+
async verify(paymentPayload, requirements) {
|
|
30727
|
+
const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;
|
|
30728
|
+
const facilitators = await this.getOrderedFacilitators(network);
|
|
30729
|
+
let lastError;
|
|
30730
|
+
for (const f of facilitators) {
|
|
30731
|
+
try {
|
|
30732
|
+
console.log(`[Registry] Trying ${f.name} for verify...`);
|
|
30733
|
+
const result = await f.verify(paymentPayload, requirements);
|
|
30734
|
+
if (result.valid) {
|
|
30735
|
+
console.log(`[Registry] ${f.name} verify succeeded`);
|
|
30736
|
+
return { ...result, facilitator: f.name };
|
|
30737
|
+
}
|
|
30738
|
+
lastError = result.error;
|
|
30739
|
+
console.log(`[Registry] ${f.name} verify failed: ${result.error}`);
|
|
30740
|
+
if (this.selection.strategy === "failover" && !this.isTransientError(result.error)) {
|
|
30741
|
+
break;
|
|
30742
|
+
}
|
|
30743
|
+
} catch (err) {
|
|
30744
|
+
lastError = err.message;
|
|
30745
|
+
console.error(`[Registry] ${f.name} error:`, err.message);
|
|
30746
|
+
}
|
|
30747
|
+
}
|
|
30748
|
+
return {
|
|
30749
|
+
valid: false,
|
|
30750
|
+
error: lastError || "All facilitators failed",
|
|
30751
|
+
facilitator: "none"
|
|
30752
|
+
};
|
|
30753
|
+
}
|
|
30754
|
+
/**
|
|
30755
|
+
* Settle payment using configured facilitators
|
|
30756
|
+
*/
|
|
30757
|
+
async settle(paymentPayload, requirements) {
|
|
30758
|
+
const network = paymentPayload.accepted?.network || paymentPayload.network || requirements.network;
|
|
30759
|
+
const facilitators = await this.getOrderedFacilitators(network);
|
|
30760
|
+
let lastError;
|
|
30761
|
+
for (const f of facilitators) {
|
|
30762
|
+
try {
|
|
30763
|
+
console.log(`[Registry] Trying ${f.name} for settle...`);
|
|
30764
|
+
const result = await f.settle(paymentPayload, requirements);
|
|
30765
|
+
if (result.success) {
|
|
30766
|
+
console.log(`[Registry] ${f.name} settle succeeded: ${result.transaction}`);
|
|
30767
|
+
return { ...result, facilitator: f.name };
|
|
30768
|
+
}
|
|
30769
|
+
lastError = result.error;
|
|
30770
|
+
console.log(`[Registry] ${f.name} settle failed: ${result.error}`);
|
|
30771
|
+
} catch (err) {
|
|
30772
|
+
lastError = err.message;
|
|
30773
|
+
console.error(`[Registry] ${f.name} error:`, err.message);
|
|
30774
|
+
}
|
|
30775
|
+
}
|
|
30776
|
+
return {
|
|
30777
|
+
success: false,
|
|
30778
|
+
error: lastError || "All facilitators failed",
|
|
30779
|
+
facilitator: "none"
|
|
30780
|
+
};
|
|
30781
|
+
}
|
|
30782
|
+
/**
|
|
30783
|
+
* Check health of all configured facilitators
|
|
30784
|
+
*/
|
|
30785
|
+
async healthCheckAll() {
|
|
30786
|
+
const results = {};
|
|
30787
|
+
for (const name of this.getConfiguredNames()) {
|
|
30788
|
+
try {
|
|
30789
|
+
const f = this.get(name);
|
|
30790
|
+
results[name] = await f.healthCheck();
|
|
30791
|
+
} catch (err) {
|
|
30792
|
+
results[name] = { healthy: false, error: err.message };
|
|
30793
|
+
}
|
|
30794
|
+
}
|
|
30795
|
+
return results;
|
|
30796
|
+
}
|
|
30797
|
+
/**
|
|
30798
|
+
* Check if an error is transient (network/server issue) vs permanent (bad request)
|
|
30799
|
+
*/
|
|
30800
|
+
isTransientError(error) {
|
|
30801
|
+
if (!error) return true;
|
|
30802
|
+
const transientPatterns = [
|
|
30803
|
+
/timeout/i,
|
|
30804
|
+
/network/i,
|
|
30805
|
+
/connection/i,
|
|
30806
|
+
/ECONNREFUSED/i,
|
|
30807
|
+
/ETIMEDOUT/i,
|
|
30808
|
+
/503/,
|
|
30809
|
+
/502/,
|
|
30810
|
+
/500/
|
|
30811
|
+
];
|
|
30812
|
+
return transientPatterns.some((p) => p.test(error));
|
|
30813
|
+
}
|
|
30814
|
+
/**
|
|
30815
|
+
* Update selection configuration
|
|
30816
|
+
*/
|
|
30817
|
+
setSelection(selection) {
|
|
30818
|
+
this.selection = selection;
|
|
30819
|
+
this.instances.clear();
|
|
30820
|
+
}
|
|
30821
|
+
/**
|
|
30822
|
+
* Get current selection configuration
|
|
30823
|
+
*/
|
|
30824
|
+
getSelection() {
|
|
30825
|
+
return { ...this.selection };
|
|
30826
|
+
}
|
|
30827
|
+
};
|
|
30828
|
+
var defaultRegistry = null;
|
|
30829
|
+
function getDefaultRegistry() {
|
|
30830
|
+
if (!defaultRegistry) {
|
|
30831
|
+
defaultRegistry = new FacilitatorRegistry();
|
|
30832
|
+
}
|
|
30833
|
+
return defaultRegistry;
|
|
30834
|
+
}
|
|
30835
|
+
function createRegistry(selection) {
|
|
30836
|
+
return new FacilitatorRegistry(selection);
|
|
30837
|
+
}
|
|
30373
30838
|
|
|
30374
30839
|
// src/server/types.ts
|
|
30375
30840
|
init_cjs_shims();
|
|
30376
30841
|
|
|
30377
30842
|
// src/server/index.ts
|
|
30378
|
-
var
|
|
30843
|
+
var X402_VERSION2 = 2;
|
|
30379
30844
|
var PAYMENT_REQUIRED_HEADER = "x-payment-required";
|
|
30380
30845
|
var PAYMENT_HEADER = "x-payment";
|
|
30381
30846
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
30382
|
-
var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
|
|
30383
|
-
var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
|
|
30384
30847
|
var USDC_ADDRESSES = {
|
|
30385
30848
|
"eip155:8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
30386
30849
|
// Base mainnet
|
|
@@ -30391,15 +30854,15 @@ var USDC_DOMAIN = {
|
|
|
30391
30854
|
name: "USD Coin",
|
|
30392
30855
|
version: "2"
|
|
30393
30856
|
};
|
|
30394
|
-
function
|
|
30857
|
+
function loadEnvFile2() {
|
|
30395
30858
|
const envPaths = [
|
|
30396
|
-
|
|
30397
|
-
|
|
30859
|
+
path2.join(process.cwd(), ".env"),
|
|
30860
|
+
path2.join(process.env.HOME || "", ".moltspay", ".env")
|
|
30398
30861
|
];
|
|
30399
30862
|
for (const envPath of envPaths) {
|
|
30400
|
-
if ((0,
|
|
30863
|
+
if ((0, import_fs2.existsSync)(envPath)) {
|
|
30401
30864
|
try {
|
|
30402
|
-
const content = (0,
|
|
30865
|
+
const content = (0, import_fs2.readFileSync)(envPath, "utf-8");
|
|
30403
30866
|
for (const line of content.split("\n")) {
|
|
30404
30867
|
const trimmed = line.trim();
|
|
30405
30868
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -30416,77 +30879,44 @@ function loadEnvFiles() {
|
|
|
30416
30879
|
}
|
|
30417
30880
|
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
30418
30881
|
break;
|
|
30419
|
-
} catch
|
|
30420
|
-
console.warn(`[MoltsPay] Failed to load ${envPath}:`, err);
|
|
30882
|
+
} catch {
|
|
30421
30883
|
}
|
|
30422
30884
|
}
|
|
30423
30885
|
}
|
|
30424
30886
|
}
|
|
30425
|
-
function getCDPConfig() {
|
|
30426
|
-
loadEnvFiles();
|
|
30427
|
-
return {
|
|
30428
|
-
useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
|
|
30429
|
-
apiKeyId: process.env.CDP_API_KEY_ID,
|
|
30430
|
-
apiKeySecret: process.env.CDP_API_KEY_SECRET
|
|
30431
|
-
};
|
|
30432
|
-
}
|
|
30433
|
-
async function getCDPAuthHeaders(method, urlPath, body) {
|
|
30434
|
-
const config = getCDPConfig();
|
|
30435
|
-
if (!config.apiKeyId || !config.apiKeySecret) {
|
|
30436
|
-
throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
|
|
30437
|
-
}
|
|
30438
|
-
try {
|
|
30439
|
-
const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
|
|
30440
|
-
const headers = await getAuthHeaders({
|
|
30441
|
-
apiKeyId: config.apiKeyId,
|
|
30442
|
-
apiKeySecret: config.apiKeySecret,
|
|
30443
|
-
requestMethod: method,
|
|
30444
|
-
requestHost: "api.cdp.coinbase.com",
|
|
30445
|
-
requestPath: urlPath,
|
|
30446
|
-
requestBody: body
|
|
30447
|
-
});
|
|
30448
|
-
return headers;
|
|
30449
|
-
} catch (err) {
|
|
30450
|
-
console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
|
|
30451
|
-
throw err;
|
|
30452
|
-
}
|
|
30453
|
-
}
|
|
30454
30887
|
var MoltsPayServer = class {
|
|
30455
30888
|
manifest;
|
|
30456
30889
|
skills = /* @__PURE__ */ new Map();
|
|
30457
30890
|
options;
|
|
30458
|
-
|
|
30459
|
-
facilitatorUrl;
|
|
30891
|
+
registry;
|
|
30460
30892
|
networkId;
|
|
30893
|
+
useMainnet;
|
|
30461
30894
|
constructor(servicesPath, options = {}) {
|
|
30462
|
-
|
|
30463
|
-
const content = (0,
|
|
30895
|
+
loadEnvFile2();
|
|
30896
|
+
const content = (0, import_fs2.readFileSync)(servicesPath, "utf-8");
|
|
30464
30897
|
this.manifest = JSON.parse(content);
|
|
30465
30898
|
this.options = {
|
|
30466
30899
|
port: options.port || 3e3,
|
|
30467
|
-
host: options.host || "0.0.0.0"
|
|
30900
|
+
host: options.host || "0.0.0.0",
|
|
30901
|
+
...options
|
|
30468
30902
|
};
|
|
30469
|
-
|
|
30470
|
-
|
|
30471
|
-
|
|
30472
|
-
|
|
30903
|
+
this.useMainnet = process.env.USE_MAINNET?.toLowerCase() === "true";
|
|
30904
|
+
this.networkId = this.useMainnet ? "eip155:8453" : "eip155:84532";
|
|
30905
|
+
const facilitatorConfig = options.facilitators || {
|
|
30906
|
+
primary: "cdp",
|
|
30907
|
+
strategy: "failover",
|
|
30908
|
+
config: {
|
|
30909
|
+
cdp: { useMainnet: this.useMainnet }
|
|
30473
30910
|
}
|
|
30474
|
-
|
|
30475
|
-
|
|
30476
|
-
|
|
30477
|
-
|
|
30478
|
-
this.networkId = "eip155:84532";
|
|
30479
|
-
}
|
|
30480
|
-
const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
30481
|
-
const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
|
|
30911
|
+
};
|
|
30912
|
+
this.registry = new FacilitatorRegistry(facilitatorConfig);
|
|
30913
|
+
const primaryFacilitator = this.registry.get(facilitatorConfig.primary);
|
|
30914
|
+
const networkName = this.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
|
|
30482
30915
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
30483
30916
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
30484
30917
|
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
30485
30918
|
console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
|
|
30486
|
-
console.log(`[MoltsPay] Facilitator: ${
|
|
30487
|
-
if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
|
|
30488
|
-
console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
|
|
30489
|
-
}
|
|
30919
|
+
console.log(`[MoltsPay] Facilitator: ${primaryFacilitator.displayName} (${facilitatorConfig.strategy || "failover"})`);
|
|
30490
30920
|
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
30491
30921
|
}
|
|
30492
30922
|
/**
|
|
@@ -30512,6 +30942,7 @@ var MoltsPayServer = class {
|
|
|
30512
30942
|
console.log(`[MoltsPay] Endpoints:`);
|
|
30513
30943
|
console.log(` GET /services - List available services`);
|
|
30514
30944
|
console.log(` POST /execute - Execute service (x402 payment)`);
|
|
30945
|
+
console.log(` GET /health - Health check (incl. facilitators)`);
|
|
30515
30946
|
});
|
|
30516
30947
|
}
|
|
30517
30948
|
/**
|
|
@@ -30532,6 +30963,9 @@ var MoltsPayServer = class {
|
|
|
30532
30963
|
if (url.pathname === "/services" && req.method === "GET") {
|
|
30533
30964
|
return this.handleGetServices(res);
|
|
30534
30965
|
}
|
|
30966
|
+
if (url.pathname === "/health" && req.method === "GET") {
|
|
30967
|
+
return await this.handleHealthCheck(res);
|
|
30968
|
+
}
|
|
30535
30969
|
if (url.pathname === "/execute" && req.method === "POST") {
|
|
30536
30970
|
const body = await this.readBody(req);
|
|
30537
30971
|
const paymentHeader = req.headers[PAYMENT_HEADER];
|
|
@@ -30557,18 +30991,37 @@ var MoltsPayServer = class {
|
|
|
30557
30991
|
output: s.output,
|
|
30558
30992
|
available: this.skills.has(s.id)
|
|
30559
30993
|
}));
|
|
30994
|
+
const selection = this.registry.getSelection();
|
|
30560
30995
|
this.sendJson(res, 200, {
|
|
30561
30996
|
provider: this.manifest.provider,
|
|
30562
30997
|
services,
|
|
30563
30998
|
x402: {
|
|
30564
|
-
version:
|
|
30999
|
+
version: X402_VERSION2,
|
|
30565
31000
|
network: this.networkId,
|
|
30566
31001
|
schemes: ["exact"],
|
|
30567
|
-
|
|
30568
|
-
|
|
31002
|
+
facilitators: {
|
|
31003
|
+
primary: selection.primary,
|
|
31004
|
+
fallback: selection.fallback,
|
|
31005
|
+
strategy: selection.strategy
|
|
31006
|
+
},
|
|
31007
|
+
mainnet: this.useMainnet
|
|
30569
31008
|
}
|
|
30570
31009
|
});
|
|
30571
31010
|
}
|
|
31011
|
+
/**
|
|
31012
|
+
* GET /health - Health check endpoint
|
|
31013
|
+
*/
|
|
31014
|
+
async handleHealthCheck(res) {
|
|
31015
|
+
const facilitatorHealth = await this.registry.healthCheckAll();
|
|
31016
|
+
const allHealthy = Object.values(facilitatorHealth).every((h) => h.healthy);
|
|
31017
|
+
this.sendJson(res, allHealthy ? 200 : 503, {
|
|
31018
|
+
status: allHealthy ? "healthy" : "degraded",
|
|
31019
|
+
network: this.networkId,
|
|
31020
|
+
facilitators: facilitatorHealth,
|
|
31021
|
+
services: this.manifest.services.length,
|
|
31022
|
+
registered: this.skills.size
|
|
31023
|
+
});
|
|
31024
|
+
}
|
|
30572
31025
|
/**
|
|
30573
31026
|
* POST /execute - Execute service with x402 payment
|
|
30574
31027
|
*/
|
|
@@ -30600,11 +31053,16 @@ var MoltsPayServer = class {
|
|
|
30600
31053
|
if (!validation.valid) {
|
|
30601
31054
|
return this.sendJson(res, 402, { error: validation.error });
|
|
30602
31055
|
}
|
|
30603
|
-
|
|
30604
|
-
|
|
31056
|
+
const requirements = this.buildPaymentRequirements(skill.config);
|
|
31057
|
+
console.log(`[MoltsPay] Verifying payment...`);
|
|
31058
|
+
const verifyResult = await this.registry.verify(payment, requirements);
|
|
30605
31059
|
if (!verifyResult.valid) {
|
|
30606
|
-
return this.sendJson(res, 402, {
|
|
31060
|
+
return this.sendJson(res, 402, {
|
|
31061
|
+
error: `Payment verification failed: ${verifyResult.error}`,
|
|
31062
|
+
facilitator: verifyResult.facilitator
|
|
31063
|
+
});
|
|
30607
31064
|
}
|
|
31065
|
+
console.log(`[MoltsPay] Verified by ${verifyResult.facilitator}`);
|
|
30608
31066
|
console.log(`[MoltsPay] Executing skill: ${service}`);
|
|
30609
31067
|
let result;
|
|
30610
31068
|
try {
|
|
@@ -30619,17 +31077,18 @@ var MoltsPayServer = class {
|
|
|
30619
31077
|
console.log(`[MoltsPay] Skill succeeded, settling payment...`);
|
|
30620
31078
|
let settlement = null;
|
|
30621
31079
|
try {
|
|
30622
|
-
settlement = await this.
|
|
30623
|
-
console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
|
|
31080
|
+
settlement = await this.registry.settle(payment, requirements);
|
|
31081
|
+
console.log(`[MoltsPay] Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
|
|
30624
31082
|
} catch (err) {
|
|
30625
31083
|
console.error("[MoltsPay] Settlement failed:", err.message);
|
|
30626
31084
|
}
|
|
30627
31085
|
const responseHeaders = {};
|
|
30628
|
-
if (settlement) {
|
|
31086
|
+
if (settlement?.success) {
|
|
30629
31087
|
const responsePayload = {
|
|
30630
31088
|
success: true,
|
|
30631
31089
|
transaction: settlement.transaction,
|
|
30632
|
-
network: payment.network
|
|
31090
|
+
network: payment.network || payment.accepted?.network,
|
|
31091
|
+
facilitator: settlement.facilitator
|
|
30633
31092
|
};
|
|
30634
31093
|
responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
|
|
30635
31094
|
JSON.stringify(responsePayload)
|
|
@@ -30638,7 +31097,7 @@ var MoltsPayServer = class {
|
|
|
30638
31097
|
this.sendJson(res, 200, {
|
|
30639
31098
|
success: true,
|
|
30640
31099
|
result,
|
|
30641
|
-
payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
|
|
31100
|
+
payment: settlement?.success ? { transaction: settlement.transaction, status: "settled", facilitator: settlement.facilitator } : { status: "pending" }
|
|
30642
31101
|
}, responseHeaders);
|
|
30643
31102
|
}
|
|
30644
31103
|
/**
|
|
@@ -30647,7 +31106,7 @@ var MoltsPayServer = class {
|
|
|
30647
31106
|
sendPaymentRequired(config, res) {
|
|
30648
31107
|
const requirements = this.buildPaymentRequirements(config);
|
|
30649
31108
|
const paymentRequired = {
|
|
30650
|
-
x402Version:
|
|
31109
|
+
x402Version: X402_VERSION2,
|
|
30651
31110
|
accepts: [requirements],
|
|
30652
31111
|
resource: {
|
|
30653
31112
|
url: `/execute?service=${config.id}`,
|
|
@@ -30670,7 +31129,7 @@ var MoltsPayServer = class {
|
|
|
30670
31129
|
* Basic payment validation
|
|
30671
31130
|
*/
|
|
30672
31131
|
validatePayment(payment, config) {
|
|
30673
|
-
if (payment.x402Version !==
|
|
31132
|
+
if (payment.x402Version !== X402_VERSION2) {
|
|
30674
31133
|
return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };
|
|
30675
31134
|
}
|
|
30676
31135
|
const scheme = payment.accepted?.scheme || payment.scheme;
|
|
@@ -30684,7 +31143,7 @@ var MoltsPayServer = class {
|
|
|
30684
31143
|
return { valid: true };
|
|
30685
31144
|
}
|
|
30686
31145
|
/**
|
|
30687
|
-
* Build
|
|
31146
|
+
* Build payment requirements for facilitator
|
|
30688
31147
|
*/
|
|
30689
31148
|
buildPaymentRequirements(config) {
|
|
30690
31149
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
@@ -30699,77 +31158,6 @@ var MoltsPayServer = class {
|
|
|
30699
31158
|
extra: USDC_DOMAIN
|
|
30700
31159
|
};
|
|
30701
31160
|
}
|
|
30702
|
-
/**
|
|
30703
|
-
* Verify payment with facilitator (testnet or CDP)
|
|
30704
|
-
*/
|
|
30705
|
-
async verifyWithFacilitator(payment, config) {
|
|
30706
|
-
try {
|
|
30707
|
-
const requirements = this.buildPaymentRequirements(config);
|
|
30708
|
-
const requestBody = {
|
|
30709
|
-
x402Version: X402_VERSION,
|
|
30710
|
-
// Required at top level for CDP
|
|
30711
|
-
paymentPayload: payment,
|
|
30712
|
-
paymentRequirements: requirements
|
|
30713
|
-
};
|
|
30714
|
-
console.log("[MoltsPay] Verify request:", JSON.stringify(requestBody, null, 2));
|
|
30715
|
-
let headers = { "Content-Type": "application/json" };
|
|
30716
|
-
if (this.cdpConfig.useMainnet) {
|
|
30717
|
-
const authHeaders = await getCDPAuthHeaders(
|
|
30718
|
-
"POST",
|
|
30719
|
-
"/platform/v2/x402/verify",
|
|
30720
|
-
requestBody
|
|
30721
|
-
);
|
|
30722
|
-
headers = { ...headers, ...authHeaders };
|
|
30723
|
-
}
|
|
30724
|
-
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
30725
|
-
method: "POST",
|
|
30726
|
-
headers,
|
|
30727
|
-
body: JSON.stringify(requestBody)
|
|
30728
|
-
});
|
|
30729
|
-
const result = await response.json();
|
|
30730
|
-
console.log("[MoltsPay] Verify response:", JSON.stringify(result, null, 2));
|
|
30731
|
-
if (!response.ok || !result.isValid) {
|
|
30732
|
-
return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
|
|
30733
|
-
}
|
|
30734
|
-
return { valid: true };
|
|
30735
|
-
} catch (err) {
|
|
30736
|
-
return { valid: false, error: `Facilitator error: ${err.message}` };
|
|
30737
|
-
}
|
|
30738
|
-
}
|
|
30739
|
-
/**
|
|
30740
|
-
* Settle payment with facilitator (execute on-chain transfer)
|
|
30741
|
-
*/
|
|
30742
|
-
async settleWithFacilitator(payment, config) {
|
|
30743
|
-
const requirements = this.buildPaymentRequirements(config);
|
|
30744
|
-
const requestBody = {
|
|
30745
|
-
x402Version: X402_VERSION,
|
|
30746
|
-
// Required at top level for CDP
|
|
30747
|
-
paymentPayload: payment,
|
|
30748
|
-
paymentRequirements: requirements
|
|
30749
|
-
};
|
|
30750
|
-
let headers = { "Content-Type": "application/json" };
|
|
30751
|
-
if (this.cdpConfig.useMainnet) {
|
|
30752
|
-
const authHeaders = await getCDPAuthHeaders(
|
|
30753
|
-
"POST",
|
|
30754
|
-
"/platform/v2/x402/settle",
|
|
30755
|
-
requestBody
|
|
30756
|
-
);
|
|
30757
|
-
headers = { ...headers, ...authHeaders };
|
|
30758
|
-
}
|
|
30759
|
-
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
30760
|
-
method: "POST",
|
|
30761
|
-
headers,
|
|
30762
|
-
body: JSON.stringify(requestBody)
|
|
30763
|
-
});
|
|
30764
|
-
const result = await response.json();
|
|
30765
|
-
if (!response.ok || !result.success) {
|
|
30766
|
-
throw new Error(result.error || result.errorReason || "Settlement failed");
|
|
30767
|
-
}
|
|
30768
|
-
return {
|
|
30769
|
-
transaction: result.transaction,
|
|
30770
|
-
status: result.status || "settled"
|
|
30771
|
-
};
|
|
30772
|
-
}
|
|
30773
31161
|
async readBody(req) {
|
|
30774
31162
|
return new Promise((resolve, reject) => {
|
|
30775
31163
|
let body = "";
|
|
@@ -30796,7 +31184,7 @@ var MoltsPayServer = class {
|
|
|
30796
31184
|
|
|
30797
31185
|
// src/client/index.ts
|
|
30798
31186
|
init_cjs_shims();
|
|
30799
|
-
var
|
|
31187
|
+
var import_fs3 = require("fs");
|
|
30800
31188
|
var import_os = require("os");
|
|
30801
31189
|
var import_path = require("path");
|
|
30802
31190
|
var import_ethers = require("ethers");
|
|
@@ -30883,7 +31271,7 @@ var ERC20_ABI = [
|
|
|
30883
31271
|
init_cjs_shims();
|
|
30884
31272
|
|
|
30885
31273
|
// src/client/index.ts
|
|
30886
|
-
var
|
|
31274
|
+
var X402_VERSION3 = 2;
|
|
30887
31275
|
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
30888
31276
|
var PAYMENT_HEADER2 = "x-payment";
|
|
30889
31277
|
var DEFAULT_CONFIG = {
|
|
@@ -31009,7 +31397,7 @@ var MoltsPayClient = class {
|
|
|
31009
31397
|
}
|
|
31010
31398
|
const authorization = await this.signEIP3009(payTo, amount, chain2);
|
|
31011
31399
|
const payload = {
|
|
31012
|
-
x402Version:
|
|
31400
|
+
x402Version: X402_VERSION3,
|
|
31013
31401
|
payload: authorization,
|
|
31014
31402
|
// v2 requires 'accepted' field with the requirements being fulfilled
|
|
31015
31403
|
accepted: {
|
|
@@ -31107,25 +31495,25 @@ var MoltsPayClient = class {
|
|
|
31107
31495
|
// --- Config & Wallet Management ---
|
|
31108
31496
|
loadConfig() {
|
|
31109
31497
|
const configPath = (0, import_path.join)(this.configDir, "config.json");
|
|
31110
|
-
if ((0,
|
|
31111
|
-
const content = (0,
|
|
31498
|
+
if ((0, import_fs3.existsSync)(configPath)) {
|
|
31499
|
+
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
31112
31500
|
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
31113
31501
|
}
|
|
31114
31502
|
return { ...DEFAULT_CONFIG };
|
|
31115
31503
|
}
|
|
31116
31504
|
saveConfig() {
|
|
31117
|
-
(0,
|
|
31505
|
+
(0, import_fs3.mkdirSync)(this.configDir, { recursive: true });
|
|
31118
31506
|
const configPath = (0, import_path.join)(this.configDir, "config.json");
|
|
31119
|
-
(0,
|
|
31507
|
+
(0, import_fs3.writeFileSync)(configPath, JSON.stringify(this.config, null, 2));
|
|
31120
31508
|
}
|
|
31121
31509
|
/**
|
|
31122
31510
|
* Load spending data from disk
|
|
31123
31511
|
*/
|
|
31124
31512
|
loadSpending() {
|
|
31125
31513
|
const spendingPath = (0, import_path.join)(this.configDir, "spending.json");
|
|
31126
|
-
if ((0,
|
|
31514
|
+
if ((0, import_fs3.existsSync)(spendingPath)) {
|
|
31127
31515
|
try {
|
|
31128
|
-
const data = JSON.parse((0,
|
|
31516
|
+
const data = JSON.parse((0, import_fs3.readFileSync)(spendingPath, "utf-8"));
|
|
31129
31517
|
const today = (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0);
|
|
31130
31518
|
if (data.date && data.date === today) {
|
|
31131
31519
|
this.todaySpending = data.amount || 0;
|
|
@@ -31144,29 +31532,29 @@ var MoltsPayClient = class {
|
|
|
31144
31532
|
* Save spending data to disk
|
|
31145
31533
|
*/
|
|
31146
31534
|
saveSpending() {
|
|
31147
|
-
(0,
|
|
31535
|
+
(0, import_fs3.mkdirSync)(this.configDir, { recursive: true });
|
|
31148
31536
|
const spendingPath = (0, import_path.join)(this.configDir, "spending.json");
|
|
31149
31537
|
const data = {
|
|
31150
31538
|
date: this.lastSpendingReset || (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0),
|
|
31151
31539
|
amount: this.todaySpending,
|
|
31152
31540
|
updatedAt: Date.now()
|
|
31153
31541
|
};
|
|
31154
|
-
(0,
|
|
31542
|
+
(0, import_fs3.writeFileSync)(spendingPath, JSON.stringify(data, null, 2));
|
|
31155
31543
|
}
|
|
31156
31544
|
loadWallet() {
|
|
31157
31545
|
const walletPath = (0, import_path.join)(this.configDir, "wallet.json");
|
|
31158
|
-
if ((0,
|
|
31546
|
+
if ((0, import_fs3.existsSync)(walletPath)) {
|
|
31159
31547
|
try {
|
|
31160
|
-
const stats = (0,
|
|
31548
|
+
const stats = (0, import_fs3.statSync)(walletPath);
|
|
31161
31549
|
const mode = stats.mode & 511;
|
|
31162
31550
|
if (mode !== 384) {
|
|
31163
31551
|
console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
|
|
31164
31552
|
console.warn(`[MoltsPay] Fixing permissions to 0600...`);
|
|
31165
|
-
(0,
|
|
31553
|
+
(0, import_fs3.chmodSync)(walletPath, 384);
|
|
31166
31554
|
}
|
|
31167
31555
|
} catch (err) {
|
|
31168
31556
|
}
|
|
31169
|
-
const content = (0,
|
|
31557
|
+
const content = (0, import_fs3.readFileSync)(walletPath, "utf-8");
|
|
31170
31558
|
return JSON.parse(content);
|
|
31171
31559
|
}
|
|
31172
31560
|
return null;
|
|
@@ -31175,7 +31563,7 @@ var MoltsPayClient = class {
|
|
|
31175
31563
|
* Initialize a new wallet (called by CLI)
|
|
31176
31564
|
*/
|
|
31177
31565
|
static init(configDir, options) {
|
|
31178
|
-
(0,
|
|
31566
|
+
(0, import_fs3.mkdirSync)(configDir, { recursive: true });
|
|
31179
31567
|
const wallet = import_ethers.Wallet.createRandom();
|
|
31180
31568
|
const walletData = {
|
|
31181
31569
|
address: wallet.address,
|
|
@@ -31183,7 +31571,7 @@ var MoltsPayClient = class {
|
|
|
31183
31571
|
createdAt: Date.now()
|
|
31184
31572
|
};
|
|
31185
31573
|
const walletPath = (0, import_path.join)(configDir, "wallet.json");
|
|
31186
|
-
(0,
|
|
31574
|
+
(0, import_fs3.writeFileSync)(walletPath, JSON.stringify(walletData, null, 2), { mode: 384 });
|
|
31187
31575
|
const config = {
|
|
31188
31576
|
chain: options.chain,
|
|
31189
31577
|
limits: {
|
|
@@ -31192,7 +31580,7 @@ var MoltsPayClient = class {
|
|
|
31192
31580
|
}
|
|
31193
31581
|
};
|
|
31194
31582
|
const configPath = (0, import_path.join)(configDir, "config.json");
|
|
31195
|
-
(0,
|
|
31583
|
+
(0, import_fs3.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
31196
31584
|
return { address: wallet.address, configDir };
|
|
31197
31585
|
}
|
|
31198
31586
|
/**
|
|
@@ -31230,7 +31618,7 @@ var import_ethers2 = require("ethers");
|
|
|
31230
31618
|
// src/wallet/createWallet.ts
|
|
31231
31619
|
init_cjs_shims();
|
|
31232
31620
|
var import_ethers3 = require("ethers");
|
|
31233
|
-
var
|
|
31621
|
+
var import_fs4 = require("fs");
|
|
31234
31622
|
var import_path2 = require("path");
|
|
31235
31623
|
var import_crypto = require("crypto");
|
|
31236
31624
|
var DEFAULT_STORAGE_DIR = (0, import_path2.join)(process.env.HOME || "~", ".moltspay");
|
|
@@ -31257,9 +31645,9 @@ function decryptPrivateKey(encrypted, password, iv, salt) {
|
|
|
31257
31645
|
}
|
|
31258
31646
|
function createWallet(options = {}) {
|
|
31259
31647
|
const storagePath = options.storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
31260
|
-
if ((0,
|
|
31648
|
+
if ((0, import_fs4.existsSync)(storagePath) && !options.overwrite) {
|
|
31261
31649
|
try {
|
|
31262
|
-
const existing = JSON.parse((0,
|
|
31650
|
+
const existing = JSON.parse((0, import_fs4.readFileSync)(storagePath, "utf8"));
|
|
31263
31651
|
return {
|
|
31264
31652
|
success: true,
|
|
31265
31653
|
address: existing.address,
|
|
@@ -31291,10 +31679,10 @@ function createWallet(options = {}) {
|
|
|
31291
31679
|
walletData.privateKey = wallet.privateKey;
|
|
31292
31680
|
}
|
|
31293
31681
|
const dir = (0, import_path2.dirname)(storagePath);
|
|
31294
|
-
if (!(0,
|
|
31295
|
-
(0,
|
|
31682
|
+
if (!(0, import_fs4.existsSync)(dir)) {
|
|
31683
|
+
(0, import_fs4.mkdirSync)(dir, { recursive: true });
|
|
31296
31684
|
}
|
|
31297
|
-
(0,
|
|
31685
|
+
(0, import_fs4.writeFileSync)(storagePath, JSON.stringify(walletData, null, 2), { mode: 384 });
|
|
31298
31686
|
return {
|
|
31299
31687
|
success: true,
|
|
31300
31688
|
address: wallet.address,
|
|
@@ -31310,11 +31698,11 @@ function createWallet(options = {}) {
|
|
|
31310
31698
|
}
|
|
31311
31699
|
function loadWallet(options = {}) {
|
|
31312
31700
|
const storagePath = options.storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
31313
|
-
if (!(0,
|
|
31701
|
+
if (!(0, import_fs4.existsSync)(storagePath)) {
|
|
31314
31702
|
return { success: false, error: "Wallet not found. Run createWallet() first." };
|
|
31315
31703
|
}
|
|
31316
31704
|
try {
|
|
31317
|
-
const data = JSON.parse((0,
|
|
31705
|
+
const data = JSON.parse((0, import_fs4.readFileSync)(storagePath, "utf8"));
|
|
31318
31706
|
if (data.encrypted) {
|
|
31319
31707
|
if (!options.password) {
|
|
31320
31708
|
return { success: false, error: "Wallet is encrypted. Password required." };
|
|
@@ -31329,20 +31717,20 @@ function loadWallet(options = {}) {
|
|
|
31329
31717
|
}
|
|
31330
31718
|
}
|
|
31331
31719
|
function getWalletAddress(storagePath) {
|
|
31332
|
-
const
|
|
31333
|
-
if (!(0,
|
|
31720
|
+
const path4 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
31721
|
+
if (!(0, import_fs4.existsSync)(path4)) {
|
|
31334
31722
|
return null;
|
|
31335
31723
|
}
|
|
31336
31724
|
try {
|
|
31337
|
-
const data = JSON.parse((0,
|
|
31725
|
+
const data = JSON.parse((0, import_fs4.readFileSync)(path4, "utf8"));
|
|
31338
31726
|
return data.address;
|
|
31339
31727
|
} catch {
|
|
31340
31728
|
return null;
|
|
31341
31729
|
}
|
|
31342
31730
|
}
|
|
31343
31731
|
function walletExists(storagePath) {
|
|
31344
|
-
const
|
|
31345
|
-
return (0,
|
|
31732
|
+
const path4 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
|
|
31733
|
+
return (0, import_fs4.existsSync)(path4);
|
|
31346
31734
|
}
|
|
31347
31735
|
|
|
31348
31736
|
// src/verify/index.ts
|
|
@@ -31485,8 +31873,8 @@ async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, ti
|
|
|
31485
31873
|
// src/cdp/index.ts
|
|
31486
31874
|
init_cjs_shims();
|
|
31487
31875
|
var fs = __toESM(require("fs"));
|
|
31488
|
-
var
|
|
31489
|
-
var DEFAULT_STORAGE_DIR2 =
|
|
31876
|
+
var path3 = __toESM(require("path"));
|
|
31877
|
+
var DEFAULT_STORAGE_DIR2 = path3.join(process.env.HOME || ".", ".moltspay");
|
|
31490
31878
|
var CDP_CONFIG_FILE = "cdp-wallet.json";
|
|
31491
31879
|
function isCDPAvailable() {
|
|
31492
31880
|
try {
|
|
@@ -31508,7 +31896,7 @@ function getCDPCredentials(config) {
|
|
|
31508
31896
|
async function initCDPWallet(config = {}) {
|
|
31509
31897
|
const storageDir = config.storageDir || DEFAULT_STORAGE_DIR2;
|
|
31510
31898
|
const chain2 = config.chain || "base";
|
|
31511
|
-
const storagePath =
|
|
31899
|
+
const storagePath = path3.join(storageDir, CDP_CONFIG_FILE);
|
|
31512
31900
|
if (fs.existsSync(storagePath)) {
|
|
31513
31901
|
try {
|
|
31514
31902
|
const data = JSON.parse(fs.readFileSync(storagePath, "utf-8"));
|
|
@@ -31570,7 +31958,7 @@ async function initCDPWallet(config = {}) {
|
|
|
31570
31958
|
}
|
|
31571
31959
|
function loadCDPWallet(config = {}) {
|
|
31572
31960
|
const storageDir = config.storageDir || DEFAULT_STORAGE_DIR2;
|
|
31573
|
-
const storagePath =
|
|
31961
|
+
const storagePath = path3.join(storageDir, CDP_CONFIG_FILE);
|
|
31574
31962
|
if (!fs.existsSync(storagePath)) {
|
|
31575
31963
|
return null;
|
|
31576
31964
|
}
|
|
@@ -31689,15 +32077,20 @@ var CDPWallet = class {
|
|
|
31689
32077
|
};
|
|
31690
32078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31691
32079
|
0 && (module.exports = {
|
|
32080
|
+
BaseFacilitator,
|
|
32081
|
+
CDPFacilitator,
|
|
31692
32082
|
CDPWallet,
|
|
31693
32083
|
CHAINS,
|
|
31694
32084
|
ERC20_ABI,
|
|
32085
|
+
FacilitatorRegistry,
|
|
31695
32086
|
MoltsPayClient,
|
|
31696
32087
|
MoltsPayServer,
|
|
32088
|
+
createRegistry,
|
|
31697
32089
|
createWallet,
|
|
31698
32090
|
getCDPWalletAddress,
|
|
31699
32091
|
getChain,
|
|
31700
32092
|
getChainById,
|
|
32093
|
+
getDefaultRegistry,
|
|
31701
32094
|
getTransactionStatus,
|
|
31702
32095
|
getWalletAddress,
|
|
31703
32096
|
initCDPWallet,
|