Como crear NFTs en Cardano

En esta guía les vamos a mostrar cómo crear un NFT (Non Fungible Token) en en Cardano. Aquí incluimos todos los pasos, desde el proceso de acuñado (minting) hasta el envío a una billetera digital.

En esta guía les voy a mostrar cómo crear un NFT (Non Fungible Token) en en Cardano. Aquí incluimos todos los pasos, desde el proceso de acuñado (minting) hasta el envío a una billetera digital.

Requerimientos

Este tutorial asume lo siguiente:

  • Ya tienes acceso a un nodo de Cardano operacional.
  • Tienes algún conocimiento básico de cómo usar los comandos del cardano-cli, aunque vamos a ir paso a paso e incluir la mayor cantidad de explicación posible.
  • Tienes al menos 5 Ada en una billetera.

Si no tienes el nodo de Cardano instalado puedes seguir esta guía https://cardano-node-installation.stakepool247.eu/

Puedes encontrar todos los comandos que aquí mostramos en nuestro repo https://github.com/tango-crypto/create-nft-cli/blob/main/create-nft-cli.txt

Creación de llaves y direcciones

Primero debemos crear una dirección de pago y para eso necesitamos dos llaves así que vamos a generarlas con el siguiente comando:

cardano-cli address key-gen \
--verification-key-file payment.vkey \
--signing-key-file payment.skey

Luego generamos la dirección de pago:

cardano-cli address build \
--payment-verification-key-file payment.vkey \
--out-file payment.addr \
--mainnet

La dirección de pago es algo como esto. Puedes ver el contenido del fichero utilizando el comando cat:

cat payment.addr
addr1vyaen9j2c2tkqwa3np8leruh2ykcxn9q5prwjyktupm0d0cg2ymdc

Ahora vamos a verificar las UTXOs (Unspent Transaction Outputs) de la dirección que estamos utilizando:

cardano-cli query utxo --address $(cat payment.addr) --mainnet 

Lo cual debe mostrar lo siguiente:

TxHash                                 TxIx        Amount
-------------------------------------------------------------------------


Esto significa que no existe ninguna transacción en esa dirección. Ahora vamos a mandar 5 Ada a esa dirección y después ejecutamos el mismo comando y vamos a ver como ya tenemos una transacción registrada:

cardano-cli query utxo --address $(cat payment.addr) --mainnet 

La ejecución del comando debe mostrar algo similar a esto:

cardano-cli query utxo --address $(cat payment.addr) --mainnet 
                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
58b7d31015482e4aefa834c5ec4911bd6952ef86bec6d77689f9b7a6bf4e9305     0        5000000 lovelace

Yo hice una transferencia de 5 Ada a la dirección que habíamos visto por lo tanto debe mostrar 5000000 Lovelace. La cantidad siempre se muestra en Lovelace en donde 1 Ada = 1,000,000 Lovelace.

Para poder construir la transacción y calcular las comisiones necesarias para acuñar el token necesitamos los parámetros del protocolo. Para esto hacemos una consulta a la blockchain y lo guardamos en el fichero protocol.json:

cardano-cli query protocol-parameters \
--mainnet \
--out-file protocol.json

La Política

Las políticas son un factor fundamental que tenemos que tener en consideración para generar nuevos tokens. La política da la capacidad de crear o quemar tokens. Un token siempre esta definido por su nombre y su policy id.

Los tokens pueden ser creados o quemados solo por la persona que posee las llaves para la política.

Primero vamos a generar un par de llaves, vamos a necesitar dos llaves para crear la política así que vamos a generarlas.

cardano-cli address key-gen \
    --verification-key-file policy.vkey \
    --signing-key-file policy.skey

El comando anterior crea dos ficheros:

  • policy.vkey (la llave de verificación pública o public verification key)
  • policy.skey (la llave privada para la firma o private signing key)

Now you are able to obtain the hash of the public verification key:

Con esto podemos obtener el hash de la llave de verificación pública.

cardano-cli address key-hash --payment-verification-key-file policy.vkey 
86c4c595371738281d374fa4fa7180b0d3adf56a7eb9ea2d9cbab109

Vamos a consultar la mainnet para ver en que slot estamos:

cardano-cli query tip --mainnet
{
    "epoch": 267,
    "hash": "ac54780aa50aa6680c682851af87af6b0af7790fe96429d200ac4d60b3737b1c",
    "slot": 30106142,
    "block": 5750431
}

