Todos los artículos

Entendiendo e implementando EIP-7702

·
27 sept 2025
·
6 min de lectura
·
••• visitas
·
••• me gusta

Traducido del artículo original en inglés. Leer en inglés

Entendiendo e implementando EIP-7702

Digamos que intentas ejecutar una transacción atómica compleja todo en uno que requiere intercambiar tokens, añadir liquidez y hacer staking de recompensas. Pero resulta que tu wallet EOA normal de Ethereum no puede hacer transacciones por lotes. Así que tendrás que firmar tres transacciones separadas, pagar gas tres veces, lo cual es un no rotundo, y luego rezar para que los bots de MEV no te hagan un sandwich entre un paso y otro.

Esta es una de las conversaciones más acaloradas del ecosistema EVM últimamente, ¿cómo les damos exactamente superpoderes a las wallets normales sin romperlo todo al mismo tiempo?

De este reto salieron dos EIPs, EIP-3074 y después EIP-7702. Ambas buscan cerrar la brecha entre una EOA simple y las smart contract accounts, pero toman enfoques distintos para delegar autoridad. Una ofrece un poder persistente con el que las cosas pueden salir muy mal y tus wallets pueden acabar vaciadas. La otra puede darte tanto delegación persistente como delegación temporal, que podría desaparecer en el momento en que tu transacción se ejecuta on-chain.

Hablemos de por qué importa esta diferencia y de cómo la propuesta de Vitalik podría haber salvado a Ethereum de una posible pesadilla de seguridad.

EIP-2718 - Sobre de transacción tipado

Antes de entrar en el tema de la delegación, hay una pieza importante de infraestructura que hace posible todo esto, y es EIP-2718. Piensa en ella como el sistema de contenedores de envío de Ethereum para las transacciones.

// before eip-2718 all transactions looked the same
const legacyTx = rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]);
 
// after eip-2718 transactions can now be typed
const typedTx = TransactionType || TransactionPayload;

EIP-2718 introduce los Typed Transaction Envelopes, donde TransactionType || TransactionPayload es una transacción válida. TransactionType identifica el formato de la transacción, mientras que Payload contiene el contenido de la transacción. Ahora, este sistema de envoltorio significa que podemos introducir comportamientos de transacción totalmente nuevos sin romper la funcionalidad existente.

Al introducir un tipo de transacción sobre, solo tenemos que garantizar la compatibilidad con las transacciones existentes, y a partir de ahí solo hay que resolver el problema mucho más simple de asegurar que no haya conflictos de numeración entre TransactionTypes. Así que cuando EIP-7702 necesitó introducir su mecanismo de delegación temporal, pudo simplemente definir un nuevo tipo de transacción, por ejemplo 0x04, sin requerir ningún cambio en la propia EVM.

EIP-3074 - Enfoque de delegación persistente

EIP-3074 adoptó un enfoque audaz para la delegación, dando a las EOAs la capacidad de delegar autoridad de forma permanente a contratos inteligentes mediante dos nuevos opcodes de la EVM - AUTH y AUTHCALL.

Cómo funcionan AUTH y AUTHCALL

El mecanismo es sencillo

// user signs an auth message
bytes32 commit = keccak256(abi.encode(invokerAddress, batchActions));
bytes memory signature = signMessage(userPrivateKey, commit);
 
// invoker contract calls AUTH opcode
AUTH(commit, signature.v, signature.r, signature.s);
// This sets the 'authorized' context variable to user's address
 
// now invoker can make calls as the user
AUTHCALL(gasLimit, targetContract, 0, 0, calldata, calldataLength, 0, 0);

AUTH recibe cuatro parámetros, commit y los valores yParity, r y s de la firma, y si la firma es válida y la dirección del firmante es igual a la autoridad, la variable de contexto authorized se establece en esa autoridad. Luego AUTHCALL permite que el contrato invoker autorizado envíe transacciones, con CALLER establecido en la dirección autorizada en lugar de la del invoker.

Ahora, imagina un contrato invoker que pudiera manejar transacciones por lotes

