By using this site, you agree to the Privacy Policy and Terms of Use.
Accept
World of SoftwareWorld of SoftwareWorld of Software
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Search
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
Reading: Turn a Regular Wallet into a Smart Account with EIP 7702 | HackerNoon
Share
Sign In
Notification Show More
Font ResizerAa
World of SoftwareWorld of Software
Font ResizerAa
  • Software
  • Mobile
  • Computing
  • Gadget
  • Gaming
  • Videos
Search
  • News
  • Software
  • Mobile
  • Computing
  • Gaming
  • Videos
  • More
    • Gadget
    • Web Stories
    • Trending
    • Press Release
Have an existing account? Sign In
Follow US
  • Privacy
  • Terms
  • Advertise
  • Contact
Copyright © All Rights Reserved. World of Software.
World of Software > Computing > Turn a Regular Wallet into a Smart Account with EIP 7702 | HackerNoon
Computing

Turn a Regular Wallet into a Smart Account with EIP 7702 | HackerNoon

News Room
Last updated: 2025/07/27 at 1:01 PM
News Room Published 27 July 2025
Share
SHARE

Web3 has made huge strides in recent years, but UX and mainstream adoption remain major hurdles. According to surveys, key management is the biggest blocker: 46.8% of users cite “security concerns” as their top deterrent, and 80% say they need stronger confidence in account protection before transacting.

Unfortunately, that fear is valid. Billions in crypto have been lost due to misplaced keys, seed phrase mishandling, and private key theft. For teams building web3 products, this creates a painful trade-off: intuitive, seamless UX often means compromising on security or self-custody.

As the saying goes — pick only two: UX, security, or sovereignty.

The community has been working to break this triangle. Among the most promising directions:

  • EIP-7702 — lets EOAs temporarily delegate execution to a smart contract without changing the user’s address or history.
  • EIP-4337 — standardizes smart contract accounts and brings the necessary infrastructure (like bundlers and paymasters) to make them work like web2 logins.
  • MPC accounts — use secure key sharing to enable social login, recovery flows, and higher resilience, without giving up control.

I’m a mentor at international web3 hackathons and regularly publish guides and open-source tools to help teams adopt modern UX architectures faster. These approaches are battle-tested in real products and adapted across blockchain stacks. Currently building at Unique Network — an infrastructure project on Polkadot focused on NFTs and developer tooling.

In this article, I break down how to apply EIP-7702 in practice — using a minimal smart wallet as an example, with step-by-step delegation, initialization, and safe migration without state loss.

What is EIP-7702

Today’s users won’t tolerate poor UX. They expect smooth experiences that can be achieved through abstraction, including:

  • Transaction batching
  • Gas sponsorship
  • Enhanced security (social recovery, spending limits, limited delegation)

Significant progress was made with smart contract wallets. However, the ecosystem carried the legacy burden of EOA accounts, preventing applications from leveraging the benefits of the Account Abstraction initiative, as there’s a fundamental difference between what can be done with smart contract wallets and EOAs.

Then, the Pectra hard fork introduced EIP-7702.

EIP-7702 is a key step in Ethereum’s account abstraction roadmap. It gives existing Externally Owned Accounts (EOAs) access to smart contract capabilities — without requiring users to switch to a new address or give up their account history.

With EIP-7702, an EOA can temporarily delegate execution to smart contract code while preserving its original structure. In simple terms, it lets you upgrade your regular wallet with smart account features like transaction batching, custom validation, and gas sponsorship — all while keeping your existing address.

What this guide covers

In this developer guide, I walk through how to apply EIP-7702 by progressively building a delegation-based smart wallet — starting from a minimal example and gradually adding ownership checks, batch execution, and safer storage patterns.

We’ll cover:

  • How Type 4 transactions and authorization_list work
  • How to handle initialization, storage safety, and ownership
  • How to batch transactions and improve UX
  • Where the edges are — and what EIP-7702 doesn’t solve

All code is written in Solidity and tested with Foundry. You’ll also find runnable examples and full tests on GitHub.

