@evvm/x402 0.0.1 → 0.1.3

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.
@@ -1,106 +0,0 @@
1
- import {
2
- Core,
3
- execute,
4
- type HexString,
5
- type IPayData,
6
- type ISerializableSignedAction,
7
- type ISigner,
8
- } from "@evvm/evvm-js";
9
- import { recoverMessageAddress } from "viem";
10
- import type { IFacilitator } from "../types";
11
-
12
- export class LocalFacilitator implements IFacilitator {
13
- constructor(public signer: ISigner) {}
14
-
15
- /**
16
- * Reconstructs the signed message and recovers the signer from the signature provided
17
- * to validate it. Asserts the nonce provided is valid.
18
- * @returns true if the recovered address match signedAction.data.from; false otherwise
19
- */
20
- async verifyPaySignature(
21
- signedAction: ISerializableSignedAction<IPayData>,
22
- ): Promise<boolean> {
23
- if (signedAction.functionName !== "pay")
24
- throw new Error("verifyPaySignature can only verify core.pay signatures");
25
-
26
- const core = new Core({
27
- address: signedAction.contractAddress as HexString,
28
- signer: this.signer,
29
- chainId: signedAction.chainId,
30
- });
31
- // replicate signed message
32
- const evvmId = await core.getEvvmID();
33
- const hashPayload = core.buildHashPayload(signedAction.functionName, {
34
- to_address: signedAction.data.to_address,
35
- to_identity: signedAction.data.to_identity,
36
- token: signedAction.data.token,
37
- amount: signedAction.data.amount,
38
- priorityFee: signedAction.data.priorityFee,
39
- });
40
-
41
- const message = core.buildMessageToSign(
42
- evvmId,
43
- signedAction.data.senderExecutor,
44
- hashPayload,
45
- signedAction.data.originExecutor,
46
- signedAction.data.nonce,
47
- signedAction.data.isAsyncExec,
48
- );
49
-
50
- // recover signer of the message
51
- const address = await recoverMessageAddress({
52
- message,
53
- signature: signedAction.data.signature as HexString,
54
- });
55
-
56
- if (address !== signedAction.data.from) {
57
- console.error("Couldn't recover address from signature");
58
- return false;
59
- }
60
-
61
- // verify nonces are ok
62
- if (signedAction.data.isAsyncExec) {
63
- // async execution, assert nonce hasn't been used before
64
- const used = await core.getIfUsedAsyncNonce(signedAction.data.nonce);
65
- if (used) {
66
- console.error("Invalid async nonce");
67
- return false;
68
- }
69
- } else {
70
- const nextExpectedNonce = await core.getNextCurrentSyncNonce();
71
- if (nextExpectedNonce.toString() != signedAction.data.nonce.toString()) {
72
- console.error("Invalid sync nonce");
73
- return false;
74
- }
75
- }
76
-
77
- // assert balances
78
- const balance = await core.getBalance(
79
- signedAction.data.from,
80
- signedAction.data.token,
81
- );
82
- if (balance <= signedAction.data.amount) {
83
- console.error("Insufficient balance");
84
- return false;
85
- }
86
-
87
- return true;
88
- }
89
-
90
- /**
91
- * Executes the evvm transaction.
92
- * @returns tx hash
93
- */
94
- async settlePayment(
95
- signedAction: ISerializableSignedAction<IPayData>,
96
- ): Promise<HexString | null> {
97
- try {
98
- const txHash = await execute(this.signer, signedAction);
99
- return txHash;
100
- } catch (error) {
101
- console.error("Failed to settle payment");
102
- console.error(error);
103
- return null;
104
- }
105
- }
106
- }
@@ -1,29 +0,0 @@
1
- import {
2
- PaymentPayloadV2Schema,
3
- type PaymentPayloadV2,
4
- } from "@x402/core/schemas";
5
-
6
- /**
7
- * Parses the PAYMENT-SIGNATURE header into PaymentPayloadV2 from x402/core
8
- * @returns PaymentPayloadV2 if it's a valid payload, null otherwise
9
- */
10
- export const parseHeader = (
11
- paymentSignatureHeader: string,
12
- ): PaymentPayloadV2 | null => {
13
- // decode header (it's a base64 encoded string)
14
- const decodedString = Buffer.from(paymentSignatureHeader, "base64").toString(
15
- "utf-8",
16
- );
17
-
18
- // assert it has the correct schema
19
- let payload = null;
20
- try {
21
- payload = PaymentPayloadV2Schema.parse(JSON.parse(decodedString));
22
- } catch (error) {
23
- console.error("Failed to parse payment payload");
24
- console.error(error);
25
- return null;
26
- }
27
-
28
- return payload;
29
- };
@@ -1,64 +0,0 @@
1
- import { type SettleResponse } from "@x402/core/types";
2
- import { type PaymentRequirementsV2 } from "@x402/core/schemas";
3
-
4
- /**
5
- * Creates the 402 payment required response, with headers, amounts and
6
- * everything else needed, returns the actual response (this should go directly to
7
- * the user)
8
- */
9
- export const paymentRequiredResponse = (
10
- offers: PaymentRequirementsV2[],
11
- ): Response => {
12
- const jsonString = JSON.stringify({ offers });
13
- const base64Payload = Buffer.from(jsonString).toString("base64");
14
-
15
- const headers = new Headers();
16
- headers.set("PAYMENT-REQUIRED", base64Payload);
17
- headers.set("Access-Control-Expose-Headers", "PAYMENT-REQUIRED"); // Vital for CORS
18
- headers.set("Content-Type", "application/json");
19
-
20
- console.log("Payment required");
21
-
22
- return new Response("Payment Required", { headers, status: 402 });
23
- };
24
-
25
- /**
26
- * Returned when an invalid response is received
27
- */
28
- export const invalidPaymentResponse = (reason: string): Response => {
29
- const settleResponse: SettleResponse = {
30
- success: false,
31
- errorMessage: "Invalid Payment",
32
- errorReason: reason,
33
- transaction: "",
34
- network: ":",
35
- };
36
-
37
- const jsonString = JSON.stringify(settleResponse);
38
- const base64Payload = Buffer.from(jsonString).toString("base64");
39
-
40
- const headers = new Headers();
41
- headers.set("PAYMENT-RESPONSE", base64Payload);
42
-
43
- console.log("Payment invalid");
44
- return new Response(`Payment Invalid: ${reason}`, { status: 400, headers });
45
- };
46
-
47
- // export const successfulPaymentResponse = (
48
- // txHash: HexString,
49
- // payload: IPaymentPayload,
50
- // ) => {
51
- // const settleResponse: SettleResponse = {
52
- // success: true,
53
- // payer: payload.payload.data.from,
54
- // transaction: txHash,
55
- // network: payload.accepted.network as "${string}:${string}",
56
- // };
57
- //
58
- // const jsonString = JSON.stringify(settleResponse);
59
- // const base64Payload = Buffer.from(jsonString).toString("base64");
60
- // const headers = new Headers();
61
- // headers.set("PAYMENT-REQUIRED", base64Payload);
62
- //
63
- // return new Response("Payment Required", { headers, status: 402 });
64
- // };
@@ -1,92 +0,0 @@
1
- import type {
2
- NextFunction,
3
- Request as ExpressRequest,
4
- Response as ExpressResponse,
5
- } from "express";
6
- import type { IEvvmSchema, IFacilitator } from "../types";
7
- import {
8
- invalidPaymentResponse,
9
- parseHeader,
10
- paymentRequiredResponse,
11
- } from "../lib";
12
- import {
13
- getSerializableSignedActionSchema,
14
- PayDataSchema,
15
- } from "@evvm/evvm-js";
16
- import type { SettleResponse } from "@x402/core/types";
17
-
18
- /**
19
- * This is used to parse Web API Response objects to express compatible
20
- * responses
21
- */
22
- const handleWebResponse = async (res: ExpressResponse, webRes: Response) => {
23
- res.status(webRes.status);
24
- webRes.headers.forEach((value, key) => {
25
- res.setHeader(key, value);
26
- });
27
-
28
- const body = await webRes.text();
29
- res.send(body);
30
- };
31
-
32
- /**
33
- * ExpressJS middleware that expects EVVM payments
34
- */
35
- export const requireEvvmPaymentExpress =
36
- (facilitator: IFacilitator, offers: IEvvmSchema[]) =>
37
- async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
38
- // assert payment is present
39
- const paymentHeader = req.header("PAYMENT-SIGNATURE");
40
- // if not present, return payment required
41
- if (!paymentHeader)
42
- return handleWebResponse(res, paymentRequiredResponse(offers));
43
-
44
- // if present, parse and validate it
45
- const parsed = parseHeader(paymentHeader);
46
- if (!parsed) {
47
- return handleWebResponse(res, invalidPaymentResponse("Invalid payment"));
48
- }
49
-
50
- const { success, data: signedAction } = getSerializableSignedActionSchema(
51
- PayDataSchema,
52
- ).safeParse(parsed.payload);
53
-
54
- if (!success) {
55
- return handleWebResponse(
56
- res,
57
- invalidPaymentResponse("Not an evvm payment"),
58
- );
59
- }
60
-
61
- // verify it
62
- if (!(await facilitator.verifyPaySignature(signedAction))) {
63
- return handleWebResponse(
64
- res,
65
- invalidPaymentResponse("Invalid signature"),
66
- );
67
- }
68
-
69
- // settle it
70
- const txHash = await facilitator.settlePayment(signedAction);
71
-
72
- if (!txHash) {
73
- return handleWebResponse(
74
- res,
75
- invalidPaymentResponse("Settlement failed"),
76
- );
77
- }
78
-
79
- const settleResponse: SettleResponse = {
80
- success: true,
81
- payer: signedAction.data.from,
82
- transaction: txHash,
83
- network: parsed.accepted.network as `${string}:${string}`,
84
- };
85
-
86
- const jsonString = JSON.stringify(settleResponse);
87
- const base64Payload = Buffer.from(jsonString).toString("base64");
88
-
89
- res.setHeader("PAYMENT-RESPONSE", base64Payload);
90
-
91
- next();
92
- };
@@ -1,79 +0,0 @@
1
- import type { NextRequest } from "next/server";
2
- import type { IEvvmSchema, IFacilitator } from "../types";
3
- import { invalidPaymentResponse, paymentRequiredResponse } from "../lib";
4
- import {
5
- getSerializableSignedActionSchema,
6
- PayDataSchema,
7
- } from "@evvm/evvm-js";
8
- import type { SettleResponse } from "@x402/core/types";
9
- import {
10
- PaymentPayloadV2Schema,
11
- type PaymentPayloadV2,
12
- } from "@x402/core/schemas";
13
-
14
- const parseHeaderEdge = (
15
- paymentSignatureHeader: string,
16
- ): PaymentPayloadV2 | null => {
17
- const decodedString = atob(paymentSignatureHeader);
18
-
19
- let payload = null;
20
- try {
21
- payload = PaymentPayloadV2Schema.parse(JSON.parse(decodedString));
22
- } catch (error) {
23
- console.error("Failed to parse payment payload");
24
- console.error(error);
25
- return null;
26
- }
27
-
28
- return payload;
29
- };
30
-
31
- /**
32
- * Creates a payment required middleware for nextjs
33
- */
34
- export const createEvvmMiddlewareNext =
35
- (facilitator: IFacilitator, offers: IEvvmSchema[]) =>
36
- async (req: NextRequest) => {
37
- const paymentHeader = req.headers.get("PAYMENT-SIGNATURE");
38
-
39
- if (!paymentHeader) {
40
- return paymentRequiredResponse(offers);
41
- }
42
-
43
- const parsed = parseHeaderEdge(paymentHeader);
44
- if (!parsed) {
45
- return invalidPaymentResponse("Invalid payment");
46
- }
47
-
48
- const { success, data: signedAction } = getSerializableSignedActionSchema(
49
- PayDataSchema,
50
- ).safeParse(parsed.payload);
51
-
52
- if (!success) {
53
- return invalidPaymentResponse("Not an evvm payment");
54
- }
55
-
56
- if (!(await facilitator.verifyPaySignature(signedAction))) {
57
- return invalidPaymentResponse("Invalid signature");
58
- }
59
-
60
- const txHash = await facilitator.settlePayment(signedAction);
61
-
62
- if (!txHash) {
63
- return invalidPaymentResponse("Settlement failed");
64
- }
65
-
66
- const settleResponse: SettleResponse = {
67
- success: true,
68
- payer: signedAction.data.from,
69
- transaction: txHash,
70
- network: parsed.accepted.network as `${string}:${string}`,
71
- };
72
-
73
- const jsonString = JSON.stringify(settleResponse);
74
- const base64Payload = btoa(jsonString);
75
-
76
- req.headers.set("PAYMENT-RESPONSE", base64Payload);
77
- };
78
-
79
- export default createEvvmMiddlewareNext;
@@ -1,19 +0,0 @@
1
- import { type HexString } from "@evvm/evvm-js";
2
- import { type PaymentRequirements } from "@x402/core/types";
3
-
4
- export interface IEvvmSchema extends PaymentRequirements {
5
- scheme: "evvm";
6
- network: `eip155:${number}`;
7
- amount: string;
8
- asset: HexString;
9
- payTo: string;
10
- maxTimeoutSeconds: number;
11
- /**
12
- * Custom metadata for successful evvm signature construction
13
- */
14
- extra: {
15
- coreContractAddress: HexString;
16
- evvmId?: number;
17
- originExecutor?: HexString;
18
- };
19
- }
@@ -1,15 +0,0 @@
1
- import type {
2
- HexString,
3
- IPayData,
4
- ISerializableSignedAction,
5
- } from "@evvm/evvm-js";
6
-
7
- export interface IFacilitator {
8
- verifyPaySignature(
9
- signedAction: ISerializableSignedAction<IPayData>,
10
- ): Promise<boolean>;
11
-
12
- settlePayment(
13
- signedAction: ISerializableSignedAction<IPayData>,
14
- ): Promise<HexString | null>;
15
- }
package/tsconfig.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
-
24
- // Some stricter flags (disabled by default)
25
- "noUnusedLocals": false,
26
- "noUnusedParameters": false,
27
- "noPropertyAccessFromIndexSignature": false
28
- }
29
- }
File without changes
File without changes