EIP-7702: Smart Account Power for EOAs (If Your Wallet Lets You)

TLDR: EIP-7702 allows externally owned accounts to temporarily behave like smart contract wallets during a transaction, enabling them to use smart contract features like batching and gas sponsorship without permanently changing the account type. BUT
Wallet/RPCs control the auth flow not apps and not all wallets have EIP-7702 support.

EIP-7702 Overview

EIP-7702 introduces a new transaction type, 0x04, in Ethereum’s Pectra upgrade that enables externally owned accounts (EOAs) to execute temporary smart contract functionality.

The delegation process looks like:

  1. A Smart Contract should exist that the EOA can designate to
    • This contract can have any functionality
    • For an example see the SimpleEIP7702Contract that allows batch execution
  2. The EOA signs an Authorization to designate the contract to the account
  3. An EIP-7702 transaction is sent along with the Authorization
    • When processed, the network records that this EOA should delegate to the specific smart contract
    • The EOA appears to have the SC code attached to it directly
    • The code will be valid until replaced by another authorization
    • msg.sender in these transactions remains the EOA’s address

Wallets Are In Control

Applications cannot directly use EIP-7702 to delegate user accounts to their preferred smart accounts.

Internal tests written to use an inject browser wallet as the Viem account throws because it is not possible to sign an authorization over JSON-RPC right now: https://github.com/wevm/viem/discussions/3285

Although its technically possible for any entity to create an EIP-7702 authorization, wallet providers have made it clear that they will reject transactions containing authorization fields from applications. Instead, wallets will manage EIP-7702 authorizations themselves, upgrading their users to the wallet’s chosen smart account implementation. There will be fragmentation as different wallets adopt different implementations.

Worth noting EIP-5792 can be used to query what capabilities a wallet supports using rpc calls (see 7702beat and 7702 checker as examples of existing tools).

Useful Links

7702beat.swiss-knife.xyz – Shows Chain & Wallet Support

Awesome EIP-7702

Data

https://www.bundlebear.com/eip7702-overview/all

https://dune.com/wintermute_research/eip7702

Dev Guides

Viem EIP-7702

  • Gives a guide showing how to use signAuthorization. Only works with a PK Account.

QuickNode EIP-7702 Guide:

  • Very detailed walk through (using Foundry and Viem)

MetaMask EIP7702 Quickstart:

  • Use MetaMask Delegation Toolkit. Still appears to need a Viem PK account.

Biconomy Guide For Apps:

  • Good explanation of the problem. Some proposed alternatives.

Photo by Jana Knorr on Unsplash

Inside a Balancer V3 Swap: A Step-by-Step Walkthrough with the MEV Hook

In this post, we’ll walk through the lifecycle of a swap in Balancer V3, using the recently released MEV hook as our lens. We’ll trace how a swap flows through the system — from the initial call to the final token transfers — highlighting the key steps and components along the way.

One of the standout strengths of Balancer V3 is how much heavy lifting it does for you. As a developer, you can focus on building your core logic — whether that’s a novel pool type or a specialised hook — without needing to reinvent the plumbing. The core swap flow is already implemented, audited, and optimised. This lets you move fast, stay safe, and build confidently on top of the protocol.

Anatomy of a Balancer V3 Swap

Before diving into the code, it’s useful to understand the main contracts involved in a swap. Balancer V3 separates responsibilities across a few key components, allowing developers to focus only on the parts they need to customise.

The diagram below outlines the four primary contracts involved and their roles in the flow. While the Router and Vault handle most of the core logic — routing, fund management, and safety checks — the Pool and Hook are the only contracts where your custom logic lives.

Primary Contracts Used During Swaps

Digging Into the Swap Flow: Code Highlights

Now that we have an understanding of the contracts involved in a Balancer V3 swap, let’s take a closer look at the code. In this section, we’ll walk through key parts of the swap flow, highlighting how each piece fits into the larger system and where the MEV hook plays a role.

The user initiates the swap by interacting with the protocol through a Router, rather than directly engaging with the Vault. This approach abstracts away some of the complexity, simplifying the user experience. In this case, the Balancer Router contract is used, and the swapSingleTokenExactIn function is called to execute the swap:

User interact with Router

The parameters passed to the swapSingleTokenExactIn function specify key swap details, including the pool, tokens, and the amount to be swapped. At this stage, the amounts are “raw”—they are scaled to the relevant token decimals but do not yet account for any rate adjustments or slippage. For example, if the user wanted to swap 1 USDC, the exactAmountIn would be 1000000 (reflecting the 6 decimal places of USDC).

It’s also important to note that at this point, the Vault is unlocked, meaning that the transient accounting state has been initiated and any subsequent actions must result in balanced accounting.

Inside the Router contract, the actual interaction with the Vault happens within the _swapHook function. This is where the Router forwards the swap request by calling _vault.swap(...), passing along the relevant parameters:

Router calls Vault swap

Inside the Vault’s swap function, the first major step is preparing the data needed to execute the swap. This involves loading the current state of the pool and converting raw token amounts into “live” values that reflect current scaling factors, rates, and fees.

Preparing for the Swap in the Vault

What’s happening here:

  • poolData: Captures the current state of the pool, including both raw and scaled balances, token rates, and scaling factors.
  • swapState: Derives token indices, applies scaling to the input amount, and loads the static swap fee for the pool.
  • poolSwapParams: Bundles all of the data required to compute the swap, with every value already adjusted to use “live” scaling.

