@account-kit/privy-integration 4.68.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +372 -0
  3. package/dist/esm/Provider.d.ts +61 -0
  4. package/dist/esm/Provider.js +100 -0
  5. package/dist/esm/Provider.js.map +1 -0
  6. package/dist/esm/hooks/internal/useEmbeddedWallet.d.ts +10 -0
  7. package/dist/esm/hooks/internal/useEmbeddedWallet.js +22 -0
  8. package/dist/esm/hooks/internal/useEmbeddedWallet.js.map +1 -0
  9. package/dist/esm/hooks/useAlchemyClient.d.ts +17 -0
  10. package/dist/esm/hooks/useAlchemyClient.js +119 -0
  11. package/dist/esm/hooks/useAlchemyClient.js.map +1 -0
  12. package/dist/esm/hooks/useAlchemyPrepareSwap.d.ts +42 -0
  13. package/dist/esm/hooks/useAlchemyPrepareSwap.js +95 -0
  14. package/dist/esm/hooks/useAlchemyPrepareSwap.js.map +1 -0
  15. package/dist/esm/hooks/useAlchemySendTransaction.d.ts +26 -0
  16. package/dist/esm/hooks/useAlchemySendTransaction.js +127 -0
  17. package/dist/esm/hooks/useAlchemySendTransaction.js.map +1 -0
  18. package/dist/esm/hooks/useAlchemySubmitSwap.d.ts +31 -0
  19. package/dist/esm/hooks/useAlchemySubmitSwap.js +93 -0
  20. package/dist/esm/hooks/useAlchemySubmitSwap.js.map +1 -0
  21. package/dist/esm/index.d.ts +6 -0
  22. package/dist/esm/index.js +8 -0
  23. package/dist/esm/index.js.map +1 -0
  24. package/dist/esm/types.d.ts +124 -0
  25. package/dist/esm/types.js +2 -0
  26. package/dist/esm/types.js.map +1 -0
  27. package/dist/esm/util/getChain.d.ts +1 -0
  28. package/dist/esm/util/getChain.js +98 -0
  29. package/dist/esm/util/getChain.js.map +1 -0
  30. package/dist/esm/version.d.ts +1 -0
  31. package/dist/esm/version.js +4 -0
  32. package/dist/esm/version.js.map +1 -0
  33. package/dist/types/Provider.d.ts +62 -0
  34. package/dist/types/Provider.d.ts.map +1 -0
  35. package/dist/types/hooks/internal/useEmbeddedWallet.d.ts +11 -0
  36. package/dist/types/hooks/internal/useEmbeddedWallet.d.ts.map +1 -0
  37. package/dist/types/hooks/useAlchemyClient.d.ts +18 -0
  38. package/dist/types/hooks/useAlchemyClient.d.ts.map +1 -0
  39. package/dist/types/hooks/useAlchemyPrepareSwap.d.ts +43 -0
  40. package/dist/types/hooks/useAlchemyPrepareSwap.d.ts.map +1 -0
  41. package/dist/types/hooks/useAlchemySendTransaction.d.ts +27 -0
  42. package/dist/types/hooks/useAlchemySendTransaction.d.ts.map +1 -0
  43. package/dist/types/hooks/useAlchemySubmitSwap.d.ts +32 -0
  44. package/dist/types/hooks/useAlchemySubmitSwap.d.ts.map +1 -0
  45. package/dist/types/index.d.ts +7 -0
  46. package/dist/types/index.d.ts.map +1 -0
  47. package/dist/types/types.d.ts +125 -0
  48. package/dist/types/types.d.ts.map +1 -0
  49. package/dist/types/util/getChain.d.ts +2 -0
  50. package/dist/types/util/getChain.d.ts.map +1 -0
  51. package/dist/types/version.d.ts +2 -0
  52. package/dist/types/version.d.ts.map +1 -0
  53. package/package.json +66 -0
  54. package/src/hooks/internal/useEmbeddedWallet.ts +29 -0
  55. package/src/hooks/useAlchemyClient.ts +160 -0
  56. package/src/hooks/useAlchemyPrepareSwap.ts +113 -0
  57. package/src/hooks/useAlchemySendTransaction.ts +160 -0
  58. package/src/hooks/useAlchemySubmitSwap.ts +120 -0
  59. package/src/index.ts +23 -0
  60. package/src/types.ts +159 -0
  61. package/src/util/getChain.ts +145 -0
  62. package/src/version.ts +3 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Alchemy Insights, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,372 @@
