paint-brush
🌈🦄 Creando tu propia DApp Art NFT generada por IA con Bacalhaupor@developerally
2,428 lecturas
2,428 lecturas

🌈🦄 Creando tu propia DApp Art NFT generada por IA con Bacalhau

por DeveloperAlly29m2023/02/08
Read on Terminal Reader

Demasiado Largo; Para Leer

Una guía completa para crear, ejecutar e implementar una DApp con su propio script de texto a imagen para crear NFT artísticos generados por IA en FVM Hyperspace Testnet. Este blog lo guiará a través de cómo crear un script de texto a imagen basado en Python de código abierto basado en Tensorflow. Elegí deliberadamente usar tanta tecnología de código abierto y descentralizada como esté disponible en esta pila.
featured image - 🌈🦄 Creando tu propia DApp Art NFT generada por IA con Bacalhau
DeveloperAlly HackerNoon profile picture
0-item

¡Una guía completa para crear, ejecutar e implementar una DApp con su propio script de texto a imagen para crear NFT artísticos generados por IA en FVM Hyperspace Testnet!


Tabla de contenido

  • 👩‍💻 Lo que haremos...
  • 🏛 Diagrama de arquitectura (más o menos)
  • 🥞 Pila de tecnología DApp
  • 🏗️ Creación del script de texto a imagen de Python
  • ⚒️ Creación e implementación del script NFT de Solidity
  • 🎬 Construyendo las interacciones front-end
    • Flujo completo
    • Interacciones de Bacalhau
    • NFT.Almacenamiento
    • Interacciones de contrato
  • 🌟 Reflexiones finales: posibilidades de IA y Blockchain
  • 🐠 La hoja de ruta de Bacalhau
  • ✍️ ¡Mantente en contacto!


🦄 Enlaces rápidos:


👩‍💻 Lo que haremos...

Este blog lo guiará a través de cómo


  1. Cree un script de texto a imagen basado en python de código abierto basado en Tensorflow (también puede usar el punto final HTTP de Bacalhau si no le interesa)

  2. Ejecute este script en Bacalhau (una plataforma de cómputo fuera de cadena p2p abierta)

  3. Cree un contrato NFT en Solidity (basado en un contrato Open Zeppelin ERC721)

  4. Implemente el contrato NFT en la red de prueba hiperespacial de Filecoin Virtual Machine (FVM) con Hardhat

  5. Interacciones front-end: cómo interactuar con el script de texto a imagen de Bacalhau y su contrato NFT en React

  6. Cómo guardar sus metadatos NFT en NFT.Storage

  7. Cómo implementar su DApp front-end en Fleek


Elegí deliberadamente usar tanta tecnología de código abierto y descentralizada como esté disponible en esta pila.


Este blog va a ser bastante largo (oye, ¡quiero dar TODA LA INFORMACIÓN y asegurarme de que seamos amigables para los principiantes e inclusivos!), así que siéntete libre de pasar a las partes que te sean útiles en la tabla. de contenidos <3

🏛 Diagrama de arquitectura (más o menos)

🥞 Pila de tecnología DApp

(consíguelo, es una pila de panqueques #lo siento, no lo siento)


Código abierto y valor Web3 desde cero :)



  • Contrato inteligente [Solidity, Open Zeppelin]
    • Solidity es un lenguaje de programación de contratos inteligentes OO para cadenas de bloques compatibles con Ethereum (EVM)
    • Open Zeppelin ofrece una biblioteca de implementación con auditoría de seguridad de componentes y contratos comunes de contratos inteligentes
  • IDE de contrato inteligente [casco]
    • Hardhat es un entorno de desarrollo para editar, compilar, depurar e implementar software Ethereum
  • Blockchain Testnet [Máquina virtual de Filecoin Hiperespacio]
    • FVM Hyperspace es una red de prueba compatible con EVM construida en la cadena de bloques de Filecoin
  • Almacenamiento de metadatos NFT [NFT.Storage]
    • NFT.Storage es un bien público creado sobre IPFS y Filecoin para almacenar metadatos NFT de forma inmutable y persistente, y ofrece almacenamiento descentralizado gratuito para NFT y un SDK de JavaScript.
  • Interfaz [ NextJS / React + NPM]
    • Probablemente todos los conozcamos... ¿verdad? :PAG
  • Interacciones de contratos inteligentes del cliente [Metamask, Ethers, Chainstack RPC Node]
    • Usando un nodo RPC público : puedo obtener interacciones de solo lectura con mi contrato de cadena de bloques.
    • Con un proveedor de Metamask (o una billetera similar que inyecta la API de Ethereum especificada por EIP-1193 en el navegador), habilitamos las llamadas de escritura al contrato de la cadena de bloques.
    • Ethers js es una biblioteca para interactuar con contratos inteligentes compatibles con EVM
  • Script de difusión estable de texto a imagen de IA [Python, Tensorflow]
    • TensorFlow es una plataforma y biblioteca de aprendizaje automático de código abierto que proporciona modelos previamente entrenados y otros datos y herramientas de aprendizaje automático.
  • Cómputo fuera de la cadena descentralizado para la generación de texto a imagen con IA [Bacalhau]
    • Bacalhau es una red de computación abierta peer-to-peer que proporciona una plataforma para procesos de computación públicos, transparentes y opcionalmente verificables. Es una capa de computación de datos fuera de la cadena descentralizada.
  • Implementación de DApp descentralizada [Fleek]
    • Fleek ofrece la implementación de sitios web en IPFS y Filecoin. Es la versión web3 de Vercel o Netlify. ¡No podemos decir que realmente tenemos una aplicación descentralizada y luego implementarla en web2! :D

