thirdweb 5.115.4 → 5.116.1
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/react.js.map +1 -1
- package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js +5 -1
- package/dist/cjs/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -1
- package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js +6 -1
- package/dist/cjs/react/native/hooks/x402/useFetchWithPayment.js.map +1 -1
- package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js +7 -1
- package/dist/cjs/react/web/hooks/x402/useFetchWithPayment.js.map +1 -1
- package/dist/cjs/react/web/ui/Bridge/bridge-widget/bridge-widget.js +0 -6
- package/dist/cjs/react/web/ui/Bridge/bridge-widget/bridge-widget.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/x402/facilitator.js +1 -0
- package/dist/cjs/x402/facilitator.js.map +1 -1
- package/dist/cjs/x402/fetchWithPayment.js +13 -1
- package/dist/cjs/x402/fetchWithPayment.js.map +1 -1
- package/dist/cjs/x402/permitSignatureStorage.js +70 -0
- package/dist/cjs/x402/permitSignatureStorage.js.map +1 -0
- package/dist/cjs/x402/schemas.js +4 -0
- package/dist/cjs/x402/schemas.js.map +1 -1
- package/dist/cjs/x402/sign.js +51 -4
- package/dist/cjs/x402/sign.js.map +1 -1
- package/dist/cjs/x402/types.js.map +1 -1
- package/dist/cjs/x402/verify-payment.js +3 -0
- package/dist/cjs/x402/verify-payment.js.map +1 -1
- package/dist/esm/exports/react.js.map +1 -1
- package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js +5 -1
- package/dist/esm/react/core/hooks/x402/useFetchWithPaymentCore.js.map +1 -1
- package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js +6 -1
- package/dist/esm/react/native/hooks/x402/useFetchWithPayment.js.map +1 -1
- package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js +8 -2
- package/dist/esm/react/web/hooks/x402/useFetchWithPayment.js.map +1 -1
- package/dist/esm/react/web/ui/Bridge/bridge-widget/bridge-widget.js +0 -6
- package/dist/esm/react/web/ui/Bridge/bridge-widget/bridge-widget.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/x402/facilitator.js +1 -0
- package/dist/esm/x402/facilitator.js.map +1 -1
- package/dist/esm/x402/fetchWithPayment.js +13 -1
- package/dist/esm/x402/fetchWithPayment.js.map +1 -1
- package/dist/esm/x402/permitSignatureStorage.js +65 -0
- package/dist/esm/x402/permitSignatureStorage.js.map +1 -0
- package/dist/esm/x402/schemas.js +4 -0
- package/dist/esm/x402/schemas.js.map +1 -1
- package/dist/esm/x402/sign.js +51 -4
- package/dist/esm/x402/sign.js.map +1 -1
- package/dist/esm/x402/types.js.map +1 -1
- package/dist/esm/x402/verify-payment.js +3 -0
- package/dist/esm/x402/verify-payment.js.map +1 -1
- package/dist/scripts/bridge-widget.js +2 -2
- package/dist/types/exports/react.d.ts +1 -0
- package/dist/types/exports/react.d.ts.map +1 -1
- package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts +6 -0
- package/dist/types/react/core/hooks/x402/useFetchWithPaymentCore.d.ts.map +1 -1
- package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts +1 -0
- package/dist/types/react/native/hooks/x402/useFetchWithPayment.d.ts.map +1 -1
- package/dist/types/react/web/hooks/x402/useFetchWithPayment.d.ts.map +1 -1
- package/dist/types/react/web/ui/Bridge/bridge-widget/bridge-widget.d.ts +0 -6
- package/dist/types/react/web/ui/Bridge/bridge-widget/bridge-widget.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/x402/facilitator.d.ts.map +1 -1
- package/dist/types/x402/fetchWithPayment.d.ts +6 -0
- package/dist/types/x402/fetchWithPayment.d.ts.map +1 -1
- package/dist/types/x402/permitSignatureStorage.d.ts +43 -0
- package/dist/types/x402/permitSignatureStorage.d.ts.map +1 -0
- package/dist/types/x402/schemas.d.ts +12 -0
- package/dist/types/x402/schemas.d.ts.map +1 -1
- package/dist/types/x402/sign.d.ts +3 -1
- package/dist/types/x402/sign.d.ts.map +1 -1
- package/dist/types/x402/types.d.ts +8 -0
- package/dist/types/x402/types.d.ts.map +1 -1
- package/dist/types/x402/verify-payment.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/exports/react.ts +1 -0
- package/src/react/core/hooks/x402/useFetchWithPaymentCore.ts +11 -1
- package/src/react/native/hooks/x402/useFetchWithPayment.ts +6 -1
- package/src/react/web/hooks/x402/useFetchWithPayment.tsx +12 -2
- package/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx +0 -6
- package/src/version.ts +1 -1
- package/src/x402/facilitator.ts +1 -0
- package/src/x402/fetchWithPayment.ts +21 -0
- package/src/x402/permitSignatureStorage.ts +99 -0
- package/src/x402/schemas.ts +4 -0
- package/src/x402/sign.ts +76 -1
- package/src/x402/types.ts +8 -0
- package/src/x402/verify-payment.ts +3 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { AsyncStorage } from "../utils/storage/AsyncStorage.js";
|
|
2
|
+
import type { RequestedPaymentPayload } from "./schemas.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Cached permit signature data structure
|
|
6
|
+
*/
|
|
7
|
+
type CachedPermitSignature = {
|
|
8
|
+
payload: RequestedPaymentPayload;
|
|
9
|
+
deadline: string;
|
|
10
|
+
maxAmount: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parameters for generating a permit cache key
|
|
15
|
+
*/
|
|
16
|
+
export type PermitCacheKeyParams = {
|
|
17
|
+
chainId: number;
|
|
18
|
+
asset: string;
|
|
19
|
+
owner: string;
|
|
20
|
+
spender: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const CACHE_KEY_PREFIX = "x402:permit";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generates a cache key for permit signature storage
|
|
27
|
+
* @param params - The parameters to generate the cache key from
|
|
28
|
+
* @returns The cache key string
|
|
29
|
+
*/
|
|
30
|
+
function getPermitCacheKey(params: PermitCacheKeyParams): string {
|
|
31
|
+
return `${CACHE_KEY_PREFIX}:${params.chainId}:${params.asset.toLowerCase()}:${params.owner.toLowerCase()}:${params.spender.toLowerCase()}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Retrieves a cached permit signature from storage
|
|
36
|
+
* @param storage - The AsyncStorage instance to use
|
|
37
|
+
* @param params - The parameters identifying the cached signature
|
|
38
|
+
* @returns The cached signature data or null if not found
|
|
39
|
+
*/
|
|
40
|
+
export async function getPermitSignatureFromCache(
|
|
41
|
+
storage: AsyncStorage,
|
|
42
|
+
params: PermitCacheKeyParams,
|
|
43
|
+
): Promise<CachedPermitSignature | null> {
|
|
44
|
+
try {
|
|
45
|
+
const key = getPermitCacheKey(params);
|
|
46
|
+
const cached = await storage.getItem(key);
|
|
47
|
+
if (!cached) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return JSON.parse(cached) as CachedPermitSignature;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Saves a permit signature to storage cache
|
|
58
|
+
* @param storage - The AsyncStorage instance to use
|
|
59
|
+
* @param params - The parameters identifying the signature
|
|
60
|
+
* @param payload - The signed payment payload to cache
|
|
61
|
+
* @param deadline - The deadline timestamp of the permit
|
|
62
|
+
* @param maxAmount - The maximum amount authorized
|
|
63
|
+
*/
|
|
64
|
+
export async function savePermitSignatureToCache(
|
|
65
|
+
storage: AsyncStorage,
|
|
66
|
+
params: PermitCacheKeyParams,
|
|
67
|
+
payload: RequestedPaymentPayload,
|
|
68
|
+
deadline: string,
|
|
69
|
+
maxAmount: string,
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
try {
|
|
72
|
+
const key = getPermitCacheKey(params);
|
|
73
|
+
const data: CachedPermitSignature = {
|
|
74
|
+
payload,
|
|
75
|
+
deadline,
|
|
76
|
+
maxAmount,
|
|
77
|
+
};
|
|
78
|
+
await storage.setItem(key, JSON.stringify(data));
|
|
79
|
+
} catch {
|
|
80
|
+
// Silently fail - caching is optional
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Clears a cached permit signature from storage
|
|
86
|
+
* @param storage - The AsyncStorage instance to use
|
|
87
|
+
* @param params - The parameters identifying the cached signature
|
|
88
|
+
*/
|
|
89
|
+
export async function clearPermitSignatureFromCache(
|
|
90
|
+
storage: AsyncStorage,
|
|
91
|
+
params: PermitCacheKeyParams,
|
|
92
|
+
): Promise<void> {
|
|
93
|
+
try {
|
|
94
|
+
const key = getPermitCacheKey(params);
|
|
95
|
+
await storage.removeItem(key);
|
|
96
|
+
} catch {
|
|
97
|
+
// Silently fail
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/x402/schemas.ts
CHANGED
|
@@ -45,6 +45,8 @@ const FacilitatorSettleResponseSchema = SettleResponseSchema.extend({
|
|
|
45
45
|
network: FacilitatorNetworkSchema,
|
|
46
46
|
errorMessage: z.string().optional(),
|
|
47
47
|
fundWalletLink: z.string().optional(),
|
|
48
|
+
allowance: z.string().optional(),
|
|
49
|
+
balance: z.string().optional(),
|
|
48
50
|
});
|
|
49
51
|
export type FacilitatorSettleResponse = z.infer<
|
|
50
52
|
typeof FacilitatorSettleResponseSchema
|
|
@@ -53,6 +55,8 @@ export type FacilitatorSettleResponse = z.infer<
|
|
|
53
55
|
const FacilitatorVerifyResponseSchema = VerifyResponseSchema.extend({
|
|
54
56
|
errorMessage: z.string().optional(),
|
|
55
57
|
fundWalletLink: z.string().optional(),
|
|
58
|
+
allowance: z.string().optional(),
|
|
59
|
+
balance: z.string().optional(),
|
|
56
60
|
});
|
|
57
61
|
|
|
58
62
|
export type FacilitatorVerifyResponse = z.infer<
|
package/src/x402/sign.ts
CHANGED
|
@@ -3,12 +3,19 @@ import type { ExactEvmPayloadAuthorization } from "x402/types";
|
|
|
3
3
|
import { getCachedChain } from "../chains/utils.js";
|
|
4
4
|
import type { ThirdwebClient } from "../client/client.js";
|
|
5
5
|
import { getContract } from "../contract/contract.js";
|
|
6
|
+
import { allowance } from "../extensions/erc20/__generated__/IERC20/read/allowance.js";
|
|
6
7
|
import { nonces } from "../extensions/erc20/__generated__/IERC20Permit/read/nonces.js";
|
|
7
8
|
import { type Address, getAddress } from "../utils/address.js";
|
|
8
9
|
import { type Hex, toHex } from "../utils/encoding/hex.js";
|
|
10
|
+
import type { AsyncStorage } from "../utils/storage/AsyncStorage.js";
|
|
9
11
|
import type { Account } from "../wallets/interfaces/wallet.js";
|
|
10
12
|
import { getSupportedSignatureType } from "./common.js";
|
|
11
13
|
import { encodePayment } from "./encode.js";
|
|
14
|
+
import {
|
|
15
|
+
getPermitSignatureFromCache,
|
|
16
|
+
type PermitCacheKeyParams,
|
|
17
|
+
savePermitSignatureToCache,
|
|
18
|
+
} from "./permitSignatureStorage.js";
|
|
12
19
|
import {
|
|
13
20
|
extractEvmChainId,
|
|
14
21
|
networkToCaip2ChainId,
|
|
@@ -63,6 +70,7 @@ function preparePaymentHeader(
|
|
|
63
70
|
* @param client - The signer wallet instance used to sign the payment header
|
|
64
71
|
* @param paymentRequirements - The payment requirements containing scheme and network information
|
|
65
72
|
* @param unsignedPaymentHeader - The unsigned payment payload to be signed
|
|
73
|
+
* @param storage - Optional storage for caching permit signatures (for "upto" scheme)
|
|
66
74
|
* @returns A promise that resolves to the signed payment payload
|
|
67
75
|
*/
|
|
68
76
|
async function signPaymentHeader(
|
|
@@ -70,6 +78,7 @@ async function signPaymentHeader(
|
|
|
70
78
|
account: Account,
|
|
71
79
|
paymentRequirements: RequestedPaymentRequirements,
|
|
72
80
|
x402Version: number,
|
|
81
|
+
storage?: AsyncStorage,
|
|
73
82
|
): Promise<RequestedPaymentPayload> {
|
|
74
83
|
const from = getAddress(account.address);
|
|
75
84
|
const caip2ChainId = networkToCaip2ChainId(paymentRequirements.network);
|
|
@@ -91,6 +100,55 @@ async function signPaymentHeader(
|
|
|
91
100
|
|
|
92
101
|
switch (supportedSignatureType) {
|
|
93
102
|
case "Permit": {
|
|
103
|
+
const shouldCache =
|
|
104
|
+
paymentRequirements.scheme === "upto" && storage !== undefined;
|
|
105
|
+
const spender = getAddress(paymentRequirements.payTo);
|
|
106
|
+
|
|
107
|
+
const cacheParams: PermitCacheKeyParams = {
|
|
108
|
+
chainId,
|
|
109
|
+
asset: paymentRequirements.asset,
|
|
110
|
+
owner: from,
|
|
111
|
+
spender,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Try to reuse cached signature for "upto" scheme
|
|
115
|
+
if (shouldCache && storage) {
|
|
116
|
+
const cached = await getPermitSignatureFromCache(storage, cacheParams);
|
|
117
|
+
|
|
118
|
+
if (cached) {
|
|
119
|
+
// Validate deadline hasn't passed
|
|
120
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
121
|
+
if (BigInt(cached.deadline) > now) {
|
|
122
|
+
// Check on-chain allowance
|
|
123
|
+
const currentAllowance = await allowance({
|
|
124
|
+
contract: getContract({
|
|
125
|
+
address: paymentRequirements.asset,
|
|
126
|
+
chain: getCachedChain(chainId),
|
|
127
|
+
client,
|
|
128
|
+
}),
|
|
129
|
+
owner: from,
|
|
130
|
+
spender,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Determine threshold - use minAmountRequired if present, else maxAmountRequired
|
|
134
|
+
const extra = paymentRequirements.extra as
|
|
135
|
+
| (ERC20TokenAmount["asset"]["eip712"] & {
|
|
136
|
+
minAmountRequired?: string;
|
|
137
|
+
})
|
|
138
|
+
| undefined;
|
|
139
|
+
const threshold = extra?.minAmountRequired
|
|
140
|
+
? BigInt(extra.minAmountRequired)
|
|
141
|
+
: BigInt(paymentRequirements.maxAmountRequired);
|
|
142
|
+
|
|
143
|
+
// If allowance >= threshold, reuse signature
|
|
144
|
+
if (currentAllowance >= threshold) {
|
|
145
|
+
return cached.payload;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Generate new signature
|
|
94
152
|
const nonce = await nonces({
|
|
95
153
|
contract: getContract({
|
|
96
154
|
address: paymentRequirements.asset,
|
|
@@ -110,13 +168,27 @@ async function signPaymentHeader(
|
|
|
110
168
|
unsignedPaymentHeader.payload.authorization,
|
|
111
169
|
paymentRequirements,
|
|
112
170
|
);
|
|
113
|
-
|
|
171
|
+
|
|
172
|
+
const signedPayload: RequestedPaymentPayload = {
|
|
114
173
|
...unsignedPaymentHeader,
|
|
115
174
|
payload: {
|
|
116
175
|
...unsignedPaymentHeader.payload,
|
|
117
176
|
signature,
|
|
118
177
|
},
|
|
119
178
|
};
|
|
179
|
+
|
|
180
|
+
// Cache the signature for "upto" scheme
|
|
181
|
+
if (shouldCache && storage) {
|
|
182
|
+
await savePermitSignatureToCache(
|
|
183
|
+
storage,
|
|
184
|
+
cacheParams,
|
|
185
|
+
signedPayload,
|
|
186
|
+
unsignedPaymentHeader.payload.authorization.validBefore,
|
|
187
|
+
paymentRequirements.maxAmountRequired,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return signedPayload;
|
|
120
192
|
}
|
|
121
193
|
case "TransferWithAuthorization": {
|
|
122
194
|
// default to transfer with authorization
|
|
@@ -153,6 +225,7 @@ async function signPaymentHeader(
|
|
|
153
225
|
* @param client - The signer wallet instance used to create the payment header
|
|
154
226
|
* @param x402Version - The version of the X402 protocol to use
|
|
155
227
|
* @param paymentRequirements - The payment requirements containing scheme and network information
|
|
228
|
+
* @param storage - Optional storage for caching permit signatures (for "upto" scheme)
|
|
156
229
|
* @returns A promise that resolves to the encoded payment header string
|
|
157
230
|
*/
|
|
158
231
|
export async function createPaymentHeader(
|
|
@@ -160,12 +233,14 @@ export async function createPaymentHeader(
|
|
|
160
233
|
account: Account,
|
|
161
234
|
paymentRequirements: RequestedPaymentRequirements,
|
|
162
235
|
x402Version: number,
|
|
236
|
+
storage?: AsyncStorage,
|
|
163
237
|
): Promise<string> {
|
|
164
238
|
const payment = await signPaymentHeader(
|
|
165
239
|
client,
|
|
166
240
|
account,
|
|
167
241
|
paymentRequirements,
|
|
168
242
|
x402Version,
|
|
243
|
+
storage,
|
|
169
244
|
);
|
|
170
245
|
return encodePayment(payment);
|
|
171
246
|
}
|
package/src/x402/types.ts
CHANGED
|
@@ -29,6 +29,8 @@ export type PaymentArgs = {
|
|
|
29
29
|
network: FacilitatorNetwork | Chain;
|
|
30
30
|
/** The price for accessing the resource - either a USD amount (e.g., "$0.10") or a specific token amount */
|
|
31
31
|
price: Money | ERC20TokenAmount;
|
|
32
|
+
/** The minimum price for accessing the resource - Only applicable for the "upto" payment scheme */
|
|
33
|
+
minPrice?: Money | ERC20TokenAmount;
|
|
32
34
|
/** The payment facilitator instance used to verify and settle payments */
|
|
33
35
|
facilitator: ThirdwebX402Facilitator;
|
|
34
36
|
/** The scheme of the payment, either "exact" or "upto", defaults to "exact" */
|
|
@@ -97,6 +99,12 @@ export type VerifyPaymentResult = Prettify<
|
|
|
97
99
|
decodedPayment: RequestedPaymentPayload;
|
|
98
100
|
/** The selected payment requirements */
|
|
99
101
|
selectedPaymentRequirements: RequestedPaymentRequirements;
|
|
102
|
+
/** The current remaining allowance of the payment of the selected payment asset, only applicable for the "upto" payment scheme */
|
|
103
|
+
allowance?: string;
|
|
104
|
+
/** The current balance of the user's wallet in the selected payment asset */
|
|
105
|
+
balance?: string;
|
|
106
|
+
/** The payer address if verification succeeded */
|
|
107
|
+
payer?: string;
|
|
100
108
|
}
|
|
101
109
|
| PaymentRequiredResult
|
|
102
110
|
>;
|
|
@@ -109,6 +109,9 @@ export async function verifyPayment(
|
|
|
109
109
|
status: 200,
|
|
110
110
|
decodedPayment,
|
|
111
111
|
selectedPaymentRequirements,
|
|
112
|
+
allowance: verification.allowance,
|
|
113
|
+
balance: verification.balance,
|
|
114
|
+
payer: verification.payer,
|
|
112
115
|
};
|
|
113
116
|
} else {
|
|
114
117
|
const error = verification.invalidReason || "Verification failed";
|