@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.
- package/LICENSE +193 -0
- package/README.md +10 -34
- package/dist/index.browser.mjs +76 -0
- package/dist/index.cjs +76 -0
- package/dist/index.mjs +76 -0
- package/dist/types/lib/local-facilitator.d.ts +20 -0
- package/dist/types/lib/parse-header.d.ts +6 -0
- package/dist/types/lib/payment-responses.d.ts +11 -0
- package/dist/types/middleware/express.d.ts +6 -0
- package/dist/types/middleware/next.d.ts +7 -0
- package/dist/types/types/evvm-schema.type.d.ts +18 -0
- package/dist/types/types/facilitator.type.d.ts +5 -0
- package/{src/types/payment-payload.type.ts → dist/types/types/payment-payload.type.d.ts} +2 -4
- package/package.json +54 -2
- package/bun.lock +0 -370
- package/index.ts +0 -1
- package/src/lib/local-facilitator.ts +0 -106
- package/src/lib/parse-header.ts +0 -29
- package/src/lib/payment-responses.ts +0 -64
- package/src/middleware/express.ts +0 -92
- package/src/middleware/next.ts +0 -79
- package/src/types/evvm-schema.type.ts +0 -19
- package/src/types/facilitator.type.ts +0 -15
- package/tsconfig.json +0 -29
- /package/{src/index.ts → dist/types/index.d.ts} +0 -0
- /package/{src/lib/index.ts → dist/types/lib/index.d.ts} +0 -0
- /package/{src/middleware/index.ts → dist/types/middleware/index.d.ts} +0 -0
- /package/{src/types/index.ts → dist/types/types/index.d.ts} +0 -0
|
@@ -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
|
-
}
|
package/src/lib/parse-header.ts
DELETED
|
@@ -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
|
-
};
|
package/src/middleware/next.ts
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|