🏗️ Creación del script de texto a imagen de Python


💡 Sugerencia TLDR 💡

Este script ya está disponible para su uso a través de Bacalhau a través de la CLI y un punto final HTTP, así que no dude en omitir esta parte.


Introducción rápida a la difusión estable


Stable Diffusion es actualmente el modelo de aprendizaje automático líder para realizar el procesamiento de texto a imagen (y es el mismo modelo que usa Dall-E). Es un tipo de aprendizaje profundo, un subconjunto de aprendizaje automático que se enseña a sí mismo a realizar una tarea específica, en este caso, convertir una entrada de texto en una salida de imagen.


En este ejemplo, estamos usando un modelo probabilístico de difusión que usa un transformador para generar imágenes a partir de texto.


Sin embargo, no se preocupe, no necesitamos ir y entrenar un modelo de aprendizaje automático para esto (aunque, si eso es lo que le gusta, ¡podría hacerlo!)


En su lugar, vamos a utilizar un modelo previamente entrenado de la biblioteca de aprendizaje automático de código abierto TensorFlow de Google en nuestra secuencia de comandos de python porque los pesos de ML se han calculado previamente para nosotros.


Más correctamente, estamos usando una bifurcación de implementación Keras/TensorFlow optimizada del modelo ML original.


El guión de Python


🦄 Puede encontrar un tutorial completo sobre cómo crear y dockerizar este script de texto a imagen y ejecutarlo en Bacalhau tanto en los documentos de Bacalhau como en este video de YouTube de @BacalhauProject. 🦄 También puede ejecutarlo en este Google Collabs Notebook


