import { x402Facilitator } from "@x402/core/facilitator";
import {
PaymentPayload,
PaymentRequirements,
SettleResponse,
VerifyResponse,
} from "@x402/core/types";
import { toFacilitatorEvmSigner } from "@x402/evm";
import { registerExactEvmScheme } from "@x402/evm/exact/facilitator";
import dotenv from "dotenv";
import express from "express";
import { createWalletClient, http, publicActions } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { skaleBaseSepoliaTestnet } from "viem/chains";
dotenv.config();
const PORT = process.env.PORT || "4022";
if (!process.env.FACILITATOR_SIGNER_PK) {
console.error("FACILITATOR_SIGNER_PK environment variable is required");
process.exit(1);
}
const evmAccount = privateKeyToAccount(
process.env.FACILITATOR_SIGNER_PK as `0x${string}`
);
console.info(`EVM Facilitator account: ${evmAccount.address}`);
const viemClient = createWalletClient({
account: evmAccount,
chain: skaleBaseSepoliaTestnet,
transport: http(),
}).extend(publicActions);
const evmSigner = toFacilitatorEvmSigner({
getCode: (args: { address: `0x${string}` }) => viemClient.getCode(args),
address: evmAccount.address,
readContract: (args: {
address: `0x${string}`;
abi: readonly unknown[];
functionName: string;
args?: readonly unknown[];
}) =>
viemClient.readContract({
...args,
args: args.args || [],
}),
verifyTypedData: (args: {
address: `0x${string}`;
domain: Record<string, unknown>;
types: Record<string, unknown>;
primaryType: string;
message: Record<string, unknown>;
signature: `0x${string}`;
}) => viemClient.verifyTypedData(args as Parameters<typeof viemClient.verifyTypedData>[0]),
writeContract: (args: {
address: `0x${string}`;
abi: readonly unknown[];
functionName: string;
args: readonly unknown[];
}) =>
viemClient.writeContract({
...args,
args: args.args || [],
}),
sendTransaction: (args: { to: `0x${string}`; data: `0x${string}` }) =>
viemClient.sendTransaction(args),
waitForTransactionReceipt: (args: { hash: `0x${string}` }) =>
viemClient.waitForTransactionReceipt(args),
});
const facilitator = new x402Facilitator()
.onBeforeVerify(async (context) => {
console.log("Before verify", context);
})
.onAfterVerify(async (context) => {
console.log("After verify", context);
})
.onVerifyFailure(async (context) => {
console.log("Verify failure", context);
})
.onBeforeSettle(async (context) => {
console.log("Before settle", context);
})
.onAfterSettle(async (context) => {
console.log("After settle", context);
})
.onSettleFailure(async (context) => {
console.log("Settle failure", context);
});
registerExactEvmScheme(facilitator, {
signer: evmSigner,
networks: "eip155:324705682",
deployERC4337WithEIP6492: true,
});
const app = express();
app.use(express.json());
app.post("/verify", async (req, res) => {
try {
const { paymentPayload, paymentRequirements } = req.body as {
paymentPayload: PaymentPayload;
paymentRequirements: PaymentRequirements;
};
if (!paymentPayload || !paymentRequirements) {
return res.status(400).json({
error: "Missing paymentPayload or paymentRequirements",
});
}
const response: VerifyResponse = await facilitator.verify(
paymentPayload,
paymentRequirements
);
res.json(response);
} catch (error) {
console.error("Verify error:", error);
res.status(500).json({
error: error instanceof Error ? error.message : "Unknown error",
});
}
});
app.post("/settle", async (req, res) => {
try {
const { paymentPayload, paymentRequirements } = req.body;
if (!paymentPayload || !paymentRequirements) {
return res.status(400).json({
error: "Missing paymentPayload or paymentRequirements",
});
}
const response: SettleResponse = await facilitator.settle(
paymentPayload as PaymentPayload,
paymentRequirements as PaymentRequirements
);
res.json(response);
} catch (error) {
console.error("Settle error:", error);
if (
error instanceof Error &&
error.message.includes("Settlement aborted:")
) {
return res.json({
success: false,
errorReason: error.message.replace("Settlement aborted: ", ""),
network: req.body?.paymentPayload?.network || "unknown",
} as SettleResponse);
}
res.status(500).json({
error: error instanceof Error ? error.message : "Unknown error",
});
}
});
app.get("/supported", async (req, res) => {
try {
const response = facilitator.getSupported();
res.json(response);
} catch (error) {
console.error("Supported error:", error);
res.status(500).json({
error: error instanceof Error ? error.message : "Unknown error",
});
}
});
app.listen(parseInt(PORT), () => {
console.log(`Facilitator listening on port ${PORT}`);
});