contract BatchInvoker {
    struct BatchCall {
        address target;
        bytes data;
        uint256 value;
    }
    
    function executeBatch(
        BatchCall[] calldata calls,
        bytes32 commit,
        uint8 v, bytes32 r, bytes32 s
    ) external {
        // authorize the user
        AUTH(commit, v, r, s);
        
        // Execute all calls as the user
        for (uint i = 0; i < calls.length; i++) {
            AUTHCALL(
                gasleft(),
                calls[i].target,
                calls[i].value,
                0, // valueExt (must be 0 for now)
                // ... calldata parameters
            );
        }
    }
}

Por muy emocionante que sea esto, estos dos opcodes generaron una serie de preocupaciones de seguridad en la comunidad EVM. La variable de contexto authorized persiste durante todo un mismo frame de ejecución del contrato, pero no se pasa a través de ninguna llamada, incluida DELEGATECALL. Básicamente, esto significa que la autorización está activa durante todo el alcance de la transacción.

El problema real no es su naturaleza temporal dentro de una transacción, sino lo que pasa cuando tienes contratos invoker muy usados. Si con el tiempo todo el mundo va a usar smart accounts, los opcodes AUTH y AUTHCALL pueden ser una enorme carga técnica y añadir complejidad a la propia EVM.

¿Qué pasa cuando un contrato invoker popular como el de arriba tiene un bug? De repente, cada usuario que alguna vez haya autorizado ese contrato podría estar en riesgo. Una actualización maliciosa, un caso límite que se pasó por alto, un desbordamiento de enteros y listo, empieza un vaciado masivo.

El ecosistema EVM se dio cuenta de que esto creaba una dinámica extraña. El modelo de seguridad de EIP-3074 depende mucho del contrato invoker. Como muchos usuarios podrían depender de la misma lógica de invoker, una vulnerabilidad o exploit en un solo invoker podría comprometer los activos o permisos de muchos usuarios a la vez, y por eso se retiró EIP-3074.

EIP-7702 - La solución de delegación flexible

Ahora, EIP-7702, propuesta por Vitalik en mayo de 2024, llegó a mainnet con la pectra upgrade en mayo de 2025 e introduce un enfoque nuevo y simplificado, que permite a las EOAs funcionar temporalmente como contratos inteligentes durante una sola transacción, ¡y eso es algo muy importante!

En lugar de nuevos opcodes, EIP-7702 aprovecha el sistema de sobres de transacción de EIP-2718's para introducir un nuevo tipo de transacción

interface EIP7702Transaction {
    chainId: number;
    nonce: number;
    maxPriorityFeePerGas: bigint;
    maxFeePerGas: bigint;
    gasLimit: bigint;
    to: string;
    value: bigint;
    data: Uint8Array;
    accessList: AccessList;
    authorizationList: Authorization[];
}
 
interface Authorization {
    chainId: number;
    address: string;  // the address of the contract code to delegate to
    nonce: number;
    yParity: 0 | 1;
    r: Uint8Array;
    s: Uint8Array;
}

Por cada objeto de autorización, se escribe un indicador de delegación (0xef0100 || address) en el código de la cuenta que autoriza. Todas las operaciones que ejecutan código deben cargar y ejecutar el código al que apunta la delegación.

Cómo funciona realmente la persistencia de la delegación

Aquí es donde mucha gente se confunde. EIP-7702 ha evolucionado desde su borrador original. Al principio, la propuesta se diseñó para que las EOAs fueran inteligentes de forma temporal durante una sola transacción. Pero la versión final te da el control sobre cuánto dura la delegación.

function processEIP7702Transaction(tx) {
    // verify all auth signatures
    for (const auth of tx.authorizationList) {
        const signer = recoverSigner(auth);
        if (signer !== getAddressFromAuth(auth)) {
            throw new Error("Invalid authorization");
        }
    }
    
    // set delegation pointers (these persist after transaction)
    for (const auth of tx.authorizationList) {
        const eoaAddress = getAddressFromAuth(auth);
        
        // set delegation pointer - 0xef0100 || contract_address
        const delegationPointer = concat(['0xef0100', auth.address]);
        setCode(eoaAddress, delegationPointer);
        
        // nowm this delegation PERSISTS unless explicitly cleared
    }
    
    // execute the transaction normally
    executeTransaction(tx);
    
    // no auto cleanup, delegation stays active
}

