---
# System prepended metadata

title: EIP-7702 support in bundlers

---

# EIP-7702 support in ERC-4337 bundlers

## Abstract

EIP-7702 allows EOAs to behave as smart contracts by issuing an authorization list which is then included in the new transaction type. This proposal defines one way to integrate features introduced by EIP-7702 with existing ERC-4337 infrastructure as seemlessly as possible.

## Motivation
 
In EIP-7702, an EOA must either propagate an authorization itself or rely on a delegate to sponsor the authorization transaction. Only after sending this initial authorization transaction can the EOA send user operations.

Rather than building new infrastructure for delegations or requiring users to send authorizations themselves, we can modify the existing workflow to include the authorization along with the user operation sent to the bundler.

This approach enables EOAs to leverage the existing ERC-4337 infrastructure (bundler + paymaster), allowing them to send both authorizations and user operations simultaneously.

## Specification

### eth_estimateUserOperationGas

New `authorizationContract` (address) field is added in the first param of eth_estimateUserOperation

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_estimateUserOperationGas",
  "params": [
    {
      sender, // address
      nonce, // uint256
      factory, // address
      factoryData, // bytes
      callData, // bytes
      paymaster, // address
      paymasterData, // bytes
      signature, // bytes
      authorizationContract // address
    },
    entryPoint // address
  ]
}
```

The bundler:
- MUST fetch the code of the contract at `authorizationContract` address and override the code of `sender` with that code during simulation
- CAN add additional `PER_EMPTY_ACCOUNT_COST` + `PER_AUTH_BASE_COST` = 37500 gas in preVerificationGas

### eth_sendUserOperation

1 new tuple (json array) is added:
- authorization: [chainId, contract, nonce, signature] 

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_sendUserOperation",
  "params": [
    {
      sender, // address
      nonce, // uint256
      factory, // address
      factoryData, // bytes
      callData, // bytes
      callGasLimit, // uint256
      verificationGasLimit, // uint256
      preVerificationGas, // uint256
      maxFeePerGas, // uint256
      maxPriorityFeePerGas, // uint256
      paymaster, // address
      paymasterVerificationGasLimit, // uint256
      paymasterPostOpGasLimit, // uint256
      paymasterData, // bytes
      signature, // bytes
      authorization, // [uint64, address, uint256, bytes]
    },
    entryPoint // address
  ]
}
```

The bundler:
- MUST validate that nonce of userop.sender matches with authorization.nonce
- MUST validate the authorization signature before putting userop into mempool
- MUST add authorization from each userops in `authorization_list` of the bundle
- MUST check that if authorization is present, factoryData should be null

### Implications for smart contract wallets

*UPD 19.11.2024 - Reference implementation of ERC-7579 now has EIP-7702 validator.*

Initial authorization userop may or may not work depending wallet implementation. For example, if we take ERC-7579's reference implementation of a wallet, the authorization userop will revert because it won't pass `validateUserOp`'s validation.

https://github.com/erc7579/erc7579-implementation/blob/main/src/MSABasic.sol#L153-L179

```solidity=
function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
) {
    // ...
    if (!_isValidatorInstalled(validator)) return VALIDATION_FAILED;
    validSignature = IValidator(validator).validateUserOp(userOp, userOpHash);
}
```

`validateUserOp` checks if the validator specified in the nonce is installed. It's not installed at the time of authorization - the wallet's storage is empty.

One way to address this is to create a EIP-7702 validator and embed it in the wallet:

```solidity
contract EIP7702Validator is IValidator {
    // ...
    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash
    ) external override returns (uint256) {
        bytes32 hash = userOpHash.toEthSignedMessageHash();
        address signer = hash.recover(userOp.signature);
        // checks if the signature was issued by EOA itself
        // msg.sender = userop.sender
        if (signer != msg.sender) {
            return VALIDATION_FAILED;
        }
        return VALIDATION_SUCCESS;
    }
    // ...
}
```

And embed this validator in the wallet:
```solidity
contract MSABasic is IMSA, ExecutionHelper, ModuleManager {
    // eip-7702 validator hardcoded in the wallet
    address public immutable eip7702Validator = address(new EIP7702Validator());
    
    // ...
    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
    )
        external
        payable
        virtual
        override
        onlyEntryPoint
        payPrefund(missingAccountFunds)
        returns (uint256 validSignature)
    {
        // ...
        uint256 nonce = userOp.nonce;
        assembly {
            validator := shr(96, nonce)
        }
        
        // if validator is not set, use EIP-7702 validator
        if (validator == address(0)) {
            return IValidator(eip7702Validator).validateUserOp(userOp, userOpHash);
        }
    }
}
```
