Session Keys System for Gasless UX Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Session Keys System for Gasless UX Development
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Session Keys System for Gasless UX

Standard Web3 app UX: each user action — separate MetaMask signature. Playing a game — sign each move. Trading on DEX — sign each swap. This is unacceptable for mass product. Session keys solve the problem: user signs one transaction (opening session), then application acts on their behalf within set limits, without constant confirmations.

This is only possible with Account Abstraction (ERC-4337) — because Smart Account can have multiple authorized signers with different rights, unlike EOA where there's only one private key.

How Session Keys Work

Architecture: validator plugin in Smart Account. Account checks signature through validator — main ECDSA validator requires user key. Session key validator requires only session key, but checks restrictions:

User's main key:
  → Validator: ECDSAValidator(userKey)
  → Can do anything

Session key:
  → Validator: SessionKeyValidator
  → Checks: correct signer + restrictions met

Session key validator implementations:

Kernel (ZeroDev) — most used. Session key validator with built-in permission modules: contract limitation, function selection, parameter ranges, spending limits.

Biconomy Smart Account — own Session Key Manager module.

Safe + safe-modules — Session Keys via Safe Plugin.

Implementation on ZeroDev Kernel

import {
  createKernelAccount,
  createKernelAccountClient,
  createZeroDevPaymasterClient,
} from '@zerodev/sdk';
import {
  signerToSessionKeyValidator,
  ParamOperator,
  oneAddress,
} from '@zerodev/session-key';
import { signerToEcdsaValidator } from '@zerodev/ecdsa-validator';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { parseAbi, encodeFunctionData } from 'viem';

// 1. Create temporary session key (ephemeral keypair)
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);

// 2. Define session permissions
const sessionKeyValidator = await signerToSessionKeyValidator(publicClient, {
  signer: sessionKeySigner,
  validatorData: {
    validUntil: Math.floor(Date.now() / 1000) + 86400, // 24 hours
    validAfter: 0,
    paymaster: oneAddress, // allow any paymaster
    permissions: [
      {
        target: GAME_CONTRACT_ADDRESS,
        valueLimit: BigInt(0), // can't send ETH
        abi: parseAbi(['function makeMove(uint8 x, uint8 y) external']),
        functionName: 'makeMove',
        args: [
          { operator: ParamOperator.LESS_THAN, value: 8n }, // x < 8
          { operator: ParamOperator.LESS_THAN, value: 8n }, // y < 8
        ],
      },
    ],
  },
});

// 3. Create account with session key validator
const account = await createKernelAccount(publicClient, {
  plugins: {
    sudo: await signerToEcdsaValidator(publicClient, { signer: userSigner }),
    regular: sessionKeyValidator,
  },
  kernelVersion: KERNEL_V3_1,
});

// 4. Save session key (in IndexedDB or memory)
const serializedSessionKey = await sessionKeyValidator.serializeSessionKey();
// → pass to backend or store locally

After session creation — backend (or browser itself) can sign transactions with session key without user interaction:

// Using saved session (e.g., on server)
const restoredValidator = await deserializeSessionKeyValidator(
  publicClient,
  { serializedSessionKey },
);

const kernelClient = createKernelAccountClient({
  account,
  chain: arbitrum,
  bundlerTransport: http(BUNDLER_RPC),
  paymaster: createZeroDevPaymasterClient({ ... }),
});

// Transaction without user signature
const txHash = await kernelClient.sendTransaction({
  to: GAME_CONTRACT_ADDRESS,
  data: encodeFunctionData({
    abi: parseAbi(['function makeMove(uint8 x, uint8 y) external']),
    functionName: 'makeMove',
    args: [3n, 4n],
  }),
});
// Gas paid by Paymaster, user doesn't sign

Gasless: Paymaster Integration

Session keys remove need to confirm each operation. Paymaster removes need to hold native token for gas. Together — completely gasless UX.

ERC-4337 Paymaster: smart contract sponsoring gas for UserOperations. Two types:

Verifying Paymaster: calls your backend before each UserOp for check. Backend signs permission. Flexible: you control which operations to sponsor.

ERC-20 Paymaster: accepts payment in ERC-20 tokens (USDC) instead of ETH. User pays gas in USDC, paymaster converts and pays in ETH.

