A carregar preços…

Como ler uma conta de carteira de contrato inteligente e as suas sessões

Uma transação numa conta inteligente não é uma transação única. É uma UserOperation que um EntryPoint partilhado encaminha, um bundler submete e um paymaster pode patrocinar. Aprenda a ler cada campo antes de aprovar.

Como ler uma conta de carteira de contrato inteligente e as suas sessões

Porque é que uma conta inteligente é um modelo mental diferente

Se alguma vez copiou um endereço, o verificou num explorador de blocos e tentou decifrar o que uma transação fez, está a partir do princípio de que uma chave controla uma conta. As carteiras de contrato inteligente quebram esse princípio. O endereço que vê no Etherscan ou num explorador semelhante não é uma chave. É um contrato. O contrato possui ativos, aplica regras e decide que instruções aceitar. O utilizador, mais precisamente o conjunto de chaves ou sessões autorizadas a agir naquele momento, envia instruções a esse contrato através de um pipeline separado.

É por isso que uma única ação on-chain parece, muitas vezes, ruidosa num explorador de blocos. O que parece uma transação é, internamente, várias camadas: a intenção assinada pelo utilizador, um relayer que a submete, um contrato EntryPoint que a valida, a lógica opcional de um paymaster que paga o gás e a callData que executa a ação real dentro da conta inteligente. A forma na cadeia é, à primeira vista, pouco habitual, e é precisamente nessa falta de familiaridade que os atacantes apostam.

Transação versus UserOperation: a distinção essencial

Uma transação Ethereum normal tem seis campos legíveis: from, to, value, data, gas limit e signature. Uma UserOperation, definida pelo padrão ERC-4337, acrescenta campos concebidos para execução delegada e patrocinada. Os mais importantes para leitura são: sender, que é o endereço da conta inteligente; nonce, que é por conta e não está ligado à contagem global de transações como o nonce de uma EOA; initCode, que implementa a conta na primeira utilização; callData, que é o que a conta inteligente vai executar se for aprovada; e signature, que pode ser uma assinatura ECDSA, uma assinatura BLS agregada de um bundler, ou um hook de validador definido pela própria conta.

Há também uma camada de pagamento de gás ausente nas transações simples. paymasterAndData aponta para um contrato paymaster que reembolsará o bundler. O paymaster pode inspecionar a sua UserOperation antes de aceitar patrociná-la e, em algumas implementações, pode alterar o passo postOp, que corre depois de a sua chamada terminar. Esse callback de pós-operação é uma superfície de ataque real: um paymaster malicioso pode ler os dados de retorno da sua chamada e utilizá-los de formas que não autorizou. Trate os paymasters como trata qualquer contraparte, porque, em sentido estrito, é isso que são.

Ler uma UserOperation significa, por isso, verificar no mínimo quatro coisas: que o sender aponta para um contrato que realmente possui ou espera, que a callData tem como destino endereços que autorizou (uma sessão ou uma lista branca aprovada por um guardião), que os parâmetros de gás não são absurdamente elevados e que o paymaster é alguém em quem confia para se comportar de forma correta.

O contrato EntryPoint: risco de infraestrutura partilhada

Cada conta inteligente ERC-4337 na mainnet da Ethereum partilha um único endereço EntryPoint à data de redação. O padrão permite explicitamente várias versões do EntryPoint, mas na prática uma implementação canónica concentra a maior parte da atividade. Esta centralização é uma escolha de design deliberada: um EntryPoint partilhado permite que bundlers, paymasters e implementações de contas interoperem sem integrações par a par. Também concentra risco.

Se o EntryPoint for atualizado, tomado por um voto de governação, ou se se descobrir que contém uma vulnerabilidade, todas as contas inteligentes que passam por ele ficam expostas de uma só vez. Isto não é hipotético. No final de 2023, um incidente de auditoria em torno de uma biblioteca relacionada com o EntryPoint foi divulgado e corrigido nas várias integrações. Nada foi perdido em escala, mas a lição manteve-se: infraestrutura partilhada é um ponto único de falha, e a segurança da sua conta só é tão forte quanto o componente mais fraco dessa cadeia.

Ler dados on-chain deve, por isso, incluir uma verificação rápida de que o EntryPoint pelo qual a sua conta passa é a versão que espera. A mainnet e a maioria das L2 publicam endereços canónicos de EntryPoint, e ferramentas como o Etherscan etiquetam as implementações auditadas. Se vir uma UserOperation a ser enviada para um EntryPoint não canónico, trate isso como um forte sinal de alerta: pode ser um relayer de phishing ou um fork concebido para capturar assinaturas.

Chaves de sessão: subaprovações com âmbito e duração limitados

