@puga-labs/x402-mantle-sdk 0.3.4 → 0.3.6

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.
@@ -0,0 +1,179 @@
1
+ import {
2
+ createMantleClient
3
+ } from "./chunk-VNO4LJWC.js";
4
+
5
+ // src/client/react/useEthersWallet.ts
6
+ import { useState, useEffect, useCallback } from "react";
7
+ import { ethers } from "ethers";
8
+ function useEthersWallet(options) {
9
+ const [address, setAddress] = useState(void 0);
10
+ const [isConnected, setIsConnected] = useState(false);
11
+ const [provider, setProvider] = useState(
12
+ void 0
13
+ );
14
+ const [chainId, setChainId] = useState(void 0);
15
+ const [error, setError] = useState(void 0);
16
+ const setProviderAndChain = useCallback(async () => {
17
+ if (typeof window === "undefined" || !window.ethereum) return;
18
+ const browserProvider = new ethers.BrowserProvider(
19
+ window.ethereum
20
+ );
21
+ setProvider(browserProvider);
22
+ const network = await browserProvider.getNetwork();
23
+ setChainId(Number(network.chainId));
24
+ }, []);
25
+ const hydrateFromPermissions = useCallback(async () => {
26
+ if (typeof window === "undefined" || !window.ethereum) return;
27
+ try {
28
+ const accounts = await window.ethereum.request({
29
+ method: "eth_accounts"
30
+ });
31
+ if (accounts && accounts.length > 0) {
32
+ const userAddress = accounts[0];
33
+ setAddress(userAddress);
34
+ setIsConnected(true);
35
+ await setProviderAndChain();
36
+ }
37
+ } catch (err) {
38
+ console.warn("[useEthersWallet] Failed to hydrate from permissions:", err);
39
+ }
40
+ }, [setProviderAndChain]);
41
+ const connect = useCallback(async () => {
42
+ try {
43
+ setError(void 0);
44
+ if (typeof window === "undefined" || !window.ethereum) {
45
+ throw new Error(
46
+ "No Ethereum wallet detected. Please install MetaMask or another wallet."
47
+ );
48
+ }
49
+ const browserProvider = new ethers.BrowserProvider(
50
+ window.ethereum
51
+ );
52
+ setProvider(browserProvider);
53
+ const accounts = await window.ethereum.request({
54
+ method: "eth_requestAccounts"
55
+ });
56
+ if (!accounts || accounts.length === 0) {
57
+ throw new Error("No accounts returned from wallet");
58
+ }
59
+ const userAddress = accounts[0];
60
+ setAddress(userAddress);
61
+ setIsConnected(true);
62
+ const network = await browserProvider.getNetwork();
63
+ setChainId(Number(network.chainId));
64
+ } catch (err) {
65
+ const errorObj = err instanceof Error ? err : new Error(String(err));
66
+ setError(errorObj);
67
+ setIsConnected(false);
68
+ setAddress(void 0);
69
+ setChainId(void 0);
70
+ throw errorObj;
71
+ }
72
+ }, []);
73
+ const disconnect = useCallback(() => {
74
+ setAddress(void 0);
75
+ setIsConnected(false);
76
+ setChainId(void 0);
77
+ setError(void 0);
78
+ }, []);
79
+ useEffect(() => {
80
+ if (typeof window === "undefined" || !window.ethereum) return;
81
+ const ethereum = window.ethereum;
82
+ const handleAccountsChanged = (accounts) => {
83
+ const accountsArray = accounts;
84
+ if (!accountsArray || accountsArray.length === 0) {
85
+ disconnect();
86
+ } else {
87
+ setAddress(accountsArray[0]);
88
+ setIsConnected(true);
89
+ void setProviderAndChain();
90
+ }
91
+ };
92
+ if (ethereum.on) {
93
+ ethereum.on("accountsChanged", handleAccountsChanged);
94
+ }
95
+ return () => {
96
+ if (ethereum.removeListener) {
97
+ ethereum.removeListener("accountsChanged", handleAccountsChanged);
98
+ }
99
+ };
100
+ }, [disconnect, setProviderAndChain]);
101
+ useEffect(() => {
102
+ if (typeof window === "undefined" || !window.ethereum) return;
103
+ const ethereum = window.ethereum;
104
+ const handleConnect = () => {
105
+ void hydrateFromPermissions();
106
+ };
107
+ if (ethereum.on) {
108
+ ethereum.on("connect", handleConnect);
109
+ }
110
+ return () => {
111
+ if (ethereum.removeListener) {
112
+ ethereum.removeListener("connect", handleConnect);
113
+ }
114
+ };
115
+ }, [hydrateFromPermissions]);
116
+ useEffect(() => {
117
+ if (typeof window === "undefined" || !window.ethereum) return;
118
+ const ethereum = window.ethereum;
119
+ const handleChainChanged = (chainIdHex) => {
120
+ const newChainId = parseInt(chainIdHex, 16);
121
+ setChainId(newChainId);
122
+ };
123
+ if (ethereum.on) {
124
+ ethereum.on("chainChanged", handleChainChanged);
125
+ }
126
+ return () => {
127
+ if (ethereum.removeListener) {
128
+ ethereum.removeListener("chainChanged", handleChainChanged);
129
+ }
130
+ };
131
+ }, []);
132
+ useEffect(() => {
133
+ void hydrateFromPermissions();
134
+ }, [hydrateFromPermissions]);
135
+ useEffect(() => {
136
+ if (options?.autoConnect) {
137
+ connect().catch((err) => {
138
+ console.warn("[useEthersWallet] Auto-connect failed:", err);
139
+ });
140
+ }
141
+ }, [options?.autoConnect, connect]);
142
+ return {
143
+ address,
144
+ isConnected,
145
+ provider,
146
+ chainId,
147
+ connect,
148
+ disconnect,
149
+ error
150
+ };
151
+ }
152
+
153
+ // src/client/react/useMantleX402.ts
154
+ function useMantleX402(opts) {
155
+ const { address, isConnected } = useEthersWallet({
156
+ autoConnect: opts?.autoConnect ?? false
157
+ });
158
+ const client = createMantleClient({
159
+ facilitatorUrl: opts?.facilitatorUrl,
160
+ resourceUrl: opts?.resourceUrl,
161
+ projectKey: opts?.projectKey,
162
+ getAccount: () => {
163
+ if (!isConnected || !address) return void 0;
164
+ return address;
165
+ },
166
+ getProvider: () => {
167
+ if (typeof window !== "undefined" && window.ethereum) {
168
+ return window.ethereum;
169
+ }
170
+ return void 0;
171
+ }
172
+ });
173
+ return client;
174
+ }
175
+
176
+ export {
177
+ useEthersWallet,
178
+ useMantleX402
179
+ };
@@ -0,0 +1,286 @@
1
+ import {
2
+ __require,
3
+ getChainIdForNetwork
4
+ } from "./chunk-HEZZ74SI.js";
5
+
6
+ // src/shared/utils.ts
7
+ function encodeJsonToBase64(value) {
8
+ const json = JSON.stringify(value);
9
+ if (typeof btoa === "function") {
10
+ const bytes = new TextEncoder().encode(json);
11
+ const binString = Array.from(
12
+ bytes,
13
+ (byte) => String.fromCodePoint(byte)
14
+ ).join("");
15
+ return btoa(binString);
16
+ }
17
+ if (typeof globalThis.Buffer !== "undefined") {
18
+ return globalThis.Buffer.from(json, "utf8").toString("base64");
19
+ }
20
+ throw new Error("No base64 implementation found in this environment");
21
+ }
22
+ function randomBytes32Hex() {
23
+ if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
24
+ const arr = new Uint8Array(32);
25
+ crypto.getRandomValues(arr);
26
+ return "0x" + Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
27
+ }
28
+ try {
29
+ const nodeCrypto = __require("crypto");
30
+ const buf = nodeCrypto.randomBytes(32);
31
+ return "0x" + buf.toString("hex");
32
+ } catch {
33
+ throw new Error(
34
+ "No cryptographically secure random number generator found. This environment does not support crypto.getRandomValues or Node.js crypto module."
35
+ );
36
+ }
37
+ }
38
+
39
+ // src/client/paymentClient.ts
40
+ function joinUrl(base, path) {
41
+ const normalizedBase = base.replace(/\/+$/, "");
42
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
43
+ return `${normalizedBase}${normalizedPath}`;
44
+ }
45
+ function buildTypedDataForAuthorization(authorization, paymentRequirements) {
46
+ const chainId = getChainIdForNetwork(paymentRequirements.network);
47
+ const verifyingContract = paymentRequirements.asset;
48
+ const domain = {
49
+ name: "USD Coin",
50
+ version: "2",
51
+ chainId,
52
+ verifyingContract
53
+ };
54
+ const types = {
55
+ TransferWithAuthorization: [
56
+ { name: "from", type: "address" },
57
+ { name: "to", type: "address" },
58
+ { name: "value", type: "uint256" },
59
+ { name: "validAfter", type: "uint256" },
60
+ { name: "validBefore", type: "uint256" },
61
+ { name: "nonce", type: "bytes32" }
62
+ ]
63
+ };
64
+ return {
65
+ domain,
66
+ types,
67
+ primaryType: "TransferWithAuthorization",
68
+ message: authorization
69
+ };
70
+ }
71
+ async function signAuthorizationWithProvider(provider, authorization, paymentRequirements) {
72
+ const { BrowserProvider } = await import("ethers");
73
+ const chainId = getChainIdForNetwork(paymentRequirements.network);
74
+ const browserProvider = new BrowserProvider(provider, chainId);
75
+ const signer = await browserProvider.getSigner();
76
+ const from = await signer.getAddress();
77
+ const authWithFrom = {
78
+ ...authorization,
79
+ from
80
+ };
81
+ const typedData = buildTypedDataForAuthorization(
82
+ authWithFrom,
83
+ paymentRequirements
84
+ );
85
+ const signature = await signer.signTypedData(
86
+ typedData.domain,
87
+ typedData.types,
88
+ typedData.message
89
+ );
90
+ return { signature, from };
91
+ }
92
+ function createPaymentClient(config) {
93
+ const {
94
+ resourceUrl,
95
+ facilitatorUrl,
96
+ provider,
97
+ userAddress: userAddressOverride,
98
+ projectKey
99
+ } = config;
100
+ if (!resourceUrl) {
101
+ throw new Error("resourceUrl is required");
102
+ }
103
+ if (!facilitatorUrl) {
104
+ throw new Error("facilitatorUrl is required");
105
+ }
106
+ if (!provider) {
107
+ throw new Error("provider is required (e.g. window.ethereum)");
108
+ }
109
+ return {
110
+ async callWithPayment(path, options) {
111
+ const method = options?.method ?? "GET";
112
+ const headers = {
113
+ ...options?.headers ?? {}
114
+ };
115
+ let body;
116
+ if (options?.body !== void 0) {
117
+ headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
118
+ body = JSON.stringify(options.body);
119
+ }
120
+ const initialUrl = joinUrl(resourceUrl, path);
121
+ const initialRes = await fetch(initialUrl, {
122
+ method,
123
+ headers,
124
+ body
125
+ });
126
+ if (initialRes.status !== 402) {
127
+ const json = await initialRes.json().catch((err) => {
128
+ console.error("[x402] Failed to parse initial response JSON:", err);
129
+ return null;
130
+ });
131
+ return {
132
+ response: json,
133
+ txHash: void 0
134
+ };
135
+ }
136
+ const bodyJson = await initialRes.json();
137
+ const paymentRequirements = bodyJson.paymentRequirements;
138
+ if (!paymentRequirements) {
139
+ throw new Error(
140
+ "402 response did not include paymentRequirements field"
141
+ );
142
+ }
143
+ const nowSec = Math.floor(Date.now() / 1e3);
144
+ const validAfter = "0";
145
+ const validBefore = String(nowSec + 10 * 60);
146
+ const nonce = randomBytes32Hex();
147
+ const valueAtomic = options?.valueOverrideAtomic ?? paymentRequirements.maxAmountRequired;
148
+ let authorization = {
149
+ from: "0x0000000000000000000000000000000000000000",
150
+ to: paymentRequirements.payTo,
151
+ value: valueAtomic,
152
+ validAfter,
153
+ validBefore,
154
+ nonce
155
+ };
156
+ const { signature, from } = await signAuthorizationWithProvider(
157
+ provider,
158
+ authorization,
159
+ paymentRequirements
160
+ );
161
+ authorization = {
162
+ ...authorization,
163
+ from
164
+ };
165
+ if (userAddressOverride && userAddressOverride.toLowerCase() !== from.toLowerCase()) {
166
+ console.warn(
167
+ "[SDK WARNING] userAddress override differs from signer address",
168
+ { override: userAddressOverride, signer: from }
169
+ );
170
+ }
171
+ const paymentHeaderObject = {
172
+ x402Version: 1,
173
+ scheme: paymentRequirements.scheme,
174
+ network: paymentRequirements.network,
175
+ payload: {
176
+ signature,
177
+ authorization
178
+ }
179
+ };
180
+ const paymentHeader = encodeJsonToBase64(paymentHeaderObject);
181
+ const settleUrl = joinUrl(facilitatorUrl, "/settle");
182
+ const settleRes = await fetch(settleUrl, {
183
+ method: "POST",
184
+ headers: {
185
+ "Content-Type": "application/json",
186
+ ...projectKey ? { "X-Project-Key": projectKey } : {}
187
+ },
188
+ body: JSON.stringify({
189
+ x402Version: 1,
190
+ paymentHeader,
191
+ paymentRequirements
192
+ })
193
+ });
194
+ if (!settleRes.ok) {
195
+ const text = await settleRes.text().catch((err) => {
196
+ console.error("[x402] Failed to read settle response text:", err);
197
+ return "";
198
+ });
199
+ throw new Error(
200
+ `Facilitator /settle failed with HTTP ${settleRes.status}: ${text}`
201
+ );
202
+ }
203
+ const settleJson = await settleRes.json();
204
+ if (!settleJson.success) {
205
+ throw new Error(
206
+ `Facilitator /settle returned error: ${settleJson.error ?? "unknown error"}`
207
+ );
208
+ }
209
+ const txHash = settleJson.txHash ?? void 0;
210
+ const retryHeaders = {
211
+ ...headers,
212
+ "X-PAYMENT": paymentHeader
213
+ };
214
+ const retryRes = await fetch(initialUrl, {
215
+ method,
216
+ headers: retryHeaders,
217
+ body
218
+ });
219
+ if (!retryRes.ok) {
220
+ const text = await retryRes.text().catch((err) => {
221
+ console.error("[x402] Failed to read retry response text:", err);
222
+ return "";
223
+ });
224
+ throw new Error(
225
+ `Protected request with X-PAYMENT failed: HTTP ${retryRes.status} ${text}`
226
+ );
227
+ }
228
+ const finalJson = await retryRes.json();
229
+ return {
230
+ response: finalJson,
231
+ txHash,
232
+ paymentHeader,
233
+ paymentRequirements
234
+ };
235
+ }
236
+ };
237
+ }
238
+
239
+ // src/client/createMantleClient.ts
240
+ function createMantleClient(config) {
241
+ const resourceUrl = config?.resourceUrl ?? (typeof window !== "undefined" ? window.location.origin : "");
242
+ const facilitatorUrl = config?.facilitatorUrl ?? (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_FACILITATOR_URL : void 0) ?? "http://localhost:8080";
243
+ return {
244
+ async postWithPayment(url, body) {
245
+ const provider = config?.getProvider?.();
246
+ if (!provider) {
247
+ throw new Error("Wallet provider not available");
248
+ }
249
+ let account = await config?.getAccount?.();
250
+ if (!account && provider?.request) {
251
+ try {
252
+ const accounts = await provider.request({
253
+ method: "eth_accounts"
254
+ });
255
+ if (Array.isArray(accounts) && accounts.length > 0) {
256
+ account = accounts[0];
257
+ }
258
+ } catch (err) {
259
+ console.warn("[x402] Failed to hydrate account from provider:", err);
260
+ }
261
+ }
262
+ if (!account) {
263
+ throw new Error(
264
+ "Wallet not connected. Please connect your wallet first."
265
+ );
266
+ }
267
+ const client = createPaymentClient({
268
+ resourceUrl,
269
+ facilitatorUrl,
270
+ provider,
271
+ userAddress: account,
272
+ projectKey: config?.projectKey
273
+ });
274
+ const result = await client.callWithPayment(url, {
275
+ method: "POST",
276
+ body
277
+ });
278
+ return result;
279
+ }
280
+ };
281
+ }
282
+
283
+ export {
284
+ createPaymentClient,
285
+ createMantleClient
286
+ };
package/dist/client.cjs CHANGED
@@ -295,16 +295,28 @@ function createMantleClient(config) {
295
295
  const facilitatorUrl = config?.facilitatorUrl ?? (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_FACILITATOR_URL : void 0) ?? "http://localhost:8080";
296
296
  return {
297
297
  async postWithPayment(url, body) {
298
- const account = await config?.getAccount?.();
298
+ const provider = config?.getProvider?.();
299
+ if (!provider) {
300
+ throw new Error("Wallet provider not available");
301
+ }
302
+ let account = await config?.getAccount?.();
303
+ if (!account && provider?.request) {
304
+ try {
305
+ const accounts = await provider.request({
306
+ method: "eth_accounts"
307
+ });
308
+ if (Array.isArray(accounts) && accounts.length > 0) {
309
+ account = accounts[0];
310
+ }
311
+ } catch (err) {
312
+ console.warn("[x402] Failed to hydrate account from provider:", err);
313
+ }
314
+ }
299
315
  if (!account) {
300
316
  throw new Error(
301
317
  "Wallet not connected. Please connect your wallet first."
302
318
  );
303
319
  }
304
- const provider = config?.getProvider?.();
305
- if (!provider) {
306
- throw new Error("Wallet provider not available");
307
- }
308
320
  const client = createPaymentClient({
309
321
  resourceUrl,
310
322
  facilitatorUrl,
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createMantleClient,
3
3
  createPaymentClient
4
- } from "./chunk-E46A7I6B.js";
4
+ } from "./chunk-VNO4LJWC.js";
5
5
  import {
6
6
  MANTLE_DEFAULTS
7
7
  } from "./chunk-HEZZ74SI.js";
package/dist/index.cjs CHANGED
@@ -622,16 +622,28 @@ function createMantleClient(config) {
622
622
  const facilitatorUrl = config?.facilitatorUrl ?? (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_FACILITATOR_URL : void 0) ?? "http://localhost:8080";
623
623
  return {
624
624
  async postWithPayment(url, body) {
625
- const account = await config?.getAccount?.();
625
+ const provider = config?.getProvider?.();
626
+ if (!provider) {
627
+ throw new Error("Wallet provider not available");
628
+ }
629
+ let account = await config?.getAccount?.();
630
+ if (!account && provider?.request) {
631
+ try {
632
+ const accounts = await provider.request({
633
+ method: "eth_accounts"
634
+ });
635
+ if (Array.isArray(accounts) && accounts.length > 0) {
636
+ account = accounts[0];
637
+ }
638
+ } catch (err) {
639
+ console.warn("[x402] Failed to hydrate account from provider:", err);
640
+ }
641
+ }
626
642
  if (!account) {
627
643
  throw new Error(
628
644
  "Wallet not connected. Please connect your wallet first."
629
645
  );
630
646
  }
631
- const provider = config?.getProvider?.();
632
- if (!provider) {
633
- throw new Error("Wallet provider not available");
634
- }
635
647
  const client = createPaymentClient({
636
648
  resourceUrl,
637
649
  facilitatorUrl,
@@ -659,6 +671,31 @@ function useEthersWallet(options) {
659
671
  );
660
672
  const [chainId, setChainId] = (0, import_react.useState)(void 0);
661
673
  const [error, setError] = (0, import_react.useState)(void 0);
674
+ const setProviderAndChain = (0, import_react.useCallback)(async () => {
675
+ if (typeof window === "undefined" || !window.ethereum) return;
676
+ const browserProvider = new import_ethers.ethers.BrowserProvider(
677
+ window.ethereum
678
+ );
679
+ setProvider(browserProvider);
680
+ const network = await browserProvider.getNetwork();
681
+ setChainId(Number(network.chainId));
682
+ }, []);
683
+ const hydrateFromPermissions = (0, import_react.useCallback)(async () => {
684
+ if (typeof window === "undefined" || !window.ethereum) return;
685
+ try {
686
+ const accounts = await window.ethereum.request({
687
+ method: "eth_accounts"
688
+ });
689
+ if (accounts && accounts.length > 0) {
690
+ const userAddress = accounts[0];
691
+ setAddress(userAddress);
692
+ setIsConnected(true);
693
+ await setProviderAndChain();
694
+ }
695
+ } catch (err) {
696
+ console.warn("[useEthersWallet] Failed to hydrate from permissions:", err);
697
+ }
698
+ }, [setProviderAndChain]);
662
699
  const connect = (0, import_react.useCallback)(async () => {
663
700
  try {
664
701
  setError(void 0);
@@ -707,6 +744,7 @@ function useEthersWallet(options) {
707
744
  } else {
708
745
  setAddress(accountsArray[0]);
709
746
  setIsConnected(true);
747
+ void setProviderAndChain();
710
748
  }
711
749
  };
712
750
  if (ethereum.on) {
@@ -717,7 +755,22 @@ function useEthersWallet(options) {
717
755
  ethereum.removeListener("accountsChanged", handleAccountsChanged);
718
756
  }
719
757
  };
720
- }, [disconnect]);
758
+ }, [disconnect, setProviderAndChain]);
759
+ (0, import_react.useEffect)(() => {
760
+ if (typeof window === "undefined" || !window.ethereum) return;
761
+ const ethereum = window.ethereum;
762
+ const handleConnect = () => {
763
+ void hydrateFromPermissions();
764
+ };
765
+ if (ethereum.on) {
766
+ ethereum.on("connect", handleConnect);
767
+ }
768
+ return () => {
769
+ if (ethereum.removeListener) {
770
+ ethereum.removeListener("connect", handleConnect);
771
+ }
772
+ };
773
+ }, [hydrateFromPermissions]);
721
774
  (0, import_react.useEffect)(() => {
722
775
  if (typeof window === "undefined" || !window.ethereum) return;
723
776
  const ethereum = window.ethereum;
@@ -734,14 +787,16 @@ function useEthersWallet(options) {
734
787
  }
735
788
  };
736
789
  }, []);
737
- const shouldAutoConnect = options?.autoConnect !== false;
738
790
  (0, import_react.useEffect)(() => {
739
- if (shouldAutoConnect) {
791
+ void hydrateFromPermissions();
792
+ }, [hydrateFromPermissions]);
793
+ (0, import_react.useEffect)(() => {
794
+ if (options?.autoConnect) {
740
795
  connect().catch((err) => {
741
796
  console.warn("[useEthersWallet] Auto-connect failed:", err);
742
797
  });
743
798
  }
744
- }, [shouldAutoConnect, connect]);
799
+ }, [options?.autoConnect, connect]);
745
800
  return {
746
801
  address,
747
802
  isConnected,
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  useEthersWallet,
3
3
  useMantleX402
4
- } from "./chunk-23QNUJIB.js";
4
+ } from "./chunk-APBCF3SX.js";
5
5
  import {
6
6
  createMantleClient,
7
7
  createPaymentClient
8
- } from "./chunk-E46A7I6B.js";
8
+ } from "./chunk-VNO4LJWC.js";
9
9
  import "./chunk-WO2MYZXT.js";
10
10
  import {
11
11
  createPaymentMiddleware,
@@ -0,0 +1,89 @@
1
+ import { NextRequest } from 'next/server';
2
+ import { M as MinimalPaywallOptions } from './types-CrOsOHcX.cjs';
3
+ import { P as PaymentRequirements } from './types-BFUqKBBO.cjs';
4
+
5
+ /**
6
+ * Error response returned when payment verification fails.
7
+ * This can be due to missing payment, invalid payment, or verification errors.
8
+ */
9
+ interface PaywallErrorResponse {
10
+ error: string;
11
+ paymentRequirements?: PaymentRequirements;
12
+ paymentHeader?: null;
13
+ invalidReason?: string | null;
14
+ details?: string;
15
+ }
16
+ /**
17
+ * Simple promise-or-value helper.
18
+ */
19
+ type Awaitable<T> = T | Promise<T>;
20
+ /**
21
+ * Next.js App Router route handler type.
22
+ *
23
+ * Accepts anything that is a valid `Response` to avoid forcing users
24
+ * to annotate union bodies on every handler.
25
+ */
26
+ type NextJSHandler = (req: NextRequest) => Awaitable<Response>;
27
+ /**
28
+ * Wrapper function that adds x402 payment verification to Next.js route handler.
29
+ * Returns a handler that may return either the original handler's response
30
+ * or a PaywallErrorResponse if payment verification fails.
31
+ */
32
+ type NextJSPaywallWrapper = (handler: NextJSHandler) => NextJSHandler;
33
+ /**
34
+ * Create Next.js App Router middleware for x402 payment verification.
35
+ * Uses Mantle mainnet defaults (USDC, exact scheme, etc.).
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // app/api/generate-image/route.ts
40
+ * import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/nextjs'
41
+ *
42
+ * const pay = mantlePaywall({
43
+ * priceUsd: 0.01,
44
+ * payTo: process.env.PAY_TO!,
45
+ * });
46
+ *
47
+ * // No 'as any' needed! TypeScript correctly infers the union type
48
+ * export const POST = pay(async (req: NextRequest) => {
49
+ * const { prompt } = await req.json();
50
+ * // Your handler code here
51
+ * return NextResponse.json({ success: true, imageUrl: "..." });
52
+ * });
53
+ * // TypeScript knows POST returns: NextResponse<{ success: boolean; imageUrl: string } | PaywallErrorResponse>
54
+ * ```
55
+ *
56
+ * @param opts - Minimal configuration (price, payTo, optional facilitator/telemetry).
57
+ * @returns Function that wraps Next.js route handlers with payment verification.
58
+ */
59
+ declare function mantlePaywall(opts: MinimalPaywallOptions): NextJSPaywallWrapper;
60
+ /**
61
+ * Type guard to check if a response is a paywall error response.
62
+ * Useful for handling the union type returned by the paywall wrapper.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const response = await fetch('/api/protected');
67
+ * const data = await response.json();
68
+ *
69
+ * if (isPaywallErrorResponse(data)) {
70
+ * // Handle error: data.error, data.paymentRequirements, etc.
71
+ * console.error('Payment required:', data.paymentRequirements);
72
+ * } else {
73
+ * // Handle success: data has type T
74
+ * console.log('Success:', data);
75
+ * }
76
+ * ```
77
+ */
78
+ declare function isPaywallErrorResponse(response: unknown): response is PaywallErrorResponse;
79
+
80
+ type nextjs_NextJSHandler = NextJSHandler;
81
+ type nextjs_NextJSPaywallWrapper = NextJSPaywallWrapper;
82
+ type nextjs_PaywallErrorResponse = PaywallErrorResponse;
83
+ declare const nextjs_isPaywallErrorResponse: typeof isPaywallErrorResponse;
84
+ declare const nextjs_mantlePaywall: typeof mantlePaywall;
85
+ declare namespace nextjs {
86
+ export { type nextjs_NextJSHandler as NextJSHandler, type nextjs_NextJSPaywallWrapper as NextJSPaywallWrapper, type nextjs_PaywallErrorResponse as PaywallErrorResponse, nextjs_isPaywallErrorResponse as isPaywallErrorResponse, nextjs_mantlePaywall as mantlePaywall };
87
+ }
88
+
89
+ export { type NextJSHandler as N, type PaywallErrorResponse as P, type NextJSPaywallWrapper as a, isPaywallErrorResponse as i, mantlePaywall as m, nextjs as n };
@@ -0,0 +1,89 @@
1
+ import { NextRequest } from 'next/server';
2
+ import { M as MinimalPaywallOptions } from './types-CqQ6OgRi.js';
3
+ import { P as PaymentRequirements } from './types-BFUqKBBO.js';
4
+
5
+ /**
6
+ * Error response returned when payment verification fails.
7
+ * This can be due to missing payment, invalid payment, or verification errors.
8
+ */
9
+ interface PaywallErrorResponse {
10
+ error: string;
11
+ paymentRequirements?: PaymentRequirements;
12
+ paymentHeader?: null;
13
+ invalidReason?: string | null;
14
+ details?: string;
15
+ }
16
+ /**
17
+ * Simple promise-or-value helper.
18
+ */
19
+ type Awaitable<T> = T | Promise<T>;
20
+ /**
21
+ * Next.js App Router route handler type.
22
+ *
23
+ * Accepts anything that is a valid `Response` to avoid forcing users
24
+ * to annotate union bodies on every handler.
25
+ */
26
+ type NextJSHandler = (req: NextRequest) => Awaitable<Response>;
27
+ /**
28
+ * Wrapper function that adds x402 payment verification to Next.js route handler.
29
+ * Returns a handler that may return either the original handler's response
30
+ * or a PaywallErrorResponse if payment verification fails.
31
+ */
32
+ type NextJSPaywallWrapper = (handler: NextJSHandler) => NextJSHandler;
33
+ /**
34
+ * Create Next.js App Router middleware for x402 payment verification.
35
+ * Uses Mantle mainnet defaults (USDC, exact scheme, etc.).
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // app/api/generate-image/route.ts
40
+ * import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/nextjs'
41
+ *
42
+ * const pay = mantlePaywall({
43
+ * priceUsd: 0.01,
44
+ * payTo: process.env.PAY_TO!,
45
+ * });
46
+ *
47
+ * // No 'as any' needed! TypeScript correctly infers the union type
48
+ * export const POST = pay(async (req: NextRequest) => {
49
+ * const { prompt } = await req.json();
50
+ * // Your handler code here
51
+ * return NextResponse.json({ success: true, imageUrl: "..." });
52
+ * });
53
+ * // TypeScript knows POST returns: NextResponse<{ success: boolean; imageUrl: string } | PaywallErrorResponse>
54
+ * ```
55
+ *
56
+ * @param opts - Minimal configuration (price, payTo, optional facilitator/telemetry).
57
+ * @returns Function that wraps Next.js route handlers with payment verification.
58
+ */
59
+ declare function mantlePaywall(opts: MinimalPaywallOptions): NextJSPaywallWrapper;
60
+ /**
61
+ * Type guard to check if a response is a paywall error response.
62
+ * Useful for handling the union type returned by the paywall wrapper.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const response = await fetch('/api/protected');
67
+ * const data = await response.json();
68
+ *
69
+ * if (isPaywallErrorResponse(data)) {
70
+ * // Handle error: data.error, data.paymentRequirements, etc.
71
+ * console.error('Payment required:', data.paymentRequirements);
72
+ * } else {
73
+ * // Handle success: data has type T
74
+ * console.log('Success:', data);
75
+ * }
76
+ * ```
77
+ */
78
+ declare function isPaywallErrorResponse(response: unknown): response is PaywallErrorResponse;
79
+
80
+ type nextjs_NextJSHandler = NextJSHandler;
81
+ type nextjs_NextJSPaywallWrapper = NextJSPaywallWrapper;
82
+ type nextjs_PaywallErrorResponse = PaywallErrorResponse;
83
+ declare const nextjs_isPaywallErrorResponse: typeof isPaywallErrorResponse;
84
+ declare const nextjs_mantlePaywall: typeof mantlePaywall;
85
+ declare namespace nextjs {
86
+ export { type nextjs_NextJSHandler as NextJSHandler, type nextjs_NextJSPaywallWrapper as NextJSPaywallWrapper, type nextjs_PaywallErrorResponse as PaywallErrorResponse, nextjs_isPaywallErrorResponse as isPaywallErrorResponse, nextjs_mantlePaywall as mantlePaywall };
87
+ }
88
+
89
+ export { type NextJSHandler as N, type PaywallErrorResponse as P, type NextJSPaywallWrapper as a, isPaywallErrorResponse as i, mantlePaywall as m, nextjs as n };
package/dist/react.cjs CHANGED
@@ -46,6 +46,31 @@ function useEthersWallet(options) {
46
46
  );
47
47
  const [chainId, setChainId] = (0, import_react.useState)(void 0);
48
48
  const [error, setError] = (0, import_react.useState)(void 0);
49
+ const setProviderAndChain = (0, import_react.useCallback)(async () => {
50
+ if (typeof window === "undefined" || !window.ethereum) return;
51
+ const browserProvider = new import_ethers.ethers.BrowserProvider(
52
+ window.ethereum
53
+ );
54
+ setProvider(browserProvider);
55
+ const network = await browserProvider.getNetwork();
56
+ setChainId(Number(network.chainId));
57
+ }, []);
58
+ const hydrateFromPermissions = (0, import_react.useCallback)(async () => {
59
+ if (typeof window === "undefined" || !window.ethereum) return;
60
+ try {
61
+ const accounts = await window.ethereum.request({
62
+ method: "eth_accounts"
63
+ });
64
+ if (accounts && accounts.length > 0) {
65
+ const userAddress = accounts[0];
66
+ setAddress(userAddress);
67
+ setIsConnected(true);
68
+ await setProviderAndChain();
69
+ }
70
+ } catch (err) {
71
+ console.warn("[useEthersWallet] Failed to hydrate from permissions:", err);
72
+ }
73
+ }, [setProviderAndChain]);
49
74
  const connect = (0, import_react.useCallback)(async () => {
50
75
  try {
51
76
  setError(void 0);
@@ -94,6 +119,7 @@ function useEthersWallet(options) {
94
119
  } else {
95
120
  setAddress(accountsArray[0]);
96
121
  setIsConnected(true);
122
+ void setProviderAndChain();
97
123
  }
98
124
  };
99
125
  if (ethereum.on) {
@@ -104,7 +130,22 @@ function useEthersWallet(options) {
104
130
  ethereum.removeListener("accountsChanged", handleAccountsChanged);
105
131
  }
106
132
  };
107
- }, [disconnect]);
133
+ }, [disconnect, setProviderAndChain]);
134
+ (0, import_react.useEffect)(() => {
135
+ if (typeof window === "undefined" || !window.ethereum) return;
136
+ const ethereum = window.ethereum;
137
+ const handleConnect = () => {
138
+ void hydrateFromPermissions();
139
+ };
140
+ if (ethereum.on) {
141
+ ethereum.on("connect", handleConnect);
142
+ }
143
+ return () => {
144
+ if (ethereum.removeListener) {
145
+ ethereum.removeListener("connect", handleConnect);
146
+ }
147
+ };
148
+ }, [hydrateFromPermissions]);
108
149
  (0, import_react.useEffect)(() => {
109
150
  if (typeof window === "undefined" || !window.ethereum) return;
110
151
  const ethereum = window.ethereum;
@@ -121,14 +162,16 @@ function useEthersWallet(options) {
121
162
  }
122
163
  };
123
164
  }, []);
