Picture this: your user wants to send tokens but has no ETH for gas. Or needs to recover wallet access without a seed phrase, but the seed phrase is lost. A classic EOA wallet can't handle these scenarios. The solution is a smart contract wallet with Account Abstraction (AA). Our engineers have 10+ years of blockchain experience and have been building such wallets since the standardization of EIP-4337. Account Abstraction transforms a wallet from a passive key storage into a programmable agent. All logic lives in smart contracts, and a specialized mempool infrastructure (Bundlers and Paymasters) processes UserOperation objects instead of regular transactions. We have delivered 20+ projects on Ethereum, Polygon, and Arbitrum. Average gas savings when using a Paymaster are $0.5–2 per transaction. The cost of an external audit is discussed individually. Order your smart contract wallet development — get an engineer consultation within 2 days.
How Account Abstraction Changes Wallet Architecture
System Components — Smart Contract Development
A classic EOA transaction goes directly to the mempool and is executed by a node. In an AA system, the chain is different:
- UserOperation — a pseudo‑transaction signed by the user. Contains callData, sender (smart wallet address), signature, gas limits, and Paymaster parameters.
- Bundler — an off‑chain agent that collects UserOperations from the alternative mempool, packs them into one on‑chain transaction, and calls the EntryPoint. Existing implementations: Stackup, Pimlico, Alchemy Rundler (written in Rust, order of magnitude faster than the reference implementation).
- EntryPoint — a singleton contract (0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 on most EVM networks), deployed by the ERC-4337 team. It verifies and executes the batch of operations. You cannot deploy your own EntryPoint — the ecosystem depends on this address.
- Account Contract — the user's smart wallet itself. Must implement the IAccount interface with the validateUserOp method. This is where all custom logic lives.
- Paymaster — an optional contract that pays gas for the user or accepts payment in ERC-20 tokens instead of ETH.
More details in the EIP-4337 specification.
Lifecycle of a 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() on each Account Contract
EntryPoint → Paymaster.validatePaymasterUserOp()
EntryPoint → execute callData
EntryPoint → postOp() on Paymaster (for gas accounting)
Important: simulation and execution are separate. The Bundler simulates using eth_callStateOverride and rejects operations that would fail on‑chain. This protects the Bundler from losing ETH on failed transactions.
What Is Factory and Counterfactual Deploy?
One key property of AA is that the wallet exists as an address even before deployment. CREATE2 with a deterministic salt (usually a hash of the owner address) gives a predictable address. The user gets the wallet address before the first transaction, can receive funds — the wallet is deployed automatically on first use.
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)); // already deployed
return SmartWallet(payable(new ERC1967Proxy{salt: bytes32(salt)}(
address(implementation), initData(owner)
)));
}
}
Implementing Gasless Transactions via Paymaster
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) {
// Sponsor only calls to whitelisted contracts
address target = address(bytes20(userOp.callData[16:36]));
require(whitelistedContracts[target], "Not whitelisted");
require(deposit() >= maxCost, "Insufficient deposit");
return (abi.encode(userOp.sender), 0);
}
}
The Paymaster must hold a deposit in the EntryPoint. The staking mechanism prevents DoS: a Paymaster without stake can sponsor at most one operation per bundle.
ERC-20 Paymaster
Accepts any ERC-20 as gas payment. An oracle is needed for conversion: Chainlink price feed or Uniswap V3 TWAP pool. Workflow: before execution, lock maxCost * exchangeRate tokens; after, deduct the actual cost through postOp.
Ready solutions: Pimlico ERC-20 Paymaster (open source), Stackup Paymaster SDK.
Implementing the Account Contract
Basic Structure
We base our work on SimpleAccount from eth-infinitism or SafeAccount from Safe (formerly Gnosis Safe). For production, we recommend Safe v1.4.1 with the 4337 module, as it is battle‑tested with $100B+ TVL.
Instead of providing the full code, we describe the key method validateUserOp. It checks the signature, nonce, and optionally pays for gas. validationData encodes three parameters: validation result (0 success, 1 failure), validAfter and validUntil timestamps (time‑bounded operations).
Extended Logic Patterns
Session keys. A restricted key (e.g., generated by a browser without exposing the seed phrase) that is allowed operations only on a specific contract, amount limit, and time window. Storage structure:
struct SessionKey {
address key;
address allowedContract;
uint256 spendingLimit;
uint48 validUntil;
bool enabled;
}
mapping(address => SessionKey) public sessionKeys;
This is the foundation for "gasless gaming" — the user signs a session for the game contract once, and the game makes transactions on their behalf.
Social recovery. Guardians — trusted addresses that can change the owner after a timelock (usually 72 hours). The Argent implementation is a good reference: M‑of‑N guardians with cancellation within the timelock window if the owner is online.
Frontend Integration
Viem + permissionless.js is the most current stack (regularly updated). permissionless builds on Viem and provides abstractions for working with Bundler and 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,
},
});
// Sending a transaction — identical to a regular wallet for the user
const txHash = await smartAccountClient.sendTransaction({
to: contractAddress,
data: encodeFunctionData({ abi, functionName: "doSomething" }),
});
ZeroDev SDK is an alternative with a higher level of abstraction, built‑in session keys, and the Kernel account (a popular Account Contract with a plugin system).
Alternatives to EIP-4337
| Solution | Requires EntryPoint | EVM Compatibility | Feature |
|---|---|---|---|
| EIP-4337 | Yes | All EVM networks | De facto standard |
| zkSync Native AA | No | zkSync Era | Built into L2, cheaper |
| EIP-7702 | No (temporary delegation) | Future Ethereum | Simpler for EOAs |
zkSync Native AA — on zkSync Era, AA is built into the protocol, no separate EntryPoint needed. Every account can be a smart contract out of the box. More gas‑efficient, but tied to zkSync.
EIP-7702 (Prague/Electra) — upcoming Ethereum hard fork. Allows an EOA to temporarily delegate execution to a smart contract via a special transaction type. It doesn't fully replace 4337, but closes some use cases more simply.
Development and Estimation Stages
| Component | Complexity | Timeline |
|---|---|---|
| Basic Account Contract (single owner) | Medium | 1–2 weeks |
| Factory + counterfactual deploy | Low | 3–5 days |
| Sponsoring Paymaster | Medium | 1 week |
| ERC-20 Paymaster + oracle | High | 1–2 weeks |
| Session keys | High | 1–2 weeks |
| Social recovery | High | 1–2 weeks |
| Frontend SDK integration | Medium | 1 week |
| Audit + fixes | — | 3–6 weeks |
More on the audit process
The audit includes manual code review with static analyzers (Slither, Mythril) and fuzzing (Echidna). Special attention is given to validateUserOp and Paymaster logic. The output is a report with bug severity and estimated fix time.
A minimum production‑ready wallet (single owner + sponsoring paymaster + frontend) takes 4–6 weeks of development. A full‑featured product with social recovery, session keys, and multi‑chain support takes 3–5 months. We will evaluate your project within 2 days — just send us your task description.
What Is Included in the Work
- Requirements analysis and architecture design
- Smart contract development (Account Contract, Factory, Paymaster)
- Integration with Bundler and Paymaster (Pimlico, Stackup, Alchemy)
- Frontend SDK using Viem + permissionless.js
- Testing (unit, integration, fuzzing via Echidna)
- Code audit (internal + external)
- Deployment to mainnet/testnet
- Documentation and team training
- Technical support for 3 months
A critical point when choosing a contractor: the validateUserOp implementation must be audited. An error in this function directly leads to loss of user funds. Saving on audit here is a conscious risk. Contact us for a preliminary assessment — we can tell you how to avoid typical mistakes. Get an engineer consultation today!







