WEB3DEV

Cover image for Estimativa de Gás para Camada 2 e Agregadores de Assinatura - ERC-4337
Paulo Gio
Paulo Gio

Posted on

Estimativa de Gás para Camada 2 e Agregadores de Assinatura - ERC-4337

No primeiro artigo da série, “Como Funciona a Estimativa de Gás do ERC-4337”, discutimos como o gás funciona no ERC-4337 e sobre nosso método para estimativa de gás. Na parte 2, “ Assinaturas Fictícias e Transferências de Token de Gás do ERC-4337 ”, descobrimos que a estimativa de gás nem sempre é direta e precisamos considerar casos excepcionais. Esta postagem abordará mais alguns dos casos excepcionais que encontramos.

O Problema da Camada 2

Parte da definição de um rollup de Camada 2 (Layer 2, ou L2) da Ethereum é: "ele permite que a Camada 1 (Layer 1, ou L1) cuide da segurança, disponibilidade de dados e descentralização, enquanto as L2 cuidam da escalabilidade."

Para alcançar isso, as L2 irão "empacotar" (roll up) muitas transações em um único lote e depois postá-las na blockchain L1. Este custo de transação não é gratuito, pois as L2 precisam pagar pelos custos dos dados de chamada (calldata) incorridos ao postar um grande lote de dados na cadeia L1.

As L2s precisam de uma forma de cobrar de seus usuários pelos custos dos dados de chamada incorridos na L1. Os frameworks dos rollups alcançam isso de diferentes maneiras.

Este artigo se concentra nos dois maiores rollups EVM: Arbitrum e Optimism.

Como o Arbitrum calcula o custo para cobrir as taxas de gás da L1?

No Arbitrum, as cobranças de gás na L2 para cobrir o custo do gás da L1 são calculadas usando a seguinte fórmula, onde o tamanho dos dados é o seu tamanho em bytes após a compressão Brotli:

Custo L1 (L1C) = preço L1 por byte de dados (L1P) * Tamanho dos dados a serem postados em bytes (L1S)
Enter fullscreen mode Exit fullscreen mode
Gás (G) = Custo L1 (L1C) / Preço do Gás L2 (P)
Enter fullscreen mode Exit fullscreen mode

Esse gás é cobrado antes de uma transação começar a execução e contabiliza para o gasLimit da transação. Portanto, deve ser contabilizado durante a estimativa de gás da transação.

Como o Optimism calcula o custo para cobrir as taxas de gás da L1?

No Optimism, o custo do gás da L1 é calculado de forma semelhante, onde o tamanho em bytes após a compressão é multiplicado por uma taxa da L1. Em vez de traduzir esse valor para gás na L2 como o Arbitrum, o Optimism deduz o ETH necessário diretamente da conta do remetente.

Os remetentes não precisam levar esse valor em consideração durante a estimativa de gás, mas não têm a capacidade de definir um limite para seus gastos. O Optimism se esforça para garantir que essa taxa não aumente muito.

Como um empacotador nas L2s pode cobrar pelas taxas da L1?

Em ambos os casos, um empacotador (bundler) que submete uma transação em lote em uma L2 é cobrado pelas taxas da L1. O empacotador precisa de uma forma de cobrar as operações de usuário agrupadas por esta taxa, aumentando o gás na L2.

O impacto efetivo no gás da L2 pode ser determinado por:

L2_gas = L1_gas * L1_fee / L2_fee
Enter fullscreen mode Exit fullscreen mode

verificationGasLimit e callGasLimit são medidos pelo ponto de entrada e, portanto, não podem ser usados ​​pelos empacotadores para cobrar por esse gás extra. Os empacotadores precisam confiar em outros métodos.

Tentativa 1: Definir uma Taxa de Prioridade Mais Alta

Exigir um maxPriorityFeePerGas mais alto poderia permitir que o empacotador recuperasse essas taxas perdidas.

O cálculo seria assim:

  1. Estime a taxa da L1 e converta em gás na L2
    1. Suponha que haja um pacote de operação de usuário de tamanho 1 e estime o gás usando métodos fornecidos pela rede, normalmente expostos como chamadas de contrato especiais.
    2. Isso exige supor tanto uma taxa base da L1 quanto uma taxa base da L2 para converter a taxa em valores de gás na L2.
  2. Estime o verificationGasLimit
    1. Como esse limite de gás está sob regras de simulação estritas, é muito provável que o valor estimado seja muito próximo do valor real, ao contrário do callGasLimit.
  3. Defina maxPriorityFeePerGasBuffer = L1_fee / verificationGasLimit‍.
  4. Adicione esse buffer a qualquer taxa de prioridade necessária.

