paint-brush
🌈🦄 Construisez votre propre DApp NFT Art généré par l'IA avec Bacalhaupar@developerally
2,427 lectures
2,427 lectures

🌈🦄 Construisez votre propre DApp NFT Art généré par l'IA avec Bacalhau

par DeveloperAlly29m2023/02/08
Read on Terminal Reader

Trop long; Pour lire

Un guide complet pour créer, exécuter et déployer un DApp avec votre propre script Text-to Image pour créer des NFT artistiques générés par l'IA sur FVM Hyperspace Testnet. Ce blog vous expliquera comment créer un script texte-image open source basé sur python basé sur Tensorflow. J'ai délibérément choisi d'utiliser autant de technologies open source et décentralisées que celles disponibles dans cette pile.
featured image - 🌈🦄 Construisez votre propre DApp NFT Art généré par l'IA avec Bacalhau
DeveloperAlly HackerNoon profile picture
0-item

Un guide complet pour créer, exécuter et déployer un DApp avec votre propre script Text-to Image pour créer des NFT artistiques générés par l'IA sur FVM Hyperspace Testnet!


Table des matières

  • 👩‍💻 Ce qu'on va faire...
  • 🏛 Schéma d'architecture (un peu)
  • 🥞 Pile technologique DApp
  • 🏗️ Construire le script Python Text-To-Image
  • ⚒️ Construire et déployer le script Solidity NFT
  • 🎬 Construire les interactions frontales
    • Flux complet
    • Interactions Bacalhau
    • Stockage NFT
    • Interactions contractuelles
  • 🌟 Réflexions finales : possibilités pour l'IA et la blockchain
  • 🐠 La feuille de route de Bacalhau
  • ✍️ Restez en contact !


🦄 Liens rapides :


👩‍💻 Ce qu'on va faire...

Ce blog vous expliquera comment


  1. Créez un script de texte en image basé sur python open source basé sur Tensorflow (vous pouvez également simplement utiliser le point de terminaison HTTP Bacalhau si cela ne vous intéresse pas)

  2. Exécutez ce script sur Bacalhau (une plate-forme de calcul hors chaîne p2p ouverte)

  3. Créer un contrat NFT dans Solidity (basé sur un contrat Open Zeppelin ERC721)

  4. Déployez le contrat NFT sur le réseau de test hyperespace de la machine virtuelle Filecoin (FVM) avec Hardhat

  5. Interactions frontales - Comment interagir avec le script texte-image Bacalhau et votre contrat NFT dans React

  6. Comment enregistrer vos métadonnées NFT sur NFT.Storage

  7. Comment déployer votre DApp Front-End sur Fleek


J'ai délibérément choisi d'utiliser autant de technologies open source et décentralisées que celles disponibles dans cette pile.


