Session Keys (SDK)

Learn how to use session keys using the Wallet Client SDK

1. Install Prerequisites

You’re going to need the @account-kit/wallet-client and @account-kit/infra. We’ll also be using LocalAccountSigner from @aa-sdk/core as the signer for demonstration purposes.

$npm install @account-kit/wallet-client @account-kit/infra @aa-sdk/core

2. Create A Smart Wallet Client

Create a client for a given signer (e.g. a LocalAccountSigner imported from @aa-sdk/core or an Alchemy Signer).

1import { createSmartWalletClient } from "@account-kit/wallet-client";
2import { alchemy, arbitrumSepolia } from "@account-kit/infra";
3import { LocalAccountSigner } from "@aa-sdk/core";
4
5const signer = LocalAccountSigner.privateKeyToAccountSigner(PRIVATE_KEY); // we use a private key signer as an example here
6
7const transport = alchemy({
8 apiKey: ALCHEMY_API_KEY, // use your Alchemy app api key here
9});
10
11const client = createSmartWalletClient({
12 transport,
13 chain: arbitrumSepolia, // use any chain imported from @account-kit/infra here
14 signer,
15});

3. Create the session key

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:

1const signerAddress = await signer.getAddress();
2
3const preparedCalls = await client.prepareCalls({
4 calls: [], // empty array since you don't want to send any calls
5 from: signerAddress,
6 capabilities: {
7 paymasterService: {
8 policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
9 },
10 },
11});
12
13const signedCalls = await client.signPreparedCalls(preparedCalls);
14
15const { preparedCallIds } = await client.sendPreparedCalls(signedCalls);
16
17const status = await client.getCallsStatus(preparedCallIds[0]);

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

1const signerAddress = await signer.getAddress();
2
3// This is where you would use your session key signer!
4const sessionKey = LocalAccountSigner.generatePrivateKeySigner();
5
6const permissions = await client.grantPermissions({
7 account: signerAddress,
8 expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
9 key: {
10 publicKey: await sessionKey.getAddress(),
11 type: "secp256k1",
12 },
13 permissions: [{ type: "root" }], // Here we grant root permissions as an example, but this is not advised in production!
14});

4. Send calls using the session key

1import { signPreparedCalls } from "@account-kit/wallet-client";
2
3const preparedCalls = await client.prepareCalls({
4 calls: [{ to: "0x0000000000000000000000000000000000000000", value: "0x0" }],
5 from: signerAddress,
6 capabilities: {
7 paymasterService: {
8 policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
9 },
10 permissions,
11 },
12});
13
14const signedCalls = await signPreparedCalls(
15 sessionKey, // Note that we now sign with the session key!
16 preparedCalls,
17);
18
19const { preparedCallIds } = await client.sendPreparedCalls({
20 ...signedCalls,
21 capabilities: {
22 permissions,
23 },
24});
25
26// Check calls status.
27const status = await client.getCallsStatus(preparedCallIds[0]);