At this point, the Vault has everything it needs to perform accurate swap calculations based on real-time conditions.

At this stage in the Vault’s swap function, any beforeSwap hook would typically be executed if configured. However, in the case of the MEV hook, this step is not used.

What is relevant here is the dynamicSwapFee hook, which is called to allow the hook contract to specify a dynamic fee based on the current context:

Calling the Hook to get SwapFee

This check ensures that the pool has a dynamic fee hook configured. If it does, the Vault delegates to the hook via HooksConfigLib, allowing custom logic to influence the swap fee before the main calculation proceeds.

Execution now enters the MevCaptureHook contract. The HooksConfigLib calls the onComputeDynamicSwapFeePercentage function, allowing the hook to define a custom fee based on the current context:

Inside the Hook — Custom Fee Logic

This is where developers can begin implementing their own logic. In the MEV hook, the function does the following:

  • Checks if MEV tax is enabled — If not, it simply returns a static fee.
  • Checks if the sender is exempt from fee — If so, it returns a static fee.
    Note the use of isTrustedRouter here, which ensures the Router is correctly forwarding the original sender. This helps enforce that only legitimate callers are considered for fee exemption.
  • Calculates the MEV-specific swap fee — This is based on configurable parameters and market context.

The actual fee calculation is handled in the _calculateSwapFeePercentage function. While we won’t dive into every detail of the MEV logic, it’s a great example of how hooks can introduce dynamic, context-aware behaviour into the swap flow. In this case, the hook adjusts the swap fee based on gas conditions to protect users from MEV exploitation.

Dynamic Fee Calculation Logic in the MEV Hook

This function dynamically adjusts the swap fee based on the priority gas price — the difference between the transaction’s gas price and the block’s base fee.

Base Conditions:

  • If the priority gas price is below the configured threshold (suggesting a retail user), the function returns the standard static fee.
  • If the maximum MEV fee cap is less than or equal to the static fee, it also returns the static fee.

Dynamic Fee Calculation:

  • For transactions with a priority gas price above the threshold, the function computes a fee increment.
  • The increment is calculated as:
    (priorityGasPrice - threshold) * multiplier / 1e18
  • This increment is then added to the static swap fee percentage.

Safety Mechanisms:

  • The function uses OpenZeppelin’s Math.tryMul to guard against overflow.
  • If an overflow is detected, it gracefully falls back to the maximum allowed MEV fee.
  • The final result is always capped at the maximum MEV swap fee percentage.

Once calculated, this dynamic fee is returned to the Vault, where it’s used in the next phase of the swap — starting with the fee deduction:

Continuing the swap: Applying the fee

In this example, we’re following an EXACT_IN type swap. That means the swap fee is deducted from the input amount provided by the user. For an EXACT_OUT swap, the fee would instead be taken from the amount calculated.

After applying the swap fee, the Vault calls the pool’s onSwap hook. This is the entry point for pool-specific logic — each pool type (WeightedStable, etc.) defines its own math to calculate how much of the output token to return for a given input.

Pool Swap Logic

The onSwap function returns the calculated amount, still in live-scaled form, meaning it reflects the token’s rate and scaling factors.

Before proceeding with transfers, the Vault converts this amount back to its raw form, so it’s compatible with token balances and transfers:

Scaling from live to raw and checking limits

Once the amount is scaled back to raw units, the Vault checks the limit: it ensures that the amountOut is greater than the minimum amount the user was willing to accept (based on the slippage tolerance). If the limit check passes, the Vault moves to the final phase of the swap.

  • takeDebt/supplyCredit: These functions handle transient accounting for the transaction.
  • The Vault also deducts protocol and creator fees from the swap.
  • It then updates the _poolTokenBalances to reflect the tokenIn and tokenOut amounts.
  • Swap event is emitted, which is useful for off-chain tracking and monitoring.

For this particular example, the Vault is essentially done here. However, for other hooks that include afterSwap functionality, this logic would be handled before the Vault returns the final amountIn and amountOut values (which are raw scaled).

Once the Vault has completed its duties, we return to the Router, where the final token transfers and accounting are handled in the swapSingleTokenHook function:

Final Token Transfers and Accounting

_takeTokenIn: This function handles the collection of input tokens from the user and the final settlement for transient accounting. It’s designed to work with both standard ERC20 tokens and the special case of ETH/WETH, making it versatile for different token types.

_sendTokenOut: This function manages the delivery of output tokens to the user and the final settlement for transient accounting. Like its counterpart, it supports both ERC20 tokens and ETH/WETH, ensuring consistent token handling regardless of the token type.

At this point, all credit and debt accumulated during the swap execution should be finalized. If transient accounting isn’t correctly settled, the swap would fail at this stage. Assuming everything checks out, the user has successfully swapped their tokens.

Conclusion

Through this walkthrough, we’ve followed the path of a swap in Balancer V3 and seen how the protocol handles the majority of the core logic under the hood. This leaves developers free to focus on the parts that matter most to them, like designing new hooks or customizing pool behavior.

Balancer’s modular architecture means you can build on a solid, audited foundation without needing to understand every detail from scratch — but having a clear picture of what’s happening under the surface can make things easier when you’re building something non-standard, troubleshooting edge cases, or just trying to understand how your code fits into the bigger system.

If you’re ready to start building check out the Balancer V3 developer docs for more detailed references and guides.

Photo by Marten Newhall on Unsplash