WEB3DEV Español

Cover image for Apócrifos Ethereum
Hector
Hector

Posted on

Apócrifos Ethereum

Trabajando en evm.storage, a menudo nos tropezamos con diseños de contratos únicos e interesantes. Si puede hacerse, probablemente alguien lo haya intentado hacer. Pensamos que esa es una característica maravillosa de las blockchains descentralizadas, incluso si algunas veces la implementación nos causa dolores de cabeza. En este post, compartiremos un par de sorpresas que hemos encontrado:

  • Un “gran” contrato
  • Muchos proxies -> una implementación
  • Un proxy -> muchas implementaciones
  • Diseños superpuestos
  • Variables profundamente anidadas

Un “gran” contrato

El interés primario de XENCrypto es que es grande. ¡Nuestros datos nos muestran que tiene más de 190MM de almacenamiento escrito! Pensarías que esto sugiere que token tiene muchos titulares, pero encontramos que sólo tienen 303K claves en _balances y 54K _allowances han sido escritos.

Así que, ¿por qué es tan grande? Hay una pista sobre cómo definen el proyecto: “XEN Crypto es un proyecto de minería social… XEN es categorizado como una cripto moneda como Prueba de Participación (Proof-of-Participation, PoP).”

Profundizando en los datos, vemos más de 10MM de claves (!) usados en el mapeo de userMints. Cada valor en ese mapeo es un struct XENCrypto.MintInfo que requiere 6 ranuras, así que tenemos 60MM de ranuras asociadas en total. Algunas de esas userMints contiene miembros cuyos valores son cero (por ejemplo).

Image description

Cada estructura MintInfo en el mapeo userMints ocupa 6 ranuras. ¡Imagina 10MM de estas!

¿Cuál es el mecanismo que maneja este alto volumen de acuñaciones? ¡Acuñaciones gratuitas! Excepto por el gas. Cualquiera que tenga una cartera puede subir una transacción para iniciar la acuñación de Xen. El usuario define un término en días y puede retirar sus tokens acuñados al final del término. La cantidad que un usuario recibe se incrementa con el número de días y se reduce en el número de otros usuarios acuñando en el mismo período.

Para completar, también vemos casi 20k claves usadas en userStakes y además 5k usadas en userBurns.

CoinGecko reporta que XENCrypto, en cierto punto, estaba usando casi la mitad del espacio del bloque Ethereum con los precios de gas resultantes, ¡impactan a contribuir significativamente a que el Ether se vuelva una divisa deflacionaria! Aparentemente, ¡ahora está en Base! A pesar de esto, XENCrypto no planteó problemas para nuestra infraestructura.

Incidentalmente, si te gustaron los datos sobre el número de claves en un mapeo, únete a nuestro grupo de Telegram y grítanos para poner el contador de las claves de mapeo en el producto. Aún no lo hemos priorizado, se lo pido a nuestro equipo de ingeniería cada ciclo pero nadie me escucha.

Muchos proxies -> una implementación

Ambisafe es un proveedor de blockchain infra, y su trabajo incluye las carteras de contrato inteligente: 0x0b…d0 es un contrato proxy sin verificar con una implementación sin verificar 0xc3…5c. Aquí está lo remarcable: ¡detectamos que 0xc3…5c es la dirección de la implementación sobre 545K contratos proxy! Y además hay otra implementación del contrato desplegado de Ambisafe, 0x07…16, que tiene 540K contratos proxy asociados.

Image description

545K contratos proxy comparten una sola implementación.

Sin saber mucho sobre su implementación, en grandes niveles tiene mucho sentido permitir a los usuarios desplegar carteras de contratos inteligentes que tienen toda su lógica a través de DELEGATECALL en la implementación compartida del contrato.

El patrón Ambisafe ha causado dificultades menores en construir evm.storage. Identificamos los pares de implementación proxy a través de DELEGATECALLs. Definimos este acercamiento porque no requiere que los contratos sean verificados o asuman que la dirección del contrato de la implementación sea escrito es una ranura en específico dentro del almacenamiento proxy (relacionado: ERC-1967).

Cuando empezamos a trabajar en la cobertura proxy, pensamos sobre un acercamiento a la capacidad de actualización. En ese escenario, cada implementación es típicamente asociada con un único proxy y un proxy que sea asociado con un puñado de implementaciones mientras el desarrollador actualiza la lógica junto a diferentes versiones.

Pero otro patrón común es usar los proxies para la optimización de gas. Si quieres ejecutar la misma lógica entre diferentes contratos, es eficiente desplegar esa lógica en la implementación de un contrato único y luego DELEGATECALL desde muchos proxies, como vemos con Ambisafe. Esto rompe nuestro acercamiento inicial para rastrear pares mientras nuestros procesos se estancan en constantemente cargar grandes listas desde la memoria DB, procesando revisiones duplicadas y escribiéndolas de nuevo. Cambiando a una estructura de lista vinculada a mantener las relaciones, nos permitió hacer una mejor escala a través de estas relaciones de muchos a uno.

Un proxy -> muchas implementaciones

Image description