Ce blog va être assez long (hé - je veux donner TOUTES LES INFOS et m'assurer que nous sommes conviviaux pour les débutants et inclusifs !) - alors n'hésitez pas à passer aux parties qui vous sont utiles dans le tableau du contenu <3

🏛 Schéma d'architecture (un peu)

🥞 Pile technologique DApp

(compris - c'est une pile de crêpes #sorrynotsorry)


Open Source & Web3 à partir de zéro :)



  • Contrat intelligent [Solidité, Open Zeppelin]
    • Solidity est un langage de programmation de contrats intelligents OO pour les chaînes de blocs compatibles Ethereum (EVM)
    • Open Zeppelin propose une bibliothèque d'implémentation auditée de sécurité de composants et de contrats intelligents courants
  • IDE de contrat intelligent [Hardhat]
    • Hardhat est un environnement de développement pour l'édition, la compilation, le débogage et le déploiement du logiciel Ethereum
  • Blockchain Testnet [Hyperespace de la machine virtuelle Filecoin]
    • FVM Hyperspace est un testnet compatible EVM construit sur la blockchain Filecoin
  • Stockage de métadonnées NFT [NFT.Storage]
    • NFT.Storage est un bien public construit sur IPFS et Filecoin pour stocker les métadonnées NFT de manière immuable et persistante et offre un stockage décentralisé gratuit pour les NFT et un sdk javascript.
  • Frontal [ NextJS / React + NPM]
    • Nous les connaissons probablement tous... n'est-ce pas ? :P
  • Interactions de contrat intelligent du client [Metamask, Ethers, Chainstack RPC Node]
    • À l'aide d'un nœud RPC public , je peux obtenir des interactions en lecture seule avec mon contrat blockchain.
    • Avec un fournisseur Metamask (ou un portefeuille similaire qui injecte l'API Ethereum spécifiée par EIP-1193 dans le navigateur), nous activons les appels en écriture au contrat blockchain.
    • Ethers js est une bibliothèque pour interagir avec des contrats intelligents compatibles EVM
  • Script de diffusion stable texte-image AI [Python, Tensorflow]
    • TensorFlow est une plate-forme et une bibliothèque d'apprentissage automatique open source qui fournit des modèles pré-formés et d'autres outils de données et de ML.
  • Calcul décentralisé hors chaîne pour la génération de texte en image par IA [Bacalhau]
    • Bacalhau est un réseau de calcul ouvert peer-to-peer qui fournit une plate-forme pour des processus de calcul publics, transparents et éventuellement vérifiables. Il s'agit d'une couche de calcul de données hors chaîne décentralisée.
  • Déploiement DApp décentralisé [Fleek]
    • Fleek propose le déploiement de sites Web sur IPFS et Filecoin. C'est la version web3 de Vercel ou Netlify - on ne peut pas dire que nous avons vraiment une application décentralisée et ensuite la déployer sur web2 ! :D

🏗️ Construire le script Python Text-To-Image


💡 Astuce TLDR 💡

Ce script est déjà disponible pour une utilisation via Bacalhau via la CLI et un point de terminaison HTTP, alors n'hésitez pas à ignorer cette partie.


Introduction rapide à la diffusion stable


Stable Diffusion est actuellement le principal modèle d'apprentissage automatique pour le traitement du texte en image (& est le même modèle que celui utilisé par Dall-E). Il s'agit d'un type de Deep Learning - un sous-ensemble de Machine Learning qui s'apprend à effectuer une tâche spécifique - dans ce cas, convertir une entrée de texte en une sortie d'image.


Dans cet exemple, nous utilisons un modèle probabiliste de diffusion qui utilise un transformateur pour générer des images à partir de texte.


Ne vous inquiétez pas cependant - nous n'avons pas besoin d'aller former un modèle d'apprentissage automatique pour cela (bien que bon - si c'est votre truc - vous pourriez tout à fait !)


Au lieu de cela, nous allons utiliser un modèle pré-formé de la bibliothèque d'apprentissage automatique open source TensorFlow de Google dans notre script python, car les poids ML ont été pré-calculés pour nous.


Plus correctement, nous utilisons un fork d'implémentation Keras/TensorFlow optimisé du modèle ML d'origine.


Le script Python


🦄 Vous pouvez trouver une procédure pas à pas complète sur la façon de créer et de Dockeriser ce script texte-image et de l'exécuter sur Bacalhau dans les documents Bacalhau et dans cette vidéo YouTube @BacalhauProject .🦄 Vous pouvez également l'exécuter dans ce bloc- notes Google Collabs


Voici le script python complet !


 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")


Le script ci-dessus prend simplement un argument d'entrée d'invite de texte et quelques autres paramètres facultatifs, puis appelle la bibliothèque forkée TensorFlow pour générer la ou les images et les enregistrer dans un fichier de sortie.


Tout le travail lourd effectué ici se produit dans la section ci-dessous - c'est là que le modèle d'apprentissage automatique fait sa magie. 🪄


 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, )


Génial, nous pouvons générer des images à partir d'une invite de texte, mais euh ... où exécuter ce script requis par le GPU ..... 🤔🤔


