Consejos de pacto: contratos de envoltura. En este artículo, aprenda sobre “envoltorio… de Stuart Popejoy Kadena Julio de 2021

En este artículo, aprenda sobre el uso de "contratos de envoltura", contratos inteligentes que llaman a otros contratos inteligentes, para ofrecer nuevas funciones.

Nota rápida: "Envolver" aquí es una forma de indirección al llamar a un contrato inteligente. No se refiere a "envolver tokens" a través del puente, que es un proceso completamente separado.

Las cadenas de bloques de contratos inteligentes son entornos de aplicaciones únicos, ya que combinan escaparates, backends y API en un entorno dinámico. Un solo dapp puede estar compuesto por numerosos contratos: un intercambio de NFT puede tener contratos separados para manejar la subasta frente a ofrecer un token, por ejemplo.

Dado que las cadenas de bloques son sistemas abiertos, todos estos contratos están a la vista para que cualquiera los inspeccione, y aunque algunas funciones son "privadas" (no se pueden llamar desde el exterior, como en Solidity, o están protegidas con una capacidad, en Pact), la mayoría de las funciones pueden ser llamado por cualquiera.

Esta es la razón por la que, por ejemplo, Kaddex o Uniswap pueden tener numerosas aplicaciones de front-end no oficiales: el negocio real de DEX ocurre dentro del contrato inteligente, mientras que el front-end es solo Chrome y la integración de billetera.

También puede hacer esto en la cadena de bloques: puede escribir un contrato inteligente "envoltorio" que tenga las mismas firmas de funciones o similares a las de un contrato inteligente existente. El contrato inteligente de envoltura llama a las funciones correspondientes en las funciones de contrato inteligente envuelto, antes o después de la funcionalidad que ofrece algún servicio adicional.

Para ver cómo se ve esto, consideremos un ejemplo concreto de un contrato inteligente sentado frente al relé de cadena de Kadena. El relé ofrece unión de tamaño fijo a 50.000 KDA. Un contrato de envoltura podría permitir que varias cuentas formen un vínculo único y repartan las recompensas en consecuencia.

Veamos cómo funcionaría un contrato de envoltura, comenzando con la creación de bonos. Crear un nuevo vínculo a nivel de contrato inteligente significa llamar a la función relay.pool.new-bond (el código se puede ver aquí):

