Разработка системы session keys для gasless UX
Стандартный UX Web3 приложения: каждое действие пользователя — отдельная подпись в MetaMask. Играешь в игру — подписываешь каждый ход. Торгуешь на DEX — подписываешь каждый swap. Это неприемлемо для массового продукта. Session keys решают проблему: пользователь подписывает одну транзакцию (открытие сессии), дальше приложение действует от его имени в рамках заданных ограничений, без постоянных подтверждений.
Это возможно только с Account Abstraction (ERC-4337) — потому что у Smart Account может быть несколько авторизованных подписантов с разными правами, в отличие от EOA где есть только один приватный ключ.
Как работают session keys
Архитектура: validator plugin в Smart Account. Account проверяет подпись через validator — основной ECDSA validator требует ключ пользователя. Session key validator требует только ключ сессии, но проверяет ограничения:
Основной ключ пользователя:
→ Validator: ECDSAValidator(userKey)
→ Может всё
Session key:
→ Validator: SessionKeyValidator
→ Проверяет: правильный подписант + ограничения соблюдены
Реализации session key validator:
Kernel (ZeroDev) — наиболее используемый. Session key validator со встроенными permission модулями: ограничение по контрактам, функциям, параметрам, лимитам расходов.
Biconomy Smart Account — собственная Session Key Manager модуль.
Safe + safe-modules — Session Keys через Safe Plugin.
Реализация на 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. Создаём временный session key (ephemeral keypair)
const sessionPrivateKey = generatePrivateKey();
const sessionKeySigner = privateKeyToAccount(sessionPrivateKey);
// 2. Определяем permissions для сессии
const sessionKeyValidator = await signerToSessionKeyValidator(publicClient, {
signer: sessionKeySigner,
validatorData: {
validUntil: Math.floor(Date.now() / 1000) + 86400, // 24 часа
validAfter: 0,
paymaster: oneAddress, // разрешить любой paymaster
permissions: [
{
target: GAME_CONTRACT_ADDRESS,
valueLimit: BigInt(0), // нельзя отправлять 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. Создаём account с session key validator
const account = await createKernelAccount(publicClient, {
plugins: {
sudo: await signerToEcdsaValidator(publicClient, { signer: userSigner }),
regular: sessionKeyValidator,
},
kernelVersion: KERNEL_V3_1,
});
// 4. Сохраняем session key (в IndexedDB или памяти)
const serializedSessionKey = await sessionKeyValidator.serializeSessionKey();
// → передаём backend или храним локально
После создания сессии — backend (или сам браузер) может подписывать транзакции session key без взаимодействия с пользователем:
// Использование сохранённой сессии (например, на сервере)
const restoredValidator = await deserializeSessionKeyValidator(
publicClient,
{ serializedSessionKey },
);
const kernelClient = createKernelAccountClient({
account,
chain: arbitrum,
bundlerTransport: http(BUNDLER_RPC),
paymaster: createZeroDevPaymasterClient({ ... }),
});
// Транзакция без подписи пользователя
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 оплачивает Paymaster, пользователь не подписывает
Gasless: Paymaster интеграция
Session keys убирают необходимость подтверждать каждую операцию. Paymaster убирает необходимость держать нативный токен для gas. Вместе — полностью gasless UX.
ERC-4337 Paymaster: смарт-контракт, который спонсирует gas для UserOperations. Два типа:
Verifying Paymaster: перед каждой UserOp вызывает ваш backend для проверки. Backend подписывает разрешение. Гибко: можно контролировать какие операции спонсировать.
ERC-20 Paymaster: принимает оплату в ERC-20 токенах (USDC) вместо ETH. Пользователь платит gas в USDC, paymaster конвертирует и платит в ETH.
// Verifying Paymaster backend logic
export async function signPaymasterRequest(
userOp: UserOperation,
): Promise<{ paymasterData: Hex; paymasterValidationGasLimit: bigint }> {
// Проверяем: можем ли спонсировать эту операцию?
const user = await getUserBySmartAccount(userOp.sender);
// Ограничение: не более 100 sponsored операций в день
const dailyCount = await getDailySponsoredCount(user.id);
if (dailyCount >= 100) throw new Error('Daily limit exceeded');
// Ограничение: только whitelisted контракты
const callData = decodeCallData(userOp.callData);
if (!isWhitelisted(callData.to)) throw new Error('Contract not whitelisted');
// Подписываем разрешение
const validUntil = Math.floor(Date.now() / 1000) + 300; // 5 минут
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,
};
}
Стоимость спонсирования: на Arbitrum One — $0.001–$0.005 за sponsored UserOp. На Ethereum mainnet — $0.50–$2.00. Для игровых приложений — L2 обязателен.
Провайдеры Paymaster-as-a-Service: Pimlico (Alto bundler + paymaster), ZeroDev, Biconomy. Пиmlico Verifying Paymaster + Alto bundler — наиболее используемая комбинация в production.
Ограничения и безопасность session keys
Что нужно ограничивать
Session key validator должен принудительно ограничивать:
- Контракты: только указанные адреса. Нельзя вызывать произвольные контракты session key-ом.
-
Функции: только конкретные функции. Session key для игры не должен мочь вызвать
transferв ERC-20. - Параметры: диапазоны значений (x < 8, amount <= maxAmount).
- Value limit: максимальный ETH который можно отправить. Для большинства игр — 0.
- Spending limit: общий лимит потраченных ERC-20 токенов за время сессии.
- Expiry: время жизни сессии. Для игры — 1–8 часов. Для trading бота — до 24 часов.
Хранение session key
Session key — приватный ключ с ограниченными правами. Но его компрометация всё равно опасна в рамках permissions.
Браузер: sessionStorage (живёт до закрытия таба) или indexedDB с шифрованием. Не использовать localStorage (доступен между сессиями, XSS риск).
Backend: если сессия управляется сервером (для server-side автоматизации) — храним в зашифрованном виде (KMS), привязываем к user session token.
// Безопасное хранение session key в браузере (зашифрован 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)),
}));
}
Практический пример: GameFi сессия
Типичный flow для Play-to-Earn игры:
1. Пользователь нажимает "Начать игру"
2. Один approve в кошельке: открываем сессию на 4 часа
Permissions: вызов makeMove(x,y), claimReward(), только GameContract
3. Пользователь играет — каждый ход подписывается session key автоматически
4. Ходы отправляются через bundler, gas оплачивает Paymaster
5. Через 4 часа сессия истекает — нужен новый approve
Результат: пользователь видит игровой интерфейс без постоянных всплывающих окон MetaMask. Onboarding близок к Web2.
Инструментарий
Ядро: @zerodev/sdk + @zerodev/session-key — наиболее зрелый. Поддержка: Arbitrum, Optimism, Base, Polygon, Ethereum mainnet. Альтернатива: Biconomy SDK v4 с сессионными модулями. Bundler: Alto (Pimlico), Stackup (Alchemy), Etherspot. Мониторинг UserOps: Jiffyscan, Pimlico dashboard.
| Задача | Инструмент |
|---|---|
| Session key validator | ZeroDev Kernel / Biconomy |
| Paymaster | Pimlico / ZeroDev |
| Bundler | Alto (Pimlico) |
| AA wallet | Kernel v3 / Safe |
| Frontend | wagmi v2 + @zerodev/wagmi |
Ориентиры по срокам
Базовая реализация (session keys + verifying paymaster, один chain): 3–4 недели. Полная система с multi-chain поддержкой, custom permission модулями, ERC-20 paymaster и аналитикой спонсированных операций: 6–8 недель.







