Skip to content
Alchemy Logo

Session Keys (SDK)

Learn how to use session keys using the Wallet Client SDK

@alchemy/wallet-apis (v5.x.x) is currently in beta but is the recommended replacement for @account-kit/wallet-client (v4.x.x). If you run into any issues, please reach out.

You're going to need @alchemy/wallet-apis and viem. We'll use privateKeyToAccount from viem/accounts as the signer for demonstration purposes.

npm install @alchemy/wallet-apis viem

Create a client for a given signer (e.g. using privateKeyToAccount from viem/accounts).

import { privateKeyToAccount } from "viem/accounts";
import { arbitrumSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";
 
const client = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: arbitrumSepolia,
  signer: privateKeyToAccount("0xYOUR_PRIVATE_KEY" as const),
});

Need to delegate your account first?

EIP-7702 accounts must be delegated onchain before creating a session. If the account has already sent calls, it will already be delegated. If it hasn't sent any calls before creating the session key, you can delegate it by sending an empty call as the owner:

const { id } = await client.sendCalls({
  calls: [], // empty array since you just want to delegate
});
 
await client.waitForCallsStatus({ id });

Now you can continue to create the session key as described below.

import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
 
// This is where you would use your session key signer
const sessionKeyPrivateKey = generatePrivateKey();
const sessionKey = privateKeyToAccount(sessionKeyPrivateKey);
 
const permissions = await client.grantPermissions({
  expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
  key: {
    publicKey: sessionKey.address,
    type: "secp256k1",
  },
  permissions: [{ type: "root" }], // Here we grant root permissions as an example, but this is not advised in production!
});

import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";
 
// Create a client with the session key as signer
const sessionKeyClient = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: arbitrumSepolia,
  signer: sessionKey, // Use the session key as signer
  account: client.account.address, // The original account that granted permissions
});
 
const { id } = await sessionKeyClient.sendCalls({
  calls: [{ to: "0x0000000000000000000000000000000000000000", value: BigInt(0) }],
  capabilities: {
    permissions,
  },
});
 
await sessionKeyClient.waitForCallsStatus({ id });
Using @account-kit/wallet-client (v4.x.x)?

The examples on this page use @alchemy/wallet-apis (v5.x.x). If you're using @account-kit/wallet-client (v4.x.x), the client setup looks like this:

client.ts (v4.x.x)
import { LocalAccountSigner } from "@aa-sdk/core";
import { createSmartWalletClient } from "@account-kit/wallet-client";
import { alchemy, sepolia } from "@account-kit/infra";
 
const signer = LocalAccountSigner.privateKeyToAccountSigner("0xYOUR_PRIVATE_KEY" as const);
 
export const client = createSmartWalletClient({
  transport: alchemy({ apiKey: "YOUR_API_KEY" }),
  chain: sepolia,
  signer,
  account: signer.address, // can also be passed per action as `from` or `account`
  // Optional: sponsor gas for your users (see "Sponsor gas" guide)
  policyId: "YOUR_POLICY_ID", 
});

Key v4.x.x differences:

  • Account address must be specified on the client or per action (from or account). In v5.x.x, the client automatically uses the signer's address as the account address via EIP-7702.
  • Chain imports come directly from @account-kit/infra instead of viem/chains.
  • Numeric values use hex strings: value: "0x0" instead of value: BigInt(0).
  • In v4.x.x, the paymaster capability on prepareCalls or sendCalls is called paymasterService instead of paymaster, or you can set the policyId directly on the client.
  • Signers use LocalAccountSigner / WalletClientSigner from @aa-sdk/core. In v5.x.x, a viem LocalAccount or WalletClient is used directly.

See the full migration guide for a complete cheat sheet.

Was this page helpful?