@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.
- package/LICENSE +21 -0
- package/README.md +372 -0
- package/dist/esm/Provider.d.ts +61 -0
- package/dist/esm/Provider.js +100 -0
- package/dist/esm/Provider.js.map +1 -0
- package/dist/esm/hooks/internal/useEmbeddedWallet.d.ts +10 -0
- package/dist/esm/hooks/internal/useEmbeddedWallet.js +22 -0
- package/dist/esm/hooks/internal/useEmbeddedWallet.js.map +1 -0
- package/dist/esm/hooks/useAlchemyClient.d.ts +17 -0
- package/dist/esm/hooks/useAlchemyClient.js +119 -0
- package/dist/esm/hooks/useAlchemyClient.js.map +1 -0
- package/dist/esm/hooks/useAlchemyPrepareSwap.d.ts +42 -0
- package/dist/esm/hooks/useAlchemyPrepareSwap.js +95 -0
- package/dist/esm/hooks/useAlchemyPrepareSwap.js.map +1 -0
- package/dist/esm/hooks/useAlchemySendTransaction.d.ts +26 -0
- package/dist/esm/hooks/useAlchemySendTransaction.js +127 -0
- package/dist/esm/hooks/useAlchemySendTransaction.js.map +1 -0
- package/dist/esm/hooks/useAlchemySubmitSwap.d.ts +31 -0
- package/dist/esm/hooks/useAlchemySubmitSwap.js +93 -0
- package/dist/esm/hooks/useAlchemySubmitSwap.js.map +1 -0
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types.d.ts +124 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/util/getChain.d.ts +1 -0
- package/dist/esm/util/getChain.js +98 -0
- package/dist/esm/util/getChain.js.map +1 -0
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +4 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/types/Provider.d.ts +62 -0
- package/dist/types/Provider.d.ts.map +1 -0
- package/dist/types/hooks/internal/useEmbeddedWallet.d.ts +11 -0
- package/dist/types/hooks/internal/useEmbeddedWallet.d.ts.map +1 -0
- package/dist/types/hooks/useAlchemyClient.d.ts +18 -0
- package/dist/types/hooks/useAlchemyClient.d.ts.map +1 -0
- package/dist/types/hooks/useAlchemyPrepareSwap.d.ts +43 -0
- package/dist/types/hooks/useAlchemyPrepareSwap.d.ts.map +1 -0
- package/dist/types/hooks/useAlchemySendTransaction.d.ts +27 -0
- package/dist/types/hooks/useAlchemySendTransaction.d.ts.map +1 -0
- package/dist/types/hooks/useAlchemySubmitSwap.d.ts +32 -0
- package/dist/types/hooks/useAlchemySubmitSwap.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types.d.ts +125 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/util/getChain.d.ts +2 -0
- package/dist/types/util/getChain.d.ts.map +1 -0
- package/dist/types/version.d.ts +2 -0
- package/dist/types/version.d.ts.map +1 -0
- package/package.json +66 -0
- package/src/hooks/internal/useEmbeddedWallet.ts +29 -0
- package/src/hooks/useAlchemyClient.ts +160 -0
- package/src/hooks/useAlchemyPrepareSwap.ts +113 -0
- package/src/hooks/useAlchemySendTransaction.ts +160 -0
- package/src/hooks/useAlchemySubmitSwap.ts +120 -0
- package/src/index.ts +23 -0
- package/src/types.ts +159 -0
- package/src/util/getChain.ts +145 -0
- 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
|
+
};
|