-
Notifications
You must be signed in to change notification settings - Fork 621
Create session key guide and update sidebar #7538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
joaquim-verges
merged 3 commits into
main
from
cursor/create-session-key-guide-and-update-sidebar-9dfd
Jul 7, 2025
+341
−0
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
325 changes: 325 additions & 0 deletions
325
apps/portal/src/app/engine/v3/guides/session-keys/page.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,325 @@ | ||
| # Session Keys Guide | ||
|
|
||
| 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. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| Before you begin, ensure you have: | ||
| - A thirdweb client configured | ||
| - Access to a session key account address | ||
| - Vault access token for Engine operations | ||
|
|
||
| ## Setup | ||
|
|
||
| First, let's set up the necessary imports and configuration: | ||
|
|
||
| ```typescript | ||
| import { | ||
| generateAccount, | ||
| smartWallet, | ||
| sendTransaction, | ||
| getContract | ||
| } from "thirdweb"; | ||
| import { sepolia } from "thirdweb/chains"; | ||
| import { getAllActiveSigners } from "thirdweb/extensions/erc4337"; | ||
| import { Engine } from "thirdweb/engine"; | ||
|
|
||
| // Configure your client | ||
| const client = createThirdwebClient({ | ||
| clientId: "your-client-id", | ||
| secretKey: "your-secret-key" // Only use in server environments | ||
| }); | ||
|
|
||
| // Your session key account address | ||
| const sessionKeyAccountAddress = "0x..."; // Replace with your session key address | ||
|
|
||
| // Target address for transactions | ||
| const targetAddress = "0x..."; // Replace with your target address | ||
| ``` | ||
|
|
||
| ## Step 1: Configure User Smart Wallet with Session Key | ||
|
|
||
| The first step is to add our session key address as a signer to the user's smart account. This can be done by configuring the smart wallet with the session key address and permissions. | ||
|
|
||
| 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. | ||
|
|
||
| ```typescript | ||
| <ConnectButton | ||
| accountAbstraction={{ | ||
| chain: sepolia, | ||
| sessionKey: { | ||
| address: sessionKeyAccountAddress, | ||
| }, | ||
| sponsorGas: true, | ||
| }} | ||
| /> | ||
| ``` | ||
|
|
||
| This can also be done in pure TypeScript by using the `smartWallet` function and connecting it to a personal account. | ||
|
|
||
| For this guide, we'll generate a random personal account that will be used to create the smart wallet: | ||
|
|
||
| ```typescript | ||
| // this would be the user's personal account | ||
| const personalAccount = await generateAccount({ | ||
| client: client, | ||
| }); | ||
|
|
||
| // wrapped in a smart wallet with session key permissions | ||
| const smart = smartWallet({ | ||
| chain: sepolia, | ||
| sessionKey: { | ||
| address: sessionKeyAccountAddress, | ||
| permissions: { | ||
| // "*" allows all targets, or specify specific contract addresses | ||
| approvedTargets: "*", | ||
| }, | ||
| }, | ||
| sponsorGas: true, // Enable gas sponsorship | ||
| }); | ||
|
|
||
| console.log("Personal account created:", personalAccount.address); | ||
| ``` | ||
|
|
||
| ### Session Key Permissions | ||
|
|
||
| The `permissions` object allows you to control what the session key can do: | ||
|
|
||
| - `approvedTargets`: Specify which contract addresses the session key can interact with | ||
| - Use `"*"` for all targets | ||
| - Use an array of addresses for specific contracts: `["0x123...", "0x456..."]` | ||
|
|
||
| ## Step 2: Connect Smart Account | ||
|
|
||
| Connect the smart wallet using the personal account: | ||
|
|
||
| ```typescript | ||
| const smartAccount = await smart.connect({ | ||
| client: client, | ||
| personalAccount: personalAccount, | ||
| }); | ||
|
|
||
| console.log("Smart account address:", smartAccount.address); | ||
| ``` | ||
|
|
||
| Note that in a React application, this would be done automatically by the `ConnectButton` or `ConnectEmbed` component. | ||
|
|
||
| ## Step 3 (Optional): Verify Session Key Registration | ||
|
|
||
| Check that the session key is properly registered as an active signer: | ||
|
|
||
| ```typescript | ||
| const signers = await getAllActiveSigners({ | ||
| contract: getContract({ | ||
| address: smartAccount.address, | ||
| chain: sepolia, | ||
| client: client, | ||
| }), | ||
| }); | ||
|
|
||
| // Verify the session key is in the list of active signers | ||
| const isSessionKeyActive = signers | ||
| .map((s) => s.signer) | ||
| .includes(sessionKeyAccountAddress); | ||
|
|
||
| console.log("Session key is active:", isSessionKeyActive); | ||
| console.log("All active signers:", signers.map((s) => s.signer)); | ||
| ``` | ||
|
|
||
| ## Step 4: Create Engine Server Wallet | ||
|
|
||
| Set up an Engine server wallet using the session key for transaction execution: | ||
|
|
||
| ```typescript | ||
| const serverWallet = Engine.serverWallet({ | ||
| address: sessionKeyAccountAddress, | ||
| chain: sepolia, | ||
| client: client, | ||
| executionOptions: { | ||
| entrypointVersion: "0.6", // ERC-4337 entrypoint version | ||
| signerAddress: sessionKeyAccountAddress, | ||
| smartAccountAddress: smartAccount.address, | ||
| type: "ERC4337", | ||
| }, | ||
| vaultAccessToken: process.env.VAULT_TOKEN as string, // Your vault access token | ||
| }); | ||
| ``` | ||
|
|
||
| ### Execution Options | ||
|
|
||
| - `entrypointVersion`: The ERC-4337 entrypoint version to use | ||
| - `signerAddress`: The session key address that will sign transactions | ||
| - `smartAccountAddress`: The smart account address that will execute transactions | ||
| - `type`: The account abstraction type (ERC4337) | ||
|
|
||
| ## Step 5: Execute Transactions | ||
|
|
||
| Now you can execute transactions using the session key: | ||
|
|
||
| ```typescript | ||
| const tx = await sendTransaction({ | ||
| account: serverWallet, | ||
| transaction: { | ||
| chain: sepolia, | ||
| client: client, | ||
| to: targetAddress, | ||
| value: 0n, // Amount in wei (0 for no ETH transfer) | ||
| // data: "0x...", // Optional: contract call data | ||
| }, | ||
| }); | ||
|
|
||
| console.log("Transaction sent:", tx.transactionHash); | ||
| ``` | ||
|
|
||
| ## Complete Example | ||
|
|
||
| Here's a complete example putting it all together: | ||
|
|
||
| ```typescript | ||
| import { | ||
| generateAccount, | ||
| smartWallet, | ||
| sendTransaction, | ||
| getContract, | ||
| createThirdwebClient | ||
| } from "thirdweb"; | ||
| import { sepolia } from "thirdweb/chains"; | ||
| import { getAllActiveSigners } from "thirdweb/extensions/erc4337"; | ||
| import { Engine } from "thirdweb/engine"; | ||
|
|
||
| async function executeTransactionWithSessionKey() { | ||
| // Configuration | ||
| const client = createThirdwebClient({ | ||
| clientId: "your-client-id", | ||
| secretKey: "your-secret-key" | ||
| }); | ||
|
|
||
| const sessionKeyAccountAddress = "0x..."; // Your session key address | ||
| const targetAddress = "0x..."; // Target address for the final transaction | ||
|
|
||
| try { | ||
| // Step 1: Create personal account | ||
| const personalAccount = await generateAccount({ client }); | ||
|
|
||
| // Step 2: Configure smart wallet | ||
| const smart = smartWallet({ | ||
| chain: sepolia, | ||
| sessionKey: { | ||
| address: sessionKeyAccountAddress, | ||
| permissions: { | ||
| approvedTargets: "*", | ||
| }, | ||
| }, | ||
| sponsorGas: true, | ||
| }); | ||
|
|
||
| // Step 3: Connect smart account | ||
| const smartAccount = await smart.connect({ | ||
| client, | ||
| personalAccount, | ||
| }); | ||
|
|
||
| // Step 4: Verify session key | ||
| const signers = await getAllActiveSigners({ | ||
| contract: getContract({ | ||
| address: smartAccount.address, | ||
| chain: sepolia, | ||
| client, | ||
| }), | ||
| }); | ||
|
|
||
| const isSessionKeyActive = signers | ||
| .map((s) => s.signer) | ||
| .includes(sessionKeyAccountAddress); | ||
|
|
||
| if (!isSessionKeyActive) { | ||
| throw new Error("Session key is not active"); | ||
| } | ||
|
|
||
| // Step 5: Create server wallet | ||
| const serverWallet = Engine.serverWallet({ | ||
| address: sessionKeyAccountAddress, | ||
| chain: sepolia, | ||
| client, | ||
| executionOptions: { | ||
| entrypointVersion: "0.6", | ||
| signerAddress: sessionKeyAccountAddress, | ||
| smartAccountAddress: smartAccount.address, | ||
| type: "ERC4337", | ||
| }, | ||
| vaultAccessToken: process.env.VAULT_TOKEN as string, | ||
| }); | ||
|
|
||
| // Step 6: Execute transaction | ||
| const tx = await sendTransaction({ | ||
| account: serverWallet, | ||
| transaction: { | ||
| chain: sepolia, | ||
| client, | ||
| to: targetAddress, | ||
| value: 0n, | ||
| }, | ||
| }); | ||
|
|
||
| console.log("Transaction successful:", tx.transactionHash); | ||
| return tx; | ||
|
|
||
| } catch (error) { | ||
| console.error("Error executing transaction:", error); | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| // Execute the function | ||
| executeTransactionWithSessionKey() | ||
| .then((tx) => console.log("Done!", tx.transactionHash)) | ||
| .catch((error) => console.error("Failed:", error)); | ||
| ``` | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| - **Session Key Storage**: Store session keys securely, preferably in a vault system | ||
| - **Permission Scope**: Limit session key permissions to only necessary targets | ||
| - **Key Rotation**: Regularly rotate session keys for enhanced security | ||
| - **Monitoring**: Monitor session key usage for suspicious activity | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| 1. **Session key not active**: Ensure the session key is properly registered with the smart account | ||
| 2. **Permission denied**: Check that the target address is included in `approvedTargets` | ||
| 3. **Gas estimation failed**: Verify that gas sponsorship is properly configured | ||
| 4. **Vault token invalid**: Ensure your vault access token is valid and has proper permissions | ||
|
|
||
| ### Error Handling | ||
|
|
||
| Always wrap your session key operations in try-catch blocks: | ||
|
|
||
| ```typescript | ||
| try { | ||
| const tx = await sendTransaction({ | ||
| account: serverWallet, | ||
| transaction: { | ||
| chain: sepolia, | ||
| client, | ||
| to: targetAddress, | ||
| value: 0n, | ||
| }, | ||
| }); | ||
| } catch (error) { | ||
| if (error.message.includes("permission")) { | ||
| console.error("Session key lacks permission for this operation"); | ||
| } else if (error.message.includes("gas")) { | ||
| console.error("Gas estimation or sponsorship failed"); | ||
| } else { | ||
| console.error("Transaction failed:", error); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Next Steps | ||
|
|
||
| - Learn more about [Smart Wallets](/engine/v3/configure-wallets/server-wallets) | ||
| - Explore [Engine API Reference](https://engine.thirdweb.com/reference) | ||
| - Check out the [TypeScript SDK](/references/typescript/v5/serverWallet) documentation | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.