Let’s get started.

How EIP-7702 Works Technically

EIP-7702 introduces a delegation mechanism that allows an EOA to temporarily point to smart contract code while keeping its original structure and history. This is enabled by a new transaction type (0x04) that includes an authorization_list.

chain_id
nonce
max_priority_fee_per_gas
max_fee_per_gas
gas_limit
destination
value
data
access_list
authorization_list
signature_y_parity
signature_r
signature_s

The authorization_list property is the most essential part of the new transaction type.

Authorization list

The authorization_list contains signatures from EOA owners authorizing their accounts to delegate execution to specific smart contracts. It’s a list of tuples that store the contract address to which the signer wants to delegate execution in the context of their EOA.

authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]
//                               ^^^^^^^
//                               This is the contract address that the EOA (the signer of this tuple) will delegate to

The authorization_list is specifically designed to contain signature components (y_parity, r, s), enabling sponsored transactions where the delegator doesn’t need to pay gas fees. Set code transactions can be executed by wallets or applications on behalf of users.

The authorization list is designed as an array to allow applications to migrate multiple accounts in a single transaction. However, this doesn’t mean you can set various delegators for one EOA — only one delegation target is allowed per account

The set code transaction goes through a specific validation process, and if successful, it sets the code for the EOA. The code set on an EOA account is called a delegation designator and has a specific format:

// EIP-7702 delegation designator structure
bytes memory delegationCode = abi.encodePacked(
    hex"ef0100",
    address(targetContract)
);
// Total: 23 bytes

Where:

  • 0xef — EIP-3541 reserved byte indicating special protocol code (not executable bytecode)
  • 01 — EIP-7702 feature identifier within the EIP-3541 reserved space
  • 00 — version of the EIP-7702 delegation format (currently version 0)
  • address (targetContract) — the final 20 bytes contain the full Ethereum address of the smart contract that will execute all function calls made to this EOA

Now your account points to a smart contract and can execute its logic.

Initialization challenges

This brings up an important implementation detail. When we delegate our EOA to, say, an ownable wallet, how does this wallet know which address is authorized to execute a transaction? When a traditional wallet is deployed, its constructor runs and sets up the initial state, configuring owners, signature requirements, and other essential parameters. But when you delegate to an existing contract, your EOA’s storage starts completely blank.

This is because EIP-7702 delegates code execution, not storage. Your EOA maintains its own storage space, separate from the implementation contract. This means the contract you delegate to must provide an initialization mechanism to set up the required state in your EOA’s storage space.

Delegating to a Smart Contract Wallet

Full code examples are available on GitHub: github.com/Maksandre/try-eip-7702

Let’s start by implementing a simplified smart wallet to understand the basic mechanics, then improve it step by step.

For this guide, we’ll use Foundry to build and test the implementation.

Our first version has a single method to execute a transaction:

// SmartWallet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract NotReallySmartWallet {
    function execute(address to, uint256 value, bytes calldata data) external payable {
        (bool success,) = to.call{value: value}(data);
        require(success, "Transaction reverted");
    }

    fallback() external payable {}

    receive() external payable {}
}

Next, we’ll write a test to understand how delegation works using Forge cheatcodes. We’ll use vm.signAndAttachDelegation, which mocks a SET_CODE_TX_TYPE for an EOA and a smart contract.

Setup steps:

  • Create two addresses: Alice (prefunded) and Bob (empty)
  • Deploy our test wallet contract
  • Make sure Alice’s EOA initially has no code
contract NotReallySmartWalletTest is Test {
    address public ALICE_ADDRESS;
    uint256 public ALICE_PRIVATE_KEY;
    address public BOB_ADDRESS;

    // Test parameters
    uint256 constant INITIAL_ALICE_BALANCE = 10 ether;
    uint256 constant TRANSFER_AMOUNT = 1 ether;

    // The contract that Alice will delegate execution to
    NotReallySmartWallet public delegationTarget;

    function setUp() public {
        // Generate addresses and keys dynamically
        (ALICE_ADDRESS, ALICE_PRIVATE_KEY) = makeAddrAndKey("alice");
        BOB_ADDRESS = makeAddr("bob");

        // Fund Alice's account
        vm.deal(ALICE_ADDRESS, INITIAL_ALICE_BALANCE);

        // Deploy the delegation contract
        delegationTarget = new NotReallySmartWallet();
    }
}