¡Aquí está el script completo de Python!


 import argparse from stable_diffusion_tf.stable_diffusion import Text2Image from PIL import Image import os parser = argparse.ArgumentParser(description="Stable Diffusion") parser.add_argument("--h",dest="height", type=int,help="height of the image",default=512) parser.add_argument("--w",dest="width", type=int,help="width of the image",default=512) parser.add_argument("--p",dest="prompt", type=str,help="Description of the image you want to generate",default="cat") parser.add_argument("--n",dest="numSteps", type=int,help="Number of Steps",default=50) parser.add_argument("--u",dest="unconditionalGuidanceScale", type=float,help="Number of Steps",default=7.5) parser.add_argument("--t",dest="temperature", type=int,help="Number of Steps",default=1) parser.add_argument("--b",dest="batchSize", type=int,help="Number of Images",default=1) parser.add_argument("--o",dest="output", type=str,help="Output Folder where to store the Image",default="./") args=parser.parse_args() height=args.height width=args.width prompt=args.prompt numSteps=args.numSteps unconditionalGuidanceScale=args.unconditionalGuidanceScale temperature=args.temperature batchSize=args.batchSize output=args.output generator = Text2Image( img_height=height, img_width=width, jit_compile=False, # You can try True as well (different performance profile) ) img = generator.generate( prompt, num_steps=numSteps, unconditional_guidance_scale=unconditionalGuidanceScale, temperature=temperature, batch_size=batchSize, ) for i in range(0,batchSize): pil_img = Image.fromarray(img[i]) image = pil_img.save(f"{output}/image{i}.png")


La secuencia de comandos anterior simplemente toma un argumento de entrada de solicitud de texto y algunos otros parámetros opcionales y luego llama a la biblioteca TensorFlow bifurcada para generar las imágenes y guardarlas en un archivo de salida.


Todo el trabajo pesado que se hace aquí ocurre en la siguiente sección: aquí es donde el modelo de aprendizaje automático hace su magia. 🪄


 generator = Text2Image( img_height=height, img_width=width, jit_compile=False, ) img = generator.generate( prompt, num_steps=numSteps, unconditional_guidance_scale=unconditionalGuidanceScale, temperature=temperature, batch_size=batchSize, )


Genial, podemos generar imágenes a partir de un indicador de texto, pero um... dónde ejecutar este script requerido por GPU... 🤔🤔


Si hay algo que la tecnología blockchain no hace inherentemente bien, es el procesamiento de grandes cantidades de datos. Esto se debe al costo de la informática en un sistema distribuido para proporcionar otras propiedades poderosas como la falta de confianza y la resistencia a la censura.


Es posible usar su máquina local para ejemplos pequeños; de hecho, logré que este ejemplo en particular funcionara en mi (muy descontento) Mac M1, sin embargo, fue una espera muy larga en los resultados (¿juego de tenis de mesa alguien?) entonces, una vez que comience a hacer un procesamiento de datos más grande, necesitará más gasolina (juego de palabras intencionado) y si no tiene un servidor dedicado en la casa, necesitará usar una máquina virtual en un plataforma de computación en la nube.


No solo está centralizado, también es ineficiente, debido a que los datos están a una distancia desconocida de la máquina de cómputo, y puede volverse costoso rápidamente. No pude encontrar ningún servicio de computación en la nube de nivel gratuito que ofreciera procesamiento de GPU para esto (¿alguien dijo prohibiciones de criptominería?) y los planes llegaron a > US $ 400 por mes (no gracias).



Bacalhau!


Sin embargo, afortunadamente, estos problemas son algunos de los problemas que Bacalhau está tratando de resolver. Hacer que el procesamiento y la computación de datos estén abiertos y disponibles para todos y acelerar los tiempos de procesamiento es posible en Bacalhau, en primer lugar, mediante el uso del procesamiento por lotes en múltiples nodos y, en segundo lugar, al colocar los nodos de procesamiento donde residen los datos.


Bacalhau tiene como objetivo ayudar a democratizar el futuro del procesamiento de datos al permitir el cálculo fuera de la cadena sobre los datos sin renunciar a los valores de descentralización inherentes a IPFS, Filecoin y Web3 en general.


Bacalhau es una red de computación abierta punto a punto que proporciona una plataforma para procesos de computación públicos, transparentes y opcionalmente verificables donde los usuarios pueden ejecutar contenedores Docker o imágenes de ensamblaje web como tareas contra cualquier dato, incluidos los datos almacenados en IPFS (y pronto Filecoin). ¡Incluso tiene soporte para trabajos de GPU y no a US $ 400 o más!

introducción | Documentos de Bacalhau

Ejecutando el script en Bacalhau


Para ejecutar este script, podemos dockerizarlo para usarlo en Bacalhau. Puede seguir el tutorial aquí si quiere aprender a hacer eso. Luego podemos ejecutarlo con Bacalhau CLI con solo una línea de código (después de instalar Bacalhau con otra línea):

 bacalhau docker run --gpu 1 ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1 -- python main.py --o ./outputs --p "Rainbow Unicorn" 



Sin embargo, en este ejemplo, voy a usar un punto final HTTP que me conecta a este script de difusión estable dockerizado, que le mostraré en la sección Integraciones.

Sin embargo, señalaré aquí que esta es una forma poderosa y flexible de ejecutar procesos de cálculo de datos que también es compatible con web3: no estamos limitados a este modelo pequeño.

¡Sin embargo, pasemos al script NFT! :)

⚒️ Creación e implementación del script NFT de Solidity

El contrato inteligente

El contrato inteligente de NFT se basa en la implementación de ERC721 de Open Zeppelin, pero utiliza la versión ERC721URIStorage, que incluye las extensiones estándar de metadatos (para que podamos pasar nuestros metadatos con direcciones IPFS, que guardaremos en NFT.Storage, al contrato) .


Este contrato base además nos brinda la funcionalidad general de un contrato NFT con funciones como mint() y transfer() ya implementadas para nosotros.


Notará que también agregué un par de funciones de captación para obtener datos para mi interfaz, así como un evento que se emitirá en la cadena cada vez que se acuñe un nuevo NFT. Esto brinda la capacidad de escuchar eventos en cadena desde la DApp.


💡 ¡ Pruébelo en remix y vea todas las funciones disponibles haciendo clic en este enlace! 💡


