Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions .github/workflows/auto-assign.yml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export function X402LeftSection(props: {
const chainId = useId();
const tokenId = useId();
const amountId = useId();
const payToId = useId();
const waitUntilId = useId();

const handleChainChange = (chainId: number) => {
Expand Down Expand Up @@ -82,13 +81,6 @@ export function X402LeftSection(props: {
}));
};

const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setOptions((v) => ({
...v,
payTo: e.target.value as `0x${string}`,
}));
};

const handleWaitUntilChange = (
value: "simulated" | "submitted" | "confirmed",
) => {
Expand Down Expand Up @@ -148,22 +140,6 @@ export function X402LeftSection(props: {
)}
</div>

{/* Pay To input */}
<div className="flex flex-col gap-2">
<Label htmlFor={payToId}>Pay To Address</Label>
<Input
id={payToId}
type="text"
placeholder="0x..."
value={options.payTo}
onChange={handlePayToChange}
className="bg-card"
/>
<p className="text-sm text-muted-foreground">
The wallet address that will receive the payment
</p>
</div>

{/* Wait Until selection */}
<div className="flex flex-col gap-2">
<Label htmlFor={waitUntilId}>Wait Until</Label>
Expand Down
25 changes: 10 additions & 15 deletions apps/playground-web/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@ const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string;
const ENGINE_VAULT_ACCESS_TOKEN = process.env
.ENGINE_VAULT_ACCESS_TOKEN as string;
const API_URL = `https://${process.env.NEXT_PUBLIC_API_URL || "api.thirdweb.com"}`;
function createFacilitator(
waitUntil: "simulated" | "submitted" | "confirmed" = "simulated",
) {
return facilitator({
baseUrl: `${API_URL}/v1/payments/x402`,
client,
serverWalletAddress: BACKEND_WALLET_ADDRESS,
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
waitUtil: waitUntil,
});
}

const twFacilitator = facilitator({
baseUrl: `${API_URL}/v1/payments/x402`,
client,
serverWalletAddress: BACKEND_WALLET_ADDRESS,
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
});

export async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
Expand All @@ -33,9 +29,8 @@ export async function middleware(request: NextRequest) {
const queryParams = request.nextUrl.searchParams;

const chainId = queryParams.get("chainId");
const payTo = queryParams.get("payTo");

if (!chainId || !payTo) {
if (!chainId) {
return NextResponse.json(
{ error: "Missing required parameters" },
{ status: 400 },
Expand All @@ -53,7 +48,6 @@ export async function middleware(request: NextRequest) {
resourceUrl,
method,
paymentData,
payTo: payTo as `0x${string}`,
network: defineChain(Number(chainId)),
price: {
amount: toUnits(amount, parseInt(decimals)).toString(),
Expand All @@ -65,7 +59,8 @@ export async function middleware(request: NextRequest) {
routeConfig: {
description: "Access to paid content",
},
facilitator: createFacilitator(waitUntil),
waitUntil,
facilitator: twFacilitator,
});

if (result.status === 200) {
Expand Down
7 changes: 2 additions & 5 deletions packages/thirdweb/src/x402/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export async function decodePaymentRequest(
facilitator,
resourceUrl,
routeConfig = {},
payTo,
method,
paymentData,
} = args;
Expand Down Expand Up @@ -105,10 +104,9 @@ export async function decodePaymentRequest(
resource: resourceUrl,
description: description ?? "",
mimeType: mimeType ?? "application/json",
payTo: getAddress(payTo),
payTo: getAddress(facilitator.address),
maxTimeoutSeconds: maxTimeoutSeconds ?? 300,
asset: getAddress(asset.address),
// TODO: Rename outputSchema to requestStructure
outputSchema: {
input: {
type: "http",
Expand All @@ -119,7 +117,6 @@ export async function decodePaymentRequest(
output: outputSchema,
},
extra: {
facilitatorAddress: facilitator.address,
...((asset as ERC20TokenAmount["asset"]).eip712 ?? {}),
},
});
Expand All @@ -139,7 +136,7 @@ export async function decodePaymentRequest(
};
}

// Verify payment
// decode b64 payment
let decodedPayment: RequestedPaymentPayload;
try {
decodedPayment = decodePayment(paymentData);
Expand Down
6 changes: 3 additions & 3 deletions packages/thirdweb/src/x402/facilitator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { withCache } from "../utils/promise/withCache.js";
import type {
FacilitatorSettleResponse,
FacilitatorSupportedResponse,
FacilitatorVerifyResponse,
RequestedPaymentPayload,
RequestedPaymentRequirements,
} from "./schemas.js";
Expand Down Expand Up @@ -35,7 +36,7 @@ export type ThirdwebX402Facilitator = {
verify: (
payload: RequestedPaymentPayload,
paymentRequirements: RequestedPaymentRequirements,
) => Promise<VerifyResponse>;
) => Promise<FacilitatorVerifyResponse>;
settle: (
payload: RequestedPaymentPayload,
paymentRequirements: RequestedPaymentRequirements,
Expand Down Expand Up @@ -126,7 +127,6 @@ export function facilitator(
},
settle: {
"x-secret-key": secretKey,
"x-settlement-wallet-address": serverWalletAddress,
...(config.vaultAccessToken
? { "x-vault-access-token": config.vaultAccessToken }
: {}),
Expand All @@ -149,7 +149,7 @@ export function facilitator(
async verify(
payload: RequestedPaymentPayload,
paymentRequirements: RequestedPaymentRequirements,
): Promise<VerifyResponse> {
): Promise<FacilitatorVerifyResponse> {
const url = config.baseUrl ?? DEFAULT_BASE_URL;

let headers = { "Content-Type": "application/json" };
Expand Down
10 changes: 10 additions & 0 deletions packages/thirdweb/src/x402/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
PaymentRequirementsSchema,
SettleResponseSchema,
SupportedPaymentKindsResponseSchema,
VerifyResponseSchema,
} from "x402/types";
import { z } from "zod";
import type { Chain } from "../chains/types.js";
Expand Down Expand Up @@ -52,11 +53,20 @@ export type RequestedPaymentRequirements = z.infer<

const FacilitatorSettleResponseSchema = SettleResponseSchema.extend({
network: FacilitatorNetworkSchema,
errorMessage: z.string().optional(),
});
export type FacilitatorSettleResponse = z.infer<
typeof FacilitatorSettleResponseSchema
>;

const FacilitatorVerifyResponseSchema = VerifyResponseSchema.extend({
errorMessage: z.string().optional(),
});

export type FacilitatorVerifyResponse = z.infer<
typeof FacilitatorVerifyResponseSchema
>;

export const SupportedSignatureTypeSchema = z.enum([
"TransferWithAuthorization",
"Permit",
Expand Down
13 changes: 7 additions & 6 deletions packages/thirdweb/src/x402/settle-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,17 @@ export async function settlePayment(
},
};
} else {
const error = settlement.errorReason || "Settlement error";
return {
status: 402,
responseHeaders: {
"Content-Type": "application/json",
},
responseBody: {
x402Version,
error:
errorMessages?.settlementFailed ||
settlement.errorReason ||
"Settlement failed",
error,
errorMessage:
errorMessages?.settlementFailed || settlement.errorMessage,
accepts: paymentRequirements,
},
};
Expand All @@ -179,9 +179,10 @@ export async function settlePayment(
},
responseBody: {
x402Version,
error:
error: "Settlement error",
errorMessage:
errorMessages?.settlementFailed ||
(error instanceof Error ? error.message : "Settlement error"),
(error instanceof Error ? error.message : undefined),
accepts: paymentRequirements,
},
};
Expand Down
11 changes: 3 additions & 8 deletions packages/thirdweb/src/x402/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,13 @@ async function signERC3009Authorization(

async function signERC2612Permit(
account: Account,
{ from, value, validBefore, nonce }: ExactEvmPayloadAuthorization,
{ from, to, value, validBefore, nonce }: ExactEvmPayloadAuthorization,
{ asset, network, extra }: RequestedPaymentRequirements,
): Promise<{ signature: Hex }> {
const chainId = networkToChainId(network);
const name = extra?.name;
const version = extra?.version;
const facilitatorAddress = extra?.facilitatorAddress;
if (!facilitatorAddress) {
throw new Error(
"facilitatorAddress is required in PaymentRequirements extra to pay with permit-based assets",
);
}

if (!name || !version) {
throw new Error(
"name and version are required in PaymentRequirements extra to pay with permit-based assets",
Expand All @@ -268,7 +263,7 @@ async function signERC2612Permit(
primaryType: "Permit" as const,
message: {
owner: getAddress(from),
spender: getAddress(facilitatorAddress), // approve the facilitator
spender: getAddress(to),
value: BigInt(value),
nonce: hexToBigInt(nonce as Hex),
deadline: BigInt(validBefore),
Expand Down
9 changes: 5 additions & 4 deletions packages/thirdweb/src/x402/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Money, PaymentMiddlewareConfig } from "x402/types";
import type z from "zod";
import type { Chain } from "../chains/types.js";
import type { Address } from "../utils/address.js";
import type { Prettify } from "../utils/type-utils.js";
import type { ThirdwebX402Facilitator, WaitUntil } from "./facilitator.js";
import type {
Expand All @@ -27,8 +26,6 @@ export type PaymentArgs = {
method: "GET" | "POST" | ({} & string);
/** The payment data/proof provided by the client, typically from the X-PAYMENT header */
paymentData?: string | null;
/** The wallet address that should receive the payment */
payTo: Address;
/** The blockchain network where the payment should be processed */
network: FacilitatorNetwork | Chain;
/** The price for accessing the resource - either a USD amount (e.g., "$0.10") or a specific token amount */
Expand All @@ -37,6 +34,8 @@ export type PaymentArgs = {
facilitator: ThirdwebX402Facilitator;
/** Optional configuration for the payment middleware route */
routeConfig?: PaymentMiddlewareConfig;
/** @deprecated Use facilitator.address instead */
payTo?: string;
};

export type SettlePaymentArgs = PaymentArgs & {
Expand All @@ -50,8 +49,10 @@ export type PaymentRequiredResult = {
responseBody: {
/** The X402 protocol version */
x402Version: number;
/** Human-readable error message */
/** error code */
error: string;
/** Human-readable error message */
errorMessage?: string;
/** Array of acceptable payment methods and requirements */
accepts: RequestedPaymentRequirements[];
/** Optional payer address if verification partially succeeded */
Expand Down
13 changes: 7 additions & 6 deletions packages/thirdweb/src/x402/verify-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,17 @@ export async function verifyPayment(
selectedPaymentRequirements,
};
} else {
const error = verification.invalidReason || "Verification failed";
return {
status: 402,
responseHeaders: {
"Content-Type": "application/json",
},
responseBody: {
x402Version,
error:
errorMessages?.verificationFailed ||
verification.invalidReason ||
"Verification failed",
error: error,
errorMessage:
errorMessages?.verificationFailed || verification.errorMessage,
accepts: paymentRequirements,
},
};
Expand All @@ -124,9 +124,10 @@ export async function verifyPayment(
},
responseBody: {
x402Version,
error:
error: "Verification error",
errorMessage:
errorMessages?.verificationFailed ||
(error instanceof Error ? error.message : "Verification error"),
(error instanceof Error ? error.message : undefined),
accepts: paymentRequirements,
},
};
Expand Down
Loading