Isso poderia funcionar, mas tem uma péssima experiência do usuário (UX).

Para se proteger, o empacotador deve supor que a quantidade de gás de chamada usada será 0 e cobrar do usuário como se o gás de verificação fosse o único componente. O usuário então pagará em excesso pelo componente de taxa de prioridade de buffer multiplicada por qualquer gás de chamada usado. Isso não é bom para o usuário.

Tentativa 2: Manipular preVerificationGas

Isso deixa preVerificationGas como o único campo razoável a ser manipulado. Isso se encaixa bem na definição fornecida acima de que preVerificationGas é "o campo de gás usado para capturar qualquer uso de gás que o ponto de entrada não pode medir". Uma vez que este é o gás que o ponto de entrada não mede, esperamos poder usar este campo para cobrar do usuário.

O cálculo seria:

  1. Estime a taxa da L1 e converta em gás na L2, definido como preVerificationGas.
    1. Suponha que haja um pacote de operação de usuário de tamanho 1 e estime o gás usando métodos fornecidos pela rede, geralmente expostos como chamadas de contrato especiais.
    2. Isso nos obriga a supor tanto uma taxa base da L1 quanto uma taxa base da L2 para converter a taxa em valores de gás na L2.
  2. Calcule o gás da L2 não medido e adicione-o ao valor calculado acima.
    1. Este é o mesmo método descrito em nossa seção de cálculo de preVerificationGas para um cálculo normal de preVerificationGas.
  3. Durante eth_sendUserOperation, também execute (1) e (2) e rejeite quaisquer operações que não tenham um preVerificationGas alto o suficiente como parte do estágio de "pré-verificação".
  4. Durante o empacotamento, execute (1) e (2) novamente imediatamente antes da submissão.
    1. Rejeite quaisquer operações que não tenham um preVerificationGas alto o suficiente.

Embora esse mecanismo funcione, ele tem um problema significativo de UX.

Durante (2), o empacotador deve assumir valores de taxa base da L1 e da L2 para realizar o cálculo do gás. Como as taxas base são dinâmicas, se entre a etapa de estimativa de gás e a etapa de submissão/empacotamento a relação de L1_fee / L2_fee aumentar, será necessário um preVerificationGas mais alto, e operações de usuários calculadas com uma relação menor serão rejeitadas.

O melhor que o usuário pode fazer para melhorar isso é supor que a relação aumentará entre a estimativa e o empacotamento e fornecer uma margem no seu preVerificationGas para melhorar suas chances.

Como o preVerificationGas é sempre cobrado na íntegra (ou seja, não é um campo de limite), o usuário é forçado a pagar por esse overhead independentemente do que aconteça com o preço. O usuário fica preso entre potencialmente pagar demais ou ter suas operações rejeitadas.

O Rundler implementa o cálculo de preVerificationGas acima. Recomendamos que os usuários dessas L2s adicionem um buffer de 25% no preVerificationGas retornado por eth_estimateUserOperationGas para melhorar a chance de que a operação não seja rejeitada.

Caso Extremo do Optimism

As taxas base do Optimism são incrivelmente baixas, muitas vezes bem abaixo de 100 wei (sim, wei). O preVerificationGas necessário para cobrar pela taxa de gás na L1 é inversamente proporcional à taxa de gás da L2, exigindo que o preVerificationGas seja extremamente alto (na casa dos milhões).

A taxa de prioridade do Optimism costuma ser ordens de magnitude mais alta do que sua taxa base. Portanto, um usuário deve ter muito cuidado para não enviar uma taxa de prioridade proporcional à taxa de prioridade da rede, pois o ponto de entrada (entry point) requer pagamento para preVerificationGas (muito alto) * taxa de prioridade (normal), causando um pagamento excessivo. Por este motivo, o Rundler exige que a taxa de prioridade seja uma porcentagem estática da taxa base para incentivar o empacotamento no Optimism.

O Problema do Agregador de Assinaturas

A agregação de assinaturas é uma característica muito discutida do ERC-4337 por sua capacidade de:

  1. Reduzir os custos de dados de chamada em L2 por meio da compressão de assinaturas, levando a economias significativas.
  2. Amortizar o custo de gás de uma verificação agregada em um pacote de operações.
    1. Esta verificação poderia ser tão simples quanto uma assinatura BLS, ou tão complicada quanto uma prova de conhecimento zero agregada.

