run402-mcp
Version:
MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.
108 lines • 4.62 kB
JavaScript
import { sha256 } from "@noble/hashes/sha2.js";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
import { bindingsEqual } from "../../binding.js";
import { DEFAULT_CLOCK_SKEW_SECONDS, Errors, HTTP_PROFILE, LNBTC_NETWORKS, LnbtcError, SCHEME, } from "../../constants.js";
import { checkExpiry, reject, unixNow, validateInvoice, validateRequirements, validateSkew, } from "../../validation.js";
/**
* Client for `exact` on `lnbtc`: validates the request-bound invoice, pays it,
* and returns the verified preimage.
*/
export class ExactLnbtcScheme {
scheme = SCHEME;
schemeHooks;
options;
skew;
clock;
networks;
/**
* Creates the client scheme.
*
* @param options - Payer adapter, request binding provider, clock, and skew
*/
constructor(options) {
const skew = options.clockSkewSeconds ?? DEFAULT_CLOCK_SKEW_SECONDS;
validateSkew(skew);
this.options = options;
this.skew = skew;
this.clock = options.clock ?? unixNow;
this.networks = options.networks ?? LNBTC_NETWORKS;
this.schemeHooks = { onBeforePaymentCreation: ctx => this.checkResource(ctx) };
}
/**
* Validates the challenge against the intended request, pays the invoice,
* and verifies the returned preimage.
*
* @param x402Version - Protocol version
* @param requirements - Selected payment requirements
* @param context - Spend cap from the client's spend controls, if any
* @returns The payload carrying the preimage
*/
async createPaymentPayload(x402Version, requirements, context) {
const advertised = validateRequirements(requirements, this.networks);
assertWithinCap(requirements.amount, context?.maxAmountPerPayment);
const intended = await this.options.requestBinding();
if (!bindingsEqual(intended, advertised))
reject(Errors.requestMismatch);
const invoiceText = requirements.extra.invoice;
const options = { now: this.clock(), skew: this.skew };
const invoice = validateInvoice(invoiceText, requirements, intended.requestHash, this.networks, options);
checkExpiry(invoice, options, "unexpired");
const payment = await this.options.payer.payInvoice(invoiceText, requirements.network);
if (payment.invoice !== invoiceText)
reject(Errors.payerInvoiceMismatch);
if (payment.paymentHash !== invoice.paymentHash)
reject(Errors.payerPaymentHashMismatch);
if (payment.amountMsat !== invoice.amountMsat)
reject(Errors.payerAmountMismatch);
if (payment.status === "in_flight")
reject(Errors.paymentInFlight);
if (payment.status !== "paid")
reject(Errors.paymentNotPaid);
const preimage = payment.preimage;
if (preimage === undefined || preimage === null)
reject(Errors.payerPreimageRequired);
if (typeof preimage !== "string" || !/^[0-9a-f]{64}$/.test(preimage)) {
reject(Errors.payerPreimageMalformed);
}
if (bytesToHex(sha256(hexToBytes(preimage))) !== invoice.paymentHash) {
reject(Errors.payerPreimageHashMismatch);
}
return { x402Version, payload: { preimage } };
}
/**
* For `http:1`, requires `PaymentRequired.resource.url` to equal the
* intended request URL before any payment.
*
* @param ctx - Payment creation context
* @returns An abort result on mismatch
*/
async checkResource(ctx) {
try {
const intended = await this.options.requestBinding();
if (intended.requestBindingProfile === HTTP_PROFILE &&
ctx.paymentRequired.resource?.url !== intended.resourceUrl) {
return { abort: true, reason: Errors.requestMismatch };
}
}
catch (error) {
if (error instanceof LnbtcError)
return { abort: true, reason: error.reason };
throw error;
}
}
}
/**
* Refuses an amount above the client's atomic spend cap. Core spend controls
* already filter requirements; this keeps the scheme safe on its own.
*
* @param amount - Required millisatoshis (validated positive integer)
* @param cap - Atomic cap from spend controls, if any
*/
function assertWithinCap(amount, cap) {
if (cap === undefined)
return;
if (!/^[0-9]+$/.test(cap) || BigInt(amount) > BigInt(cap)) {
throw new Error(`lnbtc amount ${amount} msat exceeds maxAmountPerPayment ${cap}`);
}
}
//# sourceMappingURL=scheme.js.map