In our test, we start by delegating Alice’s account to the predeployed `SmartWallet` contract.

Then we check that Alice’s account now has a delegation designator code. Remember, it has a special format starting with `ef0100`:

assertEq(
    ALICE_ADDRESS.code,
    abi.encodePacked(hex"ef0100", address(delegationTarget))
);

This is important: EOAs can no longer be identified by empty code. Instead, check if the code starts with 0xef0100 — that’s a delegated EOA under EIP-7702.

Now we can call Alice’s EOA like any contract:

NotReallySmartWallet(ALICE_ADDRESS).execute(
    BOB_ADDRESS,
    TRANSFER_AMOUNT,
    ""
);

Verify account balances after the transaction to ensure it succeeded:

// Verify final balances
assertEq(BOB_ADDRESS.balance, TRANSFER_AMOUNT);
assertEq(
    ALICE_ADDRESS.balance,
    INITIAL_ALICE_BALANCE - TRANSFER_AMOUNT
);

Building UX, Security, and Control

At this point, our contract is working — but with no access control or real safety. Let’s fix that.

1. The Lack of Access Control

We don’t have any authorization mechanism — anyone can call execute on the account. To demonstrate this, you can simply change the transaction origin from Alice to Bob, and the test will still pass:

vm.prank(/* ALICE_ADDRESS -> */ BOB_ADDRESS);
NotReallySmartWallet(ALICE_ADDRESS).execute(
    BOB_ADDRESS,
    TRANSFER_AMOUNT,
    ""
);

Let’s make sure that the signer of a transaction is actually the delegating EOA account. If you want to restrict access to just that key, you can check that msg.sender == address(this).

But what if you want more flexible access control — like multisig or passkeys? The SET_CODE_TX_TYPE doesn’t allow you to deploy a new contract with init code. You can only point to an existing one. That means the contract must already be initialized before it’s delegated to.

The most straightforward — but incorrect — approach is to set the storage in the implementation contract.

The problem is that EIP-7702 delegates code execution, not storage. Like delegatecall, execution happens in the context of the EOA, not the contract you’re delegating to.

Take the OpenZeppelin Ownable contract as an example. If you initialize it before delegation, the onlyOwner check will look for the owner in the EOA’s storage — not in the contract’s own storage — and it won’t find it. The value you set in the contract itself will be ignored.

To solve this, you need to move the owner initialization into a separate function and call it after the delegation:

// Remove constructor initialization
// constructor() {
//     owner = msg.sender;
// }

function initialize() external {
    require(owner == address(0), "Already initialized");
    owner = msg.sender;
}

Now, after invoking the initialize function, the owner will be stored in the EOA storage and, as a result, recognized correctly. However, we’ve introduced another vulnerability.

2. Front-running

If delegation and initialization are two separate transactions, someone could initialize the wallet faster and gain access to the account’s assets. There are two ways to prevent this:

  1. Delegate and initialize in the same transaction — by including the initialization call in the same transaction that performs the delegation.
  2. Use the ecrecover precompile to ensure that the initialization is performed by the EOA’s key.

Let’s start with the first option. You can use the to and data fields of the EIP-7702 transaction to include an initialization call. Suppose our implementation contract has the following function:

function initialize(address _owner) external {
    require(owner == address(0), "Already initialized");
    owner = _owner;
}

Then you can call this function in the same transaction, immediately after setting the delegation. Here’s how you can do it using viem:

...

const authorization = await walletClient.signAuthorization({
    contractAddress: implementationAddress,
    account: delegator, // <---- 1. the `delegator` account signs the authorization to `implementationAddress`
    executor: executor,
});