Na versão atual do ponto de entrada, a chamada para a função de validação do agregador de assinaturas não é medida. Isso significa que o empacotador precisa encontrar uma maneira de cobrar operações de usuários agregadas por esse gás, semelhante ao problema da L2 acima.

Como o problema da L2, a única maneira razoável de fazer isso é aumentar o preVerificationGas.

Uma maneira de fazer isso é:

  1. O empacotador assume um tamanho de pacote alvo (target).
  2. O empacotador chama validateUserOp em cada operação que recebe antes da estimativa para extrair um endereço de agregador, se usado.
  3. Quando uma operação de usuário agregada é recebida, o empacotador precisa estimar a quantidade de gás usada pelo agregador de assinaturas, por operação, naquele tamanho de pacote alvo.
    1. Uma maneira seria replicar a operação de usuário recebida o número alvo de vezes em um pacote e, em seguida, usar eth_estimateGas em aggregator.validateSignatures.
    2. Outra maneira seria manter uma lista de permissões do agregador de assinaturas com medições de gás pré-preenchidas (com um valor estático e um valor dinâmico por operação).
  4. Adicione o gás estimado ao cálculo de preVerificationGas acima e retorne esse valor.

Há alguns problemas com esta abordagem:

  1. O empacotador está correndo um risco ao assumir um tamanho de pacote alvo. Ou:
    1. O empacotador espera até que possa realmente agrupar operações alvo, prejudicando a UX através da latência.
    2. O empacotador agrupa menos do que o alvo e assume o custo.
  2. Os usuários não podem "licitar" mais ou menos dependendo de quão rápido eles querem ser incluídos.
    1. Em uma abordagem baseada em limite, se um usuário deseja garantir uma mineração rápida, ele pode superestimar o custo. Se o custo acaba sendo menor, eles não são sobretaxados. Nesta abordagem, devido ao preVerificationGas estático, os usuários devem sempre pagar todo o lance.
  3. Em uma rede p2p, os empacotadores podem ter diferentes tamanhos de pacote alvo.
    1. Isso significa que o empacotador usado para estimativa pode superestimar ou subestimar o preVerificationGas necessário por outro empacotador no mempool.
    2. Essas suposições fora da cadeia prejudicam a interoperabilidade do mempool.

Atualmente, o Rundler não possui suporte para agregadores de assinaturas devido a essas complicações. É provável que adicionemos suporte para o método acima e assumamos algum tamanho de pacote (pequeno, começando em 1) que tornará o uso de agregadores de assinaturas muito caro.

Alterações Potenciais no Ponto de Entrada

Ambos os problemas acima se devem à falta de medição no contrato do ponto de entrada para o uso significativo de gás pela operação do usuário. Confiar no preVerificationGas para cobrar por esse uso de gás tem o significativo problema de UX de exigir que os usuários paguem mais do que realmente usam para aumentar a chance de sua operação de usuário ser registrada na cadeia rapidamente.

Uma solução para esses problemas poderia ser modificar o contrato do ponto de entrada para medir este uso extra de gás e atribuí-lo a campos de gás baseados em limites.

Medição de Gás dos Dados de Chamada L2-L1

Este é um esboço muito básico de uma solução para um problema difícil. Adoraríamos ouvir ideias da comunidade!

O custo do gás dos dados de chamada da L1 pode ser medido na cadeia e as operações do usuário podem ser cobradas apenas pelo custo exato que elas incorrem, e não pelo overhead. Um campo baseado em limite deve ser usado.

Uma solução potencial é introduzir um novo campo, daCallDataGasLimit (“da” para data availability (disponibilidade de dados), título provisório). Este campo seria nativo de uma versão L2+ do ponto de entrada e usado em cadeias onde os dados de chamada da transação são postados em um sistema diferente e, portanto, devem ser cobrados de uma maneira separada.

