Traduzido do artigo original em inglês. Ler em inglês

Digamos que você esteja tentando executar uma transação atômica complexa, tudo em uma, que exige fazer swap de tokens, adicionar liquidez e fazer staking de recompensas. Só que a sua carteira Ethereum comum, uma EOA, não consegue fazer transações em lote. Então você vai precisar assinar três transações separadas, pagar gas três vezes, o que é um grande não, e depois rezar para os bots de MEV não te darem um sandwich entre um passo e outro.
Essa é uma das conversas mais quentes no ecossistema EVM ultimamente, como exatamente a gente dá superpoderes para carteiras comuns sem quebrar tudo ao mesmo tempo?
Duas EIPs surgiram desse desafio, a EIP-3074 e, depois, a EIP-7702. As duas buscam fechar a distância entre uma EOA simples e as smart contract accounts, mas elas têm abordagens diferentes para delegar autoridade. Uma oferece um poder persistente, em que as coisas podem dar muito errado e as suas carteiras podem ser drenadas. A outra pode te dar tanto delegação persistente quanto delegação temporária, que pode sumir no momento em que a sua transação é executada on-chain.
Vamos falar sobre por que essa diferença importa e como a proposta do Vitalik pode ter acabado de salvar o Ethereum de um potencial pesadelo de segurança.
Antes de entrar no assunto da delegação, existe uma peça importante de infra que torna tudo isso possível, que é a EIP-2718. Pense nela como o sistema de contêineres de transporte do Ethereum para transações.
// 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;A EIP-2718 introduz os Typed Transaction Envelopes, em que TransactionType || TransactionPayload é uma transação válida. O TransactionType identifica o formato da transação, enquanto o Payload contém o conteúdo da transação. Agora, esse sistema de wrapper significa que a gente pode introduzir comportamentos de transação totalmente novos sem quebrar a funcionalidade existente.
Ao trazer um tipo de transação envelope, só precisamos garantir a compatibilidade retroativa com as transações existentes e, a partir daí, só precisamos resolver o problema bem mais simples de garantir que não exista conflito de numeração entre TransactionTypes. Então, quando a EIP-7702 precisou introduzir o seu mecanismo de delegação temporária, ela pôde simplesmente definir um novo tipo de transação, por exemplo, 0x04, sem exigir nenhuma mudança na própria EVM.
A EIP-3074 teve uma abordagem ousada para delegação, dando às EOAs a capacidade de delegar autoridade permanentemente para smart contracts usando dois novos opcodes da EVM - AUTH e AUTHCALL.
O mecanismo é direto
// 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);O AUTH recebe quatro parâmetros, o commit e os yParity, r e s da assinatura, e se a assinatura for válida e o endereço do signatário for igual à authority, a variável de contexto authorized recebe essa authority. Depois, o AUTHCALL permite que o contrato invoker autorizado envie transações, com o CALLER definido como o endereço autorizado em vez do invoker.
Agora, imagine um contrato invoker que conseguisse lidar com transações em lote
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 mais empolgante que isso seja, esses dois opcodes levantaram uma série de preocupações de segurança na comunidade EVM. A variável de contexto authorized persiste ao longo de um único frame de execução do contrato, mas não é repassada em nenhuma chamada, incluindo DELEGATECALL. Basicamente, isso significa que a autorização fica ativa durante todo o escopo da transação.
O problema de verdade não é a natureza temporária dentro de uma transação, é o que acontece quando você tem contratos invoker muito usados. Se no fim todo mundo vai usar smart accounts, os opcodes AUTH e AUTHCALL podem virar um enorme fardo técnico e adicionar complexidade à própria EVM.
O que acontece quando um contrato invoker popular como o de cima tem um bug? De repente, todo usuário que já autorizou aquele contrato pode estar em risco. Uma atualização maliciosa, um edge case esquecido, um integer overflow e pronto, começa uma drenagem em massa.
O ecossistema EVM percebeu que isso criava uma dinâmica estranha. O modelo de segurança da EIP-3074 depende muito do contrato invoker. Como vários usuários podem depender da mesma lógica de invoker, uma vulnerabilidade ou exploit em um invoker poderia comprometer os ativos ou as permissões de muitos usuários de uma vez só, e é por isso que a EIP-3074 foi retirada.
Agora, a EIP-7702, proposta pelo Vitalik em maio de 2024, chegou à mainnet via o pectra upgrade em maio de 2025 e introduz uma nova abordagem mais enxuta, permitindo que EOAs funcionem temporariamente como smart contracts durante uma única transação, o que é algo enorme!
Em vez de novos opcodes, a EIP-7702 aproveita o sistema de envelope de transação da EIP-2718's para introduzir um novo tipo de transação
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;
}Para cada objeto de autorização, um indicador de delegação (0xef0100 || address) é gravado no code da conta que autoriza. Todas as operações que executam code precisam carregar e executar o code para o qual a delegação aponta.
É aqui que muita gente se confunde. A EIP-7702 evoluiu desde o seu rascunho original. No começo, a proposta foi pensada para deixar as EOAs temporariamente smart por só uma transação. Mas a versão final te dá controle sobre quanto tempo a delegação dura.
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: Se a execução de uma transação 7702 falhar, os indicadores de delegação já processados não sofrem rollback e persistem, a menos que sejam limpos explicitamente definindo o endereço de delegação como 0x0 em uma transação 7702 subsequente.
Isso significa que você tem três padrões de delegação:
Batching Contract por um dia, e a delegação em si é persistente até que ela seja limpa.O lado bom da EIP-7702 é que ela te dá a opção de escolher. Quer uma delegação temporária para uma transação complexa pontual? Dá para fazer. Quer fazer o upgrade da sua EOA de forma persistente para ela agir como uma smart contract wallet? Dá para fazer também.
Para delegação temporária, aqui vai um exemplo
// one off delegation
const tempDelegationTx = {
type: 0x04,
authorizationList: [{
address: batchExecutorContract,
// ... auth details
}],
to: userAddress,
data: encodeBatchCall([
// execute your complex operation
swapTokens(),
addLiquidity(),
stakeRewards(),
])
};E para Delegação Persistente, aqui vai um exemplo
// 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
};A principal diferença entre a EIP-3074 e a EIP-7702 não é só temporário vs. persistente, é sobre distribuição de risco e controle. A EIP-3074 cria um ponto único de falha compartilhado, em que o problema de um contrato popular pode afetar milhares de usuários. A EIP-7702 isola cada transação, então os bugs só podem afetar aquela transação específica. A EIP-7702 é totalmente compatível com o ERC-4337 existente e não exige novos opcodes. Basicamente, isso significa que as carteiras podem adotar aos poucos sem quebrar nenhum sistema existente.
O insight crítico é que são as wallets, e não os dApps, que gerenciam as delegações. Então o consenso geral é permitir que só as carteiras gerenciem delegações, o que significa:
Se você está construindo uma aplicação que vai suportar a EIP-7702, aqui vão algumas coisas técnicas para observar
// 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
};7702 precisa ser enviada para definir a delegação de volta para 0x0. Isso significa que a sua estimativa de gas precisa levar em contaEIP-7702 habilita a verificação de assinatura EIP-1271 para EOAs. Isso significa que a sua EOA atualizada pode assinar mensagens usando métodos alternativos como passkeys, mantendo ao mesmo tempo a compatibilidade retroativa.// 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;
}Receba os artigos mais recentes no seu e-mail.