const eip7702tx = await walletClient.sendTransaction({
    account: executor,
    // 2. During the execution of the eip7702 transaction, the `executor` calls `delegator.address`
    // this transaction will be executed at the end of the transaction. At this point the EOA already
    // has code set
    to: delegator.address,
    data: encodeFunctionData({
        abi: artifact.abi,
        functionName: "initialize", // <---- the `executor` calls the `initialize` function
        args: [delegator.address],
    }),
    type: "eip7702",
    authorizationList: [authorization],
});

This approach is safe, but it comes with a limitation: it only works for a single account. You can’t reuse the same trick when migrating multiple EOAs in one EIP-7702 transaction. In that case, you may need to use the ecrecover precompile for initialization.

It’s a big improvement — but there’s still one more issue to tackle.

3. Storage collisions

When an account redelegates from one contract to another that uses the same storage slots differently, it can cause unpredictable behavior or even security vulnerabilities.

To illustrate the problem, let’s create two simple contracts that both use slot 0 — but for different data types:

contract A {
    address public owner; // slot 0
    // ...
}

contract B {
    uint256 public balance; // slot 0
    // ...
}

After redelegation from contract A to contract B, the previous owner value will be interpreted as a huge balance. Alice’s address (20 bytes) will be read as a uint256, resulting in an absurdly large number.

To avoid such collisions, we can use EIP-7201, which introduces namespaced storage layouts. This standard defines a method for assigning pseudo-random storage slots, significantly reducing the risk of overlap.

To apply it, we first wrap our data in a struct and compute a unique slot using EIP-7201’s formula. This ensures our contract uses a dedicated storage location that won’t conflict with others.

