moltspay 0.7.2 → 0.8.1
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/cli/index.js +293 -274
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +294 -275
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +11 -5
- package/dist/client/index.d.ts +11 -5
- package/dist/client/index.js +99 -68
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +96 -55
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +435 -343
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +430 -338
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +25 -9
- package/dist/server/index.d.ts +25 -9
- package/dist/server/index.js +187 -210
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +187 -210
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30370,10 +30370,6 @@ init_cjs_shims();
|
|
|
30370
30370
|
var import_fs = require("fs");
|
|
30371
30371
|
var import_http = require("http");
|
|
30372
30372
|
|
|
30373
|
-
// src/verify/index.ts
|
|
30374
|
-
init_cjs_shims();
|
|
30375
|
-
var import_ethers = require("ethers");
|
|
30376
|
-
|
|
30377
30373
|
// src/chains/index.ts
|
|
30378
30374
|
init_cjs_shims();
|
|
30379
30375
|
var CHAINS = {
|
|
@@ -30452,165 +30448,33 @@ var ERC20_ABI = [
|
|
|
30452
30448
|
"event Approval(address indexed owner, address indexed spender, uint256 value)"
|
|
30453
30449
|
];
|
|
30454
30450
|
|
|
30455
|
-
// src/verify/index.ts
|
|
30456
|
-
var TRANSFER_EVENT_TOPIC = import_ethers.ethers.id("Transfer(address,address,uint256)");
|
|
30457
|
-
async function verifyPayment(params) {
|
|
30458
|
-
const { txHash, expectedAmount, expectedTo } = params;
|
|
30459
|
-
let chain2;
|
|
30460
|
-
try {
|
|
30461
|
-
if (typeof params.chain === "number") {
|
|
30462
|
-
chain2 = getChainById(params.chain);
|
|
30463
|
-
} else {
|
|
30464
|
-
chain2 = getChain(params.chain || "base");
|
|
30465
|
-
}
|
|
30466
|
-
if (!chain2) {
|
|
30467
|
-
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
30468
|
-
}
|
|
30469
|
-
} catch (e) {
|
|
30470
|
-
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
30471
|
-
}
|
|
30472
|
-
try {
|
|
30473
|
-
const provider = new import_ethers.ethers.JsonRpcProvider(chain2.rpc);
|
|
30474
|
-
const receipt = await provider.getTransactionReceipt(txHash);
|
|
30475
|
-
if (!receipt) {
|
|
30476
|
-
return { verified: false, error: "Transaction not found or not confirmed" };
|
|
30477
|
-
}
|
|
30478
|
-
if (receipt.status !== 1) {
|
|
30479
|
-
return { verified: false, error: "Transaction failed" };
|
|
30480
|
-
}
|
|
30481
|
-
const usdcAddress = chain2.usdc?.toLowerCase();
|
|
30482
|
-
if (!usdcAddress) {
|
|
30483
|
-
return { verified: false, error: `Chain ${chain2.name} USDC address not configured` };
|
|
30484
|
-
}
|
|
30485
|
-
for (const log of receipt.logs) {
|
|
30486
|
-
if (log.address.toLowerCase() !== usdcAddress) {
|
|
30487
|
-
continue;
|
|
30488
|
-
}
|
|
30489
|
-
if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
|
|
30490
|
-
continue;
|
|
30491
|
-
}
|
|
30492
|
-
const from = "0x" + log.topics[1].slice(-40);
|
|
30493
|
-
const to = "0x" + log.topics[2].slice(-40);
|
|
30494
|
-
const amountRaw = BigInt(log.data);
|
|
30495
|
-
const amount = Number(amountRaw) / 1e6;
|
|
30496
|
-
if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
|
|
30497
|
-
continue;
|
|
30498
|
-
}
|
|
30499
|
-
if (amount < expectedAmount) {
|
|
30500
|
-
return {
|
|
30501
|
-
verified: false,
|
|
30502
|
-
error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,
|
|
30503
|
-
amount,
|
|
30504
|
-
from,
|
|
30505
|
-
to,
|
|
30506
|
-
txHash,
|
|
30507
|
-
blockNumber: receipt.blockNumber
|
|
30508
|
-
};
|
|
30509
|
-
}
|
|
30510
|
-
return {
|
|
30511
|
-
verified: true,
|
|
30512
|
-
amount,
|
|
30513
|
-
from,
|
|
30514
|
-
to,
|
|
30515
|
-
txHash,
|
|
30516
|
-
blockNumber: receipt.blockNumber
|
|
30517
|
-
};
|
|
30518
|
-
}
|
|
30519
|
-
return { verified: false, error: "No USDC transfer found" };
|
|
30520
|
-
} catch (e) {
|
|
30521
|
-
return { verified: false, error: e.message || String(e) };
|
|
30522
|
-
}
|
|
30523
|
-
}
|
|
30524
|
-
async function getTransactionStatus(txHash, chain2 = "base") {
|
|
30525
|
-
let chainConfig;
|
|
30526
|
-
try {
|
|
30527
|
-
chainConfig = typeof chain2 === "number" ? getChainById(chain2) : getChain(chain2);
|
|
30528
|
-
if (!chainConfig) return { status: "not_found" };
|
|
30529
|
-
} catch {
|
|
30530
|
-
return { status: "not_found" };
|
|
30531
|
-
}
|
|
30532
|
-
try {
|
|
30533
|
-
const provider = new import_ethers.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
30534
|
-
const receipt = await provider.getTransactionReceipt(txHash);
|
|
30535
|
-
if (!receipt) {
|
|
30536
|
-
const tx = await provider.getTransaction(txHash);
|
|
30537
|
-
if (tx) {
|
|
30538
|
-
return { status: "pending" };
|
|
30539
|
-
}
|
|
30540
|
-
return { status: "not_found" };
|
|
30541
|
-
}
|
|
30542
|
-
const currentBlock = await provider.getBlockNumber();
|
|
30543
|
-
const confirmations = currentBlock - receipt.blockNumber;
|
|
30544
|
-
if (receipt.status === 1) {
|
|
30545
|
-
return {
|
|
30546
|
-
status: "confirmed",
|
|
30547
|
-
blockNumber: receipt.blockNumber,
|
|
30548
|
-
confirmations
|
|
30549
|
-
};
|
|
30550
|
-
} else {
|
|
30551
|
-
return {
|
|
30552
|
-
status: "failed",
|
|
30553
|
-
blockNumber: receipt.blockNumber
|
|
30554
|
-
};
|
|
30555
|
-
}
|
|
30556
|
-
} catch {
|
|
30557
|
-
return { status: "not_found" };
|
|
30558
|
-
}
|
|
30559
|
-
}
|
|
30560
|
-
async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, timeoutMs = 6e4) {
|
|
30561
|
-
let chainConfig;
|
|
30562
|
-
try {
|
|
30563
|
-
chainConfig = typeof chain2 === "number" ? getChainById(chain2) : getChain(chain2);
|
|
30564
|
-
if (!chainConfig) {
|
|
30565
|
-
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
|
|
30566
|
-
}
|
|
30567
|
-
} catch (e) {
|
|
30568
|
-
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
|
|
30569
|
-
}
|
|
30570
|
-
const provider = new import_ethers.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
30571
|
-
try {
|
|
30572
|
-
const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
|
|
30573
|
-
if (!receipt) {
|
|
30574
|
-
return { verified: false, confirmed: false, error: "Timeout waiting" };
|
|
30575
|
-
}
|
|
30576
|
-
if (receipt.status !== 1) {
|
|
30577
|
-
return { verified: false, confirmed: true, error: "Transaction failed" };
|
|
30578
|
-
}
|
|
30579
|
-
return {
|
|
30580
|
-
verified: true,
|
|
30581
|
-
confirmed: true,
|
|
30582
|
-
txHash,
|
|
30583
|
-
blockNumber: receipt.blockNumber
|
|
30584
|
-
};
|
|
30585
|
-
} catch (e) {
|
|
30586
|
-
return { verified: false, confirmed: false, error: e.message || String(e) };
|
|
30587
|
-
}
|
|
30588
|
-
}
|
|
30589
|
-
|
|
30590
30451
|
// src/server/types.ts
|
|
30591
30452
|
init_cjs_shims();
|
|
30592
30453
|
|
|
30593
30454
|
// src/server/index.ts
|
|
30594
|
-
|
|
30595
|
-
|
|
30596
|
-
|
|
30455
|
+
var X402_VERSION = 2;
|
|
30456
|
+
var PAYMENT_REQUIRED_HEADER = "x-payment-required";
|
|
30457
|
+
var PAYMENT_HEADER = "x-payment";
|
|
30458
|
+
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
30459
|
+
var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
|
|
30597
30460
|
var MoltsPayServer = class {
|
|
30598
30461
|
manifest;
|
|
30599
30462
|
skills = /* @__PURE__ */ new Map();
|
|
30600
|
-
charges = /* @__PURE__ */ new Map();
|
|
30601
30463
|
options;
|
|
30464
|
+
facilitatorUrl;
|
|
30602
30465
|
constructor(servicesPath, options = {}) {
|
|
30603
30466
|
const content = (0, import_fs.readFileSync)(servicesPath, "utf-8");
|
|
30604
30467
|
this.manifest = JSON.parse(content);
|
|
30605
30468
|
this.options = {
|
|
30606
30469
|
port: options.port || 3e3,
|
|
30607
|
-
host: options.host || "0.0.0.0"
|
|
30608
|
-
chargeExpirySecs: options.chargeExpirySecs || 300
|
|
30609
|
-
// 5 minutes
|
|
30470
|
+
host: options.host || "0.0.0.0"
|
|
30610
30471
|
};
|
|
30472
|
+
this.facilitatorUrl = options.facilitatorUrl || DEFAULT_FACILITATOR_URL;
|
|
30611
30473
|
console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
|
|
30612
30474
|
console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
|
|
30613
|
-
console.log(`[MoltsPay]
|
|
30475
|
+
console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
|
|
30476
|
+
console.log(`[MoltsPay] Facilitator: ${this.facilitatorUrl}`);
|
|
30477
|
+
console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
|
|
30614
30478
|
}
|
|
30615
30479
|
/**
|
|
30616
30480
|
* Register a skill handler for a service
|
|
@@ -30620,56 +30484,45 @@ var MoltsPayServer = class {
|
|
|
30620
30484
|
if (!config) {
|
|
30621
30485
|
throw new Error(`Service '${serviceId}' not found in manifest`);
|
|
30622
30486
|
}
|
|
30623
|
-
this.skills.set(serviceId, {
|
|
30624
|
-
id: serviceId,
|
|
30625
|
-
config,
|
|
30626
|
-
handler
|
|
30627
|
-
});
|
|
30628
|
-
console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
|
|
30487
|
+
this.skills.set(serviceId, { id: serviceId, config, handler });
|
|
30629
30488
|
return this;
|
|
30630
30489
|
}
|
|
30631
30490
|
/**
|
|
30632
|
-
* Start
|
|
30491
|
+
* Start HTTP server
|
|
30633
30492
|
*/
|
|
30634
30493
|
listen(port) {
|
|
30635
|
-
const p = port || this.options.port;
|
|
30494
|
+
const p = port || this.options.port || 3e3;
|
|
30495
|
+
const host = this.options.host || "0.0.0.0";
|
|
30636
30496
|
const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
30637
|
-
server.listen(p,
|
|
30638
|
-
console.log(`[MoltsPay] Server listening on http://${
|
|
30497
|
+
server.listen(p, host, () => {
|
|
30498
|
+
console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
|
|
30639
30499
|
console.log(`[MoltsPay] Endpoints:`);
|
|
30640
|
-
console.log(` GET /services
|
|
30641
|
-
console.log(` POST /
|
|
30642
|
-
console.log(` POST /verify - Verify payment & get result`);
|
|
30643
|
-
console.log(` GET /status/:id - Check charge status`);
|
|
30500
|
+
console.log(` GET /services - List available services`);
|
|
30501
|
+
console.log(` POST /execute - Execute service (x402 payment)`);
|
|
30644
30502
|
});
|
|
30645
30503
|
}
|
|
30504
|
+
/**
|
|
30505
|
+
* Handle incoming request
|
|
30506
|
+
*/
|
|
30646
30507
|
async handleRequest(req, res) {
|
|
30647
|
-
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
30648
|
-
const path2 = url.pathname;
|
|
30649
|
-
const method = req.method || "GET";
|
|
30650
30508
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
30651
30509
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
30652
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
30653
|
-
|
|
30510
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
|
|
30511
|
+
res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
|
|
30512
|
+
if (req.method === "OPTIONS") {
|
|
30654
30513
|
res.writeHead(204);
|
|
30655
30514
|
res.end();
|
|
30656
30515
|
return;
|
|
30657
30516
|
}
|
|
30658
30517
|
try {
|
|
30659
|
-
|
|
30518
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
30519
|
+
if (url.pathname === "/services" && req.method === "GET") {
|
|
30660
30520
|
return this.handleGetServices(res);
|
|
30661
30521
|
}
|
|
30662
|
-
if (
|
|
30663
|
-
const body = await this.readBody(req);
|
|
30664
|
-
return this.handlePay(body, res);
|
|
30665
|
-
}
|
|
30666
|
-
if (method === "POST" && path2 === "/verify") {
|
|
30522
|
+
if (url.pathname === "/execute" && req.method === "POST") {
|
|
30667
30523
|
const body = await this.readBody(req);
|
|
30668
|
-
|
|
30669
|
-
|
|
30670
|
-
if (method === "GET" && path2.startsWith("/status/")) {
|
|
30671
|
-
const chargeId = path2.replace("/status/", "");
|
|
30672
|
-
return this.handleStatus(chargeId, res);
|
|
30524
|
+
const paymentHeader = req.headers[PAYMENT_HEADER];
|
|
30525
|
+
return await this.handleExecute(body, paymentHeader, res);
|
|
30673
30526
|
}
|
|
30674
30527
|
this.sendJson(res, 404, { error: "Not found" });
|
|
30675
30528
|
} catch (err) {
|
|
@@ -30681,6 +30534,7 @@ var MoltsPayServer = class {
|
|
|
30681
30534
|
* GET /services - List available services
|
|
30682
30535
|
*/
|
|
30683
30536
|
handleGetServices(res) {
|
|
30537
|
+
const chain2 = getChain(this.manifest.provider.chain);
|
|
30684
30538
|
const services = this.manifest.services.map((s) => ({
|
|
30685
30539
|
id: s.id,
|
|
30686
30540
|
name: s.name,
|
|
@@ -30693,14 +30547,21 @@ var MoltsPayServer = class {
|
|
|
30693
30547
|
}));
|
|
30694
30548
|
this.sendJson(res, 200, {
|
|
30695
30549
|
provider: this.manifest.provider,
|
|
30696
|
-
services
|
|
30550
|
+
services,
|
|
30551
|
+
x402: {
|
|
30552
|
+
version: X402_VERSION,
|
|
30553
|
+
network: `eip155:${chain2.chainId}`,
|
|
30554
|
+
schemes: ["exact"],
|
|
30555
|
+
facilitator: this.facilitatorUrl
|
|
30556
|
+
}
|
|
30697
30557
|
});
|
|
30698
30558
|
}
|
|
30699
30559
|
/**
|
|
30700
|
-
* POST /
|
|
30560
|
+
* POST /execute - Execute service with x402 payment
|
|
30701
30561
|
* Body: { service: string, params: object }
|
|
30562
|
+
* Header: X-Payment (optional - if missing, returns 402)
|
|
30702
30563
|
*/
|
|
30703
|
-
|
|
30564
|
+
async handleExecute(body, paymentHeader, res) {
|
|
30704
30565
|
const { service, params } = body;
|
|
30705
30566
|
if (!service) {
|
|
30706
30567
|
return this.sendJson(res, 400, { error: "Missing service" });
|
|
@@ -30714,113 +30575,162 @@ var MoltsPayServer = class {
|
|
|
30714
30575
|
return this.sendJson(res, 400, { error: `Missing required param: ${key}` });
|
|
30715
30576
|
}
|
|
30716
30577
|
}
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30727
|
-
|
|
30728
|
-
|
|
30729
|
-
|
|
30730
|
-
|
|
30731
|
-
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30739
|
-
|
|
30740
|
-
|
|
30741
|
-
|
|
30578
|
+
if (!paymentHeader) {
|
|
30579
|
+
return this.sendPaymentRequired(skill.config, res);
|
|
30580
|
+
}
|
|
30581
|
+
let payment;
|
|
30582
|
+
try {
|
|
30583
|
+
const decoded = Buffer.from(paymentHeader, "base64").toString("utf-8");
|
|
30584
|
+
payment = JSON.parse(decoded);
|
|
30585
|
+
} catch {
|
|
30586
|
+
return this.sendJson(res, 400, { error: "Invalid X-Payment header" });
|
|
30587
|
+
}
|
|
30588
|
+
const validation = this.validatePayment(payment, skill.config);
|
|
30589
|
+
if (!validation.valid) {
|
|
30590
|
+
return this.sendJson(res, 402, { error: validation.error });
|
|
30591
|
+
}
|
|
30592
|
+
console.log(`[MoltsPay] Verifying payment with facilitator...`);
|
|
30593
|
+
const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
|
|
30594
|
+
if (!verifyResult.valid) {
|
|
30595
|
+
return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
|
|
30596
|
+
}
|
|
30597
|
+
console.log(`[MoltsPay] Executing skill: ${service}`);
|
|
30598
|
+
let result;
|
|
30599
|
+
try {
|
|
30600
|
+
result = await skill.handler(params || {});
|
|
30601
|
+
} catch (err) {
|
|
30602
|
+
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
30603
|
+
return this.sendJson(res, 500, {
|
|
30604
|
+
error: "Service execution failed",
|
|
30605
|
+
message: err.message
|
|
30606
|
+
});
|
|
30607
|
+
}
|
|
30608
|
+
console.log(`[MoltsPay] Skill succeeded, settling payment...`);
|
|
30609
|
+
let settlement = null;
|
|
30610
|
+
try {
|
|
30611
|
+
settlement = await this.settleWithFacilitator(payment, skill.config);
|
|
30612
|
+
console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
|
|
30613
|
+
} catch (err) {
|
|
30614
|
+
console.error("[MoltsPay] Settlement failed:", err.message);
|
|
30615
|
+
}
|
|
30616
|
+
const responseHeaders = {};
|
|
30617
|
+
if (settlement) {
|
|
30618
|
+
const responsePayload = {
|
|
30619
|
+
success: true,
|
|
30620
|
+
transaction: settlement.transaction,
|
|
30621
|
+
network: payment.network
|
|
30622
|
+
};
|
|
30623
|
+
responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
|
|
30624
|
+
JSON.stringify(responsePayload)
|
|
30625
|
+
).toString("base64");
|
|
30626
|
+
}
|
|
30627
|
+
this.sendJson(res, 200, {
|
|
30628
|
+
success: true,
|
|
30629
|
+
result,
|
|
30630
|
+
payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
|
|
30631
|
+
}, responseHeaders);
|
|
30632
|
+
}
|
|
30633
|
+
/**
|
|
30634
|
+
* Return 402 with x402 payment requirements
|
|
30635
|
+
*/
|
|
30636
|
+
sendPaymentRequired(config, res) {
|
|
30637
|
+
const chain2 = getChain(this.manifest.provider.chain);
|
|
30638
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
30639
|
+
const requirements = [{
|
|
30640
|
+
scheme: "exact",
|
|
30641
|
+
network: `eip155:${chain2.chainId}`,
|
|
30642
|
+
maxAmountRequired: amountInUnits,
|
|
30643
|
+
resource: this.manifest.provider.wallet,
|
|
30644
|
+
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
30645
|
+
// Include facilitator info for client
|
|
30646
|
+
extra: JSON.stringify({ facilitator: this.facilitatorUrl })
|
|
30647
|
+
}];
|
|
30648
|
+
const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
|
|
30649
|
+
res.writeHead(402, {
|
|
30650
|
+
"Content-Type": "application/json",
|
|
30651
|
+
[PAYMENT_REQUIRED_HEADER]: encoded
|
|
30742
30652
|
});
|
|
30653
|
+
res.end(JSON.stringify({
|
|
30654
|
+
error: "Payment required",
|
|
30655
|
+
message: `Service requires $${config.price} ${config.currency}`,
|
|
30656
|
+
x402: requirements[0]
|
|
30657
|
+
}, null, 2));
|
|
30743
30658
|
}
|
|
30744
30659
|
/**
|
|
30745
|
-
*
|
|
30746
|
-
* Body: { chargeId: string, txHash: string }
|
|
30660
|
+
* Basic payment validation (before calling facilitator)
|
|
30747
30661
|
*/
|
|
30748
|
-
|
|
30749
|
-
|
|
30750
|
-
|
|
30751
|
-
return this.sendJson(res, 400, { error: "Missing chargeId or txHash" });
|
|
30752
|
-
}
|
|
30753
|
-
const charge = this.charges.get(chargeId);
|
|
30754
|
-
if (!charge) {
|
|
30755
|
-
return this.sendJson(res, 404, { error: "Charge not found" });
|
|
30662
|
+
validatePayment(payment, config) {
|
|
30663
|
+
if (payment.x402Version !== X402_VERSION) {
|
|
30664
|
+
return { valid: false, error: `Unsupported x402 version: ${payment.x402Version}` };
|
|
30756
30665
|
}
|
|
30757
|
-
if (
|
|
30758
|
-
|
|
30759
|
-
return this.sendJson(res, 400, { error: "Charge expired" });
|
|
30666
|
+
if (payment.scheme !== "exact") {
|
|
30667
|
+
return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
|
|
30760
30668
|
}
|
|
30761
|
-
|
|
30762
|
-
|
|
30763
|
-
|
|
30764
|
-
|
|
30765
|
-
});
|
|
30669
|
+
const chain2 = getChain(this.manifest.provider.chain);
|
|
30670
|
+
const expectedNetwork = `eip155:${chain2.chainId}`;
|
|
30671
|
+
if (payment.network !== expectedNetwork) {
|
|
30672
|
+
return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
|
|
30766
30673
|
}
|
|
30674
|
+
return { valid: true };
|
|
30675
|
+
}
|
|
30676
|
+
/**
|
|
30677
|
+
* Verify payment with facilitator
|
|
30678
|
+
*/
|
|
30679
|
+
async verifyWithFacilitator(payment, config) {
|
|
30767
30680
|
try {
|
|
30768
|
-
const
|
|
30769
|
-
|
|
30770
|
-
|
|
30771
|
-
|
|
30772
|
-
|
|
30681
|
+
const chain2 = getChain(this.manifest.provider.chain);
|
|
30682
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
30683
|
+
const requirements = {
|
|
30684
|
+
scheme: "exact",
|
|
30685
|
+
network: `eip155:${chain2.chainId}`,
|
|
30686
|
+
maxAmountRequired: amountInUnits,
|
|
30687
|
+
resource: this.manifest.provider.wallet
|
|
30688
|
+
};
|
|
30689
|
+
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
30690
|
+
method: "POST",
|
|
30691
|
+
headers: { "Content-Type": "application/json" },
|
|
30692
|
+
body: JSON.stringify({
|
|
30693
|
+
paymentPayload: payment,
|
|
30694
|
+
paymentRequirements: requirements
|
|
30695
|
+
})
|
|
30773
30696
|
});
|
|
30774
|
-
|
|
30775
|
-
|
|
30776
|
-
return
|
|
30777
|
-
error: "Payment verification failed",
|
|
30778
|
-
reason: verification.error
|
|
30779
|
-
});
|
|
30697
|
+
const result = await response.json();
|
|
30698
|
+
if (!response.ok || !result.isValid) {
|
|
30699
|
+
return { valid: false, error: result.invalidReason || "Verification failed" };
|
|
30780
30700
|
}
|
|
30781
|
-
|
|
30782
|
-
charge.txHash = txHash;
|
|
30783
|
-
charge.paidAt = Date.now();
|
|
30784
|
-
const skill = this.skills.get(charge.service);
|
|
30785
|
-
console.log(`[MoltsPay] Executing skill: ${charge.service}`);
|
|
30786
|
-
const result = await skill.handler(charge.params);
|
|
30787
|
-
charge.status = "completed";
|
|
30788
|
-
charge.result = result;
|
|
30789
|
-
charge.completedAt = Date.now();
|
|
30790
|
-
this.sendJson(res, 200, {
|
|
30791
|
-
status: "completed",
|
|
30792
|
-
chargeId,
|
|
30793
|
-
txHash,
|
|
30794
|
-
result
|
|
30795
|
-
});
|
|
30701
|
+
return { valid: true };
|
|
30796
30702
|
} catch (err) {
|
|
30797
|
-
|
|
30798
|
-
charge.status = "failed";
|
|
30799
|
-
this.sendJson(res, 500, {
|
|
30800
|
-
error: "Skill execution failed",
|
|
30801
|
-
message: err.message
|
|
30802
|
-
});
|
|
30703
|
+
return { valid: false, error: `Facilitator error: ${err.message}` };
|
|
30803
30704
|
}
|
|
30804
30705
|
}
|
|
30805
30706
|
/**
|
|
30806
|
-
*
|
|
30707
|
+
* Settle payment with facilitator (execute on-chain transfer)
|
|
30807
30708
|
*/
|
|
30808
|
-
|
|
30809
|
-
const
|
|
30810
|
-
|
|
30811
|
-
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
|
|
30815
|
-
|
|
30816
|
-
|
|
30817
|
-
|
|
30818
|
-
|
|
30819
|
-
|
|
30820
|
-
|
|
30821
|
-
|
|
30822
|
-
|
|
30709
|
+
async settleWithFacilitator(payment, config) {
|
|
30710
|
+
const chain2 = getChain(this.manifest.provider.chain);
|
|
30711
|
+
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
30712
|
+
const requirements = {
|
|
30713
|
+
scheme: "exact",
|
|
30714
|
+
network: `eip155:${chain2.chainId}`,
|
|
30715
|
+
maxAmountRequired: amountInUnits,
|
|
30716
|
+
resource: this.manifest.provider.wallet
|
|
30717
|
+
};
|
|
30718
|
+
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
30719
|
+
method: "POST",
|
|
30720
|
+
headers: { "Content-Type": "application/json" },
|
|
30721
|
+
body: JSON.stringify({
|
|
30722
|
+
paymentPayload: payment,
|
|
30723
|
+
paymentRequirements: requirements
|
|
30724
|
+
})
|
|
30823
30725
|
});
|
|
30726
|
+
const result = await response.json();
|
|
30727
|
+
if (!response.ok) {
|
|
30728
|
+
throw new Error(result.error || "Settlement failed");
|
|
30729
|
+
}
|
|
30730
|
+
return {
|
|
30731
|
+
transaction: result.transaction,
|
|
30732
|
+
status: result.status || "settled"
|
|
30733
|
+
};
|
|
30824
30734
|
}
|
|
30825
30735
|
async readBody(req) {
|
|
30826
30736
|
return new Promise((resolve, reject) => {
|
|
@@ -30836,8 +30746,12 @@ var MoltsPayServer = class {
|
|
|
30836
30746
|
req.on("error", reject);
|
|
30837
30747
|
});
|
|
30838
30748
|
}
|
|
30839
|
-
sendJson(res, status, data) {
|
|
30840
|
-
|
|
30749
|
+
sendJson(res, status, data, extraHeaders) {
|
|
30750
|
+
const headers = { "Content-Type": "application/json" };
|
|
30751
|
+
if (extraHeaders) {
|
|
30752
|
+
Object.assign(headers, extraHeaders);
|
|
30753
|
+
}
|
|
30754
|
+
res.writeHead(status, headers);
|
|
30841
30755
|
res.end(JSON.stringify(data, null, 2));
|
|
30842
30756
|
}
|
|
30843
30757
|
};
|
|
@@ -30847,12 +30761,15 @@ init_cjs_shims();
|
|
|
30847
30761
|
var import_fs2 = require("fs");
|
|
30848
30762
|
var import_os = require("os");
|
|
30849
30763
|
var import_path = require("path");
|
|
30850
|
-
var
|
|
30764
|
+
var import_ethers = require("ethers");
|
|
30851
30765
|
|
|
30852
30766
|
// src/client/types.ts
|
|
30853
30767
|
init_cjs_shims();
|
|
30854
30768
|
|
|
30855
30769
|
// src/client/index.ts
|
|
30770
|
+
var X402_VERSION2 = 2;
|
|
30771
|
+
var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
|
|
30772
|
+
var PAYMENT_HEADER2 = "x-payment";
|
|
30856
30773
|
var DEFAULT_CONFIG = {
|
|
30857
30774
|
chain: "base",
|
|
30858
30775
|
limits: {
|
|
@@ -30872,7 +30789,7 @@ var MoltsPayClient = class {
|
|
|
30872
30789
|
this.config = this.loadConfig();
|
|
30873
30790
|
this.walletData = this.loadWallet();
|
|
30874
30791
|
if (this.walletData) {
|
|
30875
|
-
this.wallet = new
|
|
30792
|
+
this.wallet = new import_ethers.Wallet(this.walletData.privateKey);
|
|
30876
30793
|
}
|
|
30877
30794
|
}
|
|
30878
30795
|
/**
|
|
@@ -30916,43 +30833,112 @@ var MoltsPayClient = class {
|
|
|
30916
30833
|
return res.json();
|
|
30917
30834
|
}
|
|
30918
30835
|
/**
|
|
30919
|
-
* Pay for a service and get the result
|
|
30836
|
+
* Pay for a service and get the result (x402 protocol)
|
|
30837
|
+
*
|
|
30838
|
+
* This is GASLESS for the client - server pays gas to claim payment.
|
|
30839
|
+
* This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.
|
|
30920
30840
|
*/
|
|
30921
30841
|
async pay(serverUrl, service, params) {
|
|
30922
|
-
if (!this.wallet) {
|
|
30842
|
+
if (!this.wallet || !this.walletData) {
|
|
30923
30843
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
30924
30844
|
}
|
|
30925
|
-
|
|
30845
|
+
console.log(`[MoltsPay] Requesting service: ${service}`);
|
|
30846
|
+
const initialRes = await fetch(`${serverUrl}/execute`, {
|
|
30926
30847
|
method: "POST",
|
|
30927
30848
|
headers: { "Content-Type": "application/json" },
|
|
30928
30849
|
body: JSON.stringify({ service, params })
|
|
30929
30850
|
});
|
|
30930
|
-
if (
|
|
30931
|
-
const
|
|
30932
|
-
|
|
30851
|
+
if (initialRes.status !== 402) {
|
|
30852
|
+
const data = await initialRes.json();
|
|
30853
|
+
if (initialRes.ok && data.result) {
|
|
30854
|
+
return data.result;
|
|
30855
|
+
}
|
|
30856
|
+
throw new Error(data.error || "Unexpected response");
|
|
30857
|
+
}
|
|
30858
|
+
const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER2);
|
|
30859
|
+
if (!paymentRequiredHeader) {
|
|
30860
|
+
throw new Error("Missing x-payment-required header");
|
|
30861
|
+
}
|
|
30862
|
+
let requirements;
|
|
30863
|
+
try {
|
|
30864
|
+
const decoded = Buffer.from(paymentRequiredHeader, "base64").toString("utf-8");
|
|
30865
|
+
requirements = JSON.parse(decoded);
|
|
30866
|
+
if (!Array.isArray(requirements)) {
|
|
30867
|
+
requirements = [requirements];
|
|
30868
|
+
}
|
|
30869
|
+
} catch {
|
|
30870
|
+
throw new Error("Invalid x-payment-required header");
|
|
30933
30871
|
}
|
|
30934
|
-
const
|
|
30935
|
-
const
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
const
|
|
30872
|
+
const chain2 = getChain(this.config.chain);
|
|
30873
|
+
const network = `eip155:${chain2.chainId}`;
|
|
30874
|
+
const req = requirements.find((r) => r.scheme === "exact" && r.network === network);
|
|
30875
|
+
if (!req) {
|
|
30876
|
+
throw new Error(`No matching payment option for ${network}`);
|
|
30877
|
+
}
|
|
30878
|
+
const amount = Number(req.maxAmountRequired) / 1e6;
|
|
30879
|
+
this.checkLimits(amount);
|
|
30880
|
+
console.log(`[MoltsPay] Signing payment: $${amount} USDC (gasless)`);
|
|
30881
|
+
const authorization = await this.signEIP3009(req.resource, amount, chain2);
|
|
30882
|
+
const payload = {
|
|
30883
|
+
x402Version: X402_VERSION2,
|
|
30884
|
+
scheme: "exact",
|
|
30885
|
+
network,
|
|
30886
|
+
payload: authorization
|
|
30887
|
+
};
|
|
30888
|
+
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
30889
|
+
console.log(`[MoltsPay] Sending request with payment...`);
|
|
30890
|
+
const paidRes = await fetch(`${serverUrl}/execute`, {
|
|
30941
30891
|
method: "POST",
|
|
30942
|
-
headers: {
|
|
30943
|
-
|
|
30944
|
-
|
|
30945
|
-
|
|
30946
|
-
})
|
|
30892
|
+
headers: {
|
|
30893
|
+
"Content-Type": "application/json",
|
|
30894
|
+
[PAYMENT_HEADER2]: paymentHeader
|
|
30895
|
+
},
|
|
30896
|
+
body: JSON.stringify({ service, params })
|
|
30947
30897
|
});
|
|
30948
|
-
|
|
30949
|
-
|
|
30950
|
-
throw new Error(
|
|
30898
|
+
const result = await paidRes.json();
|
|
30899
|
+
if (!paidRes.ok) {
|
|
30900
|
+
throw new Error(result.error || "Service execution failed");
|
|
30951
30901
|
}
|
|
30952
|
-
|
|
30953
|
-
|
|
30902
|
+
this.recordSpending(amount);
|
|
30903
|
+
console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || "claimed"}`);
|
|
30954
30904
|
return result.result;
|
|
30955
30905
|
}
|
|
30906
|
+
/**
|
|
30907
|
+
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
30908
|
+
* This only signs - no on-chain transaction, no gas needed.
|
|
30909
|
+
*/
|
|
30910
|
+
async signEIP3009(to, amount, chain2) {
|
|
30911
|
+
const validAfter = 0;
|
|
30912
|
+
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
30913
|
+
const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
|
|
30914
|
+
const value = BigInt(Math.floor(amount * 1e6)).toString();
|
|
30915
|
+
const authorization = {
|
|
30916
|
+
from: this.wallet.address,
|
|
30917
|
+
to,
|
|
30918
|
+
value,
|
|
30919
|
+
validAfter: validAfter.toString(),
|
|
30920
|
+
validBefore: validBefore.toString(),
|
|
30921
|
+
nonce
|
|
30922
|
+
};
|
|
30923
|
+
const domain = {
|
|
30924
|
+
name: "USD Coin",
|
|
30925
|
+
version: "2",
|
|
30926
|
+
chainId: chain2.chainId,
|
|
30927
|
+
verifyingContract: chain2.usdc
|
|
30928
|
+
};
|
|
30929
|
+
const types = {
|
|
30930
|
+
TransferWithAuthorization: [
|
|
30931
|
+
{ name: "from", type: "address" },
|
|
30932
|
+
{ name: "to", type: "address" },
|
|
30933
|
+
{ name: "value", type: "uint256" },
|
|
30934
|
+
{ name: "validAfter", type: "uint256" },
|
|
30935
|
+
{ name: "validBefore", type: "uint256" },
|
|
30936
|
+
{ name: "nonce", type: "bytes32" }
|
|
30937
|
+
]
|
|
30938
|
+
};
|
|
30939
|
+
const signature = await this.wallet.signTypedData(domain, types, authorization);
|
|
30940
|
+
return { authorization, signature };
|
|
30941
|
+
}
|
|
30956
30942
|
/**
|
|
30957
30943
|
* Check spending limits
|
|
30958
30944
|
*/
|
|
@@ -30979,36 +30965,6 @@ var MoltsPayClient = class {
|
|
|
30979
30965
|
recordSpending(amount) {
|
|
30980
30966
|
this.todaySpending += amount;
|
|
30981
30967
|
}
|
|
30982
|
-
/**
|
|
30983
|
-
* Execute payment on-chain
|
|
30984
|
-
*/
|
|
30985
|
-
async executePayment(payment) {
|
|
30986
|
-
let chain2;
|
|
30987
|
-
try {
|
|
30988
|
-
chain2 = getChain(payment.chain);
|
|
30989
|
-
} catch {
|
|
30990
|
-
throw new Error(`Unknown chain: ${payment.chain}`);
|
|
30991
|
-
}
|
|
30992
|
-
const { ethers: ethers4 } = await import("ethers");
|
|
30993
|
-
const provider = new ethers4.JsonRpcProvider(chain2.rpc);
|
|
30994
|
-
const signer = new ethers4.Wallet(this.walletData.privateKey, provider);
|
|
30995
|
-
const usdcAddress = chain2.usdc;
|
|
30996
|
-
const usdcAbi = [
|
|
30997
|
-
"function transfer(address to, uint256 amount) returns (bool)",
|
|
30998
|
-
"function balanceOf(address account) view returns (uint256)"
|
|
30999
|
-
];
|
|
31000
|
-
const usdc = new ethers4.Contract(usdcAddress, usdcAbi, signer);
|
|
31001
|
-
const amountInUnits = ethers4.parseUnits(payment.amount.toString(), 6);
|
|
31002
|
-
const balance = await usdc.balanceOf(this.wallet.address);
|
|
31003
|
-
if (balance < amountInUnits) {
|
|
31004
|
-
throw new Error(
|
|
31005
|
-
`Insufficient USDC balance: ${ethers4.formatUnits(balance, 6)} < ${payment.amount}`
|
|
31006
|
-
);
|
|
31007
|
-
}
|
|
31008
|
-
const tx = await usdc.transfer(payment.wallet, amountInUnits);
|
|
31009
|
-
const receipt = await tx.wait();
|
|
31010
|
-
return receipt.hash;
|
|
31011
|
-
}
|
|
31012
30968
|
// --- Config & Wallet Management ---
|
|
31013
30969
|
loadConfig() {
|
|
31014
30970
|
const configPath = (0, import_path.join)(this.configDir, "config.json");
|
|
@@ -31036,7 +30992,7 @@ var MoltsPayClient = class {
|
|
|
31036
30992
|
*/
|
|
31037
30993
|
static init(configDir, options) {
|
|
31038
30994
|
(0, import_fs2.mkdirSync)(configDir, { recursive: true });
|
|
31039
|
-
const wallet =
|
|
30995
|
+
const wallet = import_ethers.Wallet.createRandom();
|
|
31040
30996
|
const walletData = {
|
|
31041
30997
|
address: wallet.address,
|
|
31042
30998
|
privateKey: wallet.privateKey,
|
|
@@ -31068,15 +31024,14 @@ var MoltsPayClient = class {
|
|
|
31068
31024
|
} catch {
|
|
31069
31025
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
31070
31026
|
}
|
|
31071
|
-
const
|
|
31072
|
-
const provider = new ethers4.JsonRpcProvider(chain2.rpc);
|
|
31027
|
+
const provider = new import_ethers.ethers.JsonRpcProvider(chain2.rpc);
|
|
31073
31028
|
const nativeBalance = await provider.getBalance(this.wallet.address);
|
|
31074
31029
|
const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
31075
|
-
const usdc = new
|
|
31030
|
+
const usdc = new import_ethers.ethers.Contract(chain2.usdc, usdcAbi, provider);
|
|
31076
31031
|
const usdcBalance = await usdc.balanceOf(this.wallet.address);
|
|
31077
31032
|
return {
|
|
31078
|
-
usdc: parseFloat(
|
|
31079
|
-
native: parseFloat(
|
|
31033
|
+
usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, 6)),
|
|
31034
|
+
native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
|
|
31080
31035
|
};
|
|
31081
31036
|
}
|
|
31082
31037
|
};
|
|
@@ -31086,11 +31041,11 @@ init_cjs_shims();
|
|
|
31086
31041
|
|
|
31087
31042
|
// src/wallet/Wallet.ts
|
|
31088
31043
|
init_cjs_shims();
|
|
31089
|
-
var
|
|
31044
|
+
var import_ethers2 = require("ethers");
|
|
31090
31045
|
|
|
31091
31046
|
// src/wallet/createWallet.ts
|
|
31092
31047
|
init_cjs_shims();
|
|
31093
|
-
var
|
|
31048
|
+
var import_ethers3 = require("ethers");
|
|
31094
31049
|
var import_fs3 = require("fs");
|
|
31095
31050
|
var import_path2 = require("path");
|
|
31096
31051
|
var import_crypto = require("crypto");
|
|
@@ -31135,7 +31090,7 @@ function createWallet(options = {}) {
|
|
|
31135
31090
|
}
|
|
31136
31091
|
}
|
|
31137
31092
|
try {
|
|
31138
|
-
const wallet =
|
|
31093
|
+
const wallet = import_ethers3.ethers.Wallet.createRandom();
|
|
31139
31094
|
const walletData = {
|
|
31140
31095
|
address: wallet.address,
|
|
31141
31096
|
label: options.label,
|
|
@@ -31206,6 +31161,143 @@ function walletExists(storagePath) {
|
|
|
31206
31161
|
return (0, import_fs3.existsSync)(path2);
|
|
31207
31162
|
}
|
|
31208
31163
|
|
|
31164
|
+
// src/verify/index.ts
|
|
31165
|
+
init_cjs_shims();
|
|
31166
|
+
var import_ethers4 = require("ethers");
|
|
31167
|
+
var TRANSFER_EVENT_TOPIC = import_ethers4.ethers.id("Transfer(address,address,uint256)");
|
|
31168
|
+
async function verifyPayment(params) {
|
|
31169
|
+
const { txHash, expectedAmount, expectedTo } = params;
|
|
31170
|
+
let chain2;
|
|
31171
|
+
try {
|
|
31172
|
+
if (typeof params.chain === "number") {
|
|
31173
|
+
chain2 = getChainById(params.chain);
|
|
31174
|
+
} else {
|
|
31175
|
+
chain2 = getChain(params.chain || "base");
|
|
31176
|
+
}
|
|
31177
|
+
if (!chain2) {
|
|
31178
|
+
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
31179
|
+
}
|
|
31180
|
+
} catch (e) {
|
|
31181
|
+
return { verified: false, error: `Unsupported chain: ${params.chain}` };
|
|
31182
|
+
}
|
|
31183
|
+
try {
|
|
31184
|
+
const provider = new import_ethers4.ethers.JsonRpcProvider(chain2.rpc);
|
|
31185
|
+
const receipt = await provider.getTransactionReceipt(txHash);
|
|
31186
|
+
if (!receipt) {
|
|
31187
|
+
return { verified: false, error: "Transaction not found or not confirmed" };
|
|
31188
|
+
}
|
|
31189
|
+
if (receipt.status !== 1) {
|
|
31190
|
+
return { verified: false, error: "Transaction failed" };
|
|
31191
|
+
}
|
|
31192
|
+
const usdcAddress = chain2.usdc?.toLowerCase();
|
|
31193
|
+
if (!usdcAddress) {
|
|
31194
|
+
return { verified: false, error: `Chain ${chain2.name} USDC address not configured` };
|
|
31195
|
+
}
|
|
31196
|
+
for (const log of receipt.logs) {
|
|
31197
|
+
if (log.address.toLowerCase() !== usdcAddress) {
|
|
31198
|
+
continue;
|
|
31199
|
+
}
|
|
31200
|
+
if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
|
|
31201
|
+
continue;
|
|
31202
|
+
}
|
|
31203
|
+
const from = "0x" + log.topics[1].slice(-40);
|
|
31204
|
+
const to = "0x" + log.topics[2].slice(-40);
|
|
31205
|
+
const amountRaw = BigInt(log.data);
|
|
31206
|
+
const amount = Number(amountRaw) / 1e6;
|
|
31207
|
+
if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
|
|
31208
|
+
continue;
|
|
31209
|
+
}
|
|
31210
|
+
if (amount < expectedAmount) {
|
|
31211
|
+
return {
|
|
31212
|
+
verified: false,
|
|
31213
|
+
error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,
|
|
31214
|
+
amount,
|
|
31215
|
+
from,
|
|
31216
|
+
to,
|
|
31217
|
+
txHash,
|
|
31218
|
+
blockNumber: receipt.blockNumber
|
|
31219
|
+
};
|
|
31220
|
+
}
|
|
31221
|
+
return {
|
|
31222
|
+
verified: true,
|
|
31223
|
+
amount,
|
|
31224
|
+
from,
|
|
31225
|
+
to,
|
|
31226
|
+
txHash,
|
|
31227
|
+
blockNumber: receipt.blockNumber
|
|
31228
|
+
};
|
|
31229
|
+
}
|
|
31230
|
+
return { verified: false, error: "No USDC transfer found" };
|
|
31231
|
+
} catch (e) {
|
|
31232
|
+
return { verified: false, error: e.message || String(e) };
|
|
31233
|
+
}
|
|
31234
|
+
}
|
|
31235
|
+
async function getTransactionStatus(txHash, chain2 = "base") {
|
|
31236
|
+
let chainConfig;
|
|
31237
|
+
try {
|
|
31238
|
+
chainConfig = typeof chain2 === "number" ? getChainById(chain2) : getChain(chain2);
|
|
31239
|
+
if (!chainConfig) return { status: "not_found" };
|
|
31240
|
+
} catch {
|
|
31241
|
+
return { status: "not_found" };
|
|
31242
|
+
}
|
|
31243
|
+
try {
|
|
31244
|
+
const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
31245
|
+
const receipt = await provider.getTransactionReceipt(txHash);
|
|
31246
|
+
if (!receipt) {
|
|
31247
|
+
const tx = await provider.getTransaction(txHash);
|
|
31248
|
+
if (tx) {
|
|
31249
|
+
return { status: "pending" };
|
|
31250
|
+
}
|
|
31251
|
+
return { status: "not_found" };
|
|
31252
|
+
}
|
|
31253
|
+
const currentBlock = await provider.getBlockNumber();
|
|
31254
|
+
const confirmations = currentBlock - receipt.blockNumber;
|
|
31255
|
+
if (receipt.status === 1) {
|
|
31256
|
+
return {
|
|
31257
|
+
status: "confirmed",
|
|
31258
|
+
blockNumber: receipt.blockNumber,
|
|
31259
|
+
confirmations
|
|
31260
|
+
};
|
|
31261
|
+
} else {
|
|
31262
|
+
return {
|
|
31263
|
+
status: "failed",
|
|
31264
|
+
blockNumber: receipt.blockNumber
|
|
31265
|
+
};
|
|
31266
|
+
}
|
|
31267
|
+
} catch {
|
|
31268
|
+
return { status: "not_found" };
|
|
31269
|
+
}
|
|
31270
|
+
}
|
|
31271
|
+
async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, timeoutMs = 6e4) {
|
|
31272
|
+
let chainConfig;
|
|
31273
|
+
try {
|
|
31274
|
+
chainConfig = typeof chain2 === "number" ? getChainById(chain2) : getChain(chain2);
|
|
31275
|
+
if (!chainConfig) {
|
|
31276
|
+
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
|
|
31277
|
+
}
|
|
31278
|
+
} catch (e) {
|
|
31279
|
+
return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
|
|
31280
|
+
}
|
|
31281
|
+
const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
|
|
31282
|
+
try {
|
|
31283
|
+
const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
|
|
31284
|
+
if (!receipt) {
|
|
31285
|
+
return { verified: false, confirmed: false, error: "Timeout waiting" };
|
|
31286
|
+
}
|
|
31287
|
+
if (receipt.status !== 1) {
|
|
31288
|
+
return { verified: false, confirmed: true, error: "Transaction failed" };
|
|
31289
|
+
}
|
|
31290
|
+
return {
|
|
31291
|
+
verified: true,
|
|
31292
|
+
confirmed: true,
|
|
31293
|
+
txHash,
|
|
31294
|
+
blockNumber: receipt.blockNumber
|
|
31295
|
+
};
|
|
31296
|
+
} catch (e) {
|
|
31297
|
+
return { verified: false, confirmed: false, error: e.message || String(e) };
|
|
31298
|
+
}
|
|
31299
|
+
}
|
|
31300
|
+
|
|
31209
31301
|
// src/cdp/index.ts
|
|
31210
31302
|
init_cjs_shims();
|
|
31211
31303
|
var fs = __toESM(require("fs"));
|
|
@@ -31327,9 +31419,9 @@ var CDPWallet = class {
|
|
|
31327
31419
|
* Get USDC balance
|
|
31328
31420
|
*/
|
|
31329
31421
|
async getBalance() {
|
|
31330
|
-
const { ethers:
|
|
31331
|
-
const provider = new
|
|
31332
|
-
const usdcContract = new
|
|
31422
|
+
const { ethers: ethers5 } = await import("ethers");
|
|
31423
|
+
const provider = new ethers5.JsonRpcProvider(this.chainConfig.rpc);
|
|
31424
|
+
const usdcContract = new ethers5.Contract(
|
|
31333
31425
|
this.chainConfig.usdc,
|
|
31334
31426
|
["function balanceOf(address) view returns (uint256)"],
|
|
31335
31427
|
provider
|
|
@@ -31340,7 +31432,7 @@ var CDPWallet = class {
|
|
|
31340
31432
|
]);
|
|
31341
31433
|
return {
|
|
31342
31434
|
usdc: (Number(usdcBalance) / 1e6).toFixed(2),
|
|
31343
|
-
eth:
|
|
31435
|
+
eth: ethers5.formatEther(ethBalance)
|
|
31344
31436
|
};
|
|
31345
31437
|
}
|
|
31346
31438
|
/**
|
|
@@ -31358,7 +31450,7 @@ var CDPWallet = class {
|
|
|
31358
31450
|
}
|
|
31359
31451
|
try {
|
|
31360
31452
|
const { CdpClient } = await import("@coinbase/cdp-sdk");
|
|
31361
|
-
const { ethers:
|
|
31453
|
+
const { ethers: ethers5 } = await import("ethers");
|
|
31362
31454
|
const cdp = new CdpClient({
|
|
31363
31455
|
apiKeyId: creds.apiKeyId,
|
|
31364
31456
|
apiKeySecret: creds.apiKeySecret,
|
|
@@ -31366,7 +31458,7 @@ var CDPWallet = class {
|
|
|
31366
31458
|
});
|
|
31367
31459
|
const account = await cdp.evm.getAccount({ address: this.address });
|
|
31368
31460
|
const amountWei = BigInt(Math.floor(params.amount * 1e6));
|
|
31369
|
-
const iface = new
|
|
31461
|
+
const iface = new ethers5.Interface([
|
|
31370
31462
|
"function transfer(address to, uint256 amount) returns (bool)"
|
|
31371
31463
|
]);
|
|
31372
31464
|
const callData = iface.encodeFunctionData("transfer", [params.to, amountWei]);
|