ECDSA Session Key
Create a Multichain ECDSA session key with policy-based permissions
Session keys are delegated keys that can sign UserOps on behalf of a smart account, but only within a set of explicit policies. You keep your primary signer offline and issue a scoped session key for a dapp, bot, or background agent. The policies act as guardrails: they define what the session key can call, how much gas it can spend, and when it can be used.
With the Multi-Chain ECDSA validator, a single approval can authorize the same session key across multiple chains. Under the hood, the per-chain permission accounts are hashed into a Merkle tree of UserOp hashes, and the owner signs the root once. Each chain verifies the session key with a Merkle proof instead of a fresh signature, which reduces prompts and keeps permissions consistent across networks.
Import
import { createAccountClient } from "@namera-ai/sdk/account";
import { createSessionKey, createSessionKeyClient } from "@namera-ai/sdk/session-key";Usage
To create a multichain ECDSA session key, you:
- Create your primary smart account client.
- Create a session key with policies.
- Build a session key client from the serialized account.
import { createAccountClient } from "@namera-ai/sdk/account";
import { createSessionKey, createSessionKeyClient } from "@namera-ai/sdk/session-key";
import { http } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { publicClient } from "./clients";
const signer = privateKeyToAccount(generatePrivateKey());
const accountClient = await createAccountClient({
type: "ecdsa",
signer,
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
chain: mainnet,
client: publicClient,
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
});
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [
// Policies for this session key
],
sessionPrivateKey,
signer,
});
const serializedAccount = sessionKey.serializedAccounts[0]
?.serializedAccount as string;
const sessionKeyClient = await createSessionKeyClient({
type: "ecdsa",
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
chain: mainnet,
client: publicClient,
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
});
// Use sessionKeyClient to send UserOps with the session key.Examples
Create a multichain ECDSA session key with a paymaster, then build a session key client.
import { createAccountClient } from "@namera-ai/sdk/account";
import {
createSessionKey,
createSessionKeyClient,
} from "@namera-ai/sdk/session-key";
import { createPublicClient, http } from "viem";
import { createPaymasterClient } from "viem/account-abstraction";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
export const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});
const paymaster = createPaymasterClient({
transport: http("ZERO_DEV_PAYMASTER_URL"),
});
const signer = privateKeyToAccount(generatePrivateKey());
const client = await createAccountClient({
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
chain: mainnet,
client: publicClient,
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
paymaster,
signer,
type: "ecdsa",
});
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
const sessionKey = await createSessionKey({
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [
// Policies for this session key
],
sessionPrivateKey,
signer,
type: "ecdsa",
});
const serializedAccount = sessionKey.serializedAccounts[0]
?.serializedAccount as string;
const sessionKeyClient = await createSessionKeyClient({
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
chain: mainnet,
client: publicClient,
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
type: "ecdsa",
});
// Use this to get the session key to send transactions.The Bundler URL above is a public endpoint. Please do not use it in production as you will likely be rate-limited. Consider using Pimlico's Bundler, Biconomy's Bundler, or another Bundler service.
Policies
Policies define the conditions under which a session key can sign UserOps. See Policies for detailed policy builders and examples.
ZeroDev exposes several policy types:
- Sudo policy: full permissions, equivalent to the primary signer.
- Call policy: restrict calls to specific contracts, functions, and parameters.
- Gas policy: cap total gas usage for the session key.
- Signature policy: limit what messages can be signed.
- Rate limit policy: bound how frequently UserOps can be sent.
- Timestamp policy: constrain when the key is valid (time windows).
Parameters
type
- Value:
"ecdsa"
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});accountType
- Value:
"ecdsa"
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});clients
- Type:
Client[] - Provide one public client per chain. For single-chain usage, pass a single client.
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});signer
- Type:
Signer
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});sessionPrivateKey
- Type:
Hex - The private key used by the session key signer.
import { generatePrivateKey } from "viem/accounts";
const sessionPrivateKey = generatePrivateKey();
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});policies
- Type:
Policy[] - See Policies.
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [/* ... */],
sessionPrivateKey,
signer,
});entrypointVersion
- Type:
EntrypointVersion=>"0.6" | "0.7" | "0.8" | "0.9"
import { EntryPointVersion } from "viem/account-abstraction";
const = await createSessionKey({
: "ecdsa",
: "ecdsa",
: [publicClient],
: "0.7",
: "0.3.2",
: [],
,
,
});kernelVersion
- Type:
KernelVersion=>"0.0.2" | "0.2.2" | "0.2.3" | "0.2.4" | "0.3.1" | "0.3.2" | "0.3.3"
Note: Kernel 0.2.x supports only Entrypoint 0.6. For Kernel 0.3.x, you can use Entrypoint 0.7 or 0.8.
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
});index
const sessionKey = await createSessionKey({
type: "ecdsa",
accountType: "ecdsa",
clients: [publicClient],
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
policies: [],
sessionPrivateKey,
signer,
index: 0n,
});serializedAccount
- Type:
string - Use the serialized account returned by
createSessionKey.
const serializedAccount = sessionKey.serializedAccounts[0]
?.serializedAccount as string;
const sessionKeyClient = await createSessionKeyClient({
type: "ecdsa",
client: publicClient,
chain: mainnet,
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
});sessionKeySigner
- Type:
Signer
import { privateKeyToAccount } from "viem/accounts";
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
const sessionKeyClient = await createSessionKeyClient({
type: "ecdsa",
client: publicClient,
chain: mainnet,
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
});client
- Type:
Client<HttpTransport, Chain, JsonRpcAccount | LocalAccount | undefined>
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
export const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});bundlerTransport
- Type:
Transport
const sessionKeyClient = await createSessionKeyClient({
type: "ecdsa",
client: publicClient,
chain: mainnet,
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
});chain
- Type:
Chain
import { mainnet } from "viem/chains";paymaster
- Type:
PaymasterClient
import { createPaymasterClient } from "viem/account-abstraction";
import { createSessionKeyClient } from "@namera-ai/sdk/session-key";
const paymaster = createPaymasterClient({
transport: http("ZERO_DEV_PAYMASTER_URL"),
});
const sessionKeyClient = await createSessionKeyClient({
type: "ecdsa",
client: publicClient,
chain: mainnet,
bundlerTransport: http("https://public.pimlico.io/v2/1/rpc"), // Public Pimlico RPC
entrypointVersion: "0.7",
kernelVersion: "0.3.2",
serializedAccount,
sessionKeySigner,
paymaster: paymasterClient,
});