moltspay 0.9.3 → 0.9.6
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/.env.example +7 -0
- package/dist/cdp/index.d.mts +1 -1
- package/dist/cdp/index.d.ts +1 -1
- package/dist/cdp/index.js +63 -0
- package/dist/cdp/index.js.map +1 -1
- package/dist/cdp/index.mjs +63 -0
- package/dist/cdp/index.mjs.map +1 -1
- package/dist/chains/index.d.mts +9 -5
- package/dist/chains/index.d.ts +9 -5
- package/dist/chains/index.js +85 -0
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/index.mjs +83 -0
- package/dist/chains/index.mjs.map +1 -1
- package/dist/cli/index.js +234 -41
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +234 -41
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +18 -3
- package/dist/client/index.d.ts +18 -3
- package/dist/client/index.js +102 -15
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +102 -15
- package/dist/client/index.mjs.map +1 -1
- package/dist/{index-Dg8n6wdW.d.mts → index-B3v8IWjM.d.mts} +11 -1
- package/dist/{index-Dg8n6wdW.d.ts → index-B3v8IWjM.d.ts} +11 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +248 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +248 -45
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +23 -1
- package/dist/server/index.d.ts +23 -1
- package/dist/server/index.js +126 -22
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +126 -22
- package/dist/server/index.mjs.map +1 -1
- package/dist/verify/index.d.mts +7 -0
- package/dist/verify/index.d.ts +7 -0
- package/dist/verify/index.js +83 -8
- package/dist/verify/index.js.map +1 -1
- package/dist/verify/index.mjs +83 -8
- package/dist/verify/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +16 -8
- package/dist/wallet/index.d.ts +16 -8
- package/dist/wallet/index.js +114 -18
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +114 -18
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schemas/moltspay.services.schema.json +13 -3
package/dist/index.js
CHANGED
|
@@ -30844,16 +30844,24 @@ var X402_VERSION2 = 2;
|
|
|
30844
30844
|
var PAYMENT_REQUIRED_HEADER = "x-payment-required";
|
|
30845
30845
|
var PAYMENT_HEADER = "x-payment";
|
|
30846
30846
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
30847
|
-
var
|
|
30848
|
-
"eip155:8453":
|
|
30849
|
-
|
|
30850
|
-
|
|
30851
|
-
|
|
30847
|
+
var TOKEN_ADDRESSES = {
|
|
30848
|
+
"eip155:8453": {
|
|
30849
|
+
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
30850
|
+
USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
|
|
30851
|
+
},
|
|
30852
|
+
"eip155:84532": {
|
|
30853
|
+
USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
30854
|
+
USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
30855
|
+
// Same as USDC on testnet
|
|
30856
|
+
}
|
|
30852
30857
|
};
|
|
30853
|
-
var
|
|
30854
|
-
name: "USD Coin",
|
|
30855
|
-
version: "2"
|
|
30858
|
+
var TOKEN_DOMAINS = {
|
|
30859
|
+
USDC: { name: "USD Coin", version: "2" },
|
|
30860
|
+
USDT: { name: "Tether USD", version: "2" }
|
|
30856
30861
|
};
|
|
30862
|
+
function getAcceptedCurrencies(config) {
|
|
30863
|
+
return config.acceptedCurrencies ?? [config.currency];
|
|
30864
|
+
}
|
|
30857
30865
|
function loadEnvFile2() {
|
|
30858
30866
|
const envPaths = [
|
|
30859
30867
|
path2.join(process.cwd(), ".env"),
|
|
@@ -30965,6 +30973,9 @@ var MoltsPayServer = class {
|
|
|
30965
30973
|
if (url.pathname === "/services" && req.method === "GET") {
|
|
30966
30974
|
return this.handleGetServices(res);
|
|
30967
30975
|
}
|
|
30976
|
+
if (url.pathname === "/.well-known/agent-services.json" && req.method === "GET") {
|
|
30977
|
+
return this.handleAgentServicesDiscovery(res);
|
|
30978
|
+
}
|
|
30968
30979
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
30969
30980
|
return await this.handleHealthCheck(res);
|
|
30970
30981
|
}
|
|
@@ -30988,6 +30999,44 @@ var MoltsPayServer = class {
|
|
|
30988
30999
|
this.sendJson(res, 500, { error: err.message || "Internal error" });
|
|
30989
31000
|
}
|
|
30990
31001
|
}
|
|
31002
|
+
/**
|
|
31003
|
+
* GET /.well-known/agent-services.json - Standard discovery endpoint
|
|
31004
|
+
*/
|
|
31005
|
+
handleAgentServicesDiscovery(res) {
|
|
31006
|
+
const services = this.manifest.services.map((s) => ({
|
|
31007
|
+
id: s.id,
|
|
31008
|
+
name: s.name,
|
|
31009
|
+
description: s.description,
|
|
31010
|
+
price: s.price,
|
|
31011
|
+
currency: s.currency,
|
|
31012
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
31013
|
+
input: s.input,
|
|
31014
|
+
output: s.output,
|
|
31015
|
+
available: this.skills.has(s.id)
|
|
31016
|
+
}));
|
|
31017
|
+
this.sendJson(res, 200, {
|
|
31018
|
+
version: "1.0",
|
|
31019
|
+
provider: {
|
|
31020
|
+
name: this.manifest.provider.name,
|
|
31021
|
+
description: this.manifest.provider.description,
|
|
31022
|
+
wallet: this.manifest.provider.wallet,
|
|
31023
|
+
chain: this.manifest.provider.chain || "base"
|
|
31024
|
+
},
|
|
31025
|
+
services,
|
|
31026
|
+
endpoints: {
|
|
31027
|
+
services: "/services",
|
|
31028
|
+
execute: "/execute",
|
|
31029
|
+
health: "/health"
|
|
31030
|
+
},
|
|
31031
|
+
payment: {
|
|
31032
|
+
protocol: "x402",
|
|
31033
|
+
version: X402_VERSION2,
|
|
31034
|
+
network: this.networkId,
|
|
31035
|
+
schemes: ["exact"],
|
|
31036
|
+
mainnet: this.useMainnet
|
|
31037
|
+
}
|
|
31038
|
+
});
|
|
31039
|
+
}
|
|
30991
31040
|
/**
|
|
30992
31041
|
* GET /services - List available services
|
|
30993
31042
|
*/
|
|
@@ -30998,6 +31047,7 @@ var MoltsPayServer = class {
|
|
|
30998
31047
|
description: s.description,
|
|
30999
31048
|
price: s.price,
|
|
31000
31049
|
currency: s.currency,
|
|
31050
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
31001
31051
|
input: s.input,
|
|
31002
31052
|
output: s.output,
|
|
31003
31053
|
available: this.skills.has(s.id)
|
|
@@ -31064,7 +31114,14 @@ var MoltsPayServer = class {
|
|
|
31064
31114
|
if (!validation.valid) {
|
|
31065
31115
|
return this.sendJson(res, 402, { error: validation.error });
|
|
31066
31116
|
}
|
|
31067
|
-
const
|
|
31117
|
+
const paymentToken = this.detectPaymentToken(payment);
|
|
31118
|
+
if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
|
|
31119
|
+
const accepted = getAcceptedCurrencies(skill.config);
|
|
31120
|
+
return this.sendJson(res, 402, {
|
|
31121
|
+
error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
|
|
31122
|
+
});
|
|
31123
|
+
}
|
|
31124
|
+
const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
|
|
31068
31125
|
console.log(`[MoltsPay] Verifying payment...`);
|
|
31069
31126
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
31070
31127
|
if (!verifyResult.valid) {
|
|
@@ -31074,10 +31131,16 @@ var MoltsPayServer = class {
|
|
|
31074
31131
|
});
|
|
31075
31132
|
}
|
|
31076
31133
|
console.log(`[MoltsPay] Verified by ${verifyResult.facilitator}`);
|
|
31077
|
-
|
|
31134
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
31135
|
+
console.log(`[MoltsPay] Executing skill: ${service} (timeout: ${timeoutSeconds}s)`);
|
|
31078
31136
|
let result;
|
|
31079
31137
|
try {
|
|
31080
|
-
result = await
|
|
31138
|
+
result = await Promise.race([
|
|
31139
|
+
skill.handler(params || {}),
|
|
31140
|
+
new Promise(
|
|
31141
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
31142
|
+
)
|
|
31143
|
+
]);
|
|
31081
31144
|
} catch (err) {
|
|
31082
31145
|
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
31083
31146
|
return this.sendJson(res, 500, {
|
|
@@ -31113,12 +31176,15 @@ var MoltsPayServer = class {
|
|
|
31113
31176
|
}
|
|
31114
31177
|
/**
|
|
31115
31178
|
* Return 402 with x402 payment requirements (v2 format)
|
|
31179
|
+
* Includes requirements for all accepted currencies
|
|
31116
31180
|
*/
|
|
31117
31181
|
sendPaymentRequired(config, res) {
|
|
31118
|
-
const
|
|
31182
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31183
|
+
const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
|
|
31119
31184
|
const paymentRequired = {
|
|
31120
31185
|
x402Version: X402_VERSION2,
|
|
31121
|
-
accepts
|
|
31186
|
+
accepts,
|
|
31187
|
+
acceptedCurrencies: acceptedTokens,
|
|
31122
31188
|
resource: {
|
|
31123
31189
|
url: `/execute?service=${config.id}`,
|
|
31124
31190
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
@@ -31133,6 +31199,7 @@ var MoltsPayServer = class {
|
|
|
31133
31199
|
res.end(JSON.stringify({
|
|
31134
31200
|
error: "Payment required",
|
|
31135
31201
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
31202
|
+
acceptedCurrencies: acceptedTokens,
|
|
31136
31203
|
x402: paymentRequired
|
|
31137
31204
|
}, null, 2));
|
|
31138
31205
|
}
|
|
@@ -31155,20 +31222,47 @@ var MoltsPayServer = class {
|
|
|
31155
31222
|
}
|
|
31156
31223
|
/**
|
|
31157
31224
|
* Build payment requirements for facilitator
|
|
31225
|
+
* Returns requirements for the primary currency (USDC by default)
|
|
31226
|
+
* Server accepts any of the acceptedCurrencies
|
|
31158
31227
|
*/
|
|
31159
|
-
buildPaymentRequirements(config) {
|
|
31228
|
+
buildPaymentRequirements(config, token) {
|
|
31160
31229
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
31161
|
-
const
|
|
31230
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31231
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
31232
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31233
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
31234
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
31162
31235
|
return {
|
|
31163
31236
|
scheme: "exact",
|
|
31164
31237
|
network: this.networkId,
|
|
31165
|
-
asset:
|
|
31238
|
+
asset: tokenAddress,
|
|
31166
31239
|
amount: amountInUnits,
|
|
31167
31240
|
payTo: this.manifest.provider.wallet,
|
|
31168
31241
|
maxTimeoutSeconds: 300,
|
|
31169
|
-
extra:
|
|
31242
|
+
extra: tokenDomain
|
|
31170
31243
|
};
|
|
31171
31244
|
}
|
|
31245
|
+
/**
|
|
31246
|
+
* Detect which token is being used in the payment
|
|
31247
|
+
*/
|
|
31248
|
+
detectPaymentToken(payment) {
|
|
31249
|
+
const asset = payment.accepted?.asset || payment.payload?.asset;
|
|
31250
|
+
if (!asset) return void 0;
|
|
31251
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31252
|
+
for (const [symbol, address] of Object.entries(tokenAddresses)) {
|
|
31253
|
+
if (address.toLowerCase() === asset.toLowerCase()) {
|
|
31254
|
+
return symbol;
|
|
31255
|
+
}
|
|
31256
|
+
}
|
|
31257
|
+
return void 0;
|
|
31258
|
+
}
|
|
31259
|
+
/**
|
|
31260
|
+
* Check if payment token is accepted for service
|
|
31261
|
+
*/
|
|
31262
|
+
isTokenAccepted(config, token) {
|
|
31263
|
+
const accepted = getAcceptedCurrencies(config);
|
|
31264
|
+
return accepted.includes(token);
|
|
31265
|
+
}
|
|
31172
31266
|
async readBody(req) {
|
|
31173
31267
|
return new Promise((resolve, reject) => {
|
|
31174
31268
|
let body = "";
|
|
@@ -31286,9 +31380,15 @@ var MoltsPayServer = class {
|
|
|
31286
31380
|
error: `Service not found: ${service}`
|
|
31287
31381
|
});
|
|
31288
31382
|
}
|
|
31383
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
31289
31384
|
let result;
|
|
31290
31385
|
try {
|
|
31291
|
-
result = await
|
|
31386
|
+
result = await Promise.race([
|
|
31387
|
+
skill.handler(params || {}),
|
|
31388
|
+
new Promise(
|
|
31389
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
31390
|
+
)
|
|
31391
|
+
]);
|
|
31292
31392
|
console.log(`[MoltsPay] /proxy: Skill succeeded, now settling payment...`);
|
|
31293
31393
|
} catch (err) {
|
|
31294
31394
|
console.error(`[MoltsPay] /proxy: Skill failed: ${err.message} - NOT settling`);
|
|
@@ -31362,18 +31462,22 @@ var MoltsPayServer = class {
|
|
|
31362
31462
|
/**
|
|
31363
31463
|
* Build payment requirements for proxy endpoint (uses provided wallet)
|
|
31364
31464
|
*/
|
|
31365
|
-
buildProxyPaymentRequirements(config, wallet) {
|
|
31465
|
+
buildProxyPaymentRequirements(config, wallet, token) {
|
|
31366
31466
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
31367
|
-
const
|
|
31467
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31468
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
31469
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31470
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
31471
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
31368
31472
|
return {
|
|
31369
31473
|
scheme: "exact",
|
|
31370
31474
|
network: this.networkId,
|
|
31371
|
-
asset:
|
|
31475
|
+
asset: tokenAddress,
|
|
31372
31476
|
amount: amountInUnits,
|
|
31373
31477
|
payTo: wallet,
|
|
31374
31478
|
// Use provided wallet, not manifest
|
|
31375
31479
|
maxTimeoutSeconds: 300,
|
|
31376
|
-
extra:
|
|
31480
|
+
extra: tokenDomain
|
|
31377
31481
|
};
|
|
31378
31482
|
}
|
|
31379
31483
|
/**
|
|
@@ -31419,7 +31523,20 @@ var CHAINS = {
|
|
|
31419
31523
|
name: "Base",
|
|
31420
31524
|
chainId: 8453,
|
|
31421
31525
|
rpc: "https://mainnet.base.org",
|
|
31526
|
+
tokens: {
|
|
31527
|
+
USDC: {
|
|
31528
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
31529
|
+
decimals: 6,
|
|
31530
|
+
symbol: "USDC"
|
|
31531
|
+
},
|
|
31532
|
+
USDT: {
|
|
31533
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
31534
|
+
decimals: 6,
|
|
31535
|
+
symbol: "USDT"
|
|
31536
|
+
}
|
|
31537
|
+
},
|
|
31422
31538
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
31539
|
+
// deprecated, for backward compat
|
|
31423
31540
|
explorer: "https://basescan.org/address/",
|
|
31424
31541
|
explorerTx: "https://basescan.org/tx/",
|
|
31425
31542
|
avgBlockTime: 2
|
|
@@ -31428,6 +31545,18 @@ var CHAINS = {
|
|
|
31428
31545
|
name: "Polygon",
|
|
31429
31546
|
chainId: 137,
|
|
31430
31547
|
rpc: "https://polygon-rpc.com",
|
|
31548
|
+
tokens: {
|
|
31549
|
+
USDC: {
|
|
31550
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
31551
|
+
decimals: 6,
|
|
31552
|
+
symbol: "USDC"
|
|
31553
|
+
},
|
|
31554
|
+
USDT: {
|
|
31555
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
31556
|
+
decimals: 6,
|
|
31557
|
+
symbol: "USDT"
|
|
31558
|
+
}
|
|
31559
|
+
},
|
|
31431
31560
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
31432
31561
|
explorer: "https://polygonscan.com/address/",
|
|
31433
31562
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -31437,6 +31566,18 @@ var CHAINS = {
|
|
|
31437
31566
|
name: "Ethereum",
|
|
31438
31567
|
chainId: 1,
|
|
31439
31568
|
rpc: "https://eth.llamarpc.com",
|
|
31569
|
+
tokens: {
|
|
31570
|
+
USDC: {
|
|
31571
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
31572
|
+
decimals: 6,
|
|
31573
|
+
symbol: "USDC"
|
|
31574
|
+
},
|
|
31575
|
+
USDT: {
|
|
31576
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
31577
|
+
decimals: 6,
|
|
31578
|
+
symbol: "USDT"
|
|
31579
|
+
}
|
|
31580
|
+
},
|
|
31440
31581
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
31441
31582
|
explorer: "https://etherscan.io/address/",
|
|
31442
31583
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -31447,6 +31588,19 @@ var CHAINS = {
|
|
|
31447
31588
|
name: "Base Sepolia",
|
|
31448
31589
|
chainId: 84532,
|
|
31449
31590
|
rpc: "https://sepolia.base.org",
|
|
31591
|
+
tokens: {
|
|
31592
|
+
USDC: {
|
|
31593
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31594
|
+
decimals: 6,
|
|
31595
|
+
symbol: "USDC"
|
|
31596
|
+
},
|
|
31597
|
+
USDT: {
|
|
31598
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31599
|
+
// Same as USDC on testnet (no official USDT)
|
|
31600
|
+
decimals: 6,
|
|
31601
|
+
symbol: "USDT"
|
|
31602
|
+
}
|
|
31603
|
+
},
|
|
31450
31604
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31451
31605
|
explorer: "https://sepolia.basescan.org/address/",
|
|
31452
31606
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -31456,6 +31610,19 @@ var CHAINS = {
|
|
|
31456
31610
|
name: "Sepolia",
|
|
31457
31611
|
chainId: 11155111,
|
|
31458
31612
|
rpc: "https://rpc.sepolia.org",
|
|
31613
|
+
tokens: {
|
|
31614
|
+
USDC: {
|
|
31615
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31616
|
+
decimals: 6,
|
|
31617
|
+
symbol: "USDC"
|
|
31618
|
+
},
|
|
31619
|
+
USDT: {
|
|
31620
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31621
|
+
// Same as USDC on testnet
|
|
31622
|
+
decimals: 6,
|
|
31623
|
+
symbol: "USDT"
|
|
31624
|
+
}
|
|
31625
|
+
},
|
|
31459
31626
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31460
31627
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
31461
31628
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -31564,8 +31731,13 @@ var MoltsPayClient = class {
|
|
|
31564
31731
|
*
|
|
31565
31732
|
* This is GASLESS for the client - server pays gas to claim payment.
|
|
31566
31733
|
* This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.
|
|
31734
|
+
*
|
|
31735
|
+
* @param serverUrl - Server URL
|
|
31736
|
+
* @param service - Service ID
|
|
31737
|
+
* @param params - Service parameters
|
|
31738
|
+
* @param options - Payment options (token selection)
|
|
31567
31739
|
*/
|
|
31568
|
-
async pay(serverUrl, service, params) {
|
|
31740
|
+
async pay(serverUrl, service, params, options = {}) {
|
|
31569
31741
|
if (!this.wallet || !this.walletData) {
|
|
31570
31742
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
31571
31743
|
}
|
|
@@ -31612,12 +31784,25 @@ var MoltsPayClient = class {
|
|
|
31612
31784
|
}
|
|
31613
31785
|
const amount = Number(amountRaw) / 1e6;
|
|
31614
31786
|
this.checkLimits(amount);
|
|
31615
|
-
|
|
31787
|
+
let token = options.token || "USDC";
|
|
31788
|
+
if (options.autoSelect) {
|
|
31789
|
+
const balances = await this.getBalance();
|
|
31790
|
+
if (balances.usdc >= amount) {
|
|
31791
|
+
token = "USDC";
|
|
31792
|
+
} else if (balances.usdt >= amount) {
|
|
31793
|
+
token = "USDT";
|
|
31794
|
+
} else {
|
|
31795
|
+
throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);
|
|
31796
|
+
}
|
|
31797
|
+
}
|
|
31798
|
+
console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
|
|
31616
31799
|
const payTo = req.payTo || req.resource;
|
|
31617
31800
|
if (!payTo) {
|
|
31618
31801
|
throw new Error("Missing payTo address in payment requirements");
|
|
31619
31802
|
}
|
|
31620
|
-
const authorization = await this.signEIP3009(payTo, amount, chain2);
|
|
31803
|
+
const authorization = await this.signEIP3009(payTo, amount, chain2, token);
|
|
31804
|
+
const tokenConfig = chain2.tokens[token];
|
|
31805
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
31621
31806
|
const payload = {
|
|
31622
31807
|
x402Version: X402_VERSION3,
|
|
31623
31808
|
payload: authorization,
|
|
@@ -31625,11 +31810,11 @@ var MoltsPayClient = class {
|
|
|
31625
31810
|
accepted: {
|
|
31626
31811
|
scheme: "exact",
|
|
31627
31812
|
network,
|
|
31628
|
-
asset:
|
|
31813
|
+
asset: tokenConfig.address,
|
|
31629
31814
|
amount: amountRaw,
|
|
31630
31815
|
payTo,
|
|
31631
31816
|
maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
|
|
31632
|
-
extra: req.extra || { name:
|
|
31817
|
+
extra: req.extra || { name: tokenName, version: "2" }
|
|
31633
31818
|
}
|
|
31634
31819
|
};
|
|
31635
31820
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
@@ -31653,12 +31838,14 @@ var MoltsPayClient = class {
|
|
|
31653
31838
|
/**
|
|
31654
31839
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
31655
31840
|
* This only signs - no on-chain transaction, no gas needed.
|
|
31841
|
+
* Supports both USDC and USDT.
|
|
31656
31842
|
*/
|
|
31657
|
-
async signEIP3009(to, amount, chain2) {
|
|
31843
|
+
async signEIP3009(to, amount, chain2, token = "USDC") {
|
|
31658
31844
|
const validAfter = 0;
|
|
31659
31845
|
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
31660
31846
|
const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
|
|
31661
|
-
const
|
|
31847
|
+
const tokenConfig = chain2.tokens[token];
|
|
31848
|
+
const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
31662
31849
|
const authorization = {
|
|
31663
31850
|
from: this.wallet.address,
|
|
31664
31851
|
to,
|
|
@@ -31667,11 +31854,12 @@ var MoltsPayClient = class {
|
|
|
31667
31854
|
validBefore: validBefore.toString(),
|
|
31668
31855
|
nonce
|
|
31669
31856
|
};
|
|
31857
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
31670
31858
|
const domain = {
|
|
31671
|
-
name:
|
|
31859
|
+
name: tokenName,
|
|
31672
31860
|
version: "2",
|
|
31673
31861
|
chainId: chain2.chainId,
|
|
31674
|
-
verifyingContract:
|
|
31862
|
+
verifyingContract: tokenConfig.address
|
|
31675
31863
|
};
|
|
31676
31864
|
const types = {
|
|
31677
31865
|
TransferWithAuthorization: [
|
|
@@ -31806,7 +31994,7 @@ var MoltsPayClient = class {
|
|
|
31806
31994
|
return { address: wallet.address, configDir };
|
|
31807
31995
|
}
|
|
31808
31996
|
/**
|
|
31809
|
-
* Get wallet balance
|
|
31997
|
+
* Get wallet balance (USDC, USDT, and native token)
|
|
31810
31998
|
*/
|
|
31811
31999
|
async getBalance() {
|
|
31812
32000
|
if (!this.wallet) {
|
|
@@ -31819,12 +32007,15 @@ var MoltsPayClient = class {
|
|
|
31819
32007
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
31820
32008
|
}
|
|
31821
32009
|
const provider = new import_ethers.ethers.JsonRpcProvider(chain2.rpc);
|
|
31822
|
-
const
|
|
31823
|
-
const
|
|
31824
|
-
|
|
31825
|
-
|
|
32010
|
+
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
32011
|
+
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
32012
|
+
provider.getBalance(this.wallet.address),
|
|
32013
|
+
new import_ethers.ethers.Contract(chain2.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
32014
|
+
new import_ethers.ethers.Contract(chain2.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
32015
|
+
]);
|
|
31826
32016
|
return {
|
|
31827
|
-
usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance,
|
|
32017
|
+
usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain2.tokens.USDC.decimals)),
|
|
32018
|
+
usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain2.tokens.USDT.decimals)),
|
|
31828
32019
|
native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
|
|
31829
32020
|
};
|
|
31830
32021
|
}
|
|
@@ -31960,7 +32151,7 @@ init_cjs_shims();
|
|
|
31960
32151
|
var import_ethers4 = require("ethers");
|
|
31961
32152
|
var TRANSFER_EVENT_TOPIC = import_ethers4.ethers.id("Transfer(address,address,uint256)");
|
|
31962
32153
|
async function verifyPayment(params) {
|
|
31963
|
-
const { txHash, expectedAmount, expectedTo } = params;
|
|
32154
|
+
const { txHash, expectedAmount, expectedTo, expectedToken } = params;
|
|
31964
32155
|
let chain2;
|
|
31965
32156
|
try {
|
|
31966
32157
|
if (typeof params.chain === "number") {
|
|
@@ -31983,12 +32174,20 @@ async function verifyPayment(params) {
|
|
|
31983
32174
|
if (receipt.status !== 1) {
|
|
31984
32175
|
return { verified: false, error: "Transaction failed" };
|
|
31985
32176
|
}
|
|
31986
|
-
const
|
|
31987
|
-
if (!
|
|
31988
|
-
|
|
32177
|
+
const tokenAddresses = {};
|
|
32178
|
+
if (!expectedToken || expectedToken === "USDC") {
|
|
32179
|
+
tokenAddresses[chain2.tokens.USDC.address.toLowerCase()] = "USDC";
|
|
32180
|
+
}
|
|
32181
|
+
if (!expectedToken || expectedToken === "USDT") {
|
|
32182
|
+
tokenAddresses[chain2.tokens.USDT.address.toLowerCase()] = "USDT";
|
|
32183
|
+
}
|
|
32184
|
+
if (Object.keys(tokenAddresses).length === 0) {
|
|
32185
|
+
return { verified: false, error: `No token addresses configured for ${chain2.name}` };
|
|
31989
32186
|
}
|
|
31990
32187
|
for (const log of receipt.logs) {
|
|
31991
|
-
|
|
32188
|
+
const logAddress = log.address.toLowerCase();
|
|
32189
|
+
const detectedToken = tokenAddresses[logAddress];
|
|
32190
|
+
if (!detectedToken) {
|
|
31992
32191
|
continue;
|
|
31993
32192
|
}
|
|
31994
32193
|
if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
|
|
@@ -31997,15 +32196,17 @@ async function verifyPayment(params) {
|
|
|
31997
32196
|
const from = "0x" + log.topics[1].slice(-40);
|
|
31998
32197
|
const to = "0x" + log.topics[2].slice(-40);
|
|
31999
32198
|
const amountRaw = BigInt(log.data);
|
|
32000
|
-
const
|
|
32199
|
+
const tokenConfig = chain2.tokens[detectedToken];
|
|
32200
|
+
const amount = Number(amountRaw) / 10 ** tokenConfig.decimals;
|
|
32001
32201
|
if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
|
|
32002
32202
|
continue;
|
|
32003
32203
|
}
|
|
32004
32204
|
if (amount < expectedAmount) {
|
|
32005
32205
|
return {
|
|
32006
32206
|
verified: false,
|
|
32007
|
-
error: `Insufficient amount: received ${amount}
|
|
32207
|
+
error: `Insufficient amount: received ${amount} ${detectedToken}, expected ${expectedAmount}`,
|
|
32008
32208
|
amount,
|
|
32209
|
+
token: detectedToken,
|
|
32009
32210
|
from,
|
|
32010
32211
|
to,
|
|
32011
32212
|
txHash,
|
|
@@ -32015,13 +32216,15 @@ async function verifyPayment(params) {
|
|
|
32015
32216
|
return {
|
|
32016
32217
|
verified: true,
|
|
32017
32218
|
amount,
|
|
32219
|
+
token: detectedToken,
|
|
32018
32220
|
from,
|
|
32019
32221
|
to,
|
|
32020
32222
|
txHash,
|
|
32021
32223
|
blockNumber: receipt.blockNumber
|
|
32022
32224
|
};
|
|
32023
32225
|
}
|
|
32024
|
-
|
|
32226
|
+
const tokenList = expectedToken ? expectedToken : "USDC/USDT";
|
|
32227
|
+
return { verified: false, error: `No ${tokenList} transfer found` };
|
|
32025
32228
|
} catch (e) {
|
|
32026
32229
|
return { verified: false, error: e.message || String(e) };
|
|
32027
32230
|
}
|