Con el hash y el número del slot podemos crear el policy.script, aquí vamos a indicar el tipo de firmado que se requiere para emitir el token, el hash de la llave de verificación pública y un tiempo para el cual la firma va a ser válida. En este caso la firma va a ser válida solo si se hace antes del slot 30106442, lo cual nos da aproximadamente 5 minutos para crear el NFT. Después de ese slot no se pueden crear mas tokens con esta política, incluso si tratamos de hacerlo con la misma llave.

cat policy.script
{
    "type": "all",
    "scripts": [
      {
        "keyHash": "86c4c595371738281d374fa4fa7180b0d3adf56a7eb9ea2d9cbab109",
        "type": "sig"
      },
      {
        "type": "before",
        "slot": 30106442
      }
    ]
}

Con el tipo sig estamos diciendo que solo se necesita la firma con la llave de verificación pública para emitir tokens nuevos.

Ahora vamos a extraer el policy ID de nuestro policy.script:

cardano-cli transaction policyid --script-file policy.script 
6596e958394d65d378086b7cdff00ff823d2463dd9d3f0739c73275b

Por el momento las billeteras oficiales de Cardano no brindan soporte para imágenes pero gracias a la comunidad tenemos herramientas como Pool.pm que nos brindan estas funcionalidades añadiendo metadatos en un formato especifico. Estos metadatos nos ayudan a mostrar imágenes, URLS y enlaces hacia la pagina del artista.

La estructura definida es la siguiente, esta permite la creación de multiples tokens utilizando varias políticas en una sola transacción.

{
    "721": {
      "<policy_id>": {
        "<asset_name>": {
          "name": "<name>",
          "image": "<uri>",
          "description": "<description>"
  
          "type": "<mime_type>",
          "src": "<uri>"
  
          <other properties>
        },
        ...
      },
      ...,
      "version": "1.0"
    }
}

Para más información visite https://github.com/Berry-Pool/NFT-Metadata-Standard-CIP/blob/main/CIP-NFTMetadataStandard.md

Tenemos que tener en cuenta que el nombre es sensible a o discrimina mayúsculas y minúsculas y tiene que ser el mismo que vamos a utilizar en la transacción que genera el token. Para la imagen vamos a usar el servicio IPFS (InterPlanetary File System) que nos brinda Pinanta. Aquí nos podemos crear una cuanta en pocos pasos y subir la imagen del NFT. Una vez subida esta va a generar un identificador de contenido (Content Identifier), lo cual no es más que un hash que identifica nuestro fichero en un sistema de ficheros distribuido. No nos tenemos que preocupar por los detalles internos de cómo funciona IPFS, solo tenemos que subir la imagen y copiar el hash en el fichero de metadatos.

Aqui vemos el contenido del fichero metadata.json que vamos a utilizar en esta guía, aquí podemos ver el policy ID al comienzo y la imagen apuntando al hash en IPFS:

cat metadata.json
{
    "721": {
        "fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7": {
          "TangoNFT": {
            "name": "Tango 0001",
            "image": "ipfs://QmY9gydScXwQA4wzhFo1vqVpDkgi7sP4gH5TFmz8GYPF98"
          }
    }
}

Las Transacciones

Cada transacción en Cardano requiere el pago de una comisión y esta esta determinada por el tamaño de lo que se quiere transmitir. Si enviamos mas bytes en los metadatos entonces vamos a pagar una mayor comisión.

Hacer una transacción en Cardano es un proceso de 4 pasos:

  1. Primero construimos la transacción sin comisiones, para esto tenemos que crear un nuevo fichero con extensión .raw que vamos a utilizar para calcular la comisión.
  2. Luego utilizamos este fichero junto con el de los parámetros del protocolo para calcular la comisión.
  3. Después creamos la transacción nuevamente pero esta ves incluyendo la comision correcta. Como lo voy a enviar a esa misma dirección la salida tiene que ser el número de Lovelace que tenemos en la UTXO de menos la comision.
  4. Y por último firmamos la transacción y la enviamos.

Veamos que tenemos que hacer en cada uno de estos pasos:

1. Construir la transacción con 0 en la comisión

En Cardano cada transacción tiene una o varias entradas y una o varias salidas. Es como los billetes en tu billetera cuando vas a pagar algo, si tienes que pagar 15 pesos puedes tomar un billete de 20 y que te devuelvan uno de 5. En este ejemplo la dirección de entrada y de salida va a ser la misma porque es vamos a tomar los Lovelace construir el NFT y volverlo a poner en la misma dirección.

Este es el comando para crear la transacción:

cardano-cli transaction build-raw \
  --fee 0 \
  --tx-in 58b7d31015482e4aefa834c5ec4911bd6952ef86bec6d77689f9b7a6bf4e9305#0 \
  --tx-out $(cat payment.addr)+5000000+"1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --mint="1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --metadata-json-file metadata.json \
  --out-file matx.raw

Vamos a ir parámetro a parámetro para entender bien la sintaxis:

  • --tx-in: hash de la transacción (TxHash, transaction hash) del UTXO de la dirección que vamos a usar para la transacción y el indice de la transacción and the (TxIx, transaction index). Esta es la sintaxis para el parámetro --tx-in:
--tx-in <TxHash>#<TxIx>

Como vimos anteriormente los los valores vienen del resultado del comando cardano-cli query utxo --address $(cat payment.addr) --mainnet

  • --tx-out: Aqui especificamos la dirección que va a recibir la transacción. En este caso el token va a ser enviado a la misma dirección origen en la que tenemos los fondos. Esta es la sintaxis para el parámetro --tx-out:
--tx-out <Bech32-encoded_source_address>+<lovelace amount>+"<token amount> <policy ID>.<TokenName>"

--mint: Define cuantos tokens van a ser creados o eliminados. La sintaxis es similar a --tx-out pero sin la dirección.

--mint "<token amount> <policy ID>.TokenName"
  • --mint: Cantidad de tokens que vamos a generar. En este caso vamos a generar solo 1.
  • --metadata-json-file: Ruta hacia el fichero metadata.json que vamos a agregar a la transacción.
  • --out-file: La transacción se salva a un fichero y en este caso lo vamos a nombrar matx.raw.

2. Calcular la comisión mínima

Con la transacción en el fichero el próximo paso es calcular la comisión:

cardano-cli transaction calculate-min-fee \
--tx-body-file matx.raw \
--tx-in-count 1 \
--tx-out-count 1 \
--witness-count 2 \
--mainnet \
--protocol-params-file protocol.json
187809 Lovelace

El cálculo necesita la transacción en formato .raw, el número de entradas de la transacción, el número de salidas de la transacción, el numero de llaves necesarias para firmar la transacción y los parámetros de la red.

3. Construir la transacción de nuevo pero esta ves incluyendo la comisión

Para calcular la cantidad de Lovelace restantes en la dirección restamos la comisión de la cantidad actual.

expr 5000000 - 187809
4812191

Esta transacción incluye la comision, consumimos los 5000000 Lovelace de la UTXO y obtenemos en retorno una nueva UTXO con 4812191 Lovelace debido a la comision de la transacción.

Es importante incluir el parámetro the --invalid-hereafter con el mismo valor en el slot que definimos en el policy.script:

cardano-cli transaction build-raw \
  --mary-era \
  --fee 187809 \
  --tx-in 58b7d31015482e4aefa834c5ec4911bd6952ef86bec6d77689f9b7a6bf4e9305#0 \
  --tx-out $(cat payment.addr)+4812191+"1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --mint="1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --metadata-json-file metadata.json \
  --invalid-hereafter=30106442\
  --out-file matx.raw

4. Firmar la transaction y enviarla a la blockchain

Para probar que una política es auténtica y que el autor es realmente quien dice ser, las transacción tiene que ser firmadas por las llaves privadas. Necesitamos la llave privada que controla la dirección que contiene los fondos (input UTXO), y la llave privada que controla la política de acuñado (minting). En este caso payment.skey es la llave que permite gastar los fondos y policy.skey es la que corresponde con el valor del hash que pusimos en el policy.script.

cardano-cli transaction sign \
  --signing-key-file payment.skey \
  --signing-key-file policy.skey \
  --script-file policy.script \
  --mainnet \
  --tx-body-file matx.raw \
  --out-file matx.signed

Nota: La transacción firmada se guarda en el fichero matx.signed.

Finalmente enviamos la transacción que básicamente generar el token y si no devuelve nada eso significa que se ejecuto correctamente, de lo contrario mostraría un error.

cardano-cli transaction submit --tx-file  matx.signed --mainnet

Si volvemos a mostrar lo que hay en la dirección vamos a ver el token .

cardano-cli query utxo --address $(cat payment.addr) --mainnet 
                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
4be19689d92e95087f29cd325388b1dcf084134a567b274f102b8e64373d4a08     0        4812191 lovelace + 1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT

5. Verificar que todo funcionó correctamente

Si vamos a https://pool.pm/tokens vamos a ver el NFT,en este caso el link es el siguiente:

https://pool.pm/fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT

Y ya esta creamos un NFT 😊.

Enviar el NFT a otra billetera

Para enviar el NFT a una billetera necesitamos crear la transacción con 0 comisión.

cardano-cli transaction build-raw \
  --mary-era \
  --fee 0 \
  --tx-in 4be19689d92e95087f29cd325388b1dcf084134a567b274f102b8e64373d4a08#0 \
  --tx-out addr1q9e56ctpw0580099eepjwr7zzylv9fr58drncp6vrq8jnhc0qrxl65dfu4tlsjnt434y9n4np4erdxrv7jtru2kc0xvqfveu50+0+"1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --tx-out $(cat payment.addr)+4812191 \
  --invalid-hereafter 0 \
  --out-file sendFTtx.raw

Calculamos la comisión mínima.

  cardano-cli transaction calculate-min-fee \
  --tx-body-file sendFTtx.raw \
  --tx-in-count 1 \
  --tx-out-count 2 \
  --witness-count 1 \
  --mainnet \
  --protocol-params-file protocol.json

  177249 Lovelace

En este caso la comision mínima fue de 177249 Lovelace.

No es posible crear transacciones que solo contengan este tipo de tokens, es necesario enviarlos junto con una cantidad mínima de Ada. Para el caso de 1 policy ID con un nombre de menos de 32 caracteres el mínimo a enviar es de 1555554 Lovelace. Para mas detalles de como calcular esto les dejo este artículo https://github.com/input-output-hk/cardano-ledger-specs/blob/master/doc/explanations/min-utxo.rst

Nuestra dirección tiene 4812191 Lovelace y necesitamos construir la transacción con la cantidad de Lovelace que recibimos de vuelta (el cambio). Por lo tanto restamos lo que tenemos en el UTXO, menos la comisión, menos la cantidad minima de ada requerida a enviar en una transacción:

expr 4812191 - 177249 - 1555554
3079388

Ahora veamos el slot actual y le añadimos un tiempo extra:

cardano-cli query tip --mainnet
  {
      "epoch": 267,
      "hash": "b0aa033d25918da905c5e79453bd6f1bc2db067b7ae7116f7b94fa438bf20820",
      "slot": 30356205,
      "block": 5762935
  }

  expr 30356205 + 12000
  30368205

Luego construimos la transacción con los parámetros que calculamos:

  cardano-cli transaction build-raw \
  --mary-era \
  --fee 177249 \
  --tx-in 4be19689d92e95087f29cd325388b1dcf084134a567b274f102b8e64373d4a08#0 \
  --tx-out addr1q9e56ctpw0580099eepjwr7zzylv9fr58drncp6vrq8jnhc0qrxl65dfu4tlsjnt434y9n4np4erdxrv7jtru2kc0xvqfveu50+1555554+"1 fcd7249ddd5bae5d98351bedd56eca999541589c872ef20b77d04ad7.TangoNFT" \
  --tx-out $(cat payment.addr)+3079388 \
  --invalid-hereafter 30368205 \
  --out-file sendFTtx.raw

Firmamos la transacción con la llave privada:

  cardano-cli transaction sign \
  --tx-body-file sendFTtx.raw \
  --signing-key-file payment.skey \
  --mainnet \
  --out-file sendFTtx.signed

Y finalmente enviamos la transacción:

cardano-cli transaction submit --tx-file  sendFTtx.signed --mainnet

Si verificamos la UTXO de la dirección con la cual creamos el NFT podemos ver que ya no esta ahi.

cardano-cli query utxo --address $(cat payment.addr) --mainnet 
                           TxHash                               TxIx   Amount
--------------------------------------------------------------------------------------
fab57c1a89050e4469c2aaf5dc22ae1413a6551d9a8781b879a497a1d296781a  1    3079388 lovelace

Podemos verificar la transacción en el Cardano Explorer

Y finalmente comprobar que fue recibido en la billetera Daedalus.

Listo, creamos un NFT y lo enviamos a nuestra billetera. Esperamos que les haya gustado este tutorial y si tienen alguna duda no tengan pena en contactarnos que los vamos a ayudar en lo que necesiten.