S'il y a une chose que la technologie blockchain ne fait pas bien en soi, c'est le traitement de données volumineuses. Cela est dû au coût de l'informatique sur un système distribué pour fournir d'autres propriétés puissantes telles que l'absence de confiance et la résistance à la censure.


Il est possible d'utiliser votre machine locale pour de petits exemples - en fait, j'ai réussi à faire fonctionner cet exemple particulier sur mon (très mécontent) Mac M1, cependant, l'attente des résultats a été très longue (jeu de tennis de table quelqu'un?) donc, une fois que vous commencerez à traiter des données plus importantes, vous aurez besoin de plus de gaz (jeu de mots) et si vous n'avez pas de serveur dédié dans la maison, vous devrez alors utiliser une machine virtuelle sur un plate-forme informatique en nuage.


Non seulement cela est centralisé, mais c'est aussi inefficace - car les données sont à une distance inconnue de la machine de calcul, et cela peut devenir coûteux rapidement. Je n'ai trouvé aucun service de cloud computing gratuit offrant un traitement GPU pour cela (quelqu'un a-t-il dit des interdictions de crypto-minage ..?) Et les plans sont arrivés à > 400 $ US par mois (non merci).



Bacalhau !


Heureusement, ces problèmes font partie des problèmes que Bacalhau tente de résoudre. Rendre le traitement et le calcul des données ouverts et accessibles à tous et accélérer les temps de traitement est possible à Bacalhau, premièrement - en utilisant le traitement par lots sur plusieurs nœuds et deuxièmement en plaçant les nœuds de traitement là où vivent les données !


Bacalhau vise à aider à démocratiser l'avenir du traitement des données en permettant le calcul hors chaîne sur les données sans renoncer aux valeurs de décentralisation inhérentes à IPFS, Filecoin et Web3 plus largement.


Bacalhau est un réseau de calcul ouvert peer-to-peer qui fournit une plate-forme pour des processus de calcul publics, transparents et éventuellement vérifiables où les utilisateurs peuvent exécuter des conteneurs Docker ou des images d'assemblage Web en tant que tâches sur toutes les données, y compris les données stockées dans IPFS (et bientôt Filecoin). Il prend même en charge les tâches GPU et non à 400 USD ou plus !

introduction | Documents Bacalhau

Exécuter le script sur Bacalhau


Pour exécuter ce script, nous pouvons le dockeriser pour une utilisation sur Bacalhau. Vous pouvez suivre le didacticiel ici si vous souhaitez apprendre à le faire. Nous pouvons ensuite l'exécuter avec la CLI Bacalhau avec une seule ligne de code (après avoir installé Bacalhau avec un autre one-liner):

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



Dans cet exemple cependant, je vais utiliser un point de terminaison HTTP qui me connecte à ce script de diffusion stable dockerisé, que je vais vous montrer dans la section Intégrations !

Je noterai cependant ici qu'il s'agit d'un moyen puissant et flexible d'exécuter des processus de calcul de données qui est également compatible avec le Web3 - nous ne sommes pas limités à ce seul petit modèle.

Passons maintenant au script NFT ! :)

⚒️ Construire et déployer le script Solidity NFT

Le contrat intelligent

Le contrat intelligent NFT est basé sur l'implémentation d'ERC721 par Open Zeppelin mais utilise la version ERC721URIStorage, qui inclut les extensions standard de métadonnées (afin que nous puissions transmettre nos métadonnées adressées IPFS - que nous enregistrerons sur NFT.Storage, au contrat) .


Ce contrat de base nous donne en outre la fonctionnalité générale d'un contrat NFT avec des fonctions comme mint() et transfer() déjà implémentées pour nous.


Vous remarquerez que j'ai également ajouté quelques fonctions getter pour récupérer des données pour mon frontal ainsi qu'un événement qui sera émis sur la chaîne chaque fois qu'un nouveau NFT est créé. Cela donne la possibilité d'écouter les événements en chaîne à partir du DApp.


💡 Essayez-le sur remix et découvrez toutes les fonctions disponibles en cliquant sur ce lien ! 💡


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]; }