BacalhauFRC721.sol


 // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@hardhat/console.sol"; contract BacalhauFRC721 is ERC721URIStorage { /** @notice Counter keeps track of the token ID number for each unique NFT minted in the NFT collection */ using Counters for Counters.Counter; Counters.Counter private _tokenIds; /** @notice This struct stores information about each NFT minted */ struct bacalhauFRC721NFT { address owner; string tokenURI; uint256 tokenId; } /** @notice Keeping an array for each of the NFT's minted on this contract allows me to get information on them all with a read-only front end call */ bacalhauFRC721NFT[] public nftCollection; /** @notice The mapping allows me to find NFT's owned by a particular wallet address. I'm only handling the case where an NFT is minted to an owner in this contract - but you'd need to handle others in a mainnet contract like sending to other wallets */ mapping(address => bacalhauFRC721NFT[]) public nftCollectionByOwner; /** @notice This event will be triggered (emitted) each time a new NFT is minted - which I will watch for on my front end in order to load new information that comes in about the collection as it happens */ event NewBacalhauFRC721NFTMinted( address indexed sender, uint256 indexed tokenId, string tokenURI ); /** @notice Creates the NFT Collection Contract with a Name and Symbol */ constructor() ERC721("Bacalhau NFTs", "BAC") { console.log("Hello Fil-ders! Now creating Bacalhau FRC721 NFT contract!"); } /** @notice The main function which will mint each NFT. The ipfsURI is a link to the ipfs content identifier hash of the NFT metadata stored on NFT.Storage. This data minimally includes name, description and the image in a JSON. */ function mintBacalhauNFT(address owner, string memory ipfsURI) public returns (uint256) { // get the tokenID for this new NFT uint256 newItemId = _tokenIds.current(); // Format info for saving to our array bacalhauFRC721NFT memory newNFT = bacalhauFRC721NFT({ owner: msg.sender, tokenURI: ipfsURI, tokenId: newItemId }); //mint the NFT to the chain _mint(owner, newItemId); //Set the NFT Metadata for this NFT _setTokenURI(newItemId, ipfsURI); _tokenIds.increment(); //Add it to our collection array & owner mapping nftCollection.push(newNFT); nftCollectionByOwner[owner].push(newNFT); // Emit an event on-chain to say we've minted an NFT emit NewBacalhauFRC721NFTMinted( msg.sender, newItemId, ipfsURI ); return newItemId; } /** * @notice helper function to display NFTs for frontends */ function getNFTCollection() public view returns (bacalhauFRC721NFT[] memory) { return nftCollection; } /** * @notice helper function to fetch NFT's by owner */ function getNFTCollectionByOwner(address owner) public view returns (bacalhauFRC721NFT[] memory){ return nftCollectionByOwner[owner]; }


Requisitos

Implementaré este contrato en Filecoin Virtual Machine Hyperspace Testnet , pero podría implementar este contrato en cualquier cadena compatible con EVM, incluidos Polygon, BSC, Optimism, Arbitrum, Avalanche y más. ¡Incluso podría modificar su interfaz para hacer un NFT de cadena múltiple (pista: este repositorio )!


Para implementar en Hyperspace Testnet, necesitaremos

  1. Configure y conecte Metamask Wallet a Hyperspace Testnet
  2. Obtenga algunos fondos de tFIL de prueba de un faucet ( Yoga o Zondax )


Implementación del contrato inteligente con Hardhat

Estoy usando un casco para implementar este contrato en la red de pruebas de Hyperspace.


🛸 Opciones de Hyperspace RPC y BlockExplorer:

Puntos finales RPC públicos

Explorador de bloques

https://filecoin-hiperespacio.chainstacklabs.com/rpc/v0

https://beryx.zondax.ch/

https://hiperespacio.filfox.info/rpc/v0

https://fvm.starboard.ventures/contratos/

https://rpc.ankr.com/filecoin_testnet

https://explorer.glif.io/?network=hiperespacionet

API abierta : beryx.zondax.ch

https://hyperspace.filfox.info/es


Para la configuración, podemos elegir cualquiera de los puntos finales RPC públicos disponibles.


hardhat.config.ts


 import '@nomicfoundation/hardhat-toolbox'; import { config as dotenvConfig } from 'dotenv'; import { HardhatUserConfig } from 'hardhat/config'; import { resolve } from 'path'; //Import our customised tasks // import './pages/api/hardhat/tasks'; const dotenvConfigPath: string = process.env.DOTENV_CONFIG_PATH || './.env'; dotenvConfig({ path: resolve(__dirname, dotenvConfigPath) }); // Ensure that we have all the environment variables we need. const walletPrivateKey: string | undefined = process.env.WALLET_PRIVATE_KEY; if (!walletPrivateKey) { throw new Error('Please set your Wallet private key in a .env file'); } const config: HardhatUserConfig = { solidity: '0.8.17', defaultNetwork: 'filecoinHyperspace', networks: { hardhat: {}, filecoinHyperspace: { url: 'https://api.hyperspace.node.glif.io/rpc/v1', chainId: 3141, accounts: [process.env.WALLET_PRIVATE_KEY ?? 'undefined'], }, // bleeding edge often-reset FVM testnet filecoinWallaby: { url: 'https://wallaby.node.glif.io/rpc/v0', chainId: 31415, accounts: [process.env.WALLET_PRIVATE_KEY ?? 'undefined'], //explorer: https://wallaby.filscan.io/ and starboard }, }, // I am using the path mapping so I can keep my hardhat deployment within the /pages folder of my DApp and therefore access the contract ABI for use on my frontend paths: { root: './pages/api/hardhat', tests: './pages/api/hardhat/tests', //who names a directory in the singular?!!! Grammarly would not be happy cache: './pages/api/hardhat/cache', }, }; export default config;

Y para implementar el contrato inteligente, creamos un script de implementación. Tenga en cuenta que estoy configurando específicamente la dirección de Wallet aquí como el firmante (propietario). Hay algunos errores de mapeo que aún se están trabajando en FEVM en el momento de la escritura que pueden causar algún comportamiento extraño.