A lógica do ponto de entrada seria a seguinte:

  1. No momento da implantação, o ponto de entrada está associado a um contrato auxiliar daGasMeter que tem uma única função measureUserOperationDaGas(UserOperation userOp)
    1. Esta função pega uma operação do usuário e mede exatamente quanto gás da DA foi usado.
    2. Por exemplo, no Arbitrum, o medidor pode fazer uma chamada para gasEstimateL1Component com uma operação de usuário em memória para determinar quase exatamente quanto gás L1 ela usou (ele não pode contabilizar compressão extra devido a um pacote com múltiplas operações).
  2. O ponto de entrada chamará essa função para cada operação do usuário antes da validação e poderá atribuir esse gás ao daCalldataGasLimit.
  3. Para evitar reversões, em vez de reverter se o gás da DA usado for maior do que o daCalldataGasLimit, o ponto de entrada simplesmente limitará a partir daqui e o empacotador terá que pagar por qualquer gás excedente a esse limite. Isso pode ser contabilizado fora da cadeia pelos empacotadores, garantindo que qualquer operação agrupada tenha um buffer suficientemente alto em seu campo de limite antes do empacotamento.

Há algumas desvantagens bastante significativas para esta abordagem:

  1. Os contratos do ponto de entrada em diferentes cadeias terão endereços diferentes.
  2. Utilização de gás durante a medição.
    1. Isso é menos problemático, uma vez que em L2+ o custo do gás de execução geralmente é muito menor do que o custo do gás de dados.

Medição de Gás do Agregador de Assinaturas

O contrato do ponto de entrada pode ser modificado para medir a quantidade de gás usada durante sua chamada para aggregator.validateSignatures, dividir isso pela quantidade de operações de usuário agregadas e atribuir o uso do gás de forma uniforme, deduzindo do verificationGasLimit.

Cada agregador de assinatura na lista de permissões deve ser associado a um valor estático, o custo base para validar a assinatura agregada, e um valor dinâmico, o aumento do custo por operação agregada. Por exemplo, um agregador de assinatura BLS poderia ter seu valor estático como o custo de verificação de assinatura único e o valor dinâmico como o custo das operações de hashing por operação. Os empacotadores podem aumentar o verificationGasLimit durante o eth_estimateUserOperationGas pelo valor estático dividido por um tamanho de pacote alvo mais o valor dinâmico.

Como definimos o tamanho do pacote alvo?

Uma solução potencial aqui é complementar os argumentos para eth_estimateUserOperationGas com um minimumBundleSize correspondente ao menor tamanho de pacote (portanto, o maior gás) que o usuário deseja incluir. Usuários dispostos a pagar mais podem diminuir seu tamanho mínimo de pacote e melhorar seu tempo até a inclusão.

Os empacotadores então precisam de uma lógica para garantir que eles só incluam uma operação de usuário em um pacote que seja pelo menos tão grande quanto o tamanho mínimo de pacote daquela operação de usuário. Esse tamanho mínimo de pacote pode ser calculado durante a simulação, acompanhando a quantidade de gás usada pelo passo de validação da conta, subtraindo isso do verificationGasLimit e, em seguida, calculando o tamanho mínimo do que resta. Os empacotadores podem armazenar esse valor ao lado da operação de usuário em sua mempool e usá-lo como uma dica para a construção de pacotes.

Uma desvantagem dessa abordagem é que ela pressupõe que o uso de gás por um agregador de assinatura é uniforme por operação. Se esse não for o caso, a medição pode ser transferida para o contrato do agregador e retornada por sua função de verificação. Os empacotadores precisariam de uma maneira de calcular esses valores não uniformes fora da cadeia também.

O que isso significa para os desenvolvedores de agregadores de assinatura?

Os agregadores de assinatura provavelmente precisarão ser diretamente incluídos na lista de permissões pelos empacotadores, fornecendo-lhes métodos para calcular assinaturas fora da cadeia e com seus componentes de custo de gás estático/dinâmico. O problema do início a frio vai ser difícil, especialmente dada a incapacidade de um usuário "licitar" para acelerar sua operação.

O que isso significa para os desenvolvedores de clientes de conta?

É importante entender as distinções entre as L2 descritas acima, especialmente quando chega a hora de estimar as taxas de gás. No Optimism, se você usar a taxa de prioridade fornecida pela rede, pode pagar em excesso por uma operação de usuário, já que essa taxa de prioridade agora também se aplica aos custos da L1. Se usar o ponto extremidade do empacotador da Alchemy, consulte nossa documentação para dicas sobre estimativa de taxas.

🦀

Continue Lendo

O próximo artigo nesta análise detalhada sobre a estimativa de gás do ERC-4337 fornece um guia do processo de estimativa de taxa de operação do usuário. Se você perdeu a parte um ou dois, saiba como funcionam a estimativa de gás do ERC-4337, os valores fictícios e o problema de transferência de tokens.

Artigo publicado por Dan Coombs e David Philipson. Traduzido por Paulinho Giovannini.

Top comments (0)