Exigences

Je vais déployer ce contrat sur Filecoin Virtual Machine Hyperspace Testnet , mais vous pouvez déployer ce contrat sur n'importe quelle chaîne compatible EVM, y compris Polygon, BSC, Optimism, Arbitrum, Avalanche et plus encore. Vous pouvez même modifier votre frontal pour créer un NFT multi-chaînes (indice : ce dépôt ) !


Pour déployer sur Hyperspace Testnet, nous devrons

  1. Configurer et connecter Metamask Wallet à Hyperspace Testnet
  2. Obtenez des fonds de test tFIL à partir d'un robinet ( Yoga ou Zondax )


Déploiement du contrat intelligent avec Hardhat

J'utilise un casque pour déployer ce contrat sur le testnet Hyperspace.


🛸 Options Hyperespace RPC et BlockExplorer :

Points de terminaison RPC publics

BlockExplorer's

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

https://beryx.zondax.ch/

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

https://fvm.starboard.ventures/contracts/

https://rpc.ankr.com/filecoin_testnet

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

API ouverte : beryx.zondax.ch

https://hyperspace.filfox.info/fr


Pour la configuration de la configuration, nous pouvons choisir parmi l'un des points de terminaison RPC publics 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;

Et pour déployer le contrat intelligent, nous créons un script de déploiement - notez que je définis spécifiquement l'adresse du portefeuille ici en tant que signataire (propriétaire) - il y a quelques erreurs de mappage toujours en cours dans FEVM au moment de la rédaction qui peuvent causer un comportement étrange.


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; });

Pour déployer, exécutez le script ci-dessus dans le terminal en utilisant le code suivant (NB : puisque nous avons défini le réseau par défaut sur filecoinHyperspace dans notre configuration, il n'est pas nécessaire de passer un indicateur pour le réseau bien que cela soit indiqué ci-dessous)

> cd ./pages/hardhat/deploy/


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


Célébrer! Nous venons de déployer notre contrat NFT sur le testnet hyperespace Filecoin !

🎬 Construire les interactions frontales

Wooo sur la jolie partie... et aussi la colle qui tient tout ensemble ici :)


Pour construire le front-end, j'utilise NextJS et Typescript. Cependant, pour être honnête, je ne profite d'aucune des fonctionnalités SSR (rendu côté serveur) de NextJS et je n'utilise même pas leur routage de page (puisqu'il s'agit d'une Dapp d'une seule page), donc vous pouvez vraiment y aller avec une configuration vanille React (ou n'importe quel framework de votre choix bien sûr !).


En ce qui concerne le tapuscrit... eh bien, je l'ai construit un peu à la hâte et je dois admettre que ce n'est pas un très bon exemple de tapuscrit - les vars semblent heureuses cependant... ;)



Anyhoo - le point principal de cette section n'est pas de vous montrer comment coder un frontal, mais de vous montrer comment interagir avec le contrat intelligent, Bacalhau (avec notre modèle stable de diffusion ML) et bien sûr, NFT.Storage - # NotOnIPFSNotYourNFT.

Flux complet

[à faire : créer un organigramme]

  • L'utilisateur entre une invite de texte dans le champ de saisie ->
  • Clique sur le bouton Générer des images -> Appelle le travail de Bacalhau pour générer des images
  • Le travail Bacalhau se termine -> les formats retournent dans l'objet NFT Metadata JSON
  • L'utilisateur clique sur le bouton Mint NFT -> NFT.Storage est appelé pour enregistrer les métadonnées NFT et revient avec un CID IPFS pour le dossier -> La fonction mint NFT du contrat intelligent est appelée avec cet IPFS_URI pour créer un NFT avec ces métadonnées ->
  • !! [FEVM gotcha] -> ici, nous attendrions généralement le retour du TX (hachage de transaction) de ce résultat, mais cela ne fonctionne pas actuellement, nous utilisons donc à la place un écouteur d'événement de contrat pour savoir quand cela se termine.
  • Fait! -> Peut maintenant récupérer à nouveau toutes les données d'affichage et donner à l'utilisateur un retour d'information sur le succès de la frappe.


