Smart Contract Wallet (Account Abstraction) 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
Smart Contract Wallet (Account Abstraction) Development
Complex
~1-2 weeks
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

Smart Contract Wallet Development (Account Abstraction)

Account Abstraction is an architectural shift that transforms a wallet from a passive key storage into a programmable agent. EIP-4337 standardized this approach without changes at the Ethereum protocol level: all logic lives in smart contracts, and specialized mempool infrastructure (Bundlers and Paymasters) processes UserOperation objects instead of regular transactions. If you're building a product where users shouldn't worry about gas, seed phrases, and approvals — you can't do without AA.

EIP-4337 Architecture: How It Actually Works

System Components

A classic EOA transaction goes directly to mempool and is executed by a node. In an AA system, the flow is different:

  1. UserOperation — a pseudo-transaction signed by the user. Contains callData, sender (smart wallet address), signature, gas limits and Paymaster parameters.
  2. Bundler — an offchain agent that collects UserOperations from an alternative mempool, packages them into a single on-chain transaction, and calls EntryPoint. Existing implementations: Stackup, Pimlico, Alchemy Rundler (written in Rust, orders of magnitude faster than reference implementation).
  3. EntryPoint — a singleton contract (0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 on most EVM networks), deployed by the ERC-4337 team. It verifies and executes a batch of operations. You cannot deploy your own EntryPoint — the ecosystem is tied to this address.
  4. Account Contract — the user's smart wallet itself. Must implement the IAccount interface with a validateUserOp method. All custom logic goes here.
  5. Paymaster — an optional contract that pays gas for the user or accepts ERC-20 tokens as payment instead of ETH.

UserOperation Lifecycle

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 via eth_callStateOverride and rejects operations that may fail on-chain. This protects the Bundler from losing ETH on failed transactions.

Implementing Account Contract

Basic Structure

Use SimpleAccount from eth-infinitism or SafeAccount from Safe (formerly Gnosis Safe) as a base. For production — Safe v1.4.1 with 4337 module, because it's battle-tested with $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 encodes three things: validation result (0 = success, 1 = failure), validAfter and validUntil timestamps. This enables time-bounded operations directly in the signature.

Advanced Logic Patterns

Multisig in a single wallet. Instead of owner, a threshold and list of authorized signers are stored. validateUserOp checks that signatures contains sufficient correct signatures.

Session keys. A restricted key (e.g., generated by the browser without seed phrase exposure) that's allowed to operate only within a specific contract, spending limit, and time window:

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 to a game contract once, then the game makes transactions on their behalf.

Social recovery. Guardians — trusted addresses that can change owner through a timelock (usually 72 hours). Argent's implementation is a good reference: threshold from M-of-N guardians, cancellation within the timelock window if the owner is online.

Paymaster: Gasless and ERC-20 Payment

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 maintain a deposit in 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. The workflow: before execution, we lock maxCost * exchangeRate tokens; after, we deduct the actual cost via postOp.

Ready-made solutions: Pimlico ERC-20 Paymaster (open source), Stackup Paymaster SDK.

Factory and Counterfactual Deployment

One of AA's key properties is that a wallet exists as an address before deployment. CREATE2 with a deterministic salt (usually a hash of the owner address) gives a predictable address. The user gets a wallet address before the first transaction, can receive funds — the wallet deploys 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)
        )));
    }
}

Frontend Integration

Viem + permissionless.js — the most current stack (2024–2025). permissionless is built 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 — an alternative with a higher level of abstraction, built-in session keys and Kernel account (a popular Account Contract with a plugin system).

Alternatives to EIP-4337

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 efficient on gas, but tied to zkSync.

EIP-7702 (Prague/Electra) — the upcoming Ethereum hard fork. Allows EOA to temporarily delegate execution to a smart contract through a special transaction type. Doesn't fully replace 4337, but covers some use cases more simply.

Development Stages and Estimation

Component Complexity Duration
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

Minimum production-ready wallet (single owner + sponsoring paymaster + frontend) — 4–6 weeks of development. Full-featured product with social recovery, session keys and multi-chain — 3–5 months.

Key point when choosing a contractor: the validateUserOp implementation must be audited. An error in this function is direct loss of user funds. Saving on an audit here = conscious risk.