Розробка Smart Contract Wallet (Account Abstraction)
Account Abstraction — це архітектурний зсув, який перетворює гаманець з пасивного сховища ключів на програмований агент. EIP-4337 стандартизував цей підхід без змін на рівні протоколу Ethereum: вся логіка живе у смарт-контрактах, а спеціалізована мемпул-інфраструктура (Bundlers та Paymasters) обробляє UserOperation об'єкти замість звичайних транзакцій. Якщо ви будуєте продукт, де користувач не повинен думати про gas, seed phrase та approve — без AA не обійтися.
Архітектура EIP-4337: як це працює насправді
Компоненти системи
Класична EOA-транзакція йде прямо в mempool і виконується нодою. У AA-системі ланцюг інший:
-
UserOperation — псевдотранзакція, підписана користувачем. Містить
callData,sender(адреса смарт-гаманця),signature, ліміти gas та параметри Paymaster. - Bundler — offchain агент, який збирає UserOperations з альтернативного mempool, упаковує їх в одну on-chain транзакцію та викликає EntryPoint. Існуючі реалізації: Stackup, Pimlico, Alchemy Rundler (написаний на Rust, на порядок швидше за референсну реалізацію).
- EntryPoint — singleton контракт (0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 на більшості EVM мереж), розгорнутий командою ERC-4337. Саме він верифікує та виконує пакет операцій. Неможливо розгорнути свій EntryPoint — екосистема привязана до цієї адреси.
-
Account Contract — сам смарт-гаманець користувача. Повинен реалізовувати інтерфейс
IAccountз методомvalidateUserOp. Сюди йде вся кастомна логіка. - Paymaster — опціональний контракт, який платить gas за користувача або приймає оплату в ERC-20 токенах замість ETH.
Життєвий цикл UserOperation
User → sign UserOp → send to Bundler RPC
Bundler → simulate via eth_estimateUserOperationGas → validate signature + paymaster
Bundler → batch multiple UserOps → call EntryPoint.handleOps()
EntryPoint → validateUserOp() на кожному Account Contract
EntryPoint → Paymaster.validatePaymasterUserOp()
EntryPoint → execute callData
EntryPoint → postOp() на Paymaster (для обліку gas)
Важливо: simulation та execution розділені. Bundler симулює через eth_callStateOverride та відхиляє операції, які можуть зафейлитися on-chain. Це захищає Bundler від втрати ETH на failed transactions.
Реалізація Account Contract
Базова структура
За основу беремо SimpleAccount від eth-infinitism або SafeAccount від Safe (раніше Gnosis Safe). Для production — Safe v1.4.1 з 4337 модулем, оскільки він battle-tested з $100B+ TVL.
contract SmartWallet is IAccount, Initializable, UUPSUpgradeable {
address public owner;
IEntryPoint private immutable _entryPoint;
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external override returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
}
function _validateSignature(
UserOperation calldata userOp,
bytes32 userOpHash
) internal view returns (uint256) {
bytes32 hash = userOpHash.toEthSignedMessageHash();
if (owner != hash.recover(userOp.signature))
return SIG_VALIDATION_FAILED;
return 0; // SIG_VALIDATION_SUCCESS
}
}
validationData кодує три речі: результат валідації (0 = успіх, 1 = невдача), validAfter та validUntil timestamps. Це дозволяє робити time-bounded операції прямо в підписі.
Шаблони розширеної логіки
Multisig в одному гаманці. Замість owner зберігається порогове значення та список авторизованих підписувачів. validateUserOp перевіряє, що signatures містить достатню кількість коректних підписів.
Session keys. Обмежений ключ (наприклад, згенерований браузером без розкриття seed phrase), якому дозволені операції лише в межах конкретного контракту, ліміту суми та часового вікна:
struct SessionKey {
address key;
address allowedContract;
uint256 spendingLimit;
uint48 validUntil;
bool enabled;
}
mapping(address => SessionKey) public sessionKeys;
Це фундамент для "gasless gaming" — користувач один раз підписує сесію на ігровий контракт, далі гра робить транзакції від його імені.
Social recovery. Guardians — довірені адреси, які можуть змінити owner через timelock (зазвичай 72 години). Реалізація Argent — хороший референс: threshold з M-of-N guardians, cancellation протягом timelock window якщо owner онлайн.
Paymaster: gasless та ERC-20 оплата
Sponsoring Paymaster
contract SponsoringPaymaster is IPaymaster {
mapping(address => bool) public whitelistedContracts;
function validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData) {
// Спонсируємо лише виклики whitelisted контрактів
address target = address(bytes20(userOp.callData[16:36]));
require(whitelistedContracts[target], "Not whitelisted");
require(deposit() >= maxCost, "Insufficient deposit");
return (abi.encode(userOp.sender), 0);
}
}
Paymaster повинен тримати депозит в EntryPoint. Механізм staking запобігає DoS: Paymaster без stake може спонсувати максимум одну операцію за bundle.
ERC-20 Paymaster
Приймає будь-який ERC-20 як оплату gas. Oracle потрібен для конвертації: Chainlink price feed або пул Uniswap V3 TWAP. Схема роботи: до виконання блокуємо maxCost * exchangeRate токенів, після — списуємо фактичну вартість через postOp.
Ready-made рішення: Pimlico ERC-20 Paymaster (open source), Stackup Paymaster SDK.
Factory та Counterfactual Deploy
Одна з ключових властивостей AA — гаманець існує як адреса ще до деплою. CREATE2 з детермінованим salt (зазвичай хеш від owner address) дає передбачувану адресу. Користувач отримує адресу гаманця до першої транзакції, може отримати кошти — гаманець деплоїться автоматично при першому використанні.
contract WalletFactory {
function getAddress(address owner, uint256 salt) public view returns (address) {
return Create2.computeAddress(
bytes32(salt),
keccak256(abi.encodePacked(
type(ERC1967Proxy).creationCode,
abi.encode(address(implementation), initData(owner))
))
);
}
function createAccount(address owner, uint256 salt) external returns (SmartWallet) {
address addr = getAddress(owner, salt);
if (addr.code.length > 0) return SmartWallet(payable(addr)); // вже розгорнутий
return SmartWallet(payable(new ERC1967Proxy{salt: bytes32(salt)}(
address(implementation), initData(owner)
)));
}
}
Frontend Integration
Viem + permissionless.js — найактуальніший стек (2024–2025). permissionless побудований на Viem і надає абстракції для роботи з Bundler та Paymaster RPC:
import { createSmartAccountClient } from "permissionless";
import { signerToSimpleSmartAccount } from "permissionless/accounts";
import { createPimlicoBundlerClient } from "permissionless/clients/pimlico";
const smartAccount = await signerToSimpleSmartAccount(publicClient, {
signer: walletClient,
factoryAddress: FACTORY_ADDRESS,
entryPoint: ENTRY_POINT_ADDRESS,
});
const smartAccountClient = createSmartAccountClient({
account: smartAccount,
chain: optimism,
bundlerTransport: http(BUNDLER_RPC_URL),
middleware: {
sponsorUserOperation: paymasterClient.sponsorUserOperation,
},
});
// Відправка транзакції — ідентично звичайному гаманцю для користувача
const txHash = await smartAccountClient.sendTransaction({
to: contractAddress,
data: encodeFunctionData({ abi, functionName: "doSomething" }),
});
ZeroDev SDK — альтернатива з вищим рівнем абстракції, вбудованими session keys та Kernel account (популярний Account Contract з plugin системою).
Альтернативи EIP-4337
zkSync Native AA — на zkSync Era AA вбудована в протокол, не потрібен окремий EntryPoint. Кожний аккаунт може бути смарт-контрактом прямо з коробки. Більш ефективно по gas, але привязка до zkSync.
EIP-7702 (Prague/Electra) — майбутній hardfork Ethereum. Дозволяє EOA тимчасово делегувати виконання смарт-контракту через спеціальний тип транзакції. Не замінює 4337 повністю, але закриває частину use cases простіше.
Етапи розробки та оцінка
| Компонент | Складність | Тривалість |
|---|---|---|
| Базовий Account Contract (single owner) | Середня | 1–2 тиж |
| Factory + counterfactual deploy | Низька | 3–5 днів |
| Sponsoring Paymaster | Середня | 1 тиж |
| ERC-20 Paymaster + oracle | Висока | 1–2 тиж |
| Session keys | Висока | 1–2 тиж |
| Social recovery | Висока | 1–2 тиж |
| Frontend SDK інтеграція | Середня | 1 тиж |
| Аудит + виправлення | — | 3–6 тиж |
Мінімальний production-ready гаманець (single owner + sponsoring paymaster + frontend) — 4–6 тижнів розробки. Повнофункціональний продукт з social recovery, session keys та мультисіттю — 3–5 місяців.
Ключовий момент при виборі підрядника: реалізація validateUserOp повинна бути аудирована. Помилка в цій функції — прямої втрати коштів користувачів. Економія на аудиті тут = усвідомлений ризик.