As sessões são a funcionalidade que mais vale a pena entender, porque são também a parte mais vezes abusada. Uma chave de sessão é um signatário secundário que concede a uma aplicação específica, durante uma janela de tempo definida, com um conjunto definido de permissões. Na prática, isto parece-se com uma dApp a pedir permissão para negociar na Uniswap em seu nome durante a próxima hora, até um limite de gasto, contra uma allowlist de contratos de tokens.

On-chain, uma sessão vive como estado dentro da sua conta inteligente. Os campos relevantes, pela ordem em que costumam aparecer em storage, são: um endereço autorizado de chave de sessão; um timestamp ou número de bloco de expiração; um limite de gasto por período (frequentemente diário ou por época); uma allowlist de destinos ou uma allowlist de seletores; e um policy hook que pode aplicar regras adicionais. As implementações variam. Os Safe Modules, Zodiac Heads, sessões Biconomy e validadores de sessão Kernel codificam tudo isto de forma ligeiramente diferente, e o layout exato dos slots importa quando se lê storage diretamente.

O ponto a reter é que as sessões são limitadas, mas não seguras por defeito. Uma sessão com âmbito para o router da Uniswap só é tão boa quanto o router da Uniswap. Uma sessão que concede uma allowlist de seletores pode ainda atingir seletores perigosos se a aplicação souber quais seletores chamar. E sessões que não impõem um limite de gasto com reset por período podem drenar fundos ao longo do tempo assim que o utilizador deixa de prestar atenção.

Dois hábitos práticos ajudam. Primeiro, leia o slot de storage exato ou a vista descodificada que a UI da wallet mostra antes de assinar a concessão da sessão, e trate isso como a fonte de verdade, não como o texto de marketing da dApp. Segundo, prefira sessões com um limite por período e um limite por chamada, para que uma única chamada descontrolada não possa exceder um máximo razoável.

Ler uma única UserOperation em detalhe

Imagine que vê um hash 0xabc... na sua wallet e quer saber o que ela fez de facto. Abra a transação num explorador de blocos e, em seguida, clique na transação do EntryPoint cujo callData começa com handleOps. O separador de transações internas torna-se a verdadeira história.

Passo 1: validateUserOp

O EntryPoint chama primeiro, de volta, a função validateUserOp da sua conta. É aqui que a assinatura é verificada, os nonces são validados contra proteção de replay, e quaisquer políticas de sessão são aplicadas. Uma validação falhada reverte o bundle inteiro, por isso uma execução bem-sucedida é prova de que pelo menos um signatário válido aprovou a operação.

Passo 2: execute

Se a validação passar, o EntryPoint chama a função execute da sua conta com o callData que forneceu. Esta é a ação visível para o utilizador: uma swap, uma transferência, uma interação com um contrato. Ler o callData desta chamada interna dá-lhe o destino real, o valor e o seletor de função que o utilizador pretendia.

Passo 3: postOp

Após a execução, corre o postOp do paymaster, que é usado para coisas como liquidar dívidas de gás, reembolsar gás não utilizado ou pagar ao bundler. Qualquer alteração de estado aqui foi autorizada pela escolha do seu paymaster, não por si diretamente. Qualquer coisa estranha que aconteça aqui, como transferências extra de tokens ou novas aprovações, é um sinal para investigar mais.

Juntar estas três camadas diz-lhe quem autorizou o quê, o que correu e que efeitos secundários ocorreram depois do facto. A maioria dos padrões de phishing torna-se visível a este nível, uma vez que se sabe o que procurar.

Paymasters e bundlers: visibilidade e confiança

Um bundler é a entidade que recebe a sua UserOperation, agrupa-a com outras e submete a transação on-chain real ao EntryPoint. Como o bundler é quem chama EntryPoint.handleOps, é o endereço do bundler que paga o gás ao nível da cadeia. Você, enquanto dono da smart account, nunca precisa de ETH para pagar gás se um paymaster estiver disposto a patrocinar. Isto é cómodo e é também uma relação de confiança.

O compromisso de um paymaster está codificado on-chain de duas formas: o stake que depositou no EntryPoint, que pode ser slashed por mau comportamento segundo as regras do padrão, e a política da sua função validatePaymasterUserOp, que decide se patrocina. O slashing existe, mas é uma rede de segurança grosseira, não uma garantia fina. Um paymaster pode ainda observar, censurar ou fazer front-run das operações que vê.

O front-running é o risco menos discutido. Um paymaster que vê a sua swap antes de ela executar pode copiar a transação para o seu próprio endereço, executá-la no mesmo bloco e capturar a melhoria de preço que esperava. Isto não é teórico; é a mesma dinâmica do miner-extractable value, reembalada para o mundo da account abstraction. Existem mitigações (mempools privadas, esquemas commit-reveal, camadas de intenção assinada), mas exigem uso ativo. Bundlers e paymasters públicos por defeito não oferecem essa proteção.

