Skip to content

Commit d2c12cc

Browse files
Create session key guide and update sidebar (#7538)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent c1b2e10 commit d2c12cc

File tree

2 files changed

+341
-0
lines changed

2 files changed

+341
-0
lines changed
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
# Session Keys Guide
2+
3+
Session keys enable secure transaction execution on behalf of smart accounts without requiring direct access to the main account's private key. This guide will walk you through creating and using session keys with the thirdweb TypeScript SDK.
4+
5+
## Prerequisites
6+
7+
Before you begin, ensure you have:
8+
- A thirdweb client configured
9+
- Access to a session key account address
10+
- Vault access token for Engine operations
11+
12+
## Setup
13+
14+
First, let's set up the necessary imports and configuration:
15+
16+
```typescript
17+
import {
18+
generateAccount,
19+
smartWallet,
20+
sendTransaction,
21+
getContract,
22+
createThirdwebClient
23+
} from "thirdweb";
24+
import { sepolia } from "thirdweb/chains";
25+
import { getAllActiveSigners } from "thirdweb/extensions/erc4337";
26+
import { Engine } from "thirdweb/engine";
27+
28+
// Configure your client
29+
const client = createThirdwebClient({
30+
clientId: "your-client-id",
31+
secretKey: "your-secret-key" // Only use in server environments
32+
});
33+
34+
// Your session key account address
35+
const sessionKeyAccountAddress = "0x..."; // Replace with your session key address
36+
37+
// Target address for transactions
38+
const targetAddress = "0x..."; // Replace with your target address
39+
```
40+
41+
## Step 1: Configure User Smart Wallet with Session Key
42+
43+
The first step is to add our session key address as a signer to the user's smart account. This is typically done on the client side since it needs explicit user approval. This can be done by configuring the smart wallet with the session key address and permissions.
44+
45+
In a React application, this can be done by using the `ConnectButton` or `ConnectEmbed` component. This will automatically configure the smart wallet with the session key address and permissions.
46+
47+
```typescript
48+
<ConnectButton
49+
accountAbstraction={{
50+
chain: sepolia,
51+
sessionKey: {
52+
address: sessionKeyAccountAddress,
53+
permissions: {
54+
// "*" allows all targets, or specify specific contract addresses
55+
approvedTargets: "*",
56+
},
57+
},
58+
sponsorGas: true,
59+
}}
60+
/>
61+
```
62+
63+
This can also be done in pure TypeScript by using the `smartWallet` function and connecting it to a personal account.
64+
65+
For this guide, we'll generate a random personal account that will be used to create the smart wallet:
66+
67+
```typescript
68+
// this would be the user's personal account
69+
const personalAccount = await generateAccount({
70+
client: client,
71+
});
72+
73+
// wrapped in a smart wallet with session key permissions
74+
const smart = smartWallet({
75+
chain: sepolia,
76+
sessionKey: {
77+
address: sessionKeyAccountAddress,
78+
permissions: {
79+
// "*" allows all targets, or specify specific contract addresses
80+
approvedTargets: "*",
81+
},
82+
},
83+
sponsorGas: true, // Enable gas sponsorship
84+
});
85+
86+
console.log("Personal account created:", personalAccount.address);
87+
```
88+
89+
### Session Key Permissions
90+
91+
The `permissions` object allows you to control what the session key can do:
92+
93+
- `approvedTargets`: Specify which contract addresses the session key can interact with
94+
- Use `"*"` for all targets
95+
- Use an array of addresses for specific contracts: `["0x123...", "0x456..."]`
96+
97+
## Step 2: Connect Smart Account
98+
99+
Connect the smart wallet using the personal account:
100+
101+
```typescript
102+
const smartAccount = await smart.connect({
103+
client: client,
104+
personalAccount: personalAccount,
105+
});
106+
107+
console.log("Smart account address:", smartAccount.address);
108+
```
109+
110+
Note that in a React application, this would be done automatically by the `ConnectButton` or `ConnectEmbed` component.
111+
112+
## Step 3 (Optional): Verify Session Key Registration
113+
114+
Check that the session key is properly registered as an active signer:
115+
116+
```typescript
117+
const signers = await getAllActiveSigners({
118+
contract: getContract({
119+
address: smartAccount.address,
120+
chain: sepolia,
121+
client: client,
122+
}),
123+
});
124+
125+
// Verify the session key is in the list of active signers
126+
const isSessionKeyActive = signers
127+
.map((s) => s.signer)
128+
.includes(sessionKeyAccountAddress);
129+
130+
console.log("Session key is active:", isSessionKeyActive);
131+
console.log("All active signers:", signers.map((s) => s.signer));
132+
```
133+
134+
## Step 4: Create Engine Server Wallet
135+
136+
Set up an Engine server wallet using the session key for transaction execution:
137+
138+
```typescript
139+
const serverWallet = Engine.serverWallet({
140+
address: sessionKeyAccountAddress,
141+
chain: sepolia,
142+
client: client,
143+
executionOptions: {
144+
entrypointVersion: "0.6", // ERC-4337 entrypoint version
145+
signerAddress: sessionKeyAccountAddress,
146+
smartAccountAddress: smartAccount.address,
147+
type: "ERC4337",
148+
},
149+
vaultAccessToken: process.env.VAULT_TOKEN as string, // Your vault access token
150+
});
151+
```
152+
153+
### Execution Options
154+
155+
- `entrypointVersion`: The ERC-4337 entrypoint version to use
156+
- `signerAddress`: The session key address that will sign transactions
157+
- `smartAccountAddress`: The smart account address that will execute transactions
158+
- `type`: The account abstraction type (ERC4337)
159+
160+
## Step 5: Execute Transactions
161+
162+
Now you can execute transactions using the session key:
163+
164+
```typescript
165+
const tx = await sendTransaction({
166+
account: serverWallet,
167+
transaction: {
168+
chain: sepolia,
169+
client: client,
170+
to: targetAddress,
171+
value: 0n, // Amount in wei (0 for no ETH transfer)
172+
// data: "0x...", // Optional: contract call data
173+
},
174+
});
175+
176+
console.log("Transaction sent:", tx.transactionHash);
177+
```
178+
179+
## Complete Example
180+
181+
Here's a complete example putting it all together:
182+
183+
```typescript
184+
import {
185+
generateAccount,
186+
smartWallet,
187+
sendTransaction,
188+
getContract,
189+
createThirdwebClient
190+
} from "thirdweb";
191+
import { sepolia } from "thirdweb/chains";
192+
import { getAllActiveSigners } from "thirdweb/extensions/erc4337";
193+
import { Engine } from "thirdweb/engine";
194+
195+
async function executeTransactionWithSessionKey() {
196+
// Configuration
197+
const client = createThirdwebClient({
198+
clientId: "your-client-id",
199+
secretKey: "your-secret-key"
200+
});
201+
202+
const sessionKeyAccountAddress = "0x..."; // Your session key address
203+
const targetAddress = "0x..."; // Target address for the final transaction
204+
205+
try {
206+
// Step 1: Create personal account
207+
const personalAccount = await generateAccount({ client });
208+
209+
// Step 2: Configure smart wallet
210+
const smart = smartWallet({
211+
chain: sepolia,
212+
sessionKey: {
213+
address: sessionKeyAccountAddress,
214+
permissions: {
215+
approvedTargets: "*",
216+
},
217+
},
218+
sponsorGas: true,
219+
});
220+
221+
// Step 3: Connect smart account
222+
const smartAccount = await smart.connect({
223+
client,
224+
personalAccount,
225+
});
226+
227+
// Step 4: Verify session key
228+
const signers = await getAllActiveSigners({
229+
contract: getContract({
230+
address: smartAccount.address,
231+
chain: sepolia,
232+
client,
233+
}),
234+
});
235+
236+
const isSessionKeyActive = signers
237+
.map((s) => s.signer)
238+
.includes(sessionKeyAccountAddress);
239+
240+
if (!isSessionKeyActive) {
241+
throw new Error("Session key is not active");
242+
}
243+
244+
// Step 5: Create server wallet
245+
const serverWallet = Engine.serverWallet({
246+
address: sessionKeyAccountAddress,
247+
chain: sepolia,
248+
client,
249+
executionOptions: {
250+
entrypointVersion: "0.6",
251+
signerAddress: sessionKeyAccountAddress,
252+
smartAccountAddress: smartAccount.address,
253+
type: "ERC4337",
254+
},
255+
vaultAccessToken: process.env.VAULT_TOKEN as string,
256+
});
257+
258+
// Step 6: Execute transaction
259+
const tx = await sendTransaction({
260+
account: serverWallet,
261+
transaction: {
262+
chain: sepolia,
263+
client,
264+
to: targetAddress,
265+
value: 0n,
266+
},
267+
});
268+
269+
console.log("Transaction successful:", tx.transactionHash);
270+
return tx;
271+
272+
} catch (error) {
273+
console.error("Error executing transaction:", error);
274+
throw error;
275+
}
276+
}
277+
278+
// Execute the function
279+
executeTransactionWithSessionKey()
280+
.then((tx) => console.log("Done!", tx.transactionHash))
281+
.catch((error) => console.error("Failed:", error));
282+
```
283+
284+
## Security Considerations
285+
286+
- **Session Key Storage**: Store session keys securely, preferably in a vault system
287+
- **Permission Scope**: Limit session key permissions to only necessary targets
288+
- **Key Rotation**: Regularly rotate session keys for enhanced security
289+
- **Monitoring**: Monitor session key usage for suspicious activity
290+
291+
## Troubleshooting
292+
293+
### Common Issues
294+
295+
1. **Session key not active**: Ensure the session key is properly registered with the smart account
296+
2. **Permission denied**: Check that the target address is included in `approvedTargets`
297+
3. **Gas estimation failed**: Verify that gas sponsorship is properly configured
298+
4. **Vault token invalid**: Ensure your vault access token is valid and has proper permissions
299+
300+
### Error Handling
301+
302+
Always wrap your session key operations in try-catch blocks:
303+
304+
```typescript
305+
try {
306+
const tx = await sendTransaction({
307+
account: serverWallet,
308+
transaction: {
309+
chain: sepolia,
310+
client,
311+
to: targetAddress,
312+
value: 0n,
313+
},
314+
});
315+
} catch (error) {
316+
if (error.message.includes("permission")) {
317+
console.error("Session key lacks permission for this operation");
318+
} else if (error.message.includes("gas")) {
319+
console.error("Gas estimation or sponsorship failed");
320+
} else {
321+
console.error("Transaction failed:", error);
322+
}
323+
}
324+
```
325+
326+
## Next Steps
327+
328+
- Learn more about [Smart Wallets](/engine/v3/configure-wallets/server-wallets)
329+
- Explore [Engine API Reference](https://engine.thirdweb.com/reference)
330+
- Check out the [TypeScript SDK](/references/typescript/v5/serverWallet) documentation

apps/portal/src/app/engine/v3/sidebar.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
BookOpenIcon,
23
BracesIcon,
34
CloudIcon,
45
CodeIcon,
@@ -51,6 +52,16 @@ export const sidebar: SideBar = {
5152
],
5253
name: "Configure Wallets",
5354
},
55+
{
56+
icon: <BookOpenIcon />,
57+
links: [
58+
{
59+
href: `${engineV3Slug}/guides/session-keys`,
60+
name: "Session Keys",
61+
},
62+
],
63+
name: "Guides",
64+
},
5465
{
5566
href: "https://engine.thirdweb.com/reference",
5667
icon: <BracesIcon />,

0 commit comments

Comments
 (0)