Nice - voyons comment nous implémentons cela dans le code !

Interactions Bacalhau

La création du point de terminaison de l'API frontale pour Bacalhau est documentée dans ce rapport de projet par l'ingénieur Luke Marsden .


L'API ne touche actuellement directement que les scripts de diffusion stables documentés dans ce blog, cependant, l'équipe est en train de l'étendre dans une API plus générique afin que vous puissiez appeler n'importe lequel des exemples, ainsi que vos propres scripts déployés à partir d'un HTTP API REST. Gardez un œil là-dessus ici ou sur le canal #bacalhau dans 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; };


Cette fonction renverra un CID IPFS (identifiant de contenu) avec une structure de dossiers comme celle ci-dessous. L'image se trouve alors sous /outputs/image0.png .


💡 Voyez-le par vous-même en cliquant ici ! 💡




Ahhh les licornes arc-en-ciel... qu'est-ce qu'il ne faut pas aimer !

Stockage NFT

NFT.Storage est un bien public (c'est-à-dire gratuit) qui facilite le stockage perpétuel des métadonnées NFT sur IPFS et Filecoin avec un SDK javascript ou HTTP.


NFT Metadata est un document JSON qui ressemble à l'exemple ci-dessous - qui est tiré directement de la documentation Open Zeppelin :



Lors de la création de NFT, il est important de noter qu'à moins que vous ne stockiez les métadonnées en chaîne (ce qui peut devenir prohibitif pour les fichiers volumineux), afin de vous conformer à la « non-fongibilité » d'un jeton, vous avez besoin d'un stockage qui est persistant, fiable et immuable.


Si votre NFT a une adresse basée sur la localisation comme dans l'exemple ci-dessus, il est assez simple de désactiver ce chemin de localisation après une vente, ce qui signifie que le NFT que vous pensiez avoir acheté devient quelque chose de complètement différent - ou une traction littérale de tapis dans le cas ci-dessous où le créateur de NFT a remplacé les images d'art par des images de tapis.



Quelque chose même Open Zeppelin met en garde !



L'utilisation de NFT.Storage signifie que nous obtenons un CID de fichier IPFS immuable ( contenu - pas emplacement - identifiant ) pour nos métadonnées qui ne sont pas seulement épinglées à IPFS, mais également stockées dans Filecoin pour la persistance. Vous n'aurez qu'à vous inscrire pour NFT.Storage et obtenez une clé API (à enregistrer dans votre fichier .env) pour celui-ci.


.env example


 NEXT_PUBLIC_NFT_STORAGE_API_KEY=xxx


Nous devons également nous assurer que nous avons créé un JSON de métadonnées correctement formé - car bien que FVM n'ait pas (encore !) de places de marché NFT... nous voulons nous assurer que lorsqu'il sera adopté, notre NFT sera toujours, espérons-le, conforme à la norme. .


 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; };


Stockons maintenant ces métadonnées dans 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 - nous avons notre image de Bacalhau, nous avons enregistré nos métadonnées de manière immuable et persistante avec NFT.Strorage, maintenant créons notre NFT !


💡 Astuce rapide 💡NFT.Storage propose également une gamme d'autres appels d'API comme storeCar & storeDirectory ainsi qu'une fonction status() - qui renvoie l'épinglage IPFS et les offres de stockage Filecoin d'un CID -> cela pourrait être un ajout plutôt cool pour un DApp FEVM (ou une implémentation NFT sur FEVM une fois que FEVM a atteint la version du réseau principal) pour vérifier l'état des NFT.

Interactions contractuelles

Il existe 3 types d'interactions ici (et quelques pièges FEVM - la technologie bêta aura toujours des fonctionnalités de bogues bizarres !)


  • appels en lecture seule pour récupérer les données de la chaîne sans les faire muter

  • écrire des appels qui nécessitent un portefeuille pour signer et payer l'essence, c'est-à-dire. des fonctions qui changent l'état de la chaîne, comme frapper le NFT !

  • auditeurs d'événements - qui écoutent les événements émis par le contrat


