Marlowe integrado en JavaScript
Marlowe está escrito como un tipo de datos Haskell, y por lo tanto es sencillo generar contratos inteligentes Marlowe utilizando Haskell. Sin embargo, también proporcionamos una biblioteca escrita en TypeScript que puede utilizarse para generar contratos inteligentes Marlowe desde TypeScript o JavaScript de forma similar a como se haría utilizando Haskell. Si no estás familiarizado con TypeScript, también puedes utilizar la API como si estuviera escrita en JavaScript ya que TypeScript es un superconjunto de JavaScript.
Puedes probar la biblioteca en línea seleccionando la pestaña JS Editor en el Marlowe Playground. En la parte superior de la pestaña puedes encontrar varios ejemplos que ilustran el uso de la biblioteca.
Uso de la pestaña JS Editor
La biblioteca JavaScript/TypeScript proporciona definiciones de constantes para las construcciones Marlowe que no tienen argumentos, como es el caso de SlotIntervalEnd:
o el contrato Close :
Las construcciones que tienen argumentos se representan como funciones, como en el caso de AvailableMoney:
Puedes ver las declaraciones de tipo para cada una de las construcciones y tipos pasando el ratón por encima del identificador de la declaración de importación al principio del archivo que aparece en el editor de la pestaña JS Editor. Tanto la declaración de importación como la declaración de la función están en gris para señalar que no deben ser modificadas, el código que genera el contrato debe ser escrito dentro del cuerpo de la función proporcionada, y el contrato resultante debe ser devuelto como resultado de la función (usando la instrucción return ).
Internamente, las funciones y constantes de la biblioteca JavaScript/TypeScript devuelven una representación JSON de las construcciones Marlowe. Por ejemplo, la función AvailableMoney se define del siguiente modo:
Al hacer clic en el botón Compile de la pestaña JS de Marlowe Playground, se ejecuta el código en el cuerpo de la pestaña, y el objeto JSON devuelto por la función durante la ejecución se transforma en un contrato real de Marlowe que puede enviarse a la pestaña Simulation donde puede simularse.
La implementación de la biblioteca en sí es bastante sencilla, puedes encontrar todo su código fuente aquí. En principio podrías escribir código JavaScript que produzca la representación JSON de Marlowe directamente, pero no deberás preocuparte por JSON en absoluto cuando uses la biblioteca JS.
Cuando usas la biblioteca JS Marlowe, y tu uso de las funciones y constantes de la biblioteca comprueba el tipo, entonces el resultado de tu código debe producir una representación JSON válida de un contrato Marlowe, por lo que garantizamos la seguridad de la generación de contratos a través del sistema de tipos de TypeScript.
El tipo SomeNumber
Hay un tipo importante que no está presente en la definición de Haskell de Marlowe, hemos llamado a ese tipo SomeNumber, y está definido de la siguiente manera:
La razón por la que tenemos este tipo es que el tipo nativo para números en JavaScript y TypeScript pierde precisión cuando se utiliza con números enteros grandes. Esto se debe a que su implementación se basa en números de punto flotante.
La siguiente expresión es válida en JavaScript:
Esto puede ser problemático para los contratos financieros, ya que en última instancia podría resultar en la pérdida de dinero.
Por lo tanto, recomendamos el uso del tipo bigint . Pero admitimos tres formas de representar los números por comodidad y retrocompatibilidad con versiones antiguas de JS:
- Números nativos:
- Son fáciles de usar
- La notación es muy sencilla y puede utilizarse con operadores estándar, por ejemplo: 32 + 57
- Pierden precisión para grandes cantidades
- Representación en strings:
- La notación sólo requiere añadir comillas alrededor de los números
- No se pueden utilizar directamente los operadores estándar, por ejemplo: "32" + "57" = "3257"
- No pierden precisión
- Los tipos bigint :
- Su uso es sencillo (sólo hay que añadir n tras los literales numéricos)
- La notación es muy sencilla y puede utilizarse con operadores estándar, por ejemplo: 32n + 57n
- No pierden precisión
Todas estas representaciones se convierten en BigNumber internamente, pero puede producirse una pérdida de precisión si se utilizan números nativos, ya que el BigNumber se construye, antes de que se produzca la conversión, y la API no puede hacer nada al respecto.
El tipo EValue y la sobrecarga de booleanos
En Haskell, las observaciones booleanas constantes se representan con TrueObs y FalseObs, y los valores enteros constantes se representan con Constant seguido por un Integer. En JavaScript y TypeScript también se pueden utilizar estos constructores, pero no es necesario, porque el tipo Observation está sobrecargado para aceptar también los booleanos nativos de JavaScript, y las funciones que en Haskell toman un Value, en JavaScript toman un EValue en su lugar, y el EValue se define de la siguiente manera:
Ejemplo: Redacción de un contrato de Swap en TypeScript
Tanto si empezamos modificando un ejemplo existente, como si creamos un nuevo contrato de JavaScript, se nos proporciona automáticamente la lista de importaciones y la declaración de la función. Podemos empezar borrando todo lo que no esté en gris, y empezar a escribir dentro de los corchetes de la definición de la función proporcionada.
Supongamos que queremos redactar un contrato para que Alice pueda intercambiar 1000 Ada con Bob por $100.
Primero vamos a calcular las cantidades con las que queremos trabajar de cada unidad, podemos definir algunas constantes numéricas utilizando const:
El importe del contrato debe escribirse en Lovelace, que es 0,000001 Ada. Así que calculamos la cantidad de Lovelace multiplicando los 1.000 Ada por 1.000.000. La cantidad de dólares es 100 en nuestro ejemplo.
La API ya proporciona un constructor para la moneda ADA, y actualmente no hay un símbolo de moneda en Cardano para los dólares, pero imaginemos que lo hay, y definámoslo así:
El string "85bb65" correspondería en realidad al símbolo de la moneda, que es un hash y debe escribirse en base16 (representación hexadecimal de un byte string). Y el string "dollar" correspondería al nombre del token.
Definamos ahora un tipo de objeto para contener la información sobre las partes y lo que quieren intercambiar, por conveniencia:
Almacenaremos el nombre de la parte en el campo party, el nombre de la moneda en el campo currency, y el importe de la moneda que la parte quiere cambiar en el campo amount:
Ahora estamos listos para empezar a escribir nuestro contrato. Primero vamos a definir los depósitos. Tomamos la información de la parte que debe hacer el depósito, el número de slot hasta el que esperaremos a que se haga el depósito, y el contrato de continuación que se ejecutará si el depósito es exitoso.
Sólo necesitamos una construcción When con un único Case que representa un Deposit de la parte src en su propia cuenta, de esta forma si abortamos el contrato antes del swap cada parte recuperará lo depositado.
A continuación definimos uno de los dos pagos del swap. Tomamos como parámetros las partes de origen y destino, así como el contrato de continuación que se ejecutará después del pago.
Para ello, sólo tenemos que utilizar la construcción Pay para pagar desde la cuenta en la que la parte de origen hizo el depósito a la parte de destino.
Finalmente podemos combinar todas las piezas:
El contrato consta de cuatro etapas:
- Alice puede depositar hasta el slot 10
- Bob puede depositar hasta el slot 20 (si no, Alice recibe un reembolso y el contrato se cancela)
- Luego pagamos el depósito de Alice a Bob
- Pagamos el depósito de Bob a Alice.
Y eso es todo. Puedes encontrar el código fuente completo del contrato inteligente de intercambio en los ejemplos del Marlowe Playground, que vemos a continuación.
© Copyright 2020, IOHK Revision 34c4781c.
Encuentra una copia oficial de este documento aquí:
https://alpha.marlowe.iohkdev.io/doc/marlowe/tutorials/javascript-embedding.html
https://docs.cardano.org/projects/plutus/en/latest/marlowe/tutorials/javascript-embedding.html
Más traducciones de Cardano en: Cardano For The World