thirdweb 5.106.0 → 5.107.0
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/dist/cjs/exports/x402.js +8 -0
- package/dist/cjs/exports/x402.js.map +1 -0
- package/dist/cjs/react/web/ui/Bridge/BuyWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/CheckoutWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/TransactionWidget.js +1 -1
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js +10 -3
- package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/wallets/smart/lib/bundler.js +3 -0
- package/dist/cjs/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/cjs/x402/facilitator.js +76 -0
- package/dist/cjs/x402/facilitator.js.map +1 -0
- package/dist/cjs/x402/fetchWithPayment.js +121 -0
- package/dist/cjs/x402/fetchWithPayment.js.map +1 -0
- package/dist/esm/exports/x402.js +3 -0
- package/dist/esm/exports/x402.js.map +1 -0
- package/dist/esm/react/web/ui/Bridge/BuyWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/CheckoutWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/TransactionWidget.js +1 -1
- package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js +10 -3
- package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/wallets/smart/lib/bundler.js +3 -0
- package/dist/esm/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/esm/x402/facilitator.js +73 -0
- package/dist/esm/x402/facilitator.js.map +1 -0
- package/dist/esm/x402/fetchWithPayment.js +118 -0
- package/dist/esm/x402/fetchWithPayment.js.map +1 -0
- package/dist/types/exports/x402.d.ts +3 -0
- package/dist/types/exports/x402.d.ts.map +1 -0
- package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/CheckoutWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/TransactionWidget.d.ts +1 -1
- package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts +8 -0
- package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/wallets/smart/lib/bundler.d.ts.map +1 -1
- package/dist/types/x402/facilitator.d.ts +48 -0
- package/dist/types/x402/facilitator.d.ts.map +1 -0
- package/dist/types/x402/fetchWithPayment.d.ts +43 -0
- package/dist/types/x402/fetchWithPayment.d.ts.map +1 -0
- package/package.json +12 -3
- package/src/exports/x402.ts +5 -0
- package/src/react/web/ui/Bridge/BuyWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/CheckoutWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/TransactionWidget.tsx +1 -1
- package/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx +24 -3
- package/src/version.ts +1 -1
- package/src/wallets/smart/lib/bundler.ts +4 -0
- package/src/x402/facilitator.ts +87 -0
- package/src/x402/fetchWithPayment.ts +173 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createPaymentHeader } from "x402/client";
|
|
2
|
+
import {
|
|
3
|
+
ChainIdToNetwork,
|
|
4
|
+
EvmNetworkToChainId,
|
|
5
|
+
type PaymentRequirements,
|
|
6
|
+
PaymentRequirementsSchema,
|
|
7
|
+
type Signer,
|
|
8
|
+
} from "x402/types";
|
|
9
|
+
import { viemAdapter } from "../adapters/viem.js";
|
|
10
|
+
import { getCachedChain } from "../chains/utils.js";
|
|
11
|
+
import type { ThirdwebClient } from "../client/client.js";
|
|
12
|
+
import type { Wallet } from "../wallets/interfaces/wallet.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enables the payment of APIs using the x402 payment protocol.
|
|
16
|
+
*
|
|
17
|
+
* This function wraps the native fetch API to automatically handle 402 Payment Required responses
|
|
18
|
+
* by creating and sending a payment header. It will:
|
|
19
|
+
* 1. Make the initial request
|
|
20
|
+
* 2. If a 402 response is received, parse the payment requirements
|
|
21
|
+
* 3. Verify the payment amount is within the allowed maximum
|
|
22
|
+
* 4. Create a payment header using the provided wallet client
|
|
23
|
+
* 5. Retry the request with the payment header
|
|
24
|
+
*
|
|
25
|
+
* @param fetch - The fetch function to wrap (typically globalThis.fetch)
|
|
26
|
+
* @param client - The thirdweb client used to access RPC infrastructure
|
|
27
|
+
* @param wallet - The wallet used to sign payment messages
|
|
28
|
+
* @param maxValue - The maximum allowed payment amount in base units (defaults to 1 USDC)
|
|
29
|
+
* @returns A wrapped fetch function that handles 402 responses automatically
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { wrapFetchWithPayment } from "thirdweb/x402";
|
|
34
|
+
* import { createThirdwebClient } from "thirdweb";
|
|
35
|
+
* import { createWallet } from "thirdweb/wallets";
|
|
36
|
+
*
|
|
37
|
+
* const client = createThirdwebClient({ clientId: "your-client-id" });
|
|
38
|
+
* const wallet = createWallet("io.metamask");
|
|
39
|
+
* await wallet.connect({ client })
|
|
40
|
+
*
|
|
41
|
+
* const fetchWithPay = wrapFetchWithPayment(fetch, client, wallet);
|
|
42
|
+
*
|
|
43
|
+
* // Make a request that may require payment
|
|
44
|
+
* const response = await fetchWithPay('https://api.example.com/paid-endpoint');
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @throws {Error} If the payment amount exceeds the maximum allowed value
|
|
48
|
+
* @throws {Error} If a payment has already been attempted for this request
|
|
49
|
+
* @throws {Error} If there's an error creating the payment header
|
|
50
|
+
*
|
|
51
|
+
* @bridge x402
|
|
52
|
+
*/
|
|
53
|
+
export function wrapFetchWithPayment(
|
|
54
|
+
fetch: typeof globalThis.fetch,
|
|
55
|
+
client: ThirdwebClient,
|
|
56
|
+
wallet: Wallet,
|
|
57
|
+
maxValue: bigint = BigInt(1 * 10 ** 6), // Default to 1 USDC
|
|
58
|
+
) {
|
|
59
|
+
return async (input: RequestInfo, init?: RequestInit) => {
|
|
60
|
+
const response = await fetch(input, init);
|
|
61
|
+
|
|
62
|
+
if (response.status !== 402) {
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { x402Version, accepts } = (await response.json()) as {
|
|
67
|
+
x402Version: number;
|
|
68
|
+
accepts: unknown[];
|
|
69
|
+
};
|
|
70
|
+
const parsedPaymentRequirements = accepts
|
|
71
|
+
.map((x) => PaymentRequirementsSchema.parse(x))
|
|
72
|
+
.filter((x) => x.scheme === "exact"); // TODO (402): accept other schemes
|
|
73
|
+
|
|
74
|
+
const account = wallet.getAccount();
|
|
75
|
+
let chain = wallet.getChain();
|
|
76
|
+
|
|
77
|
+
if (!account || !chain) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Wallet not connected. Please connect your wallet to continue.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const selectedPaymentRequirements = defaultPaymentRequirementsSelector(
|
|
83
|
+
parsedPaymentRequirements,
|
|
84
|
+
chain.id,
|
|
85
|
+
"exact",
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (BigInt(selectedPaymentRequirements.maxAmountRequired) > maxValue) {
|
|
89
|
+
throw new Error("Payment amount exceeds maximum allowed");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const paymentChainId = EvmNetworkToChainId.get(
|
|
93
|
+
selectedPaymentRequirements.network,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (!paymentChainId) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`No chain found for the selected payment requirement: ${selectedPaymentRequirements.network}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// switch to the payment chain if it's not the current chain
|
|
103
|
+
if (paymentChainId !== chain.id) {
|
|
104
|
+
await wallet.switchChain(getCachedChain(paymentChainId));
|
|
105
|
+
chain = wallet.getChain();
|
|
106
|
+
if (!chain) {
|
|
107
|
+
throw new Error(`Failed to switch chain (${paymentChainId})`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const walletClient = viemAdapter.wallet.toViem({
|
|
112
|
+
wallet: wallet,
|
|
113
|
+
chain,
|
|
114
|
+
client,
|
|
115
|
+
}) as Signer;
|
|
116
|
+
|
|
117
|
+
const paymentHeader = await createPaymentHeader(
|
|
118
|
+
walletClient,
|
|
119
|
+
x402Version,
|
|
120
|
+
selectedPaymentRequirements,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const initParams = init || {};
|
|
124
|
+
|
|
125
|
+
if ((initParams as { __is402Retry?: boolean }).__is402Retry) {
|
|
126
|
+
throw new Error("Payment already attempted");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const newInit = {
|
|
130
|
+
...initParams,
|
|
131
|
+
headers: {
|
|
132
|
+
...(initParams.headers || {}),
|
|
133
|
+
"X-PAYMENT": paymentHeader,
|
|
134
|
+
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE",
|
|
135
|
+
},
|
|
136
|
+
__is402Retry: true,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const secondResponse = await fetch(input, newInit);
|
|
140
|
+
return secondResponse;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function defaultPaymentRequirementsSelector(
|
|
145
|
+
paymentRequirements: PaymentRequirements[],
|
|
146
|
+
chainId: number,
|
|
147
|
+
scheme: "exact",
|
|
148
|
+
) {
|
|
149
|
+
if (!paymentRequirements.length) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"No valid payment requirements found in server 402 response",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const currentWalletNetwork = ChainIdToNetwork[chainId];
|
|
155
|
+
// find the payment requirements matching the connected wallet chain
|
|
156
|
+
const matchingPaymentRequirements = paymentRequirements.find(
|
|
157
|
+
(x) => x.network === currentWalletNetwork && x.scheme === scheme,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (matchingPaymentRequirements) {
|
|
161
|
+
return matchingPaymentRequirements;
|
|
162
|
+
} else {
|
|
163
|
+
// if no matching payment requirements, use the first payment requirement
|
|
164
|
+
// and switch the wallet to that chain
|
|
165
|
+
const firstPaymentRequirement = paymentRequirements.find(
|
|
166
|
+
(x) => x.scheme === scheme,
|
|
167
|
+
);
|
|
168
|
+
if (!firstPaymentRequirement) {
|
|
169
|
+
throw new Error("No suitable payment requirements found");
|
|
170
|
+
}
|
|
171
|
+
return firstPaymentRequirement;
|
|
172
|
+
}
|
|
173
|
+
}
|