NOTA: Si la ejecución de una transacción 7702 falla, los indicadores de delegación ya procesados no se revierten y persisten a menos que se borren explícitamente estableciendo la dirección de delegación en 0x0 en una transacción 7702 posterior.

Esto significa que tienes tres patrones de delegación:

Opciones temporales Y persistentes

Lo bueno de EIP-7702 es que te da la opción de elegir. ¿Quieres una delegación temporal para una transacción compleja puntual? Puedes hacerlo. ¿Quieres actualizar tu EOA de forma persistente para que actúe como una wallet de contrato inteligente? También puedes hacerlo.

Para la delegación temporal, aquí tienes un ejemplo

// one off delegation
const tempDelegationTx = {
    type: 0x04,
    authorizationList: [{
        address: batchExecutorContract,
        // ... auth details
    }],
    to: userAddress,
    data: encodeBatchCall([
        // execute your complex operation
        swapTokens(),
        addLiquidity(), 
        stakeRewards(),
    ])
};

Y para la delegación persistente, aquí tienes un ejemplo

// upgrade EOA to smart wallet
const persistentDelegationTx = {
    type: 0x04,
    authorizationList: [{
        address: smartWalletImplementation,
        // ... auth details  
    }],
    to: userAddress,
    data: encodeCall("initializeSmartFeatures", [])
    // this delegation persists for future txns
};
 
// later txns can now use smart wallet features
const futureTransaction = {
    to: userAddress, // your EOA will now execute smart wallet code
    data: encodeCall("batchExecute", [multipleOperations])
    // no new auth needed
};

Por qué 7702 es más segura

La diferencia clave entre EIP-3074 y EIP-7702 no es solo temporal vs. persistente, sino la distribución del riesgo y el control. EIP-3074 crea un punto de fallo compartido en el que el problema de un contrato popular podría afectar a miles de usuarios. EIP-7702 aísla cada transacción, así que los bugs solo podrían afectar a esa transacción concreta. EIP-7702 es totalmente compatible con el ERC-4337 existente y no requiere nuevos opcodes. Básicamente, esto significa que las wallets pueden adoptarla de forma gradual sin romper ningún sistema existente.

La idea clave es que son las wallets, y no las dApps, las que gestionan las delegaciones. Así que el acuerdo general es permitir que solo las wallets gestionen las delegaciones, lo que significa que:

Lo que los devs necesitan saber

Si estás construyendo una aplicación que va a soportar EIP-7702, hay algunas cosas técnicas a tener en cuenta

  1. Cambios en la construcción de transacciones Tu lógica de construcción de transacciones tiene que manejar listas de autorización
// old: simple EOA transaction
const tx = {
    to: contractAddress,
    data: encodedCall,
    // ... standard fields
};
 
// new way with eip-7702 delegation
const tx = {
    type: 0x04,
    to: userAddress, // calling user's own address
    data: encodeBatchCall([
        contract.interface.encodeFunctionData("swap", [tokenA, tokenB, amount]),
        contract.interface.encodeFunctionData("addLiquidity", [lpTokens]),
        contract.interface.encodeFunctionData("stake", [lpAmount])
    ]),
    authorizationList: [{
        address: batchExecutorImplementation,
        // ... authorization signature
    }],
    // ... standard fields
};
  1. Complejidad en la estimación de gas Si la EOA decide revocar el control del contrato inteligente, hay que enviar otra transacción 7702 para volver a establecer la delegación en 0x0. Esto significa que tu estimación de gas tiene que tener en cuenta
  1. Soporte de firmas de contratos inteligentes EIP-7702 habilita la verificación de firmas EIP-1271 para las EOAs. Esto significa que tu EOA actualizada puede firmar mensajes con métodos alternativos como las passkeys, manteniendo la compatibilidad con versiones anteriores.
// your EOA can now implement smart signature verification
function isValidSignature(bytes32 hash, bytes signature) external view returns (bytes4) {
    // can verify passkey signatures, multisig, etc.
    if (verifyPasskeySignature(hash, signature)) {
        return 0x1626ba7e; // EIP-1271 magic value
    }
    return 0xffffffff;
}

Enlaces esenciales

•••
Comparte este artículo:

Mantente al día

Recibe los últimos artículos en tu bandeja de entrada.