contract ContractA_7201 {
    // @custom:storage-location erc7201:example.contractA
    struct ContractAStorage {
        address owner;
        uint256 nonce;
    }

    // keccak256(abi.encode(uint256(keccak256("example.contractA")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ContractAStorageLocation =
        0xd01525294f88a57baa3c94c84cf5cf8d70d334377609d6aabd7ec7c9ce460d00;

The access function returns a reference to our namespaced storage. It uses assembly to point directly to our calculated slot location.

function _getContractAStorage() private pure returns (ContractAStorage storage $) {
    assembly {
        $.slot := ContractAStorageLocation
    }
}

Both functions first obtain a reference to the namespaced storage, then perform read/write operations. The initialize function sets the owner once, and the ownerfunction retrieves it. All storage interactions go through our collision-safe location.

function initialize() external {
    ContractAStorage storage $ = _getContractAStorage();
    require($.owner == address(0), "Already initialized");
    $.owner = msg.sender;
}

function owner() public view returns (address) {
    ContractAStorage storage $ = _getContractAStorage();
    return $.owner;
}

Great! We’ve set up the first version of access control that actually works. Now let’s focus on improving usability.

4. Better UX: batch transactions

Ethereum’s design often requires multiple separate transactions for related actions, creating friction and increasing gas costs. For instance, swapping tokens usually involves approving the spend first and then executing the swap — two signatures, two gas payments. One of the key benefits of smart contract wallets is the ability to batch multiple operations into a single transaction. Let’s add that capability:

struct Call {
    address to;
    uint256 value;
    bytes data;
}

function executeBatch(Call[] calldata calls) external payable onlyOwner {
    for (uint256 i = 0; i < calls.length; i++) {
        (bool success,) = calls[i].to.call{value: calls[i].value}(calls[i].data);
        require(success, "Batch transaction reverted");
    }
}

This allows users to perform multiple operations atomically — for example, approving a token and then swapping it in a single transaction, reducing gas costs and improving the user experience. No more endless transaction signing!

You can also imagine additional UX improvements, like session keys with limited access to wallet funds or spending limits.

Now, let’s turn our focus to security.

5. Security Improvements

A vast amount of funds is permanently lost due to lost private keys. The inability to restore them is a major problem. Fortunately, with programmability, we can address this issue. Using smart contracts, you can set up social recovery, giving trusted friends the ability to transfer ownership of your account to a new EOA. To prevent abuse, you can add timelocks that give you time to cancel a malicious recovery attempt.

Other possible security improvements include setting transfer limits. So even if you accidentally sign a malicious transaction, the attacker can only drain a small amount.

You have a lot of power with programmability. But unfortunately, EIP-7702 doesn’t solve one crucial problem.

Private Key Remains a Security Risk

If someone gets access to your EOA’s private key, you’re in trouble. No recovery method will help you get your funds back or limit your losses. That private key is like having the master key to everything. It doesn’t matter what smart account features you’ve set up; the private key can override all of them.

The only way to truly protect yourself from this scenario is to use pure smart contract accounts that don’t rely on a single private key.

Delegation Clearing

Users can remove their attached code by authorizing a delegation to the zero address. After this, the code behind the EOA becomes empty — it’s no longer 0xef01000000…. However, note that this does not reset or wipe the storage associated with the EOA.

Here’s how to do it using viem:

const cleanupAuthorization = await walletClient.signAuthorization({
    contractAddress: zeroAddress,
    account: delegator,
    executor: "self",
});

const cleanupTx = await walletClient.sendTransaction({
    account: delegator,
    type: "eip7702",
    authorizationList: [cleanupAuthorization],
});

Drawbacks

While EIP-7702 brings meaningful improvements, several key limitations remain.

EIP-7702 enables smart contract functionality, but truly gasless transactions still depend on EIP-4337 infrastructure. The authorization list supports sponsored transactions, but you’ll still need bundlers and paymasters for a full gasless experience.

Complexity and attack vectors

Delegation introduces added complexity that can lead to unexpected behavior or new attack surfaces. Developers must carefully manage storage layout, initialization logic, and possible contract conflicts when working with EIP-7702.

Developer experience

The separation of code execution (delegated) and storage (local to the EOA) introduces a new mental model. Misunderstanding this model can easily lead to bugs or unintended side effects.

Despite these challenges, EIP-7702 marks a major step toward making Ethereum more intuitive and flexible, while preserving the security and decentralization that define the platform. It helps bridge the gap between EOAs and full account abstraction, providing a gradual path that retains user control and account history.

This is a foundational step toward native account abstraction. Future EIPs will likely build on it to offer more comprehensive solutions, potentially unifying EOAs and smart contracts into a single, seamless account model.

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Twitter Email Print
Share
What do you think?
Love0
Sad0
Happy0
Sleepy0
Angry0
Dead0
Wink0
Previous Article Google AI Overviews has devastating impact on website traffic, study says
Next Article NHTSA Closes 14-Month Investigation into Waymo Collisions
Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Stay Connected

248.1k Like
69.1k Follow
134k Pin
54.3k Follow

Latest News

DOGE has an AI tool to help decide which federal regulations to ‘delete’
News
The Best School Security Strategies and Technologies in 2025 and Beyond, According to Jake Williams
Gadget
Apple’s F1 movie crosses $500 million at global box office – 9to5Mac
News
You Can Try Google’s New ‘Vibe Coding’ App For Free Right Now – BGR
News

You Might also Like

Computing

20 Free Notion To-Do List Templates to Stay Productive

40 Min Read
Computing

Godot 4.4 Beta 1: Everything New | HackerNoon

41 Min Read
Computing

Roleplaying With ChatGPT: A Deeper Look | HackerNoon

10 Min Read
Computing

Bitcoin Mining Could Make Our Electricity Grids Smarter | HackerNoon

11 Min Read
//

World of Software is your one-stop website for the latest tech news and updates, follow us now to get the news that matters to you.

Quick Link

  • Privacy Policy
  • Terms of use
  • Advertise
  • Contact

Topics

  • Computing
  • Software
  • Press Release
  • Trending

Sign Up for Our Newsletter

Subscribe to our newsletter to get our newest articles instantly!

World of SoftwareWorld of Software
Follow US
Copyright © All Rights Reserved. World of Software.
Welcome Back!

Sign in to your account

Lost your password?