Un Proxy Mínimo en Campo Hostil

Moisés Sosa
Origin Protocol Spanish
5 min readJun 19, 2021

La reciente carga de la red en Ethereum ha provocado que los equipos, tanto grandes como pequeños, reconsideren y optimicen su solidez y la arquitectura de implementación de contratos. El equipo de Origin Protocol no es una excepción y ha estado trabajando para optimizar nuestra base de código existente para la eficiencia y el costo en nuestros productos OUSD y el Launchpad. Un ejemplo reciente es el uso de “Minimal Proxy(s)” en la interfaz de depósito de nuestro producto “Launchpad de NFTs”.

Gracias a Daniel Von Fange y Yu Pan por la implementación inicial de Origin y la provisión de recursos para este artículo.

Este es un artículo bastante técnico, por lo que si los patrones de diseño y el código de Solidity le hacen llorar, entendemos si desea omitir este…

Contrato de Proxy Mínimo

Cuando la mayoría de los desarrolladores de blockchain escuchan el término proxy, inmediatamente piensan en un patrón de diseño para la capacidad de actualización. Es decir, el usuario final interactúa con ProxyOrigin, que envía sus llamadas a ContractOriginAlpha. Esto permite a los desarrolladores de Origin intercambiar ContractOriginAlpha con ContractOriginBeta simplemente actualizando una dirección en ProxyOrigin.

El patrón anterior NO ES lo que implica un contrato de “Minimal Proxy”. Un Minimal Proxy se usa normalmente cuando su proyecto necesita implementar varios contratos grandes cuyo código es más o menos el mismo pero requieren una inicialización diferente. La implementación del proxy mínimo le permite implementar un contrato independiente extremadamente pequeño que hereda toda la lógica de un contrato implementado más grande. La introducción de EIP-1167 y la integración del estándar en OpenZeppelin ha permitido a los desarrolladores aprovechar esta poderosa herramienta.

Probablemente, la implementación de EIP-1167 más utilizada es la de Uniswap V1 en la creación de sus grupos de AMM. Existe un único contrato de AMM que gestiona la distribución de dos activos, pero la magia ocurre cuando se crea un nuevo grupo. Uniswap V1 crea un proxy mínimo para el contrato de AMM subyacente cada vez que se agrega un nuevo par. Podemos observar este onchain mirando el código subyacente de una dirección de grupo AMM como el grupo de liquidez SAI/WETH. Si observa el código del contrato, hay una pequeña cadena de código de bytes:

0x3660006000376110006000366000732157a7894439191e520825fe9399ab8655e0f7085af41558576110006000f3

Bastante bien, ¿eh?

La implementación del proxy mínimo le permite implementar un contrato independiente extremadamente pequeño que hereda toda la lógica de un contrato implementado más grande.

En el caso de Origin, teníamos un contrato de tamaño decente para recibir depósitos de usuarios, pero no queríamos implementar ese contrato para cada usuario que depositaba en nuestra plataforma. El uso correcto de un proxy mínimo puede resultar en una reducción sustancial de los costos de implementación y menos mantenimiento del contrato en la cadena.

Un ejemplo simple

La implementación de Origin está más allá del alcance de este artículo, pero hemos preparado un ejemplo simple para que se encamine hacia el minimalismo.

Nuestro proyecto de ejemplo será un AMM ficticio simple que utiliza un contrato de fábrica para crear contratos de par a través del patrón “Minimal Proxy”.

Spoiler: es más barato. MUCHO más barato…

Configurar

#Clone example project
> git clone git@github.com:OriginProtocol/minimal-proxy-example.git
#Install dependencies
> cd minimal-proxy-example && yarn
# Verify
> npx hardhat test

Las pruebas deberían pasar y dar como resultado algo como esto

Minimal Proxy | EIP-1167
✓ Should deploy master Pair contract (654ms)
✓ Should deploy PairFactory contract (65ms)
✓ Should deploy a cloned Pair contract and allow initialization of custom pair info (212ms)
✓ Minimal Proxy deployment should cost less than a standard deployment
4 passing (937ms)