deploy/deployBacalhauFRC721.ts


 import hre from 'hardhat'; import type { BacalhauFRC721 } from '../typechain-types/contracts/BacalhauFRC721'; import type { BacalhauFRC721__factory } from '../typechain-types/factories/contracts/BacalhauFRC721__factory'; async function main() { console.log('Bacalhau721 deploying....'); // !!!needed as hardhat's default does not map correctly to the FEVM const owner = new hre.ethers.Wallet( process.env.WALLET_PRIVATE_KEY || 'undefined', hre.ethers.provider ); const bacalhauFRC721Factory: BacalhauFRC721__factory = < BacalhauFRC721__factory > await hre.ethers.getContractFactory('BacalhauFRC721', owner); const bacalhauFRC721: BacalhauFRC721 = <BacalhauFRC721>( await bacalhauFRC721Factory.deploy() ); await bacalhauFRC721.deployed(); console.log('bacalhauFRC721 deployed to ', bacalhauFRC721.address); // optionally log to a file here } main().catch((error) => { console.error(error); process.exitCode = 1; });

Para implementar, ejecute la secuencia de comandos anterior en la terminal usando el siguiente código (NB: dado que hemos configurado la red predeterminada en filecoinHyperspace en nuestra configuración, no es necesario pasar una bandera para la red, aunque esto se muestra a continuación)

> cd ./pages/hardhat/deploy/


 npx hardhat run ./deployBacalhauFRC721.ts --network filecoinHyperspace


¡Celebrar! ¡Acabamos de implementar nuestro contrato NFT en la red de prueba hiperespacial de Filecoin!

🎬 Construyendo las interacciones front-end

Wooo en la parte bonita... y también el pegamento que mantiene todo junto aquí :)


Para construir el front-end, estoy usando NextJS y Typescript. Sin embargo, para ser honesto, no estoy aprovechando ninguna de las funciones SSR (representación del lado del servidor) de NextJS y ni siquiera uso su enrutamiento de página (ya que es un Dapp de una sola página), por lo que realmente podría ir con una configuración Vanilla React (¡o cualquier marco de su elección, por supuesto!).


En cuanto al mecanografiado... bueno, construí esto con un poco de prisa y tengo que admitir que este no es un muy buen ejemplo de mecanografiado; sin embargo, los vars parecen felices... ;)



De todos modos, el punto principal de esta sección no es mostrarle cómo codificar una interfaz, sino mostrarle cómo interactuar con el contrato inteligente, Bacalhau (con nuestro modelo ML de difusión estable) y, por supuesto, NFT.Storage - # No en IPFS No en su NFT.

Flujo completo

[tarea: construir un diagrama de flujo]

  • El usuario ingresa un mensaje de texto en el campo de entrada ->
  • Hace clic en el botón Generar imágenes -> Llama al trabajo de Bacalhau para generar imágenes
  • El trabajo de Bacalhau se completa -> los formatos regresan al objeto JSON de metadatos NFT
  • El usuario hace clic en el botón Mint NFT -> NFT. Se llama al almacenamiento para guardar los metadatos de NFT y regresa con un CID de IPFS para la carpeta -> Se llama a la función mint NFT del contrato inteligente con este IPFS_URI para acuñar un NFT con estos metadatos ->
  • !! [FEVM gotcha] -> aquí generalmente esperaríamos a que regrese el TX (hash de transacción) de este resultado, pero actualmente no funciona, por lo que en su lugar estamos usando un detector de eventos de contrato para averiguar cuándo se completa.
  • ¡Hecho! -> Ahora puede volver a obtener cualquier dato de visualización y dar al usuario comentarios sobre el éxito de la acuñación.


Genial, ¡veamos cómo implementamos esto en el código!

Interacciones de Bacalhau

La creación del extremo de la API de front-end para Bacalhau está documentada en este informe de proyecto por el ingeniero Luke Marsden .


Actualmente, la API solo afecta directamente a los scripts de difusión estables documentados en este blog; sin embargo, el equipo está en proceso de ampliarla a una API más genérica para que pueda llamar a cualquiera de los ejemplos, y también a sus propios scripts implementados desde un HTTP. API REST. Esté atento a esto aquí o en el canal #bacalhau en FilecoinProject slack.


>run/test in terminal


 curl -XPOST -d '{"prompt": "rainbow unicorn"}' 'http://dashboard.bacalhau.org:1000/api/v1/stablediffusion';


