diff --git a/docs/docs.yml b/docs/docs.yml index 8cae0f4c8f..1b760781ad 100644 --- a/docs/docs.yml +++ b/docs/docs.yml @@ -196,6 +196,11 @@ navigation: path: wallets/pages/transactions/retry-transactions/index.mdx - page: Sponsor gas on Solana path: wallets/pages/transactions/solana/sponsor-gas-solana.mdx + - section: Permissions (Session keys) + path: wallets/pages/transactions/session-keys/index.mdx + contents: + - page: Permission Types + path: wallets/pages/transactions/session-keys/supported-permissions.mdx - section: Low-Level Infra contents: - page: Overview diff --git a/docs/pages/transactions/session-keys/api.mdx b/docs/pages/transactions/session-keys/api.mdx new file mode 100644 index 0000000000..fa836f0465 --- /dev/null +++ b/docs/pages/transactions/session-keys/api.mdx @@ -0,0 +1,264 @@ + + The examples provided use [foundry](https://getfoundry.sh/) and + [jq](https://jqlang.org/) to prepare and parse API requests. + + +See the [`wallet_createSession` API +reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-create-session) +for full descriptions of the parameters used in the following example. + + + + If the user does not yet have a smart account, you must create one. +```bash twoslash + ACCOUNT_ADDRESS=$(curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_requestAccount", + "params": [ + { + "signerAddress": "'$SIGNER_ADDRESS'" + } + ] +} +' | jq -r '.result.accountAddress') +``` + + + Create a session key with specific permissions for the account. +```bash twoslash +SESSION_RESPONSE=$(curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_createSession", + "params": [ + { + "account": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID_HEX'", + "expirySec": '$(date -d "+1 hour" +%s)', + "key": { + "publicKey": "'$SESSION_KEY_ADDRESS'", + "type": "secp256k1" + }, + "permissions": [ + { + "type": "erc20-token-transfer", + "data": { + "address": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443", + "allowance": "0x56bc75e2d630e0000" + } + }, + { + "type": "contract-access", + "data": { + "address": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443" + } + } + ] + } + ] +}') + +SESSION_ID=$(echo $SESSION_RESPONSE | jq -r '.result.sessionId') +SIGNATURE_REQUEST=$(echo $SESSION_RESPONSE | jq -r '.result.signatureRequest.rawPayload') + +```` + + + Sign the signature request using the account owner's key to authorize the session key. +```bash twoslash +# Sign the signature request with the owner's private key +PERMISSION_SIGNATURE=$(cast wallet sign --private-key $OWNER_PRIVATE_KEY $SIGNATURE_REQUEST) +```` + + + + Prepare calls using the session key permissions. +```bash twoslash +curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [ + { + "capabilities": { + "paymasterService": { + "policyId": "'$GAS_MANAGER_POLICY_ID'" + }, + "permissions": { + "sessionId": "'$SESSION_ID'", + "signature": "'$PERMISSION_SIGNATURE'" + } + }, + "calls": [ + { + "to": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443", + "data": "'$(cast calldata "transfer(address,uint256)" 0x1234567890123456789012345678901234567890 $(cast parse-units 10 6))'" + } + ], + "from": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID_HEX'" + } + ] +}' +``` + + + Sign the returned signature request with the session key and send using `wallet_sendPreparedCalls`. +```bash twoslash +# Sign the userop with the session key +USEROP_SIGNATURE=$(cast wallet sign --private-key $SESSION_KEY_PRIVATE_KEY $USEROP_HASH) + +# Send the prepared calls + +curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ +"id": 1, +"jsonrpc": "2.0", +"method": "wallet_sendPreparedCalls", +"params": [ +{ +"type": "user-operation-v070", +"data": {...useropRequest}, +"chainId": "'$CHAIN_ID_HEX'", +"capabilities": { +"permissions": { +"sessionId": "'$SESSION_ID'", +"signature": "'$PERMISSION_SIGNATURE'" +} +}, +"signature": { +"type": "secp256k1", +"data": "'$USEROP_SIGNATURE'" +} +} +] +}' + +```` + + + +## Advanced Session Key Example + +Here's an example of creating a session key with multiple permission types: + + + + Create a session key with multiple permission types for more granular control. +```bash twoslash +ADVANCED_SESSION_RESPONSE=$(curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_createSession", + "params": [ + { + "account": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID_HEX'", + "expirySec": '$(date -d "+24 hours" +%s)', + "key": { + "publicKey": "'$SESSION_KEY_ADDRESS'", + "type": "secp256k1" + }, + "permissions": [ + { + "type": "erc20-token-transfer", + "data": { + "address": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443", + "allowance": "0x3635c9adc5dea00000" + } + }, + { + "type": "native-token-transfer", + "data": { + "allowance": "0x16345785d8a0000" + } + }, + { + "type": "gas-limit", + "data": { + "limit": "0x7a120" + } + }, + { + "type": "functions-on-contract", + "data": { + "address": "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C", + "functions": ["0x38ed1739", "0x7ff36ab5"] + } + } + ] + } + ] +}') + +ADVANCED_SESSION_ID=$(echo $ADVANCED_SESSION_RESPONSE | jq -r '.result.sessionId') +ADVANCED_SIGNATURE_REQUEST=$(echo $ADVANCED_SESSION_RESPONSE | jq -r '.result.signatureRequest.rawPayload') +```` + + + + Sign the signature request to authorize the advanced session key. +```bash twoslash +ADVANCED_PERMISSION_SIGNATURE=$(cast wallet sign --private-key $OWNER_PRIVATE_KEY $ADVANCED_SIGNATURE_REQUEST) +``` + + + Use the advanced session key to perform complex operations like automated swaps. +```bash twoslash +curl --request POST \ + --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \ + --header 'accept: application/json' \ + --data ' +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_prepareCalls", + "params": [ + { + "capabilities": { + "paymasterService": { + "policyId": "'$GAS_MANAGER_POLICY_ID'" + }, + "permissions": { + "sessionId": "'$ADVANCED_SESSION_ID'", + "signature": "'$ADVANCED_PERMISSION_SIGNATURE'" + } + }, + "calls": [ + { + "to": "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443", + "data": "'$(cast calldata "approve(address,uint256)" 0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C $(cast parse-units 100 6))'" + }, + { + "to": "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C", + "data": "'$(cast calldata "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)" $(cast parse-units 100 6) $(cast parse-units 0.05 18) "[\"0xCFf7C6dA719408113DFcb5e36182c6d5aa491443\",\"0x0000000000000000000000000000000000000000\"]" "'$ACCOUNT_ADDRESS'" '$(date -d "+5 minutes" +%s)')'" + } + ], + "from": "'$ACCOUNT_ADDRESS'", + "chainId": "'$CHAIN_ID_HEX'" + } + ] +}' +``` + + diff --git a/docs/pages/transactions/session-keys/client.mdx b/docs/pages/transactions/session-keys/client.mdx new file mode 100644 index 0000000000..fddea217ce --- /dev/null +++ b/docs/pages/transactions/session-keys/client.mdx @@ -0,0 +1,279 @@ +Required SDK version: ^v4.59.1 + +See the [`grantPermissions` SDK +reference](/wallets/reference/account-kit/wallet-client/functions/grantPermissions) +for full descriptions of the parameters used in the following example. + +You can create and use session keys using the smart wallet client `grantPermissions` and `sendCalls` actions. + + + +```ts title="sessionKeyExample.ts" +import { + type Address, + erc20Abi, + encodeFunctionData, + parseUnits, + toHex, + PrivateKeyAccount, + Hex, +} from "viem"; +import { LocalAccountSigner } from "@aa-sdk/core"; +import { client, createSessionKeyClient } from "./client"; + +const DEMO_USDC_ADDRESS: Address = "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; + +const DEMO_RECIPIENT_ADDRESS: Address = + "0x1234567890123456789012345678901234567890"; + +async function createSessionKey() { + try { + // Generate a new session key + const sessionKey = LocalAccountSigner.generatePrivateKeySigner(); + // Grant permissions to the session key + const permissions = await client.grantPermissions({ + expirySec: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hour from now + key: { + publicKey: await sessionKey.getAddress(), + type: "secp256k1", + }, + permissions: [ + { + type: "erc20-token-transfer", + data: { + address: DEMO_USDC_ADDRESS, + allowance: toHex(parseUnits("100", 6)), // 100 USDC limit + }, + }, + { + type: "contract-access", + data: { + address: DEMO_USDC_ADDRESS, + }, + }, + ], + }); + + console.log("Session key created with permissions context:", permissions); + + // Store the session key for later use + return { + sessionKey, + permissions, + }; + } catch (error) { + console.error("Error creating session key:", error); + throw error; + } +} + +async function useSessionKey( + sessionKey: LocalAccountSigner, + permissionsContext: Hex, +) { + try { + const sessionKeyClient = createSessionKeyClient(sessionKey); + + // Send a transaction using the session key + const { preparedCallIds } = await sessionKeyClient.sendCalls({ + calls: [ + { + // Transfer 10 USDC using the session key + to: DEMO_USDC_ADDRESS, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "transfer", + args: [DEMO_RECIPIENT_ADDRESS, parseUnits("10", 6)], + }), + }, + ], + capabilities: { + permissions: { + context: permissionsContext, + }, + }, + }); + + console.log("Transaction sent with ID:", preparedCallIds[0]); + return preparedCallIds[0]; + } catch (error) { + console.error("Error using session key:", error); + throw error; + } +} + +// Example usage +async function main() { + const { sessionKey, permissions } = await createSessionKey(); + await useSessionKey(sessionKey, permissions.context); +} +``` + +```ts title="advancedSessionKeyExample.ts" +import { + type Address, + erc20Abi, + encodeFunctionData, + parseUnits, + toHex, + PrivateKeyAccount, + Hex, +} from "viem"; +import { LocalAccountSigner } from "@aa-sdk/core"; +import { swapAbi } from "./swapAbi"; +import { client, createSessionKeyClient } from "./client"; + +const DEMO_USDC_ADDRESS: Address = "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; + +const DEMO_SWAP_ADDRESS: Address = "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C"; + +async function createAdvancedSessionKey() { + try { + // Generate a new session key + const sessionKey = LocalAccountSigner.generatePrivateKeySigner(); + + // Grant multiple permissions to create a tightly scoped session key + const permissions = await client.grantPermissions({ + expirySec: Math.floor(Date.now() / 1000) + 24 * 60 * 60, // 24 hours from now + key: { + publicKey: await sessionKey.getAddress(), + type: "secp256k1", + }, + permissions: [ + // Allow spending up to 1000 USDC + { + type: "erc20-token-transfer", + data: { + address: DEMO_USDC_ADDRESS, + allowance: toHex(parseUnits("1000", 6)), + }, + }, + // Allow spending up to 0.1 ETH for gas + { + type: "native-token-transfer", + data: { + allowance: toHex(parseUnits("0.1", 18)), + }, + }, + // Allow gas spending up to 500,000 gas + { + type: "gas-limit", + data: { + limit: "0x7a120", // 500,000 gas in hex + }, + }, + // Allow specific functions on the swap contract + { + type: "functions-on-contract", + data: { + address: DEMO_SWAP_ADDRESS, + functions: [ + "0x38ed1739", // swapExactTokensForTokens + "0x7ff36ab5", // swapExactETHForTokens + ], + }, + }, + ], + }); + + console.log("Advanced session key created:", permissions); + + return { + sessionKey, + permissions, + }; + } catch (error) { + console.error("Error creating advanced session key:", error); + throw error; + } +} + +async function performAutomatedSwap( + sessionKey: LocalAccountSigner, + permissionsContext: Hex, +) { + try { + const sessionKeyClient = createSessionKeyClient(sessionKey); + + // Perform an automated swap using the session key + const { preparedCallIds } = await sessionKeyClient.sendCalls({ + calls: [ + { + // Approve USDC for the swap + to: DEMO_USDC_ADDRESS, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "approve", + args: [DEMO_SWAP_ADDRESS, parseUnits("100", 6)], + }), + }, + { + // Execute the swap (this would need the actual swap contract ABI) + to: DEMO_SWAP_ADDRESS, + data: encodeFunctionData({ + abi: swapAbi, + functionName: "swapUSDCtoWETH", + args: [parseUnits("100", 6), parseUnits("0.05", 18)], + }), + }, + ], + capabilities: { + permissions: { + context: permissionsContext, + }, + }, + }); + + console.log("Automated swap completed with ID:", preparedCallIds[0]); + return preparedCallIds[0]; + } catch (error) { + console.error("Error performing automated swap:", error); + throw error; + } +} + +// Example usage +async function main() { + const { sessionKey, permissions } = await createAdvancedSessionKey(); + await performAutomatedSwap(sessionKey, permissions.context); +} +``` + +```ts title="client.ts" +import "dotenv/config"; +import type { Address, Hex, PrivateKeyAccount } from "viem"; +import { LocalAccountSigner } from "@aa-sdk/core"; +import { alchemy, sepolia } from "@account-kit/infra"; +import { createSmartWalletClient } from "@account-kit/wallet-client"; + +const clientParams = { + transport: alchemy({ + apiKey: process.env.ALCHEMY_API_KEY!, + }), + chain: sepolia, + signer: LocalAccountSigner.privateKeyToAccountSigner( + process.env.PRIVATE_KEY! as Hex, + ), +}; + +const clientWithoutAccount = createSmartWalletClient(clientParams); + +const account = await clientWithoutAccount.requestAccount(); + +export const client = createSmartWalletClient({ + ...clientParams, + account: account.address, +}); + +export const createSessionKeyClient = ( + signer: LocalAccountSigner, +) => { + return createSmartWalletClient({ + ...clientParams, + signer, + account: account.address, + }); +}; +``` + + diff --git a/docs/pages/transactions/session-keys/index.mdx b/docs/pages/transactions/session-keys/index.mdx new file mode 100644 index 0000000000..5d0ed9d07e --- /dev/null +++ b/docs/pages/transactions/session-keys/index.mdx @@ -0,0 +1,54 @@ +--- +title: Use session keys +description: Create permissioned secondary keys to perform transactions on behalf of users +slug: wallets/transactions/session-keys/ +--- + +Session keys are a powerful feature of Smart Wallets that allow you to assign specific permissions to secondary keys on your users' accounts. These additional keys can perform transactions on behalf of the user, but are bound by the permission restrictions you define. This enables secure, automated interactions while maintaining granular control over what actions can be performed. + +## How it works + +Session keys allow you to create temporary or permanent secondary signers with scoped permissions that are validated onchain. These keys can perform actions like token transfers, contract interactions, or automated workflows without requiring the user's main private key for each transaction. + +Key benefits include: + +- **Reduced UX friction**: Skip duplicate confirmations for repeated actions +- **Automated workflows**: Enable server-side wallet actions for recurring tasks +- **Enhanced security**: Limit exposure of the main account key +- **Granular permissions**: Control exactly what, when, and how much a session key can do + + + See the full list of supported permissions here. + + +## Prerequisites + +- API key from your [dashboard](https://dashboard.alchemy.com/apps) +- [Smart Wallets installed and configured in your project](/wallets/pages/react/setup.mdx) +- A [gas manager policy ID](/wallets/transactions/sponsor-gas/sponsor-gas) if sponsoring gas + +## Implementation + + + + + + + + + + + + + +## Next steps + +Build more: + +- [Sponsor gas for users](/wallets/transactions/sponsor-gas/sponsor-gas) +- [Send batch transactions](/wallets/transactions/send-batch-transactions) diff --git a/docs/pages/transactions/session-keys/react.mdx b/docs/pages/transactions/session-keys/react.mdx new file mode 100644 index 0000000000..bc4ab2b0a7 --- /dev/null +++ b/docs/pages/transactions/session-keys/react.mdx @@ -0,0 +1,291 @@ +Required SDK version: ^v4.59.1 + +Use the `useGrantPermissions` and `useSendCalls` hooks to create and use session keys in your React application. + + + +```tsx title="SessionKeyExample.tsx" focus={6-9,19-41} +import { + useGrantPermissions, + useSendCalls, + useSmartAccountClient, +} from "@account-kit/react"; +import { type Address, encodeFunctionData, erc20Abi, parseUnits } from "viem"; +import { LocalAccountSigner } from "@aa-sdk/core"; + +export default function SessionKeyExample() { + const { client } = useSmartAccountClient({}); + const { grantPermissionsAsync } = useGrantPermissions({ + client, + }); + const { sendCallsAsync } = useSendCalls({ + client, + }); + + const DEMO_USDC_ADDRESS: Address = + "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; + + const DEMO_RECIPIENT_ADDRESS: Address = + "0x1234567890123456789012345678901234567890"; + + const handleCreateSessionKey = async () => { + try { + // Generate a new session key + const sessionKey = LocalAccountSigner.generatePrivateKeySigner(); + + // Request account for the owner + const account = await client.requestAccount(); + + // Grant permissions to the session key + const permissions = await grantPermissionsAsync({ + account: account.address, + expirySec: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hour from now + key: { + publicKey: await sessionKey.getAddress(), + type: "secp256k1", + }, + permissions: [ + { + type: "erc20-token-transfer", + data: { + address: DEMO_USDC_ADDRESS, + allowance: parseUnits("100", 6), // 100 USDC limit + }, + }, + { + type: "contract-access", + data: { + address: DEMO_USDC_ADDRESS, + }, + }, + ], + }); + + console.log("Session key created with permissions:", permissions); + + // Store the session key and permissions for later use + localStorage.setItem( + "sessionKey", + JSON.stringify({ + privateKey: sessionKey.privateKey, + permissions: permissions, + }), + ); + } catch (error) { + console.error("Error creating session key:", error); + } + }; + + const handleUseSessionKey = async () => { + try { + // Retrieve stored session key + const stored = localStorage.getItem("sessionKey"); + if (!stored) { + console.error("No session key found. Create one first."); + return; + } + + const { privateKey, permissions } = JSON.parse(stored); + const sessionKey = + LocalAccountSigner.privateKeyToAccountSigner(privateKey); + + // Request account + const account = await client.requestAccount(); + + // Send a transaction using the session key + const { ids } = await sendCallsAsync({ + calls: [ + { + // Transfer 10 USDC using the session key + to: DEMO_USDC_ADDRESS, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "transfer", + args: [DEMO_RECIPIENT_ADDRESS, parseUnits("10", 6)], + }), + }, + ], + capabilities: { + permissions, + }, + signer: sessionKey, // Use the session key to sign + }); + + console.log("Transaction sent with ID:", ids[0]); + } catch (error) { + console.error("Error using session key:", error); + } + }; + + return ( +
+ + +
+ ); +} +``` + +```tsx title="AdvancedSessionKeyExample.tsx" +import { + useGrantPermissions, + useSendCalls, + useSmartAccountClient, +} from "@account-kit/react"; +import { type Address, encodeFunctionData, erc20Abi, parseUnits } from "viem"; +import { LocalAccountSigner } from "@aa-sdk/core"; + +export default function AdvancedSessionKeyExample() { + const { client } = useSmartAccountClient({}); + const { grantPermissionsAsync } = useGrantPermissions({ + client, + }); + const { sendCallsAsync } = useSendCalls({ + client, + }); + + const DEMO_USDC_ADDRESS: Address = + "0xCFf7C6dA719408113DFcb5e36182c6d5aa491443"; + + const DEMO_SWAP_ADDRESS: Address = + "0xB0AEC4c25E8332256A91bBaf169E3C32dfC3C33C"; + + const handleCreateAdvancedSessionKey = async () => { + try { + // Generate a new session key + const sessionKey = LocalAccountSigner.generatePrivateKeySigner(); + + // Request account for the owner + const account = await client.requestAccount(); + + // Grant multiple permissions to create a tightly scoped session key + const permissions = await grantPermissionsAsync({ + account: account.address, + expirySec: Math.floor(Date.now() / 1000) + 24 * 60 * 60, // 24 hours from now + key: { + publicKey: await sessionKey.getAddress(), + type: "secp256k1", + }, + permissions: [ + // Allow spending up to 1000 USDC + { + type: "erc20-token-transfer", + data: { + address: DEMO_USDC_ADDRESS, + allowance: parseUnits("1000", 6), + }, + }, + // Allow spending up to 0.1 ETH for gas + { + type: "native-token-transfer", + data: { + allowance: parseUnits("0.1", 18), + }, + }, + // Allow gas spending up to 500,000 gas + { + type: "gas-limit", + data: { + limit: "0x7a120", // 500,000 gas in hex + }, + }, + // Allow specific functions on the swap contract + { + type: "functions-on-contract", + data: { + address: DEMO_SWAP_ADDRESS, + functions: [ + "0x38ed1739", // swapExactTokensForTokens + "0x7ff36ab5", // swapExactETHForTokens + ], + }, + }, + ], + }); + + console.log("Advanced session key created:", permissions); + + // Store the session key and permissions + localStorage.setItem( + "advancedSessionKey", + JSON.stringify({ + privateKey: sessionKey.privateKey, + permissions: permissions, + }), + ); + } catch (error) { + console.error("Error creating advanced session key:", error); + } + }; + + const handleAutomatedSwap = async () => { + try { + // Retrieve stored session key + const stored = localStorage.getItem("advancedSessionKey"); + if (!stored) { + console.error("No advanced session key found. Create one first."); + return; + } + + const { privateKey, permissions } = JSON.parse(stored); + const sessionKey = + LocalAccountSigner.privateKeyToAccountSigner(privateKey); + + // Request account + const account = await client.requestAccount(); + + // Perform an automated swap using the session key + const { ids } = await sendCallsAsync({ + calls: [ + { + // Approve USDC for the swap + to: DEMO_USDC_ADDRESS, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "approve", + args: [DEMO_SWAP_ADDRESS, parseUnits("100", 6)], + }), + }, + { + // Execute the swap (this would need the actual swap contract ABI) + to: DEMO_SWAP_ADDRESS, + data: encodeFunctionData({ + abi: [], // Replace with actual swap contract ABI + functionName: "swapExactTokensForTokens", + args: [ + parseUnits("100", 6), // amountIn + parseUnits("0.05", 18), // amountOutMin + [ + DEMO_USDC_ADDRESS, + "0x0000000000000000000000000000000000000000", + ], // path + account.address, // to + Math.floor(Date.now() / 1000) + 300, // deadline (5 minutes) + ], + }), + }, + ], + capabilities: { + permissions, + }, + signer: sessionKey, // Use the session key to sign + }); + + console.log("Automated swap completed with ID:", ids[0]); + } catch (error) { + console.error("Error performing automated swap:", error); + } + }; + + return ( +
+ + +
+ ); +} +``` + +
diff --git a/docs/pages/transactions/session-keys/supported-permissions.mdx b/docs/pages/transactions/session-keys/supported-permissions.mdx new file mode 100644 index 0000000000..0e2cc0b39c --- /dev/null +++ b/docs/pages/transactions/session-keys/supported-permissions.mdx @@ -0,0 +1,99 @@ +--- +title: Permission Types +description: See the supported permission types for session keys. +slug: wallets/transactions/session-keys/supported-permissions +--- + +## Permission Types + +Session keys work best when they are tightly scoped. You can apply multiple permission types to create granular control over what a session key can do: + +### Native Token Transfer + +Allows transfer of native tokens (like Ether) from the account with a specified limit. + +```ts +{ + type: "native-token-transfer"; + data: { + allowance: "0xde0b6b3a7640000"; // 1 ETH in hex + } +} +``` + +### ERC20 Token Transfer + +Allows transfer or approval of ERC20 tokens from the account. Both transfers and approvals count towards the limit. + +```ts +{ + type: "erc20-token-transfer"; + data: { + address: "0xA0b86a33E6441b8c4C8C0e1cB8C4C8C0e1cB8C4C8"; // token contract address + allowance: "0x56bc75e2d630e0000"; // 100 USDC in hex + } +} +``` + +### Gas Limit + +Allows the session key to spend gas for user operations up to a specified limit. + +```ts +{ + type: "gas-limit"; + data: { + limit: "0x493e0"; // 300,000 gas in hex + } +} +``` + +### Contract Access + +Grants access to **all** functions in a specific contract. + +```ts +{ + type: "contract-access"; + data: { + address: "0x1234567890123456789012345678901234567890"; // target contract address + } +} +``` + +### Functions On Contract + +Grants access to specific function selectors on **one** contract. + +```ts +{ + type: "functions-on-contract"; + data: { + address: "0x1234567890123456789012345678901234567890"; + functions: ["0xddf252ad", "0x095ea7b3"]; // transfer and approve function selectors + } +} +``` + +### Account Functions + +Grants access to specific functions on the smart account itself. + +```ts +{ + type: "account-functions"; + data: { + functions: ["0xabcdef01", "0x12345678"]; // allowed function selectors + } +} +``` + +### Root + +Grants full access to everything. **Use with extreme caution in production.** + +```ts +{ + type: "root"; // no additional data required +} +```