Pour toutes ces fonctions, nous utiliserons la bibliothèque ethers.js - un wrapper léger pour l'API Ethereum, pour nous connecter à notre contrat et y effectuer des appels.


Connexion au contrat en lecture avec un RPC public :


 //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 );


Ecoute des événements sur le contrat. Puisqu'il s'agit d'un événement en lecture seule (get), nous pouvons utiliser le RPC public pour écouter les émissions d'événements sur la chaîne.


 //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 } );


Connexion au contrat en mode écriture - cela nécessite que l'objet Ethereum soit injecté dans le navigateur Web par un portefeuille afin qu'un utilisateur puisse signer une transaction et payer du gaz - c'est pourquoi nous recherchons une fenêtre.ethereum objet.


 //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); }

Appel de la fonction mint à l'aide du contrat connecté en écriture.


Tout d'abord, assurez-vous que nous avons une adresse de portefeuille de l'utilisateur et que nous sommes sur la chaîne FVM Hyperspace. Voici quelques fonctions de portefeuille utiles que vous pourriez souhaiter, notamment comment vérifier le chainId et comment ajouter par programme le réseau Hyperspace à Metamask / wallet.Vous pouvez interagir avec les portefeuilles en utilisant directement l'objet Ethereum ou en utilisant 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); }); } };


Appelez la fonction contract mint en mode écriture....


 // 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 frappé !! C'est l'heure du mode danse de la Licorne !

🌟 Réflexions finales : possibilités pour l'IA et la blockchain

Bacalhau se prête bien à l'exécution de tâches de traitement répétitives et déterministes sur des données.

  • Processus ETL

  • Apprentissage automatique et IA

  • Intégration de données IOT

  • Traitement par lots, y compris pour

    • Données financières et de marché
  • Traitement vidéo et image - idéal pour les créatifs


Il existe de nombreux exemples dans la documentation de Bacalhau sur la manière d'atteindre certains des objectifs ci-dessus.

Alors que Bacalhau est en train de développer une intégration pour appeler directement Bacalhau à partir des contrats FEVM Smart, voici quelques réflexions sur les collaborations Bacalhau x FVM :


  • Aide à l'intégration et à la désintégration des données Filecoin à l'avenir
  • Aidez à bâtir une réputation et une couche de qualité de service pour Filecoin en traitant les données récupérées en chaîne sur les offres et les fournisseurs de stockage.
  • Bacalhau pourrait fournir des calculs pour les données de marché et de paiement
  • Bacalhau pourrait aider au traitement des données des DAO et DataDAO
  • Bacalhau pourrait aider à renforcer l'automatisation des activités créatives telles que le traitement des vidéos et des images
  • Bacalhau peut activer le traitement des données de jeu et de métaverse, y compris pour la réalité virtuelle et la réalité augmentée.
  • Bacalhau, IOT & Simulations sont possibles
  • Applications IA et ML

🐠 La feuille de route de Bacalhau


Nous construisons actuellement un moyen pour vous d'exécuter Bacalhau directement à partir de vos contrats intelligents !!!! Ce projet s'appelle Project Frog / Project Lilypad - et sera une couche d'intégration qui permettra d'appeler des travaux Bacalhau à partir de contrats intelligents FEVM.


Gardez un œil sur les progrès de cela en vous inscrivant à notre newsletter ou en rejoignant les réseaux sociaux ci-dessous.

✍️ Restez en contact !

Félicitations si vous avez lu jusqu'au bout !!!


J'apprécierais un like, un commentaire, un follow ou un partage si cela vous a été utile ! <3


Restez en contact avec Bacalhau !



Avec ♥️ DeveloperAlly

Avez-vous trouvé cet article précieux?

Soutenez Alison Haire en devenant un sponsor. Tout montant est apprécié!


En savoir plus sur les sponsors Hashnode


Également publié ici .