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

If you are using an EIP-7702 account, the account must be delegated onchain before creating the 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 preparedCalls = await client.prepareCalls({
2 calls: [], // empty array since you don't want to send any calls
3 from: account.address,
4 capabilities: {
5 paymasterService: {
6 policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
7 },
8 },
9});
10
11const signedCalls = await client.signPreparedCalls(preparedCalls);
12
13const { preparedCallIds } = await client.sendPreparedCalls(signedCalls);
14
15const status = await client.getCallsStatus(preparedCallIds[0]);

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

1const account = await client.requestAccount(); // Request an account for the owner signer
2
3// This is where you would use your session key signer!
4const sessionKey = LocalAccountSigner.generatePrivateKeySigner();
5
6const permissions = await client.grantPermissions({
7 account: account.address,
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: account.address,
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]);