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.mjs
CHANGED
|
@@ -30802,16 +30802,24 @@ var X402_VERSION2 = 2;
|
|
|
30802
30802
|
var PAYMENT_REQUIRED_HEADER = "x-payment-required";
|
|
30803
30803
|
var PAYMENT_HEADER = "x-payment";
|
|
30804
30804
|
var PAYMENT_RESPONSE_HEADER = "x-payment-response";
|
|
30805
|
-
var
|
|
30806
|
-
"eip155:8453":
|
|
30807
|
-
|
|
30808
|
-
|
|
30809
|
-
|
|
30805
|
+
var TOKEN_ADDRESSES = {
|
|
30806
|
+
"eip155:8453": {
|
|
30807
|
+
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
30808
|
+
USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
|
|
30809
|
+
},
|
|
30810
|
+
"eip155:84532": {
|
|
30811
|
+
USDC: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
30812
|
+
USDT: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
30813
|
+
// Same as USDC on testnet
|
|
30814
|
+
}
|
|
30810
30815
|
};
|
|
30811
|
-
var
|
|
30812
|
-
name: "USD Coin",
|
|
30813
|
-
version: "2"
|
|
30816
|
+
var TOKEN_DOMAINS = {
|
|
30817
|
+
USDC: { name: "USD Coin", version: "2" },
|
|
30818
|
+
USDT: { name: "Tether USD", version: "2" }
|
|
30814
30819
|
};
|
|
30820
|
+
function getAcceptedCurrencies(config) {
|
|
30821
|
+
return config.acceptedCurrencies ?? [config.currency];
|
|
30822
|
+
}
|
|
30815
30823
|
function loadEnvFile2() {
|
|
30816
30824
|
const envPaths = [
|
|
30817
30825
|
path3.join(process.cwd(), ".env"),
|
|
@@ -30923,6 +30931,9 @@ var MoltsPayServer = class {
|
|
|
30923
30931
|
if (url.pathname === "/services" && req.method === "GET") {
|
|
30924
30932
|
return this.handleGetServices(res);
|
|
30925
30933
|
}
|
|
30934
|
+
if (url.pathname === "/.well-known/agent-services.json" && req.method === "GET") {
|
|
30935
|
+
return this.handleAgentServicesDiscovery(res);
|
|
30936
|
+
}
|
|
30926
30937
|
if (url.pathname === "/health" && req.method === "GET") {
|
|
30927
30938
|
return await this.handleHealthCheck(res);
|
|
30928
30939
|
}
|
|
@@ -30946,6 +30957,44 @@ var MoltsPayServer = class {
|
|
|
30946
30957
|
this.sendJson(res, 500, { error: err.message || "Internal error" });
|
|
30947
30958
|
}
|
|
30948
30959
|
}
|
|
30960
|
+
/**
|
|
30961
|
+
* GET /.well-known/agent-services.json - Standard discovery endpoint
|
|
30962
|
+
*/
|
|
30963
|
+
handleAgentServicesDiscovery(res) {
|
|
30964
|
+
const services = this.manifest.services.map((s) => ({
|
|
30965
|
+
id: s.id,
|
|
30966
|
+
name: s.name,
|
|
30967
|
+
description: s.description,
|
|
30968
|
+
price: s.price,
|
|
30969
|
+
currency: s.currency,
|
|
30970
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
30971
|
+
input: s.input,
|
|
30972
|
+
output: s.output,
|
|
30973
|
+
available: this.skills.has(s.id)
|
|
30974
|
+
}));
|
|
30975
|
+
this.sendJson(res, 200, {
|
|
30976
|
+
version: "1.0",
|
|
30977
|
+
provider: {
|
|
30978
|
+
name: this.manifest.provider.name,
|
|
30979
|
+
description: this.manifest.provider.description,
|
|
30980
|
+
wallet: this.manifest.provider.wallet,
|
|
30981
|
+
chain: this.manifest.provider.chain || "base"
|
|
30982
|
+
},
|
|
30983
|
+
services,
|
|
30984
|
+
endpoints: {
|
|
30985
|
+
services: "/services",
|
|
30986
|
+
execute: "/execute",
|
|
30987
|
+
health: "/health"
|
|
30988
|
+
},
|
|
30989
|
+
payment: {
|
|
30990
|
+
protocol: "x402",
|
|
30991
|
+
version: X402_VERSION2,
|
|
30992
|
+
network: this.networkId,
|
|
30993
|
+
schemes: ["exact"],
|
|
30994
|
+
mainnet: this.useMainnet
|
|
30995
|
+
}
|
|
30996
|
+
});
|
|
30997
|
+
}
|
|
30949
30998
|
/**
|
|
30950
30999
|
* GET /services - List available services
|
|
30951
31000
|
*/
|
|
@@ -30956,6 +31005,7 @@ var MoltsPayServer = class {
|
|
|
30956
31005
|
description: s.description,
|
|
30957
31006
|
price: s.price,
|
|
30958
31007
|
currency: s.currency,
|
|
31008
|
+
acceptedCurrencies: getAcceptedCurrencies(s),
|
|
30959
31009
|
input: s.input,
|
|
30960
31010
|
output: s.output,
|
|
30961
31011
|
available: this.skills.has(s.id)
|
|
@@ -31022,7 +31072,14 @@ var MoltsPayServer = class {
|
|
|
31022
31072
|
if (!validation.valid) {
|
|
31023
31073
|
return this.sendJson(res, 402, { error: validation.error });
|
|
31024
31074
|
}
|
|
31025
|
-
const
|
|
31075
|
+
const paymentToken = this.detectPaymentToken(payment);
|
|
31076
|
+
if (paymentToken && !this.isTokenAccepted(skill.config, paymentToken)) {
|
|
31077
|
+
const accepted = getAcceptedCurrencies(skill.config);
|
|
31078
|
+
return this.sendJson(res, 402, {
|
|
31079
|
+
error: `Token ${paymentToken} not accepted. Accepted: ${accepted.join(", ")}`
|
|
31080
|
+
});
|
|
31081
|
+
}
|
|
31082
|
+
const requirements = this.buildPaymentRequirements(skill.config, paymentToken);
|
|
31026
31083
|
console.log(`[MoltsPay] Verifying payment...`);
|
|
31027
31084
|
const verifyResult = await this.registry.verify(payment, requirements);
|
|
31028
31085
|
if (!verifyResult.valid) {
|
|
@@ -31032,10 +31089,16 @@ var MoltsPayServer = class {
|
|
|
31032
31089
|
});
|
|
31033
31090
|
}
|
|
31034
31091
|
console.log(`[MoltsPay] Verified by ${verifyResult.facilitator}`);
|
|
31035
|
-
|
|
31092
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
31093
|
+
console.log(`[MoltsPay] Executing skill: ${service} (timeout: ${timeoutSeconds}s)`);
|
|
31036
31094
|
let result;
|
|
31037
31095
|
try {
|
|
31038
|
-
result = await
|
|
31096
|
+
result = await Promise.race([
|
|
31097
|
+
skill.handler(params || {}),
|
|
31098
|
+
new Promise(
|
|
31099
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
31100
|
+
)
|
|
31101
|
+
]);
|
|
31039
31102
|
} catch (err) {
|
|
31040
31103
|
console.error("[MoltsPay] Skill execution failed:", err.message);
|
|
31041
31104
|
return this.sendJson(res, 500, {
|
|
@@ -31071,12 +31134,15 @@ var MoltsPayServer = class {
|
|
|
31071
31134
|
}
|
|
31072
31135
|
/**
|
|
31073
31136
|
* Return 402 with x402 payment requirements (v2 format)
|
|
31137
|
+
* Includes requirements for all accepted currencies
|
|
31074
31138
|
*/
|
|
31075
31139
|
sendPaymentRequired(config, res) {
|
|
31076
|
-
const
|
|
31140
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31141
|
+
const accepts = acceptedTokens.map((token) => this.buildPaymentRequirements(config, token));
|
|
31077
31142
|
const paymentRequired = {
|
|
31078
31143
|
x402Version: X402_VERSION2,
|
|
31079
|
-
accepts
|
|
31144
|
+
accepts,
|
|
31145
|
+
acceptedCurrencies: acceptedTokens,
|
|
31080
31146
|
resource: {
|
|
31081
31147
|
url: `/execute?service=${config.id}`,
|
|
31082
31148
|
description: `${config.name} - $${config.price} ${config.currency}`,
|
|
@@ -31091,6 +31157,7 @@ var MoltsPayServer = class {
|
|
|
31091
31157
|
res.end(JSON.stringify({
|
|
31092
31158
|
error: "Payment required",
|
|
31093
31159
|
message: `Service requires $${config.price} ${config.currency}`,
|
|
31160
|
+
acceptedCurrencies: acceptedTokens,
|
|
31094
31161
|
x402: paymentRequired
|
|
31095
31162
|
}, null, 2));
|
|
31096
31163
|
}
|
|
@@ -31113,20 +31180,47 @@ var MoltsPayServer = class {
|
|
|
31113
31180
|
}
|
|
31114
31181
|
/**
|
|
31115
31182
|
* Build payment requirements for facilitator
|
|
31183
|
+
* Returns requirements for the primary currency (USDC by default)
|
|
31184
|
+
* Server accepts any of the acceptedCurrencies
|
|
31116
31185
|
*/
|
|
31117
|
-
buildPaymentRequirements(config) {
|
|
31186
|
+
buildPaymentRequirements(config, token) {
|
|
31118
31187
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
31119
|
-
const
|
|
31188
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31189
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
31190
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31191
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
31192
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
31120
31193
|
return {
|
|
31121
31194
|
scheme: "exact",
|
|
31122
31195
|
network: this.networkId,
|
|
31123
|
-
asset:
|
|
31196
|
+
asset: tokenAddress,
|
|
31124
31197
|
amount: amountInUnits,
|
|
31125
31198
|
payTo: this.manifest.provider.wallet,
|
|
31126
31199
|
maxTimeoutSeconds: 300,
|
|
31127
|
-
extra:
|
|
31200
|
+
extra: tokenDomain
|
|
31128
31201
|
};
|
|
31129
31202
|
}
|
|
31203
|
+
/**
|
|
31204
|
+
* Detect which token is being used in the payment
|
|
31205
|
+
*/
|
|
31206
|
+
detectPaymentToken(payment) {
|
|
31207
|
+
const asset = payment.accepted?.asset || payment.payload?.asset;
|
|
31208
|
+
if (!asset) return void 0;
|
|
31209
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31210
|
+
for (const [symbol, address] of Object.entries(tokenAddresses)) {
|
|
31211
|
+
if (address.toLowerCase() === asset.toLowerCase()) {
|
|
31212
|
+
return symbol;
|
|
31213
|
+
}
|
|
31214
|
+
}
|
|
31215
|
+
return void 0;
|
|
31216
|
+
}
|
|
31217
|
+
/**
|
|
31218
|
+
* Check if payment token is accepted for service
|
|
31219
|
+
*/
|
|
31220
|
+
isTokenAccepted(config, token) {
|
|
31221
|
+
const accepted = getAcceptedCurrencies(config);
|
|
31222
|
+
return accepted.includes(token);
|
|
31223
|
+
}
|
|
31130
31224
|
async readBody(req) {
|
|
31131
31225
|
return new Promise((resolve, reject) => {
|
|
31132
31226
|
let body = "";
|
|
@@ -31244,9 +31338,15 @@ var MoltsPayServer = class {
|
|
|
31244
31338
|
error: `Service not found: ${service}`
|
|
31245
31339
|
});
|
|
31246
31340
|
}
|
|
31341
|
+
const timeoutSeconds = parseInt(process.env.SKILL_TIMEOUT_SECONDS || "1200");
|
|
31247
31342
|
let result;
|
|
31248
31343
|
try {
|
|
31249
|
-
result = await
|
|
31344
|
+
result = await Promise.race([
|
|
31345
|
+
skill.handler(params || {}),
|
|
31346
|
+
new Promise(
|
|
31347
|
+
(_, reject) => setTimeout(() => reject(new Error(`Skill timeout after ${timeoutSeconds}s`)), timeoutSeconds * 1e3)
|
|
31348
|
+
)
|
|
31349
|
+
]);
|
|
31250
31350
|
console.log(`[MoltsPay] /proxy: Skill succeeded, now settling payment...`);
|
|
31251
31351
|
} catch (err) {
|
|
31252
31352
|
console.error(`[MoltsPay] /proxy: Skill failed: ${err.message} - NOT settling`);
|
|
@@ -31320,18 +31420,22 @@ var MoltsPayServer = class {
|
|
|
31320
31420
|
/**
|
|
31321
31421
|
* Build payment requirements for proxy endpoint (uses provided wallet)
|
|
31322
31422
|
*/
|
|
31323
|
-
buildProxyPaymentRequirements(config, wallet) {
|
|
31423
|
+
buildProxyPaymentRequirements(config, wallet, token) {
|
|
31324
31424
|
const amountInUnits = Math.floor(config.price * 1e6).toString();
|
|
31325
|
-
const
|
|
31425
|
+
const acceptedTokens = getAcceptedCurrencies(config);
|
|
31426
|
+
const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
|
|
31427
|
+
const tokenAddresses = TOKEN_ADDRESSES[this.networkId] || {};
|
|
31428
|
+
const tokenAddress = tokenAddresses[selectedToken];
|
|
31429
|
+
const tokenDomain = TOKEN_DOMAINS[selectedToken] || TOKEN_DOMAINS.USDC;
|
|
31326
31430
|
return {
|
|
31327
31431
|
scheme: "exact",
|
|
31328
31432
|
network: this.networkId,
|
|
31329
|
-
asset:
|
|
31433
|
+
asset: tokenAddress,
|
|
31330
31434
|
amount: amountInUnits,
|
|
31331
31435
|
payTo: wallet,
|
|
31332
31436
|
// Use provided wallet, not manifest
|
|
31333
31437
|
maxTimeoutSeconds: 300,
|
|
31334
|
-
extra:
|
|
31438
|
+
extra: tokenDomain
|
|
31335
31439
|
};
|
|
31336
31440
|
}
|
|
31337
31441
|
/**
|
|
@@ -31377,7 +31481,20 @@ var CHAINS = {
|
|
|
31377
31481
|
name: "Base",
|
|
31378
31482
|
chainId: 8453,
|
|
31379
31483
|
rpc: "https://mainnet.base.org",
|
|
31484
|
+
tokens: {
|
|
31485
|
+
USDC: {
|
|
31486
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
31487
|
+
decimals: 6,
|
|
31488
|
+
symbol: "USDC"
|
|
31489
|
+
},
|
|
31490
|
+
USDT: {
|
|
31491
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
31492
|
+
decimals: 6,
|
|
31493
|
+
symbol: "USDT"
|
|
31494
|
+
}
|
|
31495
|
+
},
|
|
31380
31496
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
31497
|
+
// deprecated, for backward compat
|
|
31381
31498
|
explorer: "https://basescan.org/address/",
|
|
31382
31499
|
explorerTx: "https://basescan.org/tx/",
|
|
31383
31500
|
avgBlockTime: 2
|
|
@@ -31386,6 +31503,18 @@ var CHAINS = {
|
|
|
31386
31503
|
name: "Polygon",
|
|
31387
31504
|
chainId: 137,
|
|
31388
31505
|
rpc: "https://polygon-rpc.com",
|
|
31506
|
+
tokens: {
|
|
31507
|
+
USDC: {
|
|
31508
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
31509
|
+
decimals: 6,
|
|
31510
|
+
symbol: "USDC"
|
|
31511
|
+
},
|
|
31512
|
+
USDT: {
|
|
31513
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
31514
|
+
decimals: 6,
|
|
31515
|
+
symbol: "USDT"
|
|
31516
|
+
}
|
|
31517
|
+
},
|
|
31389
31518
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
31390
31519
|
explorer: "https://polygonscan.com/address/",
|
|
31391
31520
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -31395,6 +31524,18 @@ var CHAINS = {
|
|
|
31395
31524
|
name: "Ethereum",
|
|
31396
31525
|
chainId: 1,
|
|
31397
31526
|
rpc: "https://eth.llamarpc.com",
|
|
31527
|
+
tokens: {
|
|
31528
|
+
USDC: {
|
|
31529
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
31530
|
+
decimals: 6,
|
|
31531
|
+
symbol: "USDC"
|
|
31532
|
+
},
|
|
31533
|
+
USDT: {
|
|
31534
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
31535
|
+
decimals: 6,
|
|
31536
|
+
symbol: "USDT"
|
|
31537
|
+
}
|
|
31538
|
+
},
|
|
31398
31539
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
31399
31540
|
explorer: "https://etherscan.io/address/",
|
|
31400
31541
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -31405,6 +31546,19 @@ var CHAINS = {
|
|
|
31405
31546
|
name: "Base Sepolia",
|
|
31406
31547
|
chainId: 84532,
|
|
31407
31548
|
rpc: "https://sepolia.base.org",
|
|
31549
|
+
tokens: {
|
|
31550
|
+
USDC: {
|
|
31551
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31552
|
+
decimals: 6,
|
|
31553
|
+
symbol: "USDC"
|
|
31554
|
+
},
|
|
31555
|
+
USDT: {
|
|
31556
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31557
|
+
// Same as USDC on testnet (no official USDT)
|
|
31558
|
+
decimals: 6,
|
|
31559
|
+
symbol: "USDT"
|
|
31560
|
+
}
|
|
31561
|
+
},
|
|
31408
31562
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
31409
31563
|
explorer: "https://sepolia.basescan.org/address/",
|
|
31410
31564
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -31414,6 +31568,19 @@ var CHAINS = {
|
|
|
31414
31568
|
name: "Sepolia",
|
|
31415
31569
|
chainId: 11155111,
|
|
31416
31570
|
rpc: "https://rpc.sepolia.org",
|
|
31571
|
+
tokens: {
|
|
31572
|
+
USDC: {
|
|
31573
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31574
|
+
decimals: 6,
|
|
31575
|
+
symbol: "USDC"
|
|
31576
|
+
},
|
|
31577
|
+
USDT: {
|
|
31578
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31579
|
+
// Same as USDC on testnet
|
|
31580
|
+
decimals: 6,
|
|
31581
|
+
symbol: "USDT"
|
|
31582
|
+
}
|
|
31583
|
+
},
|
|
31417
31584
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
31418
31585
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
31419
31586
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -31522,8 +31689,13 @@ var MoltsPayClient = class {
|
|
|
31522
31689
|
*
|
|
31523
31690
|
* This is GASLESS for the client - server pays gas to claim payment.
|
|
31524
31691
|
* This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.
|
|
31692
|
+
*
|
|
31693
|
+
* @param serverUrl - Server URL
|
|
31694
|
+
* @param service - Service ID
|
|
31695
|
+
* @param params - Service parameters
|
|
31696
|
+
* @param options - Payment options (token selection)
|
|
31525
31697
|
*/
|
|
31526
|
-
async pay(serverUrl, service, params) {
|
|
31698
|
+
async pay(serverUrl, service, params, options = {}) {
|
|
31527
31699
|
if (!this.wallet || !this.walletData) {
|
|
31528
31700
|
throw new Error("Client not initialized. Run: npx moltspay init");
|
|
31529
31701
|
}
|
|
@@ -31570,12 +31742,25 @@ var MoltsPayClient = class {
|
|
|
31570
31742
|
}
|
|
31571
31743
|
const amount = Number(amountRaw) / 1e6;
|
|
31572
31744
|
this.checkLimits(amount);
|
|
31573
|
-
|
|
31745
|
+
let token = options.token || "USDC";
|
|
31746
|
+
if (options.autoSelect) {
|
|
31747
|
+
const balances = await this.getBalance();
|
|
31748
|
+
if (balances.usdc >= amount) {
|
|
31749
|
+
token = "USDC";
|
|
31750
|
+
} else if (balances.usdt >= amount) {
|
|
31751
|
+
token = "USDT";
|
|
31752
|
+
} else {
|
|
31753
|
+
throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);
|
|
31754
|
+
}
|
|
31755
|
+
}
|
|
31756
|
+
console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);
|
|
31574
31757
|
const payTo = req.payTo || req.resource;
|
|
31575
31758
|
if (!payTo) {
|
|
31576
31759
|
throw new Error("Missing payTo address in payment requirements");
|
|
31577
31760
|
}
|
|
31578
|
-
const authorization = await this.signEIP3009(payTo, amount, chain2);
|
|
31761
|
+
const authorization = await this.signEIP3009(payTo, amount, chain2, token);
|
|
31762
|
+
const tokenConfig = chain2.tokens[token];
|
|
31763
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
31579
31764
|
const payload = {
|
|
31580
31765
|
x402Version: X402_VERSION3,
|
|
31581
31766
|
payload: authorization,
|
|
@@ -31583,11 +31768,11 @@ var MoltsPayClient = class {
|
|
|
31583
31768
|
accepted: {
|
|
31584
31769
|
scheme: "exact",
|
|
31585
31770
|
network,
|
|
31586
|
-
asset:
|
|
31771
|
+
asset: tokenConfig.address,
|
|
31587
31772
|
amount: amountRaw,
|
|
31588
31773
|
payTo,
|
|
31589
31774
|
maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
|
|
31590
|
-
extra: req.extra || { name:
|
|
31775
|
+
extra: req.extra || { name: tokenName, version: "2" }
|
|
31591
31776
|
}
|
|
31592
31777
|
};
|
|
31593
31778
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
@@ -31611,12 +31796,14 @@ var MoltsPayClient = class {
|
|
|
31611
31796
|
/**
|
|
31612
31797
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
31613
31798
|
* This only signs - no on-chain transaction, no gas needed.
|
|
31799
|
+
* Supports both USDC and USDT.
|
|
31614
31800
|
*/
|
|
31615
|
-
async signEIP3009(to, amount, chain2) {
|
|
31801
|
+
async signEIP3009(to, amount, chain2, token = "USDC") {
|
|
31616
31802
|
const validAfter = 0;
|
|
31617
31803
|
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
31618
31804
|
const nonce = ethers.hexlify(ethers.randomBytes(32));
|
|
31619
|
-
const
|
|
31805
|
+
const tokenConfig = chain2.tokens[token];
|
|
31806
|
+
const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
31620
31807
|
const authorization = {
|
|
31621
31808
|
from: this.wallet.address,
|
|
31622
31809
|
to,
|
|
@@ -31625,11 +31812,12 @@ var MoltsPayClient = class {
|
|
|
31625
31812
|
validBefore: validBefore.toString(),
|
|
31626
31813
|
nonce
|
|
31627
31814
|
};
|
|
31815
|
+
const tokenName = token === "USDC" ? "USD Coin" : "Tether USD";
|
|
31628
31816
|
const domain = {
|
|
31629
|
-
name:
|
|
31817
|
+
name: tokenName,
|
|
31630
31818
|
version: "2",
|
|
31631
31819
|
chainId: chain2.chainId,
|
|
31632
|
-
verifyingContract:
|
|
31820
|
+
verifyingContract: tokenConfig.address
|
|
31633
31821
|
};
|
|
31634
31822
|
const types = {
|
|
31635
31823
|
TransferWithAuthorization: [
|
|
@@ -31764,7 +31952,7 @@ var MoltsPayClient = class {
|
|
|
31764
31952
|
return { address: wallet.address, configDir };
|
|
31765
31953
|
}
|
|
31766
31954
|
/**
|
|
31767
|
-
* Get wallet balance
|
|
31955
|
+
* Get wallet balance (USDC, USDT, and native token)
|
|
31768
31956
|
*/
|
|
31769
31957
|
async getBalance() {
|
|
31770
31958
|
if (!this.wallet) {
|
|
@@ -31777,12 +31965,15 @@ var MoltsPayClient = class {
|
|
|
31777
31965
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
31778
31966
|
}
|
|
31779
31967
|
const provider = new ethers.JsonRpcProvider(chain2.rpc);
|
|
31780
|
-
const
|
|
31781
|
-
const
|
|
31782
|
-
|
|
31783
|
-
|
|
31968
|
+
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
31969
|
+
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
31970
|
+
provider.getBalance(this.wallet.address),
|
|
31971
|
+
new ethers.Contract(chain2.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
31972
|
+
new ethers.Contract(chain2.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
31973
|
+
]);
|
|
31784
31974
|
return {
|
|
31785
|
-
usdc: parseFloat(ethers.formatUnits(usdcBalance,
|
|
31975
|
+
usdc: parseFloat(ethers.formatUnits(usdcBalance, chain2.tokens.USDC.decimals)),
|
|
31976
|
+
usdt: parseFloat(ethers.formatUnits(usdtBalance, chain2.tokens.USDT.decimals)),
|
|
31786
31977
|
native: parseFloat(ethers.formatEther(nativeBalance))
|
|
31787
31978
|
};
|
|
31788
31979
|
}
|
|
@@ -31918,7 +32109,7 @@ init_esm_shims();
|
|
|
31918
32109
|
import { ethers as ethers4 } from "ethers";
|
|
31919
32110
|
var TRANSFER_EVENT_TOPIC = ethers4.id("Transfer(address,address,uint256)");
|
|
31920
32111
|
async function verifyPayment(params) {
|
|
31921
|
-
const { txHash, expectedAmount, expectedTo } = params;
|
|
32112
|
+
const { txHash, expectedAmount, expectedTo, expectedToken } = params;
|
|
31922
32113
|
let chain2;
|
|
31923
32114
|
try {
|
|
31924
32115
|
if (typeof params.chain === "number") {
|
|
@@ -31941,12 +32132,20 @@ async function verifyPayment(params) {
|
|
|
31941
32132
|
if (receipt.status !== 1) {
|
|
31942
32133
|
return { verified: false, error: "Transaction failed" };
|
|
31943
32134
|
}
|
|
31944
|
-
const
|
|
31945
|
-
if (!
|
|
31946
|
-
|
|
32135
|
+
const tokenAddresses = {};
|
|
32136
|
+
if (!expectedToken || expectedToken === "USDC") {
|
|
32137
|
+
tokenAddresses[chain2.tokens.USDC.address.toLowerCase()] = "USDC";
|
|
32138
|
+
}
|
|
32139
|
+
if (!expectedToken || expectedToken === "USDT") {
|
|
32140
|
+
tokenAddresses[chain2.tokens.USDT.address.toLowerCase()] = "USDT";
|
|
32141
|
+
}
|
|
32142
|
+
if (Object.keys(tokenAddresses).length === 0) {
|
|
32143
|
+
return { verified: false, error: `No token addresses configured for ${chain2.name}` };
|
|
31947
32144
|
}
|
|
31948
32145
|
for (const log of receipt.logs) {
|
|
31949
|
-
|
|
32146
|
+
const logAddress = log.address.toLowerCase();
|
|
32147
|
+
const detectedToken = tokenAddresses[logAddress];
|
|
32148
|
+
if (!detectedToken) {
|
|
31950
32149
|
continue;
|
|
31951
32150
|
}
|
|
31952
32151
|
if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
|
|
@@ -31955,15 +32154,17 @@ async function verifyPayment(params) {
|
|
|
31955
32154
|
const from = "0x" + log.topics[1].slice(-40);
|
|
31956
32155
|
const to = "0x" + log.topics[2].slice(-40);
|
|
31957
32156
|
const amountRaw = BigInt(log.data);
|
|
31958
|
-
const
|
|
32157
|
+
const tokenConfig = chain2.tokens[detectedToken];
|
|
32158
|
+
const amount = Number(amountRaw) / 10 ** tokenConfig.decimals;
|
|
31959
32159
|
if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
|
|
31960
32160
|
continue;
|
|
31961
32161
|
}
|
|
31962
32162
|
if (amount < expectedAmount) {
|
|
31963
32163
|
return {
|
|
31964
32164
|
verified: false,
|
|
31965
|
-
error: `Insufficient amount: received ${amount}
|
|
32165
|
+
error: `Insufficient amount: received ${amount} ${detectedToken}, expected ${expectedAmount}`,
|
|
31966
32166
|
amount,
|
|
32167
|
+
token: detectedToken,
|
|
31967
32168
|
from,
|
|
31968
32169
|
to,
|
|
31969
32170
|
txHash,
|
|
@@ -31973,13 +32174,15 @@ async function verifyPayment(params) {
|
|
|
31973
32174
|
return {
|
|
31974
32175
|
verified: true,
|
|
31975
32176
|
amount,
|
|
32177
|
+
token: detectedToken,
|
|
31976
32178
|
from,
|
|
31977
32179
|
to,
|
|
31978
32180
|
txHash,
|
|
31979
32181
|
blockNumber: receipt.blockNumber
|
|
31980
32182
|
};
|
|
31981
32183
|
}
|
|
31982
|
-
|
|
32184
|
+
const tokenList = expectedToken ? expectedToken : "USDC/USDT";
|
|
32185
|
+
return { verified: false, error: `No ${tokenList} transfer found` };
|
|
31983
32186
|
} catch (e) {
|
|
31984
32187
|
return { verified: false, error: e.message || String(e) };
|
|
31985
32188
|
}
|