>react / typescript code


 import { CID } from 'multiformats/cid'; export const callBacalhauJob = async (promptInput: string) => { //Bacalahau HTTP Stable Diffusion Endpoint const url = 'http://dashboard.bacalhau.org:1000/api/v1/stablediffusion'; const headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; const data = { prompt: promptInput, //The user text prompt! }; /* FETCH FROM BACALHAU ENDPOINT */ const cid = await fetch(url, { method: 'POST', body: JSON.stringify(data), headers: headers, }) .then(async (res) => { let body = await res.json(); if (body.cid) { /* Bacalhau returns a V0 CID which we want to convert to a V1 CID for easier usage with http gateways (ie. displaying the image on web), so I'm using the IPFS multiformats package to convert it here */ return CID.parse(body.cid).toV1().toString(); } }) .catch((err) => { console.log('error in bac job', err); }); return cid; };


Esta función devolverá un CID (identificador de contenido) de IPFS con una estructura de carpetas como la siguiente. La imagen se puede encontrar en /outputs/image0.png .


💡 ¡Véalo usted mismo haciendo clic aquí ! 💡




Ahhh unicornios arcoíris... ¡Qué no puede gustar!

NFT.Almacenamiento

NFT.Storage es un bien público (también conocido como gratuito) que facilita el almacenamiento de metadatos NFT de forma perpetua en IPFS y Filecoin con JavaScript o HTTP SDK.


NFT Metadata es un documento JSON que se parece al siguiente ejemplo, que se toma directamente de los documentos de Open Zeppelin:



Al crear NFT, es importante tener en cuenta que, a menos que almacene los metadatos en la cadena (lo que puede volverse prohibitivamente costoso para archivos grandes), para cumplir con la 'no fungibilidad' de un token, necesita un almacenamiento que sea persistente, confiable e inmutable.


Si su NFT tiene una dirección basada en la ubicación como el ejemplo anterior, entonces es bastante simple que esta ruta de ubicación se cambie después de una venta, lo que significa que el NFT que pensó que compró se convierte en algo completamente diferente, o literalmente en un tirón de alfombra en el caso a continuación, donde el creador de NFT cambió las imágenes artísticas por imágenes de alfombras.



¡Algo de lo que incluso Open Zeppelin advierte!



El uso de NFT.Storage significa que obtenemos un CID de archivo IPFS inmutable ( contenido , no ubicación, identificador de identificación) para nuestros metadatos que no solo se anclan a IPFS, sino que también se almacenan en Filecoin para persistencia. NFT.Storage y obtenga una clave API (para guardar en su archivo .env) para este.


.env example


 NEXT_PUBLIC_NFT_STORAGE_API_KEY=xxx


También debemos asegurarnos de haber creado un JSON de metadatos correctamente formado, porque si bien FVM no tiene (¡todavía!) NFT Marketplaces... queremos asegurarnos de que, cuando se adopte, nuestro NFT todavía se ajusta al estándar. .


 import { NFTStorage } from 'nft.storage'; //connect to NFT.Storage Client const NFTStorageClient = new NFTStorage({ token: process.env.NEXT_PUBLIC_NFT_STORAGE_API_KEY, }); const createNFTMetadata = async ( promptInput: string, imageIPFSOrigin: string, //the ipfs path eg. ipfs://[CID] imageHTTPURL: string //an ipfs address fetchable through http for the front end to use (ie. including an ipfs http gateway on it like https://[CID].ipfs.nftstorage.link) ) => { console.log('Creating NFT Metadata...'); let nftJSON; // let's get the image data Blob from the IPFS CID that was returned from Bacalhau earlier... await getImageBlob(status, setStatus, imageHTTPURL).then( async (imageData) => { // Now let's create a unique CID for that image data - since we don't really want the rest of the data returned from the Bacalhau job.. await NFTStorageClient.storeBlob(imageData) .then((imageIPFS) => { console.log(imageIPFS); //Here's the JSON construction - only name, description and image are required fields- but I also want to save some other properties like the ipfs link and perhaps you have other properties that give your NFT's rarity to add as well nftJSON = { name: 'Bacalhau Hyperspace NFTs 2023', description: promptInput, image: imageIPFSOrigin, properties: { prompt: promptInput, type: 'stable-diffusion-image', origins: { ipfs: `ipfs://${imageIPFS}`, bacalhauipfs: imageIPFSOrigin, }, innovation: 100, content: { 'text/markdown': promptInput, }, }, }; }) .catch((err) => console.log('error creating blob cid', err)); } ); return nftJSON; };


¡Ahora almacenemos estos metadatos en NFT.Storage!


 await NFTStorageClient.store(nftJson) .then((metadata) => { // DONE! - do something with this returned metadata! console.log('NFT Data pinned to IPFS & stored on Filecoin!'); console.log('Metadata URI: ', metadata.url); // once saved we can use it to mint the NFT // mintNFT(metadata); }) .catch((err) => { console.log('error uploading to nft.storage'); });


Woot: tenemos nuestra imagen de Bacalhau, hemos guardado nuestros metadatos de manera inmutable y persistente con NFT. Strorage, ¡ahora acuñemos nuestro NFT!


💡 Sugerencia rápida 💡NFT.Storage también ofrece una gama de otras llamadas API como storeCar y storeDirectory, así como una función de estado () , que devuelve las ofertas de almacenamiento de pines IPFS y Filecoin de un CID -> esta podría ser una adición genial para una DApp de FEVM (o implementación de NFT en FEVM una vez que FEVM llega al lanzamiento de la red principal) para comprobar el estado de los NFT.

Interacciones de contrato

Hay 3 tipos de interacciones aquí (y algunos errores de FEVM: ¡la tecnología beta siempre tendrá algunas características de errores extravagantes!)


  • llamadas de solo lectura para recuperar datos de la cadena sin mutarlos

  • escribe llamadas que requieren una billetera para firmar y pagar gasolina, es decir. ¡funciones que cambian el estado de la cadena, como acuñar el NFT!

  • detectores de eventos: que escuchan los eventos emitidos por el contrato


Para todas estas funciones, usaremos la biblioteca ethers.js , un contenedor liviano para la API de Ethereum, para conectarnos a nuestro contrato y realizar llamadas.


Conexión al contrato en modo lectura con una RPC pública:


 //The compiled contract found in pages/api/hardhat/artifacts/contracts import BacalhauCompiledContract from '@Contracts/BacalhauFRC721.sol/BacalhauFRC721.json'; //On-chain address of the contract const contractAddressHyperspace = '0x773d8856dd7F78857490e5Eea65111D8d466A646'; //A public RPC Endpoint (see table from contract section) const rpc = 'https://api.hyperspace.node.glif.io/rpc/v1'; const provider = new ethers.providers.JsonRpcProvider(rpc); const connectedReadBacalhauContract = new ethers.Contract( contractAddressHyperspace, BacalhauCompiledContract.abi, provider );


Escucha de eventos en el contrato. Dado que este es un evento de solo lectura (obtener), podemos usar el RPC público para escuchar las emisiones de eventos en la cadena.


 //use the read-only connected Bacalhau Contract connectedReadBacalhauContract.on( // Listen for the specific event we made in our contract 'NewBacalhauFRC721NFTMinted', (sender: string, tokenId: number, tokenURI: string) => { //DO STUFF WHEN AN EVENT COMES IN // eg. re-fetch NFT's, store in state and change page status } );


Conectarse al contrato en modo de escritura : esto requiere que una billetera inyecte el objeto Ethereum en el navegador web para que un usuario pueda firmar una transacción y pagar la gasolina, razón por la cual estamos buscando una ventana.ethereum objeto.


 //Typescript needs to know window is an object with potentially and ethereum value. There might be a better way to do this? Open to tips! declare let window: any; //The compiled contract found in pages/api/hardhat/artifacts/contracts import BacalhauCompiledContract from '@Contracts/BacalhauFRC721.sol/BacalhauFRC721.json'; //On-chain address of the contract const contractAddressHyperspace = '0x773d8856dd7F78857490e5Eea65111D8d466A646'; //check for the ethereum object if (!window.ethereum) { //ask user to install a wallet or connect //abort this } // else there's a wallet provider else { // same function - different provider - this one has a signer - the user's connected wallet address const provider = new ethers.providers.Web3Provider(window.ethereum); const contract = new ethers.Contract( contractAddressHyperspace, BacalhauCompiledContract.abi, provider ); const signer = provider.getSigner(); const connectedWriteBacalhauContract = contract.connect(signer); }

Llamar a la función mint usando el contrato conectado de escritura.


Primero, asegúrese de que tenemos una dirección de billetera del usuario y que estamos en la cadena FVM Hyperspace. Aquí hay algunas funciones útiles de billetera que puede desear, incluida la forma de verificar el chainId y cómo agregar mediante programación la red Hyperspace a Metamask/billetera. Puede interactuar con las billeteras usando el objeto Ethereum directamente o usando ethers.js.


 declare let window: any; const fetchWalletAccounts = async () => { console.log('Fetching wallet accounts...'); await window.ethereum //use ethers? .request({ method: 'eth_requestAccounts' }) .then((accounts: string[]) => { return accounts; }) .catch((error: any) => { if (error.code === 4001) { // EIP-1193 userRejectedRequest error console.log('Please connect to MetaMask.'); } else { console.error(error); } }); }; const fetchChainId = async () => { console.log('Fetching chainId...'); await window.ethereum .request({ method: 'eth_chainId' }) .then((chainId: string[]) => { return chainId; }) .catch((error: any) => { if (error.code === 4001) { // EIP-1193 userRejectedRequest error console.log('Please connect to MetaMask.'); } else { console.error(error); } }); }; //!! This function checks for a wallet connection WITHOUT being intrusive to to the user or opening their wallet export const checkForWalletConnection = async () => { if (window.ethereum) { console.log('Checking for Wallet Connection...'); await window.ethereum .request({ method: 'eth_accounts' }) .then(async (accounts: String[]) => { console.log('Connected to wallet...'); // Found a user wallet return true; }) .catch((err: Error) => { console.log('Error fetching wallet', err); return false; }); } else { //Handle no wallet connection return false; } }; //Subscribe to changes on a user's wallet export const setWalletListeners = () => { console.log('Setting up wallet event listeners...'); if (window.ethereum) { // subscribe to provider events compatible with EIP-1193 standard. window.ethereum.on('accountsChanged', (accounts: any) => { //logic to check if disconnected accounts[] is empty if (accounts.length < 1) { //handle the locked wallet case } if (userWallet.accounts[0] !== accounts[0]) { //user has changed address } }); // Subscribe to chainId change window.ethereum.on('chainChanged', () => { // handle changed chain case }); } else { //handle the no wallet case } }; export const changeWalletChain = async (newChainId: string) => { console.log('Changing wallet chain...'); const provider = window.ethereum; try { await provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: newChainId }], //newChainId }); } catch (error: any) { alert(error.message); } }; //AddHyperspaceChain export const addHyperspaceNetwork = async () => { console.log('Adding the Hyperspace Network to Wallet...'); if (window.ethereum) { window.ethereum .request({ method: 'wallet_addEthereumChain', params: [ { chainId: '0xc45', rpcUrls: [ 'https://hyperspace.filfox.info/rpc/v0', 'https://filecoin-hyperspace.chainstacklabs.com/rpc/v0', ], chainName: 'Filecoin Hyperspace', nativeCurrency: { name: 'tFIL', symbol: 'tFIL', decimals: 18, }, blockExplorerUrls: [ 'https://fvm.starboard.ventures/contracts/', 'https://hyperspace.filscan.io/', 'https://beryx.zondax.chfor', ], }, ], }) .then((res: XMLHttpRequestResponseType) => { console.log('added hyperspace successfully', res); }) .catch((err: ErrorEvent) => { console.log('Error adding hyperspace network', err); }); } };


Llame a la función de menta del contrato en modo de escritura....


 // Pass in the metadata return from saving to NFT.Storage const mintNFT = async (metadata: any) => { await connectedWriteBacalhauContract // The name of our function in our smart contract .mintBacalhauNFT( userWallet.accounts[0], //users account to use metadata.url //test ipfs address ) .then(async (data: any) => { console.log('CALLED CONTRACT MINT FUNCTION', data); await data .wait() .then(async (tx: any) => { console.log('tx', tx); //CURRENTLY NOT RETURNING TX - (I use event triggering to know when this function is complete) let tokenId = tx.events[1].args.tokenId.toString(); console.log('tokenId args', tokenId); setStatus({ ...INITIAL_TRANSACTION_STATE, success: successMintingNFTmsg(data), }); }) .catch((err: any) => { console.log('ERROR', err); setStatus({ ...status, loading: '', error: errorMsg(err.message, 'Error minting NFT'), }); }); }) .catch((err: any) => { console.log('ERROR1', err); setStatus({ ...status, loading: '', error: errorMsg( err && err.message ? err.message : null, 'Error minting NFT' ), }); }); }


Wooo - ¡NFT acuñado! Hora del modo de baile de unicornio!

🌟 Reflexiones finales: posibilidades de IA y Blockchain

Bacalhau se presta bien para realizar trabajos de procesamiento repetitivos y deterministas sobre datos.

  • Procesos ETL

  • Aprendizaje automático e IA

  • Integración de datos IOT

  • Procesamiento por lotes, incluido para

    • Datos financieros y de mercado
  • Procesamiento de video e imagen: ideal para creativos


Hay múltiples ejemplos en los documentos de Bacalhau de cómo lograr algo de lo anterior también.

Si bien Bacalhau está ocupado construyendo una integración para llamar directamente a Bacalhau desde los contratos inteligentes de FEVM, aquí hay algunas ideas sobre las colaboraciones de Bacalhau x FVM:


  • Ayuda para la incorporación y desvinculación de datos de Filecoin en el futuro
  • Ayude a construir una capa de reputación y calidad de servicio para Filecoin al procesar los datos recuperados en la cadena sobre ofertas y proveedores de almacenamiento.
  • Bacalhau podría proporcionar computación para datos de pago y mercado
  • Bacalhau podría ayudar con el procesamiento de datos de DAO y DataDAO
  • Bacalhau podría ayudar a potenciar una mayor automatización para esfuerzos creativos como el procesamiento de imágenes y videos.
  • Bacalhau puede habilitar el procesamiento de datos de juegos y metaversos, incluso para VR y AR.
  • Bacalhau, IOT y simulaciones son posibles
  • Aplicaciones de IA y ML

🐠 La hoja de ruta de Bacalhau


¡Actualmente estamos construyendo una forma para que usted pueda ejecutar Bacalhau directamente desde sus contratos inteligentes! Este proyecto se llama Project Frog / Project Lilypad, y será una capa de integración que permitirá llamar a trabajos de Bacalhau desde contratos inteligentes FEVM.


Esté atento al progreso de esto suscribiéndose a nuestro boletín o uniéndose a las redes sociales a continuación.

✍️ ¡Mantente en contacto!

Felicidades si lo leíste completo!!!


¡Agradecería un me gusta, comentario, seguir o compartir si esto te fue útil! <3


¡Mantente en contacto con Bacalhau!



Con ♥️ DeveloperAlly

¿Encontraste este artículo valioso?

Apoye a Alison Haire convirtiéndose en patrocinador. ¡Cualquier cantidad es apreciada!


Obtenga más información sobre los patrocinadores de Hashnode


También publicado aquí .