1
+ # @account-kit/privy-integration
2
+
3
+ Add gas sponsorship and smart wallet features to your Privy app in under 5 minutes.
4
+
5
+ ## What This Package Does
6
+
7
+ If you're already using [Privy](https://privy.io) for authentication, this package lets you upgrade your users' wallets with:
8
+
9
+ - **🔄 EIP-7702 Delegation** - Upgrade EOAs to smart accounts without migration
10
+ - **⛽ Gas Sponsorship** - Pay gas fees for your users via Alchemy Gas Manager
11
+ - **💱 Token Swaps** - Execute swaps through Alchemy's swap infrastructure
12
+ - **🚀 Batched Transactions** - Send multiple operations in a single transaction
13
+
14
+ All while keeping Privy as your authentication provider. No need to change your auth flow or migrate user accounts.
15
+
16
+ ## Why Use This?
17
+
18
+ **Already using Privy?** Add smart account features without changing your existing setup:
19
+
20
+ - Drop-in React hooks that replace Privy's transaction hooks
21
+ - Automatic EIP-7702 delegation to upgrade wallets on-the-fly
22
+ - Route transactions through Alchemy's infrastructure for sponsorship and reliability
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @account-kit/privy-integration
28
+ # or
29
+ yarn add @account-kit/privy-integration
30
+ # or
31
+ pnpm add @account-kit/privy-integration
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Wrap Your App with Both Providers
37
+
38
+ **Important:** `AlchemyProvider` must be nested **inside** `PrivyProvider` to access authentication state.
39
+
40
+ ```tsx
41
+ import { PrivyProvider } from "@privy-io/react-auth";
42
+ import { AlchemyProvider } from "@account-kit/privy-integration";
43
+
44
+ function App() {
45
+ return (
46
+ <PrivyProvider
47
+ appId="your-privy-app-id"
48
+ config={
49
+ {
50
+ /* your privy config */
51
+ }
52
+ }
53
+ >
54
+ <AlchemyProvider
55
+ apiKey="your-alchemy-api-key"
56
+ policyId="your-gas-policy-id" // optional, for gas sponsorship
57
+ >
58
+ <YourApp />
59
+ </AlchemyProvider>
60
+ </PrivyProvider>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ### 2. Send Gasless Transactions
66
+
67
+ ```tsx
68
+ import { useAlchemySendTransaction } from "@account-kit/privy-integration";
69
+
70
+ function SendButton() {
71
+ const { sendTransaction, isLoading, error, data } =
72
+ useAlchemySendTransaction();
73
+
74
+ const handleSend = async () => {
75
+ try {
76
+ const result = await sendTransaction({
77
+ to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
78
+ data: "0x...",
79
+ value: "1000000000000000000", // 1 ETH
80
+ });
81
+
82
+ console.log("Transaction hash:", result.txnHash);
83
+ } catch (err) {
84
+ console.error("Transaction failed:", err);
85
+ }
86
+ };
87
+
88
+ return (
89
+ <button onClick={handleSend} disabled={isLoading}>
90
+ {isLoading ? "Sending..." : "Send Transaction"}
91
+ </button>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ### 3. Execute Token Swaps
97
+
98
+ ```tsx
99
+ import {
100
+ useAlchemyPrepareSwap,
101
+ useAlchemySubmitSwap,
102
+ } from "@account-kit/privy-integration";
103
+
104
+ function SwapButton() {
105
+ const { prepareSwap } = useAlchemyPrepareSwap();
106
+ const { submitSwap, isLoading } = useAlchemySubmitSwap();
107
+
108
+ const handleSwap = async () => {
109
+ try {
110
+ // Step 1: Get quote and prepare swap
111
+ // Two modes available:
112
+
113
+ // Option A: Specify exact amount to swap FROM
114
+ const preparedSwap = await prepareSwap({
115
+ fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH
116
+ toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
117
+ fromAmount: "0xde0b6b3a7640000", // Swap exactly 1 ETH
118
+ });
119
+
120
+ // Option B: Specify minimum amount to receive TO
121
+ /* const preparedSwap = await prepareSwap({
122
+ fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // ETH
123
+ toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
124
+ minimumToAmount: "0x5f5e100", // Receive at least 100 USDC (6 decimals)
125
+ }); */
126
+
127
+ console.log(
128
+ "Quote expiry:",
129
+ new Date(parseInt(preparedSwap.quote.expiry) * 1000),
130
+ );
131
+
132
+ // Step 2: Execute swap
133
+ const result = await submitSwap(preparedSwap);
134
+ console.log("Swap confirmed:", result.txnHash);
135
+ } catch (err) {
136
+ console.error("Swap failed:", err);
137
+ }
138
+ };
139
+
140
+ return (
141
+ <button onClick={handleSwap} disabled={isLoading}>
142
+ {isLoading ? "Swapping..." : "Swap Tokens"}
143
+ </button>
144
+ );
145
+ }
146
+ ```
147
+
148
+ ## Configuration
149
+
150
+ ### AlchemyProvider Props
151
+
152
+ | Prop | Type | Required | Description |
153
+ | -------------------- | -------------------- | ------------- | ---------------------------------------------------------------------------------------------------- |
154
+ | `apiKey` | `string` | Conditional\* | Your Alchemy API key for @account-kit/infra transport |
155
+ | `jwt` | `string` | Conditional\* | JWT token for authentication (alternative to `apiKey`) |
156
+ | `rpcUrl` | `string` | Conditional\* | Custom RPC URL (can be used alone or with `jwt`) |
157
+ | `policyId` | `string \| string[]` | No | Gas Manager policy ID(s) for sponsorship. If array is provided, backend uses first applicable policy |
158
+ | `disableSponsorship` | `boolean` | No | Set to `true` to disable gas sponsorship by default (default: `false`) |
159
+
160
+ \* **Required configuration (pick one):**
161
+
162
+ - `apiKey` alone
163
+ - `jwt` alone
164
+ - `rpcUrl` alone
165
+ - `rpcUrl` + `jwt` together
166
+
167
+ ### Transaction Options
168
+
169
+ Control sponsorship per transaction:
170
+
171
+ ```tsx
172
+ // Sponsored transaction (default if policyId is set and disableSponsorship is not true)
173
+ await sendTransaction({ to: "0x...", data: "0x..." });
174
+
175
+ // Disable sponsorship for this specific transaction
176
+ await sendTransaction(
177
+ { to: "0x...", data: "0x..." },
178
+ { disableSponsorship: true },
179
+ );
180
+ ```
181
+
182
+ ## API Reference
183
+
184
+ ### Hooks
185
+
186
+ #### `useAlchemySendTransaction()`
187
+
188
+ Send transactions with optional gas sponsorship.
189
+
190
+ **Returns:**
191
+
192
+ - `sendTransaction(input, options?)` - Send a transaction
193
+ - `isLoading` - Loading state
194
+ - `error` - Error object if failed
195
+ - `data` - Transaction result with `txnHash`
196
+ - `reset()` - Reset hook state
197
+
198
+ #### `useAlchemyPrepareSwap()`
199
+
200
+ Request swap quotes and prepare swap calls.
201
+
202
+ **Returns:**
203
+
204
+ - `prepareSwap(request)` - Get quote and prepare swap (returns full response with `quote` and call data)
205
+ - `isLoading` - Loading state
206
+ - `error` - Error object if failed
207
+ - `data` - Prepared swap result
208
+ - `reset()` - Reset hook state
209
+
210
+ #### `useAlchemySubmitSwap()`
211
+
212
+ Sign and submit prepared swap calls.
213
+
214
+ **Returns:**
215
+
216
+ - `submitSwap(preparedSwap)` - Execute prepared swap (accepts result from `prepareSwap`)
217
+ - `isLoading` - Loading state
218
+ - `error` - Error object if failed
219
+ - `data` - Swap result with `txnHash`
220
+ - `reset()` - Reset hook state
221
+
222
+ #### `useAlchemyClient()`
223
+
224
+ Get the underlying smart wallet client (advanced use cases).
225
+
226
+ **Returns:**
227
+
228
+ - `getClient()` - Async function that returns `SmartWalletClient`
229
+
230
+ ## How It Works
231
+
232
+ ### EIP-7702 Delegation
233
+
234
+ This package uses [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) to upgrade your users' Privy wallets into smart accounts **without requiring them to deploy new contracts or migrate funds**.
235
+
236
+ When a user sends their first transaction:
237
+
238
+ 1. Their EOA signs an EIP-7702 authorization
239
+ 2. The authorization delegates to Alchemy's smart account implementation
240
+ 3. The transaction is executed with smart account features (batching, sponsorship, etc.)
241
+ 4. Gas is optionally sponsored by your Gas Manager policy
242
+
243
+ ### Smart Wallet Client
244
+
245
+ Under the hood, this package:
246
+
247
+ 1. Connects to your user's Privy embedded wallet
248
+ 2. Wraps it with `WalletClientSigner` from `@aa-sdk/core`
249
+ 3. Creates a `SmartWalletClient` with EIP-7702 support
250
+ 4. Routes transactions through Alchemy infrastructure
251
+ 5. Automatically handles sponsorship via Gas Manager policies
252
+
253
+ ## Get Your API Keys
254
+
255
+ ### Alchemy API Key
256
+
257
+ 1. Go to [Alchemy Dashboard](https://dashboard.alchemy.com/)
258
+ 2. Create or select an app
259
+ 3. Copy your API key
260
+
261
+ ### Gas Manager Policy ID (Optional)
262
+
263
+ 1. Go to [Gas Manager](https://dashboard.alchemy.com/gas-manager)
264
+ 2. Create a new policy with your desired rules
265
+ 3. Copy the policy ID
266
+
267
+ ### Privy App ID
268
+
269
+ 1. Go to [Privy Dashboard](https://dashboard.privy.io/)
270
+ 2. Create or select an app
271
+ 3. Copy your app ID
272
+
273
+ ## Migration from Privy Transactions
274
+
275
+ If you're currently using Privy's `useSendTransaction` hook:
276
+
277
+ ### Before
278
+
279
+ ```tsx
280
+ import { useSendTransaction } from "@privy-io/react-auth";
281
+
282
+ const { sendTransaction } = useSendTransaction({
283
+ onSuccess: (txHash) => console.log(txHash),
284
+ });
285
+ ```
286
+
287
+ ### After
288
+
289
+ ```tsx
290
+ import { useAlchemySendTransaction } from "@account-kit/privy-integration";
291
+
292
+ const { sendTransaction, data } = useAlchemySendTransaction();
293
+
294
+ // Now with gas sponsorship!
295
+ ```
296
+
297
+ The API is nearly identical, making migration seamless.
298
+
299
+ ## Advanced Usage
300
+
301
+ ### Access the Smart Wallet Client
302
+
303
+ For advanced use cases, access the underlying client:
304
+
305
+ ```tsx
306
+ import { useAlchemyClient } from "@account-kit/privy-integration";
307
+
308
+ function AdvancedComponent() {
309
+ const { getClient } = useAlchemyClient();
310
+
311
+ const doAdvancedOperation = async () => {
312
+ const client = await getClient();
313
+
314
+ // Use any SmartWalletClient method
315
+ const address = await client.getAddress();
316
+
317
+ // Batch multiple calls
318
+ await client.sendCalls({
319
+ from: address,
320
+ calls: [
321
+ { to: "0x...", data: "0x..." },
322
+ { to: "0x...", data: "0x..." },
323
+ ],
324
+ capabilities: {
325
+ eip7702Auth: true,
326
+ paymasterService: { policyId: "your-policy-id" },
327
+ },
328
+ });
329
+ };
330
+
331
+ return <button onClick={doAdvancedOperation}>Advanced Op</button>;
332
+ }
333
+ ```
334
+
335
+ ## Troubleshooting
336
+
337
+ ### TypeScript error: "Type ... is not assignable to type 'AlchemyProviderConfig'"
338
+
339
+ The provider requires exactly one valid transport configuration. Valid combinations:
340
+
341
+ - `apiKey` only
342
+ - `jwt` only
343
+ - `rpcUrl` only
344
+ - `rpcUrl` + `jwt` together
345
+
346
+ Invalid combinations like `apiKey` + `jwt` will now show TypeScript errors.
347
+
348
+ ### Swaps failing with "Received raw calls"
349
+
350
+ The swap API should return prepared calls by default. This error means the API returned raw calls. Ensure you're not setting `returnRawCalls: true` in the request.
351
+
352
+ ## Examples
353
+
354
+ Check out the [`examples/`](../../examples/) directory for complete applications:
355
+
356
+ - **Privy Integration Demo** - Demos sponsored transactions and sponsored swaps
357
+
358
+ ## Resources
359
+
360
+ - [Alchemy Smart Wallet Documentation](https://www.alchemy.com/docs/wallets/)
361
+ - [EIP-7702 Specification](https://eips.ethereum.org/EIPS/eip-7702)
362
+ - [Gas Manager Dashboard](https://dashboard.alchemy.com/services/gas-manager/overview)
363
+
364
+ ## Support
365
+
366
+ - [Discord](https://discord.gg/alchemy)
367
+ - [GitHub Issues](https://github.com/alchemyplatform/aa-sdk/issues)
368
+ - [Alchemy Support](https://www.alchemy.com/support)
369
+
370
+ ## License
371
+
372
+ MIT
@@ -0,0 +1,61 @@
1
+ import { type PropsWithChildren } from "react";
2
+ import type { SmartWalletClient } from "@account-kit/wallet-client";
3
+ import type { AlchemyProviderConfig } from "./types.js";
4
+ /**
5
+ * Client cache stored in React tree (similar to QueryClient in React Query)
6
+ *
7
+ * @internal
8
+ */
9
+ interface ClientCache {
10
+ client: SmartWalletClient | null;
11
+ cacheKey: string | null;
12
+ }
13
+ /**
14
+ * Provider component that configures Alchemy infrastructure for transaction handling
15
+ * Must be nested INSIDE PrivyProvider to access authentication state
16
+ * Automatically manages client cache lifecycle and resets on logout
17
+ *
18
+ * @param {PropsWithChildren<AlchemyProviderConfig>} props - Component props
19
+ * @param {React.ReactNode} props.children - React children to wrap with Alchemy configuration
20
+ * @param {string} [props.apiKey] - Your Alchemy API key
21
+ * @param {string} [props.jwt] - JWT token for authentication
22
+ * @param {string} [props.rpcUrl] - Custom RPC URL
23
+ * @param {string | string[]} [props.policyId] - Gas Manager policy ID(s)
24
+ * @param {boolean} [props.disableSponsorship] - Set to true to disable sponsorship by default (default: false)
25
+ * @returns {JSX.Element} Provider component
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <PrivyProvider appId="...">
30
+ * <AlchemyProvider
31
+ * apiKey="your-alchemy-api-key"
32
+ * policyId="your-gas-policy-id"
33
+ * >
34
+ * <YourApp />
35
+ * </AlchemyProvider>
36
+ * </PrivyProvider>
37
+ * ```
38
+ */
39
+ export declare function AlchemyProvider({ children, ...config }: PropsWithChildren<AlchemyProviderConfig>): import("react/jsx-runtime").JSX.Element;
40
+ /**
41
+ * Hook to access Alchemy provider configuration
42
+ * Must be used within an <AlchemyProvider> component
43
+ *
44
+ * @returns {AlchemyProviderConfig} The current Alchemy configuration
45
+ * @throws {Error} If used outside of AlchemyProvider
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * const config = useAlchemyConfig();
50
+ * console.log('Policy ID:', config.policyId);
51
+ * ```
52
+ */
53
+ export declare function useAlchemyConfig(): AlchemyProviderConfig;
54
+ /**
55
+ * Hook to access the client cache (internal use only)
56
+ *
57
+ * @internal
58
+ * @returns {ClientCache} The client cache object
59
+ */
60
+ export declare function useClientCache(): ClientCache;
61
+ export {};
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useRef, useEffect, } from "react";
3
+ import { usePrivy } from "@privy-io/react-auth";
4
+ const AlchemyContext = createContext(null);
5
+ const ClientCacheContext = createContext(null);
6
+ /**
7
+ * Provider component that configures Alchemy infrastructure for transaction handling
8
+ * Must be nested INSIDE PrivyProvider to access authentication state
9
+ * Automatically manages client cache lifecycle and resets on logout
10
+ *
11
+ * @param {PropsWithChildren<AlchemyProviderConfig>} props - Component props
12
+ * @param {React.ReactNode} props.children - React children to wrap with Alchemy configuration
13
+ * @param {string} [props.apiKey] - Your Alchemy API key
14
+ * @param {string} [props.jwt] - JWT token for authentication
15
+ * @param {string} [props.rpcUrl] - Custom RPC URL
16
+ * @param {string | string[]} [props.policyId] - Gas Manager policy ID(s)
17
+ * @param {boolean} [props.disableSponsorship] - Set to true to disable sponsorship by default (default: false)
18
+ * @returns {JSX.Element} Provider component
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <PrivyProvider appId="...">
23
+ * <AlchemyProvider
24
+ * apiKey="your-alchemy-api-key"
25
+ * policyId="your-gas-policy-id"
26
+ * >
27
+ * <YourApp />
28
+ * </AlchemyProvider>
29
+ * </PrivyProvider>
30
+ * ```
31
+ */
32
+ export function AlchemyProvider({ children, ...config }) {
33
+ const { authenticated, user } = usePrivy();
34
+ // Store cache in a ref - persists across renders but scoped to this component instance
35
+ // This makes it SSR-safe (each request gets its own cache) and React StrictMode-safe
36
+ const cache = useRef({
37
+ client: null,
38
+ cacheKey: null,
39
+ });
40
+ // Track previous state to detect logout and wallet changes
41
+ const prevAuthenticatedRef = useRef(authenticated);
42
+ const prevWalletAddressRef = useRef(user?.wallet?.address);
43
+ // Automatically reset cache when user logs out or switches wallets
44
+ useEffect(() => {
45
+ const wasAuthenticated = prevAuthenticatedRef.current;
46
+ const prevWalletAddress = prevWalletAddressRef.current;
47
+ const currentWalletAddress = user?.wallet?.address;
48
+ // Reset cache on logout
49
+ if (wasAuthenticated && !authenticated) {
50
+ cache.current.client = null;
51
+ cache.current.cacheKey = null;
52
+ }
53
+ // Reset cache on wallet address change (account switching)
54
+ if (authenticated &&
55
+ prevWalletAddress &&
56
+ currentWalletAddress &&
57
+ prevWalletAddress !== currentWalletAddress) {
58
+ cache.current.client = null;
59
+ cache.current.cacheKey = null;
60
+ }
61
+ // Update refs for next render
62
+ prevAuthenticatedRef.current = authenticated;
63
+ prevWalletAddressRef.current = currentWalletAddress;
64
+ }, [authenticated, user?.wallet?.address]);
65
+ return (_jsx(AlchemyContext.Provider, { value: config, children: _jsx(ClientCacheContext.Provider, { value: cache.current, children: children }) }));
66
+ }
67
+ /**
68
+ * Hook to access Alchemy provider configuration
69
+ * Must be used within an <AlchemyProvider> component
70
+ *
71
+ * @returns {AlchemyProviderConfig} The current Alchemy configuration
72
+ * @throws {Error} If used outside of AlchemyProvider
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * const config = useAlchemyConfig();
77
+ * console.log('Policy ID:', config.policyId);
78
+ * ```
79
+ */
80
+ export function useAlchemyConfig() {
81
+ const context = useContext(AlchemyContext);
82
+ if (!context) {
83
+ throw new Error("useAlchemyConfig must be used within <AlchemyProvider />");
84
+ }
85
+ return context;
86
+ }
87
+ /**
88
+ * Hook to access the client cache (internal use only)
89
+ *
90
+ * @internal
91
+ * @returns {ClientCache} The client cache object
92
+ */
93
+ export function useClientCache() {
94
+ const context = useContext(ClientCacheContext);
95
+ if (!context) {
96
+ throw new Error("useClientCache must be used within <AlchemyProvider />. Make sure AlchemyProvider is nested inside PrivyProvider.");
97
+ }
98
+ return context;
99
+ }
100
+ //# sourceMappingURL=Provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Provider.js","sourceRoot":"","sources":["../../src/Provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAEL,aAAa,EACb,UAAU,EACV,MAAM,EACN,SAAS,GACV,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIhD,MAAM,cAAc,GAAG,aAAa,CAA+B,IAAI,CAAC,CAAC;AAYzE,MAAM,kBAAkB,GAAG,aAAa,CAAqB,IAAI,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,QAAQ,EACR,GAAG,MAAM,EACgC;IACzC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;IAE3C,uFAAuF;IACvF,qFAAqF;IACrF,MAAM,KAAK,GAAG,MAAM,CAAc;QAChC,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC;IAEH,2DAA2D;IAC3D,MAAM,oBAAoB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3D,mEAAmE;IACnE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,OAAO,CAAC;QACtD,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,OAAO,CAAC;QACvD,MAAM,oBAAoB,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;QAEnD,wBAAwB;QACxB,IAAI,gBAAgB,IAAI,CAAC,aAAa,EAAE,CAAC;YACvC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAC5B,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;QAChC,CAAC;QAED,2DAA2D;QAC3D,IACE,aAAa;YACb,iBAAiB;YACjB,oBAAoB;YACpB,iBAAiB,KAAK,oBAAoB,EAC1C,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAC5B,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;QAChC,CAAC;QAED,8BAA8B;QAC9B,oBAAoB,CAAC,OAAO,GAAG,aAAa,CAAC;QAC7C,oBAAoB,CAAC,OAAO,GAAG,oBAAoB,CAAC;IACtD,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAE3C,OAAO,CACL,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YACpC,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,CAAC,OAAO,YAC9C,QAAQ,GACmB,GACN,CAC3B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,mHAAmH,CACpH,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import {\n type PropsWithChildren,\n createContext,\n useContext,\n useRef,\n useEffect,\n} from \"react\";\nimport { usePrivy } from \"@privy-io/react-auth\";\nimport type { SmartWalletClient } from \"@account-kit/wallet-client\";\nimport type { AlchemyProviderConfig } from \"./types.js\";\n\nconst AlchemyContext = createContext<AlchemyProviderConfig | null>(null);\n\n/**\n * Client cache stored in React tree (similar to QueryClient in React Query)\n *\n * @internal\n */\ninterface ClientCache {\n client: SmartWalletClient | null;\n cacheKey: string | null;\n}\n\nconst ClientCacheContext = createContext<ClientCache | null>(null);\n\n/**\n * Provider component that configures Alchemy infrastructure for transaction handling\n * Must be nested INSIDE PrivyProvider to access authentication state\n * Automatically manages client cache lifecycle and resets on logout\n *\n * @param {PropsWithChildren<AlchemyProviderConfig>} props - Component props\n * @param {React.ReactNode} props.children - React children to wrap with Alchemy configuration\n * @param {string} [props.apiKey] - Your Alchemy API key\n * @param {string} [props.jwt] - JWT token for authentication\n * @param {string} [props.rpcUrl] - Custom RPC URL\n * @param {string | string[]} [props.policyId] - Gas Manager policy ID(s)\n * @param {boolean} [props.disableSponsorship] - Set to true to disable sponsorship by default (default: false)\n * @returns {JSX.Element} Provider component\n *\n * @example\n * ```tsx\n * <PrivyProvider appId=\"...\">\n * <AlchemyProvider\n * apiKey=\"your-alchemy-api-key\"\n * policyId=\"your-gas-policy-id\"\n * >\n * <YourApp />\n * </AlchemyProvider>\n * </PrivyProvider>\n * ```\n */\nexport function AlchemyProvider({\n children,\n ...config\n}: PropsWithChildren<AlchemyProviderConfig>) {\n const { authenticated, user } = usePrivy();\n\n // Store cache in a ref - persists across renders but scoped to this component instance\n // This makes it SSR-safe (each request gets its own cache) and React StrictMode-safe\n const cache = useRef<ClientCache>({\n client: null,\n cacheKey: null,\n });\n\n // Track previous state to detect logout and wallet changes\n const prevAuthenticatedRef = useRef(authenticated);\n const prevWalletAddressRef = useRef(user?.wallet?.address);\n\n // Automatically reset cache when user logs out or switches wallets\n useEffect(() => {\n const wasAuthenticated = prevAuthenticatedRef.current;\n const prevWalletAddress = prevWalletAddressRef.current;\n const currentWalletAddress = user?.wallet?.address;\n\n // Reset cache on logout\n if (wasAuthenticated && !authenticated) {\n cache.current.client = null;\n cache.current.cacheKey = null;\n }\n\n // Reset cache on wallet address change (account switching)\n if (\n authenticated &&\n prevWalletAddress &&\n currentWalletAddress &&\n prevWalletAddress !== currentWalletAddress\n ) {\n cache.current.client = null;\n cache.current.cacheKey = null;\n }\n\n // Update refs for next render\n prevAuthenticatedRef.current = authenticated;\n prevWalletAddressRef.current = currentWalletAddress;\n }, [authenticated, user?.wallet?.address]);\n\n return (\n <AlchemyContext.Provider value={config}>\n <ClientCacheContext.Provider value={cache.current}>\n {children}\n </ClientCacheContext.Provider>\n </AlchemyContext.Provider>\n );\n}\n\n/**\n * Hook to access Alchemy provider configuration\n * Must be used within an <AlchemyProvider> component\n *\n * @returns {AlchemyProviderConfig} The current Alchemy configuration\n * @throws {Error} If used outside of AlchemyProvider\n *\n * @example\n * ```tsx\n * const config = useAlchemyConfig();\n * console.log('Policy ID:', config.policyId);\n * ```\n */\nexport function useAlchemyConfig(): AlchemyProviderConfig {\n const context = useContext(AlchemyContext);\n if (!context) {\n throw new Error(\"useAlchemyConfig must be used within <AlchemyProvider />\");\n }\n return context;\n}\n\n/**\n * Hook to access the client cache (internal use only)\n *\n * @internal\n * @returns {ClientCache} The client cache object\n */\nexport function useClientCache(): ClientCache {\n const context = useContext(ClientCacheContext);\n if (!context) {\n throw new Error(\n \"useClientCache must be used within <AlchemyProvider />. Make sure AlchemyProvider is nested inside PrivyProvider.\",\n );\n }\n return context;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { type ConnectedWallet as PrivyWallet } from "@privy-io/react-auth";
2
+ /**
3
+ * Internal hook to get the Privy embedded wallet
4
+ * Shared across multiple hooks to avoid duplication
5
+ *
6
+ * @internal
7
+ * @returns {() => PrivyWallet} Function that returns the embedded wallet
8
+ * @throws {Error} If embedded wallet is not found
9
+ */
10
+ export declare function useEmbeddedWallet(): () => PrivyWallet;
@@ -0,0 +1,22 @@
1
+ import { useCallback } from "react";
2
+ import { useWallets, } from "@privy-io/react-auth";
3
+ /**
4
+ * Internal hook to get the Privy embedded wallet
5
+ * Shared across multiple hooks to avoid duplication
6
+ *
7
+ * @internal
8
+ * @returns {() => PrivyWallet} Function that returns the embedded wallet
9
+ * @throws {Error} If embedded wallet is not found
10
+ */
11
+ export function useEmbeddedWallet() {
12
+ const { wallets } = useWallets();
13
+ const getEmbeddedWallet = useCallback(() => {
14
+ const embedded = wallets.find((w) => w.walletClientType === "privy");
15
+ if (!embedded) {
16
+ throw new Error("Privy embedded wallet not found. Please ensure the user is authenticated.");
17
+ }
18
+ return embedded;
19
+ }, [wallets]);
20
+ return getEmbeddedWallet;
21
+ }
22
+ //# sourceMappingURL=useEmbeddedWallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEmbeddedWallet.js","sourceRoot":"","sources":["../../../../src/hooks/internal/useEmbeddedWallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,UAAU,GAEX,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,CAAC;IAEjC,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAgB,EAAE;QACtD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,KAAK,OAAO,CAAC,CAAC;QACrE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport {\n useWallets,\n type ConnectedWallet as PrivyWallet,\n} from \"@privy-io/react-auth\";\n\n/**\n * Internal hook to get the Privy embedded wallet\n * Shared across multiple hooks to avoid duplication\n *\n * @internal\n * @returns {() => PrivyWallet} Function that returns the embedded wallet\n * @throws {Error} If embedded wallet is not found\n */\nexport function useEmbeddedWallet() {\n const { wallets } = useWallets();\n\n const getEmbeddedWallet = useCallback((): PrivyWallet => {\n const embedded = wallets.find((w) => w.walletClientType === \"privy\");\n if (!embedded) {\n throw new Error(\n \"Privy embedded wallet not found. Please ensure the user is authenticated.\",\n );\n }\n return embedded;\n }, [wallets]);\n\n return getEmbeddedWallet;\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import { type SmartWalletClient } from "@account-kit/wallet-client";
2
+ /**
3
+ * Hook to get and memoize a SmartWalletClient instance
4
+ * The client is cached in the AlchemyProvider context (React tree scoped)
5
+ * Automatically clears cache on logout via the provider
6
+ *
7
+ * @returns {{ getClient: () => Promise<SmartWalletClient> }} Object containing the smart wallet client getter
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * const { getClient } = useAlchemyClient();
12
+ * const smartWalletClient = await getClient();
13
+ * ```
14
+ */
15
+ export declare function useAlchemyClient(): {
16
+ getClient: () => Promise<SmartWalletClient>;
17
+ };