Skip to content

Commit fd20a6c

Browse files
[SDK] Deprecate payTo parameter and simplify facilitator implementation (#8177)
1 parent 9095041 commit fd20a6c

File tree

10 files changed

+47
-90
lines changed

10 files changed

+47
-90
lines changed

.github/workflows/auto-assign.yml

Lines changed: 0 additions & 19 deletions
This file was deleted.

apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ export function X402LeftSection(props: {
4444
const chainId = useId();
4545
const tokenId = useId();
4646
const amountId = useId();
47-
const payToId = useId();
4847
const waitUntilId = useId();
4948

5049
const handleChainChange = (chainId: number) => {
@@ -82,13 +81,6 @@ export function X402LeftSection(props: {
8281
}));
8382
};
8483

85-
const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>) => {
86-
setOptions((v) => ({
87-
...v,
88-
payTo: e.target.value as `0x${string}`,
89-
}));
90-
};
91-
9284
const handleWaitUntilChange = (
9385
value: "simulated" | "submitted" | "confirmed",
9486
) => {
@@ -148,22 +140,6 @@ export function X402LeftSection(props: {
148140
)}
149141
</div>
150142

151-
{/* Pay To input */}
152-
<div className="flex flex-col gap-2">
153-
<Label htmlFor={payToId}>Pay To Address</Label>
154-
<Input
155-
id={payToId}
156-
type="text"
157-
placeholder="0x..."
158-
value={options.payTo}
159-
onChange={handlePayToChange}
160-
className="bg-card"
161-
/>
162-
<p className="text-sm text-muted-foreground">
163-
The wallet address that will receive the payment
164-
</p>
165-
</div>
166-
167143
{/* Wait Until selection */}
168144
<div className="flex flex-col gap-2">
169145
<Label htmlFor={waitUntilId}>Wait Until</Label>

apps/playground-web/src/middleware.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,13 @@ const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string;
1313
const ENGINE_VAULT_ACCESS_TOKEN = process.env
1414
.ENGINE_VAULT_ACCESS_TOKEN as string;
1515
const API_URL = `https://${process.env.NEXT_PUBLIC_API_URL || "api.thirdweb.com"}`;
16-
function createFacilitator(
17-
waitUntil: "simulated" | "submitted" | "confirmed" = "simulated",
18-
) {
19-
return facilitator({
20-
baseUrl: `${API_URL}/v1/payments/x402`,
21-
client,
22-
serverWalletAddress: BACKEND_WALLET_ADDRESS,
23-
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
24-
waitUtil: waitUntil,
25-
});
26-
}
16+
17+
const twFacilitator = facilitator({
18+
baseUrl: `${API_URL}/v1/payments/x402`,
19+
client,
20+
serverWalletAddress: BACKEND_WALLET_ADDRESS,
21+
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
22+
});
2723

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

3531
const chainId = queryParams.get("chainId");
36-
const payTo = queryParams.get("payTo");
3732

38-
if (!chainId || !payTo) {
33+
if (!chainId) {
3934
return NextResponse.json(
4035
{ error: "Missing required parameters" },
4136
{ status: 400 },
@@ -53,7 +48,6 @@ export async function middleware(request: NextRequest) {
5348
resourceUrl,
5449
method,
5550
paymentData,
56-
payTo: payTo as `0x${string}`,
5751
network: defineChain(Number(chainId)),
5852
price: {
5953
amount: toUnits(amount, parseInt(decimals)).toString(),
@@ -65,7 +59,8 @@ export async function middleware(request: NextRequest) {
6559
routeConfig: {
6660
description: "Access to paid content",
6761
},
68-
facilitator: createFacilitator(waitUntil),
62+
waitUntil,
63+
facilitator: twFacilitator,
6964
});
7065

7166
if (result.status === 200) {

packages/thirdweb/src/x402/common.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export async function decodePaymentRequest(
4646
facilitator,
4747
resourceUrl,
4848
routeConfig = {},
49-
payTo,
5049
method,
5150
paymentData,
5251
} = args;
@@ -105,10 +104,9 @@ export async function decodePaymentRequest(
105104
resource: resourceUrl,
106105
description: description ?? "",
107106
mimeType: mimeType ?? "application/json",
108-
payTo: getAddress(payTo),
107+
payTo: getAddress(facilitator.address),
109108
maxTimeoutSeconds: maxTimeoutSeconds ?? 300,
110109
asset: getAddress(asset.address),
111-
// TODO: Rename outputSchema to requestStructure
112110
outputSchema: {
113111
input: {
114112
type: "http",
@@ -119,7 +117,6 @@ export async function decodePaymentRequest(
119117
output: outputSchema,
120118
},
121119
extra: {
122-
facilitatorAddress: facilitator.address,
123120
...((asset as ERC20TokenAmount["asset"]).eip712 ?? {}),
124121
},
125122
});
@@ -139,7 +136,7 @@ export async function decodePaymentRequest(
139136
};
140137
}
141138

142-
// Verify payment
139+
// decode b64 payment
143140
let decodedPayment: RequestedPaymentPayload;
144141
try {
145142
decodedPayment = decodePayment(paymentData);

packages/thirdweb/src/x402/facilitator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { withCache } from "../utils/promise/withCache.js";
55
import type {
66
FacilitatorSettleResponse,
77
FacilitatorSupportedResponse,
8+
FacilitatorVerifyResponse,
89
RequestedPaymentPayload,
910
RequestedPaymentRequirements,
1011
} from "./schemas.js";
@@ -35,7 +36,7 @@ export type ThirdwebX402Facilitator = {
3536
verify: (
3637
payload: RequestedPaymentPayload,
3738
paymentRequirements: RequestedPaymentRequirements,
38-
) => Promise<VerifyResponse>;
39+
) => Promise<FacilitatorVerifyResponse>;
3940
settle: (
4041
payload: RequestedPaymentPayload,
4142
paymentRequirements: RequestedPaymentRequirements,
@@ -126,7 +127,6 @@ export function facilitator(
126127
},
127128
settle: {
128129
"x-secret-key": secretKey,
129-
"x-settlement-wallet-address": serverWalletAddress,
130130
...(config.vaultAccessToken
131131
? { "x-vault-access-token": config.vaultAccessToken }
132132
: {}),
@@ -149,7 +149,7 @@ export function facilitator(
149149
async verify(
150150
payload: RequestedPaymentPayload,
151151
paymentRequirements: RequestedPaymentRequirements,
152-
): Promise<VerifyResponse> {
152+
): Promise<FacilitatorVerifyResponse> {
153153
const url = config.baseUrl ?? DEFAULT_BASE_URL;
154154

155155
let headers = { "Content-Type": "application/json" };

packages/thirdweb/src/x402/schemas.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
PaymentRequirementsSchema,
77
SettleResponseSchema,
88
SupportedPaymentKindsResponseSchema,
9+
VerifyResponseSchema,
910
} from "x402/types";
1011
import { z } from "zod";
1112
import type { Chain } from "../chains/types.js";
@@ -52,11 +53,20 @@ export type RequestedPaymentRequirements = z.infer<
5253

5354
const FacilitatorSettleResponseSchema = SettleResponseSchema.extend({
5455
network: FacilitatorNetworkSchema,
56+
errorMessage: z.string().optional(),
5557
});
5658
export type FacilitatorSettleResponse = z.infer<
5759
typeof FacilitatorSettleResponseSchema
5860
>;
5961

62+
const FacilitatorVerifyResponseSchema = VerifyResponseSchema.extend({
63+
errorMessage: z.string().optional(),
64+
});
65+
66+
export type FacilitatorVerifyResponse = z.infer<
67+
typeof FacilitatorVerifyResponseSchema
68+
>;
69+
6070
export const SupportedSignatureTypeSchema = z.enum([
6171
"TransferWithAuthorization",
6272
"Permit",

packages/thirdweb/src/x402/settle-payment.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,17 +156,17 @@ export async function settlePayment(
156156
},
157157
};
158158
} else {
159+
const error = settlement.errorReason || "Settlement error";
159160
return {
160161
status: 402,
161162
responseHeaders: {
162163
"Content-Type": "application/json",
163164
},
164165
responseBody: {
165166
x402Version,
166-
error:
167-
errorMessages?.settlementFailed ||
168-
settlement.errorReason ||
169-
"Settlement failed",
167+
error,
168+
errorMessage:
169+
errorMessages?.settlementFailed || settlement.errorMessage,
170170
accepts: paymentRequirements,
171171
},
172172
};
@@ -179,9 +179,10 @@ export async function settlePayment(
179179
},
180180
responseBody: {
181181
x402Version,
182-
error:
182+
error: "Settlement error",
183+
errorMessage:
183184
errorMessages?.settlementFailed ||
184-
(error instanceof Error ? error.message : "Settlement error"),
185+
(error instanceof Error ? error.message : undefined),
185186
accepts: paymentRequirements,
186187
},
187188
};

packages/thirdweb/src/x402/sign.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,13 @@ async function signERC3009Authorization(
230230

231231
async function signERC2612Permit(
232232
account: Account,
233-
{ from, value, validBefore, nonce }: ExactEvmPayloadAuthorization,
233+
{ from, to, value, validBefore, nonce }: ExactEvmPayloadAuthorization,
234234
{ asset, network, extra }: RequestedPaymentRequirements,
235235
): Promise<{ signature: Hex }> {
236236
const chainId = networkToChainId(network);
237237
const name = extra?.name;
238238
const version = extra?.version;
239-
const facilitatorAddress = extra?.facilitatorAddress;
240-
if (!facilitatorAddress) {
241-
throw new Error(
242-
"facilitatorAddress is required in PaymentRequirements extra to pay with permit-based assets",
243-
);
244-
}
239+
245240
if (!name || !version) {
246241
throw new Error(
247242
"name and version are required in PaymentRequirements extra to pay with permit-based assets",
@@ -268,7 +263,7 @@ async function signERC2612Permit(
268263
primaryType: "Permit" as const,
269264
message: {
270265
owner: getAddress(from),
271-
spender: getAddress(facilitatorAddress), // approve the facilitator
266+
spender: getAddress(to),
272267
value: BigInt(value),
273268
nonce: hexToBigInt(nonce as Hex),
274269
deadline: BigInt(validBefore),

packages/thirdweb/src/x402/types.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { Money, PaymentMiddlewareConfig } from "x402/types";
22
import type z from "zod";
33
import type { Chain } from "../chains/types.js";
4-
import type { Address } from "../utils/address.js";
54
import type { Prettify } from "../utils/type-utils.js";
65
import type { ThirdwebX402Facilitator, WaitUntil } from "./facilitator.js";
76
import type {
@@ -27,8 +26,6 @@ export type PaymentArgs = {
2726
method: "GET" | "POST" | ({} & string);
2827
/** The payment data/proof provided by the client, typically from the X-PAYMENT header */
2928
paymentData?: string | null;
30-
/** The wallet address that should receive the payment */
31-
payTo: Address;
3229
/** The blockchain network where the payment should be processed */
3330
network: FacilitatorNetwork | Chain;
3431
/** The price for accessing the resource - either a USD amount (e.g., "$0.10") or a specific token amount */
@@ -37,6 +34,8 @@ export type PaymentArgs = {
3734
facilitator: ThirdwebX402Facilitator;
3835
/** Optional configuration for the payment middleware route */
3936
routeConfig?: PaymentMiddlewareConfig;
37+
/** @deprecated Use facilitator.address instead */
38+
payTo?: string;
4039
};
4140

4241
export type SettlePaymentArgs = PaymentArgs & {
@@ -50,8 +49,10 @@ export type PaymentRequiredResult = {
5049
responseBody: {
5150
/** The X402 protocol version */
5251
x402Version: number;
53-
/** Human-readable error message */
52+
/** error code */
5453
error: string;
54+
/** Human-readable error message */
55+
errorMessage?: string;
5556
/** Array of acceptable payment methods and requirements */
5657
accepts: RequestedPaymentRequirements[];
5758
/** Optional payer address if verification partially succeeded */

packages/thirdweb/src/x402/verify-payment.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,17 @@ export async function verifyPayment(
101101
selectedPaymentRequirements,
102102
};
103103
} else {
104+
const error = verification.invalidReason || "Verification failed";
104105
return {
105106
status: 402,
106107
responseHeaders: {
107108
"Content-Type": "application/json",
108109
},
109110
responseBody: {
110111
x402Version,
111-
error:
112-
errorMessages?.verificationFailed ||
113-
verification.invalidReason ||
114-
"Verification failed",
112+
error: error,
113+
errorMessage:
114+
errorMessages?.verificationFailed || verification.errorMessage,
115115
accepts: paymentRequirements,
116116
},
117117
};
@@ -124,9 +124,10 @@ export async function verifyPayment(
124124
},
125125
responseBody: {
126126
x402Version,
127-
error:
127+
error: "Verification error",
128+
errorMessage:
128129
errorMessages?.verificationFailed ||
129-
(error instanceof Error ? error.message : "Verification error"),
130+
(error instanceof Error ? error.message : undefined),
130131
accepts: paymentRequirements,
131132
},
132133
};

0 commit comments

Comments
 (0)