O Ethereum é atualmente a blockchain mais bem estabelecida, onde os usuários trocam tokens, negociam NFTs, e formam DAOs entre uma infinidade de outras atividades on-chain. Muitas das inovações que temos visto em finanças descentralizadas e no espaço da NFT se originaram do extenso efeito de rede desse protocolo dos usuários e desenvolvedores de nível mundial. Apesar do seu sucesso imenso como um protocolo, muitos usuários não estão cientes do fato de que sempre que uma transação é feita, pode estar na mira de um robô sniper (atirador) hiper aprimorado. Esses robôs sofisticados são ajustados em torno da arquitetura tecnológica central do Ethereum para caçar oportunidades lucrativas. No início deste ano, não era incomum ver esses robôs fazerem mais do que $250k numa única negociação. Muitas histórias já foram documentadas sobre os perigos desses robôs, como a viagem na Floresta Escura de Dan Robinson e fuga da Floresta Escura de samczun. Felizmente, inovações recentes no ecossistema do Ethereum foram desenvolvidas para ajudar a reduzir as externalidades negativas desses atiradores.
Beware: your transaction is being watched closely by a sniper bot
Este trabalho de pesquisa será estruturado nas seguintes seções:
- Parte 1: Visão Geral das Transações do Ethereum e Valor Máximo Extraível (MEV)
- Parte 2: Visão Geral dos Flashbots
- Parte 3: Estudo de Caso – Resgatando uma NFT com Flashbots
- Parte 4: Estudo de Caso – Explorando um Contrato Inteligente com Flashbots
1. Visão Geral das Transações do Ethereum e MEV
Sempre que uma transação é feita na rede Ethereum existe uma variedade de passos que precisam acontecer para que a transação seja confirmada na blockchain. Antes de mergulharmos em alguns exemplos, abaixo estão algumas definições chave que serão importantes para se ter em mente. (adaptado de ethereum.org).
- Transação:sempre que um usuário deseja mudar o estado da blockchain do Ethereum, isso envolve uma transação. Se for comprar uma NFT no OpenSea, obter rendimento por meio de diversos protocolos, ou simplesmente transferir ETH para outra conta. Todas essas atividades são exemplos de mudanças de estado para o blockchain do Ethereum que precisam ser verificadas pelos mineradores para serem consideradas definitivas. Para evitar spam, é solicitado que os usuários da rede paguem aos mineradores uma taxa para cada transação.
- Bloco: isso pode ser pensado como um container que armazena novas transações dos usuários em toda a rede. Há um limite do número de transações que cabem em um bloco. Portanto, os mineradores priorizam as transações com as mais altas taxas.
- Mineração: essas partes são responsáveis por criar novos blocos. Como mencionado acima, existe um limite do número de transações que podem ser incluídas num bloco, de forma que os mineradores precisam ser seletivos nas transações a serem incluídas. Como resultado, os mineradores agruparão as transações pendentes em blocos e priorizarão as que tenham taxas de gas mais altas. Os mineradores vão, então, competir contra outros mineradores para resolver uma função complicada na esperança de chegar à primeira solução. O minerador que resolver o problema e enviar seu bloco para a rede primeiro, ganha todas as taxas da transação naquele determinado bloco. Uma vez que o bloco do minerador vencedor é minerado na blockchain, todas as transações dentro daquele bloco são consideradas permanentes (existem algumas exceções, mas não vamos discuti-las aqui). Um ponto crítico a ser entendido é que os mineradores têm a capacidade de ordenar as transações em um bloco. Como iremos discutir abaixo, isso tem consequências significativas na vivência do usuário.
-
Nó: é um dispositivo que está executando uma implementação do software do Ethereum, que verifica todas as transações em cada bloco. Os nós têm um papel importante em garantir que a rede é segura e os dados são precisos. Sempre que o usuário deseja realizar uma transação, ela deve ser inicialmente transmitida de um nó para o resto da rede. A grande maioria de nós executa o GETH que é a implementação do Go para o Ethereum. Esses nós GETH interagem com a blockchain do Ethereum por meio de um procedimento remoto chamado (RPC). Por exemplo, chamando o RPC
eth_blockNumber
endpoint vai recuperar o número atual do bloco na rede. - Gas: é a unidade de medida solicitada para enviar transações na blockchain do Ethereum. As transações de maior complexidade demandarão taxas de gas mais altas. Por exemplo, uma simples transferência de 1 ETH de um endereço A para um endereço B custará 21,000 unidades de gas. Entretanto, comprar o mais atual NFT no OpenSea pode custar em torno de 300,000 unidades de gas. Embora uma simples transferência de ETH sempre custe 21,000 unidades de gas, o preço do gas flutua com base na atividade corrente na rede, tornando variável a taxa total da transação. Usuários que desejam um ajuste fino de suas taxas de transações, provavelmente terão que otimizar os diversos componentes das taxas de gas, o que inclui o seguinte:
Limite de Gas: o máximo de gas que pode ser gasto pela transação
Preço do Gas: o preço máximo por unidade de gas que o usuário esteja querendo pagar
Taxa Básica: as unidades de gas requeridos para executar uma transação (algoritmicamente determinada pela rede Ethereum - ou seja, no caso de uma simples transferência de ETH, esse valor será sempre 21,000 unidades de gas)
Taxa de Prioridade: uma gratificação opcional que é paga aos mineradores - isso pode incentivar mineradores a incluir a transação num bloco
- Mempool: antes que uma transação seja verificada numa blockchain, ela fica num pool de outras transações pendentes chamado de mempool. Uma transação que está no mempool não está confirmada até que seja selecionada por um minerador para ser incluída num bloco. Se a transação ficar no mempool por um longo tempo, provavelmente significa que o usuário não especificou uma taxa de gas suficientemente alta para atrair mineradores a incluir a transação no bloco.
Vamos considerar uma simples transferência de ETH.
Suponha que a Alice deseje enviar 1 ETH para o Bob de sua carteira de hardware Ledger. Nesse caso, a transação seria roteada através do nó de Ethereum da Ethereum Da Ledger, que iria propagar a transação para um pool de outras transações pendentes no mempool.
É responsabilidade dos mineradores agrupar essas transações em blocos. As transações com maiores taxas de gas têm maiores chances de serem incluídas porque os mineradores, desse modo, recebem mais taxas. Se a Alice estivesse com pressa de enviar o 1 ETH para o Bob, ela deveria aumentar seu limite de gas da transação. Isso resultaria em sua transação apresentar maior prioridade do que as transações existentes no mempool.
Por esse exemplo, nós podemos ver que os mineradores são as partes responsáveis por gravar quaisquer mudanças de estado da rede Ethereum– eles têm um papel importante em manter a funcionalidade da blockchain. Como retorno dos seus serviços, os mineradores são recompensados com taxas das transações no bloco e o Ethereum recém emitido (ou seja, recompensas em moedas).
Com esse histórico, agora temos o contexto suficiente para avaliar os perigos potenciais de enviar transações em uma blockchain pública. Vamos considerar o cenário da vida real para refletir o risco de enviar dados de transações confidenciais para um mempool público. É importante observar que ninguém pode olhar para as transações não confirmadas no mempool público — incluindo robôs sniper.
Arbitragem DEX
Suponha que o token XYZ está sendo negociado por $100 no Uniswap, mas $120 no Sushiswap. Uma estratégia de arbitragem simples seria comprar o token XYZ por $100 no Uniswap e vender o mesmo token no Sushiswap por $120, o que resultaria numa arbitragem de $20. A pessoa que arbitra poderia enviar ambas as negociações ao mesmo tempo para garantir que a posição seja aberta e fechada na mesma transação que garante um lucro relativamente livre de risco. Nesse caso, vamos supor que a pessoa que arbitra envie essa transação com uma taxa de transação de gas no valor de $5, deixando-o com um lucro líquido de $15.
Quando o arbitrador envia essa transação para a rede, ele não sabe que os robôs snipers estão farejando o mempool procurando por oportunidades exploráveis. Dado que a negociação do usuário é acessível publicamente para qualquer pessoa ver, antes de ser finalizada na blockchain, um robô sniper sofisticado poderia passar à frente desse usuário, simplesmente, subornando o minerador com taxas de gas mais altas. Nesse caso, o robô sniper poderia simplesmente copiar exatamente a mesma negociação do arbitrador, mas oferecer uma taxa de gas um pouco mais alta de $10, deixando o robô sniper com um lucro líquido de $10. Ironicamente, esse robô pode ser atacado por outro robô disposto a pagar um suborno de taxa de gas mais alta que a do minerador. Esse novo robô sniper pode querer pagar $15 pelas taxas de transação para receber um lucro líquido de arbitragem de $5. Essa natureza competitiva dos robôs snipers, que tentam se superar, resulta em taxas de transação mais altas em toda a rede Ethereum, enquanto roubam a negociação original do usuário. É importante observar que apenas um robô pode capturar a oportunidade de negociação, já que as negociações de todos os outros fracassarão. Essas negociações fracassadas ocupam espaço no bloco e expulsam os outros usuários. É dessa forma que os robôs podem aumentar os preços do gas para todos os outros. Isso pode ser frustrante para o arbitrador inicial que gastou tempo e esforço para detectar a negociação, mas acabou sendo executado por robôs competindo entre si.
Isso coloca usuários e snipers num problema interessante de teoria de jogos. De um lado, usuários querendo minimizar as taxas, mas ao mesmo tempo, eles são altamente incentivados a elevar as taxas de transação para garantir que sua transação seja selecionada pelos mineradores. Em geral, são os mineradores que mais se beneficiam desses robôs snipers, já que essa atividade causa um aumento das taxas de transação em toda a rede. “Valor Máximo Extraível” ou MEV é um termo popular usado para descrever o lucro adicional que o minerador obtém pela reordenação das transações na blockchain em detrimento dos usuários. Dado que o MEV surge de mineradores que têm a capacidade de reordenar transações em um bloco, isso significa que quase todo o blockchain tem algum grau de risco MEV, incluindo Bitcoin. Para o Ethereum especificamente, desde o início de 2020, quase $750M USD em MEV foram extraídos dos usuários!
Como um todo, a presença de MEV é uma das questões mais prementes que o Ecossistema do_ Ethereum_ enfrenta. O MEV é, também, uma das principais razões pelas quais os preços do gas para taxas de transações são voláteis, já que os robôs, competitivamente, aumentam os preços do gas para lutar por oportunidades. Esse problema não ocorre apenas com negociações de arbitragem – como será visto abaixo, qualquer negociação frágil que pode alertar robôs snipers pode levar a transação a ser explorada e o usuário a perder dinheiro. Com esse entendimento, agora podemos reconhecer uma solução potencial para a crise do MEV e como esta tecnologia pode ser usada para proteger usuários comuns.
2. Uma Visão Geral dos Flashbots
Como discutimos anteriormente, a questão principal de enviar transações públicas diretamente na rede Ethereum é que todas as nossas transações pendentes no mempool são visíveis para o resto da rede. Isso significa que um sniper pode procurar qualquer negociação explorável e, consequentemente, atacá-la.
Com a introdução dos Flashbots, os usuários não estão mais à mercê desses robôs snipers hiper aprimorados. Ao invés de usar um mempool público, os Flashbots permitem que os usuários enviem transações como um pacote por meio do retransmissor de Flashbots, o qual entrega as transações diretamente para os mineradores. A partir daí, os mineradores podem optar em aceitar as transações do pacote Flashbots como se fossem transações normais do Ethereum. Em uma nota técnica, os mineradores que desejam receber pacotes do Flashbots devem executar um nó especial chamado MEV-GETH. Semelhante ao GETH, essa implementação de nó simplesmente inclui dois novos endpoints RPC chamados sendBundle
e callBundle
que são usados para interagir com as transações do retransmissor dos Flashbots.
Algumas coisas importantes para observar:
Um dos benefícios mais importantes do uso de Flashbots é que os usuários conseguem manter seus dados de pré-transação inacessíveis aos robôs snipers. Os dados da transação são publicados somente após serem completamente minerados na blockchain. No caso em que uma transação não é incluída pelo minerador em um bloco corrente (ou seja, outras transações podem oferecer taxas de gas mais altas), os desenvolvedores usando a API de Proteção de Flashbots terão que reenviar continuamente seu pacote em cada bloco, até que ele seja aceito pelo minerador. O mais importante é que se a transação não for incluída no bloco pelo minerador imediatamente, está ok, porque a transação não será transmitida para a rede e então, nenhum robô sniper poderá tomar a frente da negociação. Esse é um grande passo à frente para preservar a privacidade de pré-transação para os usuários. Ao mesmo tempo, isso cria um mercado de preços de gas mais robusto e mais saudável, ao invés de ter robôs snipers competindo lances um contra o outro.
Um outro benefício chave dos Flashbots é que eles possibilitam um mecanismo de leilão mais eficiente para o MEV. Como discutido anteriormente, os lances falhados são mantidos off-chain, o que libera o espaço do bloco para todos os outros, mantendo os preços de gas mais baixos.
Vamos revisitar a transação de arbitragem DEX anterior. Lembre-se de que o objetivo do arbitrador é executar a transação de arbitragem sem ser eliminado por robôs snipers. No exemplo original, a questão principal era que a transação de arbitragem foi enviada pelo mempool público para todos verem. Contudo, se o arbitrador direcionasse essa transação através do serviço de retransmissão de Flashbots, ele contornaria o mempool público e enviaria diretamente a transação para os mineradores. Se, por alguma razão, não tenha sido incluída no bloco corrente (ou seja, o bloco pode estar cheio ou outras transações podem ter maior prioridade por possuírem taxas de gas altas), o arbitrador poderia tentar de novo para o próximo bloco. O mais importante a ser observado é que a transação do arbitrador não está ociosa no mempool público, aguardando ser interceptada por robôs, já que ela permanece oculta. Somente depois que a transação é minerada ela se torna pública para todos verem, o que seria tarde demais para um robô sniper liderar a negociação.
Embora, os usuários possam ser grandemente beneficiados com os Flashbots, os robôs snipers podem também, eles mesmos, usar o retransmissor do Flashbots para executar negociações. Se um robô detecta uma oportunidade de arbitragem potencial, seria bobagem enviar a transação para o mempool público. Todos os outros robôs snipers estarão ouvindo as transações pendentes e aumentarão agressivamente o preço do gas da transação, até que a oportunidade de arbitragem não seja mais lucrativa. Por outro lado, o robô sniper que detectou a oportunidade de arbitragem seria o mais adequado para encaminhar a transação através do retransmissor do Flashbots, que é onde ele pode evitar ter que revelar sua transação antes de ser confirmada. Desse modo, o robô sniper pode garantir que a transação somente será conhecida depois de ser minerada no blockchain.
3. Estudo de Caso — Recuperando uma NFT encalhada
Nesse exemplo, suponha que um desenvolvedor esteja trabalhando num projeto de blockchain e, acidentalmente, mande sua chave privada do Ethereum para o Github. Se o repositório for público, é provável que seus fundos tenham desaparecido por alguns minutos já que existem robôs rastreando chaves privadas expostas no Github e no StackOverflow. Uma vez que os robôs detectam chaves privadas expostas, eles imediatamente transferem o saldo inteiro de ETH para um endereço separado de seu controle. Embora todo o ETH pudesse ser drenado da carteira do desenvolvedor, seria muito mais difícil para o robô detectar quaisquer tokens NFTs ou ERC-20 armazenados nesse endereço.
Suponha que o desenvolvedor tivesse uma NFT valiosa armazenada nesse endereço utilizado. Aqui está um problema – para transferir o NFT para um endereço seguro, deve haver ETH no endereço utilizado para pagar as taxas de gas para a transferência. No momento em que qualquer ETH é enviado para o endereço utilizado, robôs detectarão a transferência recebida e imediatamente retirarão os fundos para um endereço separado. Isso faz com que o NFT fique preso no endereço utilizado, já que o desenvolvedor não pode financiar o endereço com ETH suficiente para pagar as taxas de gas a fim de recuperar seu NFT. Além disso, mesmo que ele fosse bem sucedido em financiar o endereço utilizado, quando o desenvolvedor tentar transferir a NFT para um novo endereço, existe o risco de que essa atividade possa alertar robôs e fazer com que eles executem a transação na frente, levando-os a roubar o NFT também.
Felizmente o desenvolvedor pode usar os Flashbots para salvar seu NFT. Uma das características interessantes dos Flashbots é que os usuários podem agrupar múltiplas transações no mesmo pacote, desde que sejam atômicos. Isso significa que a transação deve ser tudo ou nada – toda transação no pacote precisa ser executada para que seja enviada com sucesso pelo retransmissor Flashbots. Isso apresenta um conceito interessante de transações de patrocinadores onde um usuário pode fazer uma transação da conta X, mas paga as taxas de gas da transação com a conta Y. Isso soa exatamente como o que precisamos em nosso caso!
No diagrama abaixo, vamos combinar duas transações em um pacote e enviar isso por meio do retransmissor do Flashbots.
- Transação 1: O endereço do patrocinador vai transferir uma pequena quantia de ETH para o endereço utilizado para pagar pelas taxas de gas a fim de transferir o NFT para um endereço seguro
- **Transação 2: **Transferir o NFT do endereço utilizado para o endereço seguro
Vamos revisar alguns dos aspectos de alto nível sobre como vamos executar essa transação. Para quem está interessado na implementação detalhada do código JS, verifique o repositório do Github para esse projeto de pesquisa aqui. Ao invés de testar nosso código no main-net e arriscar perder milhares de dólares, vamos usar o testnet do Goerli para simular essas transações com a ajuda do Incrível faucet testnet do Paradigm.
Como pode ser visto abaixo, o NFT nesse exemplo está retido no endereço utilizado. Atualmente, não há ETH no endereço utilizado e se alguém enviasse ETH para esse endereço, seria imediatamente varrido pelos robôs snipers.
Ao mesmo tempo, o usuário possui um endereço de patrocinador que será usado para pagar as taxas de gas da transação a fim de recuperar o NFT e movê-lo para o endereço do patrocinador.
O desenvolvedor terá que completar essas duas transações num pacote. A primeira transação vai envolver enviar algum ETH para o endereço utilizado do endereço do patrocinador a fim de pagar as taxas de gas para transferir o NFT. A segunda transação vai envolver a transferência do próprio NFT para o endereço do patrocinador. O pacote de transações chamado signedTxBundle
será enviado para o retransmissor do Flashbots. Nota: já que tudo isso é feito ao mesmo tempo (ou seja, atomicamente) não há maneira viável do sniper se antecipar e nos vencer nessa transação. Daqui, o retransmissor do Flashbots irá conectar a transação do usuário, diretamente aos mineradores. Se os mineradores optarem por incluir esse pacote no bloco, então o desenvolvedor recuperou, com sucesso, seu NFT. No caso de o minerador não incluir essa transação no bloco corrente, ninguém ficará sabendo sobre essa transação, uma vez que ela nem tocou o mempool público. Quando isso acontece o desenvolvedor terá que reenviar sua transação até que o minerador finalmente a inclua no bloco (ou seja, o desenvolvedor poderia aumentar a taxa de gas para motivar o minerador a incluí-la no bloco mais rapidamente).
Usando o código JS o desenvolvedor conseguiu transferir o NFT, com sucesso, do endereço utilizado. Observe como o endereço do patrocinador, agora, possui o NFT que foi previamente retido no endereço utilizado.
No mesmo tempo, nós podemos ver que o endereço utilizado não possui mais o NFT na conta. Entretanto, existe algum ETH restante da transação do patrocinador (nota: podemos ser mais precisos em termos de estimativa de taxas de gas - nesse caso apenas usamos 0.01 ETH arbitrário como a taxa de gas estimada). Esse excesso de ETH seria eliminado imediatamente uma vez que atingisse o endereço utilizado.
4. Estudo de Caso – Explorando um Smart Contract
Nesse caso, somos o sniper explorando oportunidades na blockchain - reviravolta na história!
Suponha que um desenvolvedor lançou um contrato falho que permite que qualquer usuário retire todos os fundos do contrato. Abaixo temos um exemplo desse tipo de contrato – pode receber ETH de qualquer pessoa, mas, ao mesmo tempo, qualquer usuário também pode retirar o saldo todo de ETH do contrato. Isso é claramente uma grande falha no contrato porque se o saldo armazenado no contrato inteligente é suficiente para cobrir as taxas de gas, então é um negócio óbvio retirar todos os fundos o quanto antes. Esse contrato pode ser visto no Etherscan do Goerli aqui e originariamente tinha um saldo de 0.20 ETH.
Um robô sniper ingênuo poderia imediatamente chamar a função withdraw
no contrato inteligente e retransmitir essa transação por meio do mempool público. Mesmo que esse robô sniper possa ter pago uma taxa de gas mais alta para agilizar a transação, ironicamente, essa transação será liderada por robôs snipers mais sofisticados.
Por que é assim? Toda vez que uma transação é executada com um contrato inteligente no Ethereum, haverá dados de uma chamada associada codificados, que são acessíveis para qualquer pessoa ver quando atingir o mempool. Por exemplo, se o explorador desejou chamar a função withdraw
no seu contrato inteligente deveria assinar uma transação com o seguinte hex call-data:0x3ccfd60b
como mostra a animação abaixo. Esses dados são visíveis para todos os robôs snipers que rastreiam o mempool.
Esse campo de dados estão em hex e não são decifráveis para humanos. Contudo, robôs snipers sofisticados podem pegar esses dados e simular como seria a transação se eles próprios executassem-na. Portanto, se essa transação é deixada no mempool público, então esses robôs snipers ainda podem tomar a frente, oferecendo taxas mais altas de gas da transação. Poderia chegar a um ponto em que os robôs snipers estivessem pagando 0.199 ETH de taxas de gas e recebendo 0.2 ETH, portanto, resultando em um lucro de 0.001 ETH.
Por esse exemplo podemos ver como esses robôs snipers podem, agressivamente, aumentar o preço das taxas de gas para a rede inteira devido aos competitivos leilões de gas. Portanto, devemos evitar que essa transação seja detectada por outros robôs snipers, antes de ser minerada na blockchain, a fim de explorar esse contrato com sucesso. Seria melhor retransmitir essa transação por meio de Flashbots como mostrado no diagrama abaixo. Isso vai assegurar que a transação não será revelada até que ela seja minerada na blockchain.
Conclusão e Pensamentos Futuros
Quando vemos milhões de novos participantes chegarem ao Ethereum, ferramentas como_ Flashbots serão fundamentais para garantir ao usuário, uma experiência positiva e um ecossistema eficiente. Sempre haverá algum grau de MEV em qualquer blockchain, contudo, as ferramentas do tipo _FlashBots podem ajudar a reduzir externalidades negativas desses robôs snipers e incentivar potencialmente, um mercado eficiente para transações on-chain.
Embora a maior parte dessa análise tenha sido a favor dos Flashbots, um risco que deve ser mencionado é que todas as transações precisam passar por um retransmissor de _Flashbot_s centralizado que é mantido por um time principal. Apesar desse risco centralizado, eu acredito que essa é a melhor abordagem pois, descentralizar muito cedo pode limitar muito o crescimento geral do projeto e a velocidade da inovação. O time, atualmente tem planos para descentralizar todo o projeto Flashbots entretanto, esse é um processo para vários anos.
Além disso, apesar deste texto ter sido crítico em relação aos robôs snipers, eu acho que esses participantes desempenham um papel importante em manter a eficiência dos mercados de DEX on-chain. Por exemplo, oportunidades de arbitragem nas exchanges DEX, agora, são extremamente competitivas dado que centenas de robôs estão constantemente rastreando ineficiências de preço. Coletivamente, isso torna os preços mais líquidos e precisos, o que beneficia o mercado como um todo.
No geral, acredito que projetos focados em proteger seus usuários contra explorações de robôs snipers serão bem recebidos pela comunidade. Ao mesmo tempo, vimos um aumento na demanda por soluções de negociações resistentes ao MEV, tais como Cowswap, MistX, ou simplesmente usando a proteção de Flashbots na MetaMask. É indiscutível que snipers se tornarão mais sofisticados. Entretanto, times inteligentes continuarão a inovar e constantemente focarão em otimizar para a melhor experiência possível de usuário.
Agradecimentos: _goofyballer (_desenvolvedor anônimo no grupo Flashbots) por me ajudar a entender como enviar pacotes Flashbot com taxas de gas suficientes. Ishan Verma por passar horas me guiando pela mecânica do MEV e revisando esse texto com sugestões úteis para várias definições técnicas. E finalmente, Robert Miller por também revisar e fornecer seu valioso feedback sobre vários tópicos relacionados à arquitetura principal do Flashbots.
Declaração: Nada mencionado nesse artigo constitui orientação financeira. Opiniões expressas são exclusivamente minhas e não expressam os pontos de vista e opiniões do LedgerPrime.
Esse artigo foi escrito por Samneet Chepal e traduzido por Fátima Lima. Você pode encontrar o artigo original aqui.
Samneet Chepal
Dec 15, 2021
Latest comments (0)