124
- const shouldAutoConnect = options?.autoConnect !== false;
125
165
  (0, import_react.useEffect)(() => {
126
- if (shouldAutoConnect) {
166
+ void hydrateFromPermissions();
167
+ }, [hydrateFromPermissions]);
168
+ (0, import_react.useEffect)(() => {
169
+ if (options?.autoConnect) {
127
170
  connect().catch((err) => {
128
171
  console.warn("[useEthersWallet] Auto-connect failed:", err);
129
172
  });
130
173
  }
131
- }, [shouldAutoConnect, connect]);
174
+ }, [options?.autoConnect, connect]);
132
175
  return {
133
176
  address,
134
177
  isConnected,
@@ -399,16 +442,28 @@ function createMantleClient(config) {
399
442
  const facilitatorUrl = config?.facilitatorUrl ?? (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_FACILITATOR_URL : void 0) ?? "http://localhost:8080";
400
443
  return {
401
444
  async postWithPayment(url, body) {
402
- const account = await config?.getAccount?.();
445
+ const provider = config?.getProvider?.();
446
+ if (!provider) {
447
+ throw new Error("Wallet provider not available");
448
+ }
449
+ let account = await config?.getAccount?.();
450
+ if (!account && provider?.request) {
451
+ try {
452
+ const accounts = await provider.request({
453
+ method: "eth_accounts"
454
+ });
455
+ if (Array.isArray(accounts) && accounts.length > 0) {
456
+ account = accounts[0];
457
+ }
458
+ } catch (err) {
459
+ console.warn("[x402] Failed to hydrate account from provider:", err);
460
+ }
461
+ }
403
462
  if (!account) {
404
463
  throw new Error(
405
464
  "Wallet not connected. Please connect your wallet first."
406
465
  );
407
466
  }
408
- const provider = config?.getProvider?.();
409
- if (!provider) {
410
- throw new Error("Wallet provider not available");
411
- }
412
467
  const client = createPaymentClient({
413
468
  resourceUrl,
414
469
  facilitatorUrl,
package/dist/react.d.cts CHANGED
@@ -44,7 +44,7 @@ declare function useMantleX402(opts?: UseMantleX402Options): MantleClient;
44
44
  interface UseEthersWalletOptions {
45
45
  /**
46
46
  * Auto-connect to existing MetaMask connection on mount.
47
- * @default true
47
+ * @default false
48
48
  */
49
49
  autoConnect?: boolean;
50
50
  }
package/dist/react.d.ts CHANGED
@@ -44,7 +44,7 @@ declare function useMantleX402(opts?: UseMantleX402Options): MantleClient;
44
44
  interface UseEthersWalletOptions {
45
45
  /**
46
46
  * Auto-connect to existing MetaMask connection on mount.
47
- * @default true
47
+ * @default false
48
48
  */
49
49
  autoConnect?: boolean;
50
50
  }
package/dist/react.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  useEthersWallet,
3
3
  useMantleX402
4
- } from "./chunk-23QNUJIB.js";
5
- import "./chunk-E46A7I6B.js";
4
+ } from "./chunk-APBCF3SX.js";
5
+ import "./chunk-VNO4LJWC.js";
6
6
  import "./chunk-HEZZ74SI.js";
7
7
  export {
8
8
  useEthersWallet,
@@ -1,4 +1,4 @@
1
- export { N as NextJSHandler, a as NextJSPaywallWrapper, P as PaywallErrorResponse, i as isPaywallErrorResponse, m as mantlePaywall } from './nextjs-CY13JXkV.cjs';
1
+ export { N as NextJSHandler, a as NextJSPaywallWrapper, P as PaywallErrorResponse, i as isPaywallErrorResponse, m as mantlePaywall } from './nextjs-CGOVHJXl.cjs';
2
2
  export { M as MinimalPaywallOptions, c as PaymentCheckInput, d as PaymentCheckResult, P as PaymentLogEntry, R as RouteKey, a as RoutePricingConfig, b as RoutesConfig, T as TelemetryConfig } from './types-CrOsOHcX.cjs';
3
3
  export { A as AssetConfig, a as Authorization, E as EIP1193Provider, N as NetworkId, d as PaymentHeaderBase64, c as PaymentHeaderObject, b as PaymentHeaderPayload, P as PaymentRequirements } from './types-BFUqKBBO.cjs';
4
4
  export { M as MANTLE_DEFAULTS } from './constants-CsIL25uQ.cjs';
@@ -1,4 +1,4 @@
1
- export { N as NextJSHandler, a as NextJSPaywallWrapper, P as PaywallErrorResponse, i as isPaywallErrorResponse, m as mantlePaywall } from './nextjs-BgyAWzT3.js';
1
+ export { N as NextJSHandler, a as NextJSPaywallWrapper, P as PaywallErrorResponse, i as isPaywallErrorResponse, m as mantlePaywall } from './nextjs-w61gRvpz.js';
2
2
  export { M as MinimalPaywallOptions, c as PaymentCheckInput, d as PaymentCheckResult, P as PaymentLogEntry, R as RouteKey, a as RoutePricingConfig, b as RoutesConfig, T as TelemetryConfig } from './types-CqQ6OgRi.js';
3
3
  export { A as AssetConfig, a as Authorization, E as EIP1193Provider, N as NetworkId, d as PaymentHeaderBase64, c as PaymentHeaderObject, b as PaymentHeaderPayload, P as PaymentRequirements } from './types-BFUqKBBO.js';
4
4
  export { M as MANTLE_DEFAULTS } from './constants-0ncqvV_O.js';
package/dist/server.d.cts CHANGED
@@ -2,7 +2,7 @@ import { c as PaymentCheckInput, d as PaymentCheckResult } from './types-CrOsOHc
2
2
  export { M as MinimalPaywallOptions, P as PaymentLogEntry, R as RouteKey, a as RoutePricingConfig, b as RoutesConfig, T as TelemetryConfig } from './types-CrOsOHcX.cjs';
3
3
  import { c as PaymentHeaderObject } from './types-BFUqKBBO.cjs';
4
4
  export { M as MantleMiddleware, c as createPaymentMiddleware, e as express, m as mantlePaywall } from './express-eQOPxfnI.cjs';
5
- export { n as nextjs } from './nextjs-CY13JXkV.cjs';
5
+ export { n as nextjs } from './nextjs-CGOVHJXl.cjs';
6
6
  export { w as web } from './web-standards-BNQyWzBC.cjs';
7
7
  import { P as PaymentLogEntry, T as TelemetryConfig, c as TelemetryEvent } from './types-CoOdbZSp.cjs';
8
8
  import 'express';
package/dist/server.d.ts CHANGED
@@ -2,7 +2,7 @@ import { c as PaymentCheckInput, d as PaymentCheckResult } from './types-CqQ6OgR
2
2
  export { M as MinimalPaywallOptions, P as PaymentLogEntry, R as RouteKey, a as RoutePricingConfig, b as RoutesConfig, T as TelemetryConfig } from './types-CqQ6OgRi.js';
3
3
  import { c as PaymentHeaderObject } from './types-BFUqKBBO.js';
4
4
  export { M as MantleMiddleware, c as createPaymentMiddleware, e as express, m as mantlePaywall } from './express-D8EwEcOL.js';
5
- export { n as nextjs } from './nextjs-BgyAWzT3.js';
5
+ export { n as nextjs } from './nextjs-w61gRvpz.js';
6
6
  export { w as web } from './web-standards-D8j1kZxd.js';
7
7
  import { P as PaymentLogEntry, T as TelemetryConfig, c as TelemetryEvent } from './types-DTzov_EE.js';
8
8
  import 'express';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puga-labs/x402-mantle-sdk",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "x402 payments SDK for Mantle (USDC, gasless, facilitator-based)",
5
5
  "license": "MIT",
6
6
  "author": "Evgenii Pugachev <cyprus.pugamuga@gmail.com>",