// Verifying Paymaster backend logic
export async function signPaymasterRequest(
  userOp: UserOperation,
): Promise<{ paymasterData: Hex; paymasterValidationGasLimit: bigint }> {
  // Check: can we sponsor this operation?
  const user = await getUserBySmartAccount(userOp.sender);
  
  // Restriction: no more than 100 sponsored operations per day
  const dailyCount = await getDailySponsoredCount(user.id);
  if (dailyCount >= 100) throw new Error('Daily limit exceeded');
  
  // Restriction: only whitelisted contracts
  const callData = decodeCallData(userOp.callData);
  if (!isWhitelisted(callData.to)) throw new Error('Contract not whitelisted');
  
  // Sign permission
  const validUntil = Math.floor(Date.now() / 1000) + 300; // 5 minutes
  const signature = await paymasterSigner.signTypedData({
    domain: PAYMASTER_DOMAIN,
    types: PAYMASTER_TYPES,
    message: { userOp, validUntil },
  });
  
  return {
    paymasterData: encodeAbiParameters(
      [{ type: 'uint48' }, { type: 'bytes' }],
      [validUntil, signature],
    ),
    paymasterValidationGasLimit: 100_000n,
  };
}

Sponsorship cost: on Arbitrum One — $0.001–$0.005 per sponsored UserOp. On Ethereum mainnet — $0.50–$2.00. For gaming apps — L2 is mandatory.

Paymaster-as-a-Service providers: Pimlico (Alto bundler + paymaster), ZeroDev, Biconomy. Pimlico Verifying Paymaster + Alto bundler — most used combination in production.

Session Keys Limitations and Security

What to Restrict

Session key validator must enforce restrictions:

  • Contracts: only specified addresses. Can't call arbitrary contracts with session key.
  • Functions: only specific functions. Session key for game shouldn't call transfer in ERC-20.
  • Parameters: value ranges (x < 8, amount <= maxAmount).
  • Value limit: max ETH to send. For most games — 0.
  • Spending limit: total ERC-20 tokens spent during session.
  • Expiry: session lifetime. For game — 1–8 hours. For trading bot — up to 24 hours.

Session Key Storage

Session key — private key with limited rights. But compromising it is still dangerous within permissions scope.

Browser: sessionStorage (lives until tab close) or indexedDB with encryption. Don't use localStorage (accessible between sessions, XSS risk).

Backend: if session managed by server (for server-side automation) — store encrypted (KMS), bind to user session token.

// Secure session key storage in browser (encrypted with user-derived key)
async function storeSessionKey(
  sessionPrivateKey: Hex,
  serializedPermissions: string,
  userAuthKey: CryptoKey,
): Promise<void> {
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const data = new TextEncoder().encode(
    JSON.stringify({ sessionPrivateKey, serializedPermissions }),
  );
  
  const encrypted = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    userAuthKey,
    data,
  );
  
  sessionStorage.setItem('session_key', JSON.stringify({
    iv: Array.from(iv),
    data: Array.from(new Uint8Array(encrypted)),
  }));
}

Practical Example: GameFi Session

Typical flow for Play-to-Earn game:

1. User clicks "Start Game"
2. One approval in wallet: open session for 4 hours
   Permissions: call makeMove(x,y), claimReward(), only GameContract
3. User plays — each move signed by session key automatically
4. Moves sent via bundler, gas paid by Paymaster
5. After 4 hours session expires — need new approval

Result: user sees game interface without constant MetaMask popups. Onboarding close to Web2.

Tooling

Core: @zerodev/sdk + @zerodev/session-key — most mature. Support: Arbitrum, Optimism, Base, Polygon, Ethereum mainnet. Alternative: Biconomy SDK v4 with session modules. Bundler: Alto (Pimlico), Stackup (Alchemy), Etherspot. Monitoring UserOps: Jiffyscan, Pimlico dashboard.

Task Tool
Session key validator ZeroDev Kernel / Biconomy
Paymaster Pimlico / ZeroDev
Bundler Alto (Pimlico)
AA wallet Kernel v3 / Safe
Frontend wagmi v2 + @zerodev/wagmi

Timeline Guidelines

Basic implementation (session keys + verifying paymaster, one chain): 3–4 weeks. Full system with multi-chain support, custom permission modules, ERC-20 paymaster and sponsorship analytics: 6–8 weeks.