Cavando más profundo

Nuestra aplicación de ejemplo realmente consta de dos contratos, el PairFactory y el Pair. En un AMM descentralizado, cualquiera debería poder crear un par de tokens con el que comerciar si aún no existe. Ese es el propósito principal de PairFactory: permitir la creación de una instancia de Pair. El contrato de pair en nuestra aplicación se puede considerar como una biblioteca implementada o un marcador de posición para los muchos pares que queremos implementar. Repasemos la prueba para comprender mejor.

test.js

https://gist.githubusercontent.com/cipherzzz/31a49f435f99a494e7b9837223b729b8/raw/4308fb292fb57b639d843b18ac43297d79206e0f/test.js

En la prueba anterior, implementamos el contrato PairFactory y Pair en las líneas 23 y 29. Ahora que están implementados, estamos en un buen lugar para comenzar la implementación de nuestro clon de Proxy Mínimo.

Implementación del proxy mínimo

Observe en la línea 38 de la prueba que obtenemos la dirección esperada del contrato implementado usando el método PairFactory.getPairAddress y un salt. La biblioteca OpenZeppelin Clone nos permite obtener la dirección esperada de forma determinista. También podemos implementar el contrato de par de proxy mínimo usando la misma sal que se ve en la línea 41. Tenga en cuenta que la dirección implementada y la dirección precargada son las mismas en la línea 53.

PairFactory.sol

https://gist.githubusercontent.com/cipherzzz/17e303d85a9fef97b03e6aa2ca0c2163/raw/87d72fe89ef1c48706e112d10e711aa2137d8364/PairFactory.js

Los métodos mágicos subyacentes que proporcionan la búsqueda y clonación de direcciones deterministas son posibles gracias a la importación en la línea 3

import “@openzeppelin/contracts/proxy/Clones.sol”;

La línea 15 esencialmente decora el objeto de address con las funciones disponibles en Clones.sol habilitando las funciones predictDeterministicAddress y cloneDeterministic que usamos en las líneas 23 y 27 respectivamente.

Pair.sol

https://gist.githubusercontent.com/cipherzzz/a78a2491b78d2dc2feddb4278c13985e/raw/79dbf7aad9be524cf275f1672b2737fa7cb1e851/Pair.sol

El contrato Pair.sol es el contrato master del que derivarán la funcionalidad todos los Proxies Mínimos. Tenga en cuenta que una vez que se ha implementado el proxy, se puede inicializar y mantener su propio almacenamiento separado del del contrato master. Esto permite que PairFactory implemente de forma económica una gran cantidad de instancias de Pair.

$$$

Felicidades por haber llegado tan lejos. A estas alturas, debería tener una buena idea de cómo usar el patrón Minimal Proxy en sus aplicaciones para administrar el costo y el mantenimiento de sus dApps. ¿Descubrió la afirmación en test.js donde comparamos el costo de una implementación típica del contrato de pair con una implementación de proxy mínimo? Está debajo y en la línea 70 del archivo de prueba.

expect(Number(pairStandaloneGas)).to.be.greaterThan(Number(pairProxyGas *10)

Lo leyó bien, el patrón de proxy mínimo es al menos 10 veces más eficiente en gas que un despliegue de contrato típico en este caso. Las cifras reales de gas para la implementación de proxy estándar frente a la mínima son 710,453 y 64,995, respectivamente.

Conclusión

El Proxy mínimo es un gran patrón para agregar a su kit de herramientas como desarrollador de blockchain y puede tener grandes beneficios cuando se aplica correctamente. El equipo de Origin Protocol se compromete a proporcionar los productos más seguros, eficaces y rentables del mercado. La comunidad es una gran parte del espíritu de Origin y nos encantan las contribuciones de nuestra comunidad de código abierto. Aquí hay un par de sugerencias sobre por dónde empezar.

PD. De hecho, también contratamos a colaboradores de nuestra comunidad, así que si estás interesado en trabajar en Origin, ¡envía un PR a nosotros!

Recursos

Aprende más sobre Origin:

--

--