(defun nuevo enlace: cadena
(grupo: cadena ;; nombre del grupo de enlaces
cuenta: cadena ;; Cuenta KDA
guard: guard ;; guardia de administración de bonos
)

El caso "normal" es que la cuenta es una cuenta KDA de un solo firmante, y la protección es la clave de esa cuenta. La función devuelve el nombre del nuevo bono, que se utiliza más tarde para dar servicio al bono.

Para nuestro ejemplo, queremos que un contrato inteligente esté a cargo, y "cerrar" la llamada a un nuevo vínculo es la forma más sencilla de hacerlo.

El cierre de la llamada a new-bond se realiza fácilmente en nuestro contrato de envoltura.

(defun wrap-new-bond: cadena
(grupo: cadena ;; nombre del grupo de enlaces
cuenta: cadena ;; Cuenta KDA
guard: guard ;; guardia de administración de bonos
)
(protector de cuenta del fondo común relay.pool.new-bond)
)

Por supuesto, esto no es muy interesante en sí mismo, pero ilustra el mecanismo: no hay nada que impida que otro contrato emita la llamada relay.pool.new-bond en lugar de aceptarla directamente desde una aplicación de front-end. Aquí hace exactamente lo que haría una llamada directa: proporciona los argumentos sin cambios y devuelve el nombre del enlace.

Lo primero que queremos hacer es darle al contrato de contenedor control sobre la administración de bonos, lo que se logra fácilmente con un módulo de protección:

(defun wrap-new-bond: cadena
(grupo: cadena ;; nombre del grupo de enlaces
cuenta: cadena ;; Cuenta KDA
guard: guard ;; guardia de administración de bonos
)
(cuenta del fondo común de relay.pool.new-bond
(create-module-guard "bond-wrapper"))
)

Esto ahora significa que el protector del módulo llamado "bond-wrapper" administra el enlace: ahora, solo las llamadas desde el módulo que define wrap-new-bond podrán invocar cosas como renovar-enlace y otras operaciones de servicio de bonos. Hemos logrado un control autónomo sobre el servicio de bonos.

Esto significa que ahora también tenemos que envolver las otras llamadas, a fin de aprovechar la protección del módulo para la administración. Lo veremos más adelante en el artículo.

El siguiente paso es modelar realmente el multibond en sí. Un enfoque simple simplemente recopila las cuentas y los tamaños de los tramos en un objeto "múltiple":

(tramo defschema
cuenta: cadena ;; Cuenta KDA
cantidad: decimal ;; monto del tramo
) (defschema multi
tamaño: decimal ;; tamaño de enlace
tramos: (objeto {tramo})) ;; tramos (deftable multis: {multi}) ;; almacenado por varias cuentas de KDA

¡Hecho! Un tramo combina una cuenta KDA con un monto de tramo. Un múltiplo simplemente recopila tramos que se suman al tamaño del bono. La tabla multis almacena múltiples registros bajo un nombre de cuenta KDA único junto con el tamaño del enlace.

¡Estamos listos para crear un nuevo multibond!

Los pasos necesarios para utilizar el multibond son:

Transfiera el dinero del tramo a una nueva cuenta de KDA. Este nombre de cuenta servirá como ID de enlace múltiple. Almacene el registro de enlace múltiple para recuperarlo más tarde. Cree el enlace utilizando la nueva cuenta de KDA y el módulo de protección. (Defun new-multibond: string
(multi: object {multi} ;; tramos múltiples
cuenta: cadena ;; Cuenta KDA para identificación múltiple / múltiple
) ;; débito de cada tramo
(mapa (débito-tramo (cuenta)) (en 'tramos múltiples)) ;; almacenar el multi
(insertar cuenta multis multi) ;; permitir la transferencia autónoma al banco de retransmisión
(capacidad de instalación
(coin.TRANSFER cuenta "relé-banco" (en 'tamaño multi))) ;; crear el vínculo
(cuenta relay.pool.new-bond "kda-relay-pool"
(create-module-guard "multibond"))
)

La función new-multibond toma los tramos de varios objetos y un nombre de cuenta KDA que actúa como la cuenta de almacenamiento para el bono inicial y las recompensas futuras, así como la identificación del multibond en sí.

El primer paso es transferir los montos del tramo a la nueva cuenta compartida. La función debit-tramo se llama en cada tramo usando map. Esta es una función simple que solo llama a coin.transfer-create:

(defun débito-tramo (cuenta: cadena tramo: objeto {tramo})
(coin.transfer-create
(en 'tramo de cuenta)
cuenta
(crear-módulo-guardia "tramo")
(en 'tramo de cantidad))
)

Tenga en cuenta que la cuenta KDA compartida también está protegida de forma autónoma por el módulo mediante el "tranche" create-module-guard. Esto significa que solo este código de módulo puede debitar de la nueva cuenta.

Luego almacenamos el multibond por cuenta. Esto es sencillo.

(insertar cuenta multis multi)

El tercer paso configura la creación de bonos instalando la capacidad de TRANSFERENCIA. Así es como se gestiona la propiedad autónoma. Normalmente, en una transferencia, el usuario agrega la capacidad de TRANSFERENCIA a sus firmas. Dado que se trata de un contrato inteligente, simplemente "instala" la capacidad.

(capacidad de instalación
(coin.TRANSFER cuenta "relé-banco" (en 'tamaño múltiple)))

¡Estamos listos para crear el vínculo!

(cuenta de relay.pool.new-bond relay.relay.POOL
(create-module-guard "multibond"))

Consideremos lo que está sucediendo aquí. El relevo intentará transferir el monto del bono de la cuenta al banco de relevo. Por lo tanto, si las transferencias del tramo anterior no fueran suficientes, la transacción fallará, que es lo que queremos. Hablaremos de otro caso de error (los tramos suman demasiado) más adelante.

¡Eso es! Agregamos una funcionalidad significativa a la vinculación con solo unas pocas líneas de código.

La administración del bono requiere la asignación de recompensas. Echemos un vistazo a cómo podría funcionar:

(defun renovar-multibond: cadena (cuenta: cadena)

;; rastrear el antiguo equilibrio
(dejar ((saldo antiguo (moneda.obtener-saldo cuenta))
(multi (leer cuenta multis)))

;; renovar, acreditará la cuenta
(cuenta de relay.pool.renew)

;; calcular nueva cantidad
(dejar ((monto (- (moneda.obtener-saldo cuenta) antiguo-balance)))

;; asignar
(mapa
(asignar el monto de la cuenta (en 'tamaño múltiple))
(en 'tramos múltiples))))
)

Es realmente bastante simple. Verificamos el saldo de la cuenta KDA multibond para calcular el aumento de las recompensas. Empaquetamos relay.pool.renew para dar servicio al enlace, que tendrá éxito porque este módulo lo administra de forma autónoma. Calculamos la nueva cantidad usando el saldo actual y el saldo anterior.

La asignación es la única parte complicada, pero armados con nuestros tramos, el tamaño del bono y la nueva cantidad, podemos simplemente mapear los tramos para realizar la asignación.

(defun asignar
(cuenta: cadena ;; cuenta múltiple
cantidad: decimal ;; monto total a asignar
tamaño: decimal ;; tamaño de enlace
tramo: objeto {tramo} ;; tramo
)
(dejar ((a (en 'tramo de cuenta))

;; calcular el monto del tramo
(monto del tramo (* monto (/ (al 'monto del tramo de monto) tamaño))))

(capacidad de instalación
(moneda.TRANSFER cuenta al monto del tramo))
(cuenta de transferencia de moneda al monto del tramo))
)

El código principal aquí calcula el monto del tramo multiplicando el monto total de la recompensa por la proporción del tramo. Luego instala la capacidad de TRANSFERENCIA autónoma y la asigna a la cuenta de origen.

Esto no cubre todo. unbond aún necesita ser envuelto, además las actividades de retransmisión proponen, endosan y otras deberán ser envueltas como un simple paso a través para que el módulo pueda proporcionar la autorización simplemente llamando al código envuelto.

También hay casos de error que deben manejarse:

Si los tramos suman demasiado. Esto resultará en monedas perdidas, si el tamaño no coincide con la suma de los tramos. Esto dará como resultado errores en la vinculación y la asignación, por lo que es mejor UX probar esto explícitamente.

Finalmente, hay características para agregar:

Reutilizando tramos multibond para más de un enlace. Los enlaces de seguimiento por multibond.renew y unbond están actualmente desprotegidos, por lo que mientras el relé asegura que estos no harán ningún daño, alguien podría afligir a los multibond, p. Ej. terminando su vínculo.

Finalmente, vale la pena señalar que la llamada de nuevo bono será una transacción multi-sig porque todo se hace en un solo paso, por lo que todos los propietarios de tramos deben firmar la misma transacción. Probablemente sería más fácil desde una perspectiva de billetera inicial cobrar los montos de los tramos de antemano y luego hacer el bono.

Con suerte, esto muestra el poder de los contratos de envoltura como una forma de ofrecer nuevos servicios a los usuarios de blockchain. Pact hace que estos contratos sean fáciles y seguros. De hecho, es mucho más seguro que Solidity, ya que realmente no se puede equivocar el contrato de destino, ya que el contenedor no se cargará si hay un error tipográfico. En Solidity, será mejor que obtenga la dirección del contrato de destino correcta o se terminará el juego, y no puede decirle si sus llamadas realmente tendrán éxito. Pact es un cambio de juego para los contratos inteligentes, ¡así que comience con su dapp Pact en Kadena blockchain y experimente el poder por usted mismo!