Un MEV Bot muy social. Desearía tener más amigos.

Ambisafe tiene la implementación con la mayoría de los proxies. ¿Cuál es el proxy con la mayoría de implementaciones? Nuestros datos sugieren que es este MEV Bot con 418 contratos de implementación asociados (por ejemplo). Ten en cuenta que sólo estoy contando DELEGATECALLs distintos. Estos, probablemente, no representan las relaciones del estándar del proxy de la implementación. Trece de estos tienen rangos de implementación de menos de diez bloques.

[Editado: banteg apuntó que este contrato puede ser alguna variante de la familia proxy ds], popularizada por MakerDAO. Hicimos algunos análisis estáticos para confirmar. No es una copia directa ya que fue compilado con una versión solc nueva (0.8.9) y contiene algunas funciones que no se encuentran en el original:

{
   "1cff79cd": "execute(address,bytes)",
   "1f6a1eb9": "execute(bytes,bytes)",
   "60c7d295": "cache()",
   "65fae35e": "rely(address)",
   "78e111f6": "Unresolved",
   "948f5076": "setCache(address)",
   "97645e37": "Unresolved",
   "9c52a7f1": "deny(address)",
   "a90e8731": "Unresolved",
   "bf353dbb": "wards(address)",
   "c9892a5f": "Unresolved"
}
Enter fullscreen mode Exit fullscreen mode

Pero definitivamente parece ser alguna variante. ¡Gracias!]

Ahora no hay mucho más que podamos decir ya que los contratos están sin verificar, pero estamos trabajando en generar diseños de almacenamiento directamente desde el bytecode, el cual proporcionará más visibilidad. Aquí hay un teaser:

Image description

Diseños de almacenamiento inferidos, ¡próximamente a las páginas de contrato no verificadas cerca de ti!

Una cosa que vemos es que los rangos relevantes para la implementación de los contratos se superponen. Esto quiere decir que el proxy está delegando la lógica a múltiples implementaciones a la vez. Esta lógica también está en ERC-2535: Diamonds, Multi-Facet Proxy. La idea es que un contrato proxy dado (un “diamante”) puede que use la lógica en múltiples implementaciones de contrato (“facetas”), cada una es responsable por una parte separable de la lógica y establece las ranuras en el almacenamiento del proxy.

Actualmente, sólo indexamos pares de implementación proxy basados en los rangos de su bloque, es decir, la duración desde el primer bloque en el cual observamos un DELEGATECALL hasta el final. Para proporcionar la cobertura completa para Diamantes, también necesitamos grabar las ranuras relevantes para cada una. Estamos planeando esto pero aún no hemos empezado.

Diseños superpuestos

Un patrón común que vemos con los diseños proxy no vacíos, que es los diseños de los proxy es idéntico a la de la implementación (por ejemplo, PositionToken). Pensamos en añadir una concesión visual para que sea claro cuál capa en cada entrada en el mapa de almacenamiento unificado (y la tabla) de dónde viene. Ahora, en estos casos, parecen ser datos duplicados.

Un patrón similar es tener un diseño que contiene variables gap explícitos en las ranuras gobernadas por el diseño del otro contrato, como vemos con este proxy Spice Finance. Otra “característica” que descubrimos es que un contrato único puede asignar el mismo nombre a diferentes variables, mientras estén en diferentes ámbitos, así que puedes tener muchas de estas variables gap idénticas.

También encontramos algunos usos más creativos. EternalStorageProxy coloca una dirección para _upgradeabilityOwner en la ranura 0x00…000 a pesar que su implementación usa las mismas ranuras para un mapeo. Esta sobrecarga funciona porque el valor actualmente usado en la ranura del mapeo no tiene sentido; típicamente se deja como ceros, pero no hay una razón por la cual no pueda servir otro propósito. Hace un truco similar en las ranuras 0x00…03 a través de 0x00…05, excepto en estos casos el proxy y la implementación acuerdan que es un mapeo pero están en desacuerdo sobre qué mapeo es. Por supuesto, esto puede causar colisiones si cualquier clave es usada en ambos mapeos.

Image description

El uintStorage de una persona en el byteStorage de otra.

Variables profundamente anidadas

Este no es un apócrifo per se, ya que frecuentemente nos encontramos con ejemplos como estos pero, ¡nos gustan las variables profundamente anidadas! Revisa nuestra asignación en el permiso de Uniswap 2: ¡un mapeo triple anidado con una estructura de tres miembros al final del arcoíris! ¡Hermoso!

Image description

¡Anida estos mapeos!

Brindemos a los colegas de smlXL (y @banteg) quienes ayudaron con este post. Maldiciones a aquellos que siguen negando a priorizar las cuentas del mapeo de claves.

Este artículo es una traducción de Shane Auerbach, hecha por Héctor Botero. Puedes encontrar el artículo original aquí.
Sería genial escucharte en nuestro Discord, puedes contarnos tus ideas, comentarios, sugerencias y dejarnos saber lo que necesitas.
Si prefieres puedes escribirnos a @web3dev_es en Twitter.

Discussion (0)