Skip to content

Session Keys ​

Session keys allow you to grant AI agents scoped, time-limited access to your smart wallet. Instead of sharing the owner's private key, you create a session key that can only call specific contracts and functions, with optional parameter-level constraints.

What Are Session Keys? ​

  • Scoped — Each session key is restricted to specific contract addresses and function selectors.
  • Time-limited — Sessions have an expiration timestamp. After expiry, the key is useless.
  • On-chain enforcement — Permissions are enforced by the Smart Sessions validator module (ERC-7579), not just client-side checks.
  • Damage-limited — If a session key is leaked, the attacker can only perform the actions allowed by the session's permission scope.

Architecture ​

Smart Sessions (developed by Rhinestone and Biconomy) is an ERC-7579 validator module. The wallet owner installs it and creates permission structures that map session keys to allowed actions. When an agent submits a UserOp signed with a session key, the Smart Sessions validator checks the signature and enforces the permission scope entirely on-chain.

Creating a Session ​

typescript
const session = await client.createSession(wallet, {
  actions: [
    {
      target: "0xDeFiRouter",
      selector: "0x38ed1739", // swapExactTokensForTokens
    },
    {
      target: "0xTokenAddress",
      selector: "0xa9059cbb", // transfer
      rules: [{
        offset: 32n,                                    // Check 2nd param (amount)
        condition: "less",
        value: "0x00000000000000000000000000000000000000000000000000038d7ea4c68000", // < 0.001 ETH
      }],
    },
  ],
  expiresAt: Math.floor(Date.now() / 1000) + 3600, // 1 hour
}, ownerPrivateKey);

console.log(`Session key: ${session.sessionKey}`);
console.log(`Private key: ${session.privateKey}`);
console.log(`Permission ID: ${session.permissionId}`);

The createSession method:

  1. Generates a new key pair for the session.
  2. Builds the permission structure from the provided actions and rules.
  3. Installs the Smart Sessions validator module (if not already installed).
  4. Has the owner sign the enable details to authorize the session.
  5. Returns the session key, private key, and permission ID.

Using a Session Key ​

Pass the session key's private key when executing transactions:

typescript
await client.execute(wallet, {
  target: "0xDeFiRouter",
  data: encodeFunctionData({...}),
  sessionKey: session.privateKey,
});

The UserOp will be signed with the session key instead of the owner key. The Smart Sessions validator verifies that the call is within the session's allowed scope.

Revoking a Session ​

typescript
await client.revokeSession(wallet, session.permissionId, ownerPrivateKey);

Revocation is an on-chain action that invalidates the permission. Any subsequent UserOps signed with the revoked session key will be rejected by the validator.

Listing Active Sessions ​

typescript
const sessions = client.getActiveSessions(wallet.address);
for (const s of sessions) {
  console.log(`Key: ${s.sessionKey}, Expires: ${new Date(s.expiresAt * 1000)}`);
}

WARNING

Session data is stored in-memory. If the process restarts, session metadata is lost. However, on-chain sessions remain valid until their expiry time or until explicitly revoked.

Session Rules ​

Rules add parameter-level constraints to individual actions. They allow you to restrict not just which function is called, but what arguments are passed.

typescript
interface SessionRule {
  offset: bigint;    // Byte offset in calldata (32n = 2nd param, 64n = 3rd, etc.)
  condition: "equal" | "greater" | "less" | "notEqual";
  value: Hex;        // 32-byte ABI-encoded value to compare against
}

Offset Calculation ​

Function calldata is structured as a 4-byte selector followed by 32-byte ABI-encoded parameters:

OffsetParameter
0n1st parameter (bytes 4-35)
32n2nd parameter (bytes 36-67)
64n3rd parameter (bytes 68-99)
96n4th parameter (bytes 100-131)

Example: Limit Transfer Amount ​

typescript
{
  target: "0xTokenAddress",
  selector: "0xa9059cbb", // transfer(address to, uint256 amount)
  rules: [{
    offset: 32n,          // 2nd parameter = amount
    condition: "less",
    value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000", // < 1 ETH
  }],
}

Example: Restrict Recipient ​

typescript
{
  target: "0xTokenAddress",
  selector: "0xa9059cbb", // transfer(address to, uint256 amount)
  rules: [{
    offset: 0n,           // 1st parameter = to address
    condition: "equal",
    value: "0x000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  }],
}

Multiple rules on the same action are ANDed together -- all rules must pass for the call to be allowed.

Released under the MIT License.