Ler dados on-chain também ajuda aqui. Se vir consistentemente as suas operações a cair no mesmo número de slot ou logo após um endereço específico, esse bundler ou paymaster está a observá-lo de perto. Mudar para uma mempool privada, ou usar paymasters que se comprometam explicitamente com políticas de não front-running e tenham um histórico verificável, é um verdadeiro upgrade de segurança.

Revogar sessões e permissões on-chain

O erro mais comum que os utilizadores de contas inteligentes cometem é tratar uma sessão como se revogá-la na interface da dApp fosse suficiente. Regra geral, não é. A interface da dApp pode simplesmente esquecer-se da sessão, enquanto a concessão on-chain continua ativa até expirar. Até que o contrato da conta inteligente imponha um tempo limite de inatividade, ou até que a revogues ativamente, a chave de sessão pode continuar a assinar operações que o EntryPoint aceitará.

A revogação efetiva tem três formas práticas. A primeira é chamar a função de revogação da conta inteligente, caso exista, passando o identificador da chave de sessão. A segunda é rodar o assinante principal da conta, o que invalida qualquer sessão cuja autoridade estivesse ligada ao assinante. A terceira é deixar a sessão expirar naturalmente, o que só resulta se tiveres definido, desde o início, uma janela de tempo suficientemente curta.

Ler uma revogação on-chain significa verificar duas coisas: a posição de armazenamento da sessão, que deve refletir o estado revogado ou expirado, e a sequência de nonce da conta, que já não deve estar a avançar para a chave revogada. Se vires uma chave de sessão a continuar a assinar operações após uma suposta revogação, é o momento para parar e investigar. Pode tratar-se de uma interface desatualizada, ou pode ser uma sessão que nunca foi corretamente delimitada e que está agora a ser explorada.

Como acompanhar a atividade de contas inteligentes da forma inteligente

A atividade de contas inteligentes move-se depressa, e o rasto on-chain só faz sentido depois de compreenderes as camadas que lhe estão por baixo. Tentar acompanhar manualmente quem reencaminha as tuas operações, que paymasters as patrocinam e que sessões ainda estão ativas é uma batalha perdida. A Zippfeed apresenta as principais notícias sobre carteiras e protocolos com pontuação de sentimento (bullish, neutral ou bearish) e uma classificação de importância, para que possas identificar os padrões por trás do ruído antes da tua próxima assinatura.

Perguntas frequentes

Uma carteira de contrato inteligente é mais segura do que uma carteira de cripto normal?
Pode ser, mas depende da implementação e dos seus hábitos. Uma conta inteligente permite-lhe definir limites diários, exigir várias assinaturas, rodar chaves e recuperar o acesso sem seed phrases. Essas funcionalidades ajudam. Os novos riscos são a infraestrutura partilhada do EntryPoint, a confiança no paymaster e sessões que não são revogadas corretamente. Uma chave de sessão mal usada pode esvaziar fundos tão depressa como uma seed phrase exposta, pelo que a segurança está na forma como configura e auditoriza, não apenas na tecnologia. Isto é educação, não aconselhamento financeiro.
O que é uma UserOperation em termos simples?
Uma UserOperation é uma pseudotransação concebida para contas inteligentes ao abrigo do ERC-4337. Transporta um sender, nonce, initCode, callData, signature e dados do paymaster, sendo submetida a um contrato EntryPoint partilhado em vez de executada diretamente. Pense nela como uma instrução selada que o EntryPoint valida e executa em nome da sua conta, com o gás pago opcionalmente por um paymaster.
Devo aprovar todos os pedidos de chave de sessão que uma dApp faz?
Trate as chaves de sessão como trata uma API key nas suas ferramentas de trabalho. Conceda o menor âmbito possível, o prazo mais curto razoável e um limite de gasto claro por chamada e por período. Leia o armazenamento que a interface da carteira lhe mostra antes de assinar e revogue as sessões assim que deixar de usar a dApp. Não há razão para aceitar sessões amplas e de longa duração em interações do dia a dia.
Como revogo uma sessão de carteira inteligente on-chain?
Chame a função de revogação da conta, se existir, ou rode o signer principal para que quaisquer sessões dependentes deixem de ser válidas. Deixar uma sessão expirar naturalmente só é eficaz se o timeout for curto. Verifique sempre on-chain depois, consultando o slot de armazenamento da sessão num explorador de blocos. O facto de a interface de uma dApp se esquecer da sessão não significa que a autorização on-chain desapareceu.