FVM Hyperspace Testnet'te AI tarafından oluşturulan sanat NFT'lerini basmak için kendi Metinden Görüntüye komut dosyanızla bir DApp oluşturma, çalıştırma ve dağıtmaya yönelik eksiksiz bir kılavuz!
Bu blog size nasıl yapılacağı konusunda yol gösterecek
Tensorflow'u temel alan açık kaynaklı python tabanlı bir metinden görüntüye komut dosyası oluşturun (bu ilginizi çekmiyorsa Bacalhau HTTP uç noktasını da kullanabilirsiniz)
Bu betiği Bacalhau'da (açık bir p2p zincir dışı bilgi işlem platformu) çalıştırın
Solidity'de bir NFT Sözleşmesi oluşturun (Açık Zeppelin ERC721 sözleşmesine dayalı olarak)
NFT Sözleşmesini Hardhat ile Filecoin Sanal Makinesi (FVM) Hiperuzay Test Ağına Dağıtın
Ön uç etkileşimleri - React'te Bacalhau metinden resme komut dosyası ve NFT sözleşmenizle nasıl etkileşime girilir?
NFT Meta Verilerinizi NFT.Storage'a nasıl kaydedersiniz?
Ön Uç DApp'inizi Fleek'e nasıl dağıtabilirsiniz?
Bu yığında mevcut olduğu kadar açık kaynaklı ve merkezi olmayan teknolojiyi bilinçli olarak kullanmayı seçtim.
Bu blog oldukça uzun olacak (hey - TÜM BİLGİLERİ vermek ve yeni başlayanlar için uygun ve kapsayıcı olduğumuzdan emin olmak istiyorum!) - bu nedenle tabloda sizin için yararlı olan kısımlara atlamaktan çekinmeyin içindekiler <3
(anlayın - bu bir gözleme yığını #üzgünümözürlenmiyorum)
Sıfırdan Açık Kaynak ve Web3 değerli :)
💡TLDR İpucu 💡
Bu komut dosyası halihazırda CLI ve HTTP uç noktası aracılığıyla Bacalhau aracılığıyla kullanıma hazır olduğundan bu bölümü atlamaktan çekinmeyin.
Kararlı Difüzyona Hızlı Giriş
Kararlı Difüzyon şu anda metinden görüntüye işleme için önde gelen Makine Öğrenimi Modelidir (& Dall-E'nin kullandığı modelin aynısıdır). Bu, bir tür Derin Öğrenmedir (Makine Öğreniminin bir alt kümesidir ve kendine belirli bir görevi gerçekleştirmeyi öğretir), bu durumda bir metin girişini bir görüntü çıkışına dönüştürür.
Bu örnekte, metinden görüntüler oluşturmak için dönüştürücü kullanan bir yayılma olasılık modeli kullanıyoruz.
Yine de endişelenmeyin; bunun için gidip bir makine öğrenimi modeli yetiştirmemize gerek yok (gerçi hey, eğer bu sizin işinizse, kesinlikle yapabilirsiniz!)
Bunun yerine, ML ağırlıkları bizim için önceden hesaplandığından, python betiğimizde Google'ın TensorFlow açık kaynaklı Makine Öğrenimi kitaplığından önceden eğitilmiş bir model kullanacağız.
Daha doğrusu, orijinal ML modelinin optimize edilmiş Keras/TensorFlow uygulama çatalını kullanıyoruz.
Python Komut Dosyası
🦄 Bu metinden resme komut dosyasının nasıl oluşturulup Dockerize edileceğine ve Bacalhau'da çalıştırılacağına ilişkin eksiksiz bir kılavuzu hem Bacalhau belgelerinde hem de bu @BacalhauProject YouTube videosunda bulabilirsiniz. 🦄 Bunu ayrıca bu Google Collabs Not Defteri'nde de çalıştırabilirsiniz.
İşte tam python betiği!
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")
Yukarıdaki komut dosyası, bir metin istemi giriş argümanını ve diğer bazı isteğe bağlı parametreleri alır ve ardından görüntüleri oluşturmak ve bunları bir çıktı dosyasına kaydetmek için çatallı TensorFlow kitaplığını çağırır.
Burada yapılan tüm ağır işler aşağıdaki bölümde gerçekleşir; Makine Öğrenimi Modelinin sihrini gösterdiği yer burasıdır. 🪄
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, )
Harika, bir metin isteminden görüntüler üretebiliriz, ama hım... GPU gerektiren bu komut dosyasını nerede çalıştıracağız..... 🤔🤔
Blockchain teknolojisinin doğası gereği iyi yapamadığı bir şey varsa, o da büyük veri işlemedir. Bunun nedeni, güvensizlik ve sansüre dayanıklılık gibi diğer güçlü özellikleri sağlamak için dağıtılmış bir sistem üzerinden bilgi işlem yapmanın maliyetidir.
Küçük örnekler için yerel makinenizi kullanmak mümkündür - aslında bu özel örneği Mac M1'imde çalıştırmayı başardım (bundan çok memnun değilim), ancak sonuçlar için çok uzun bir bekleyiş oldu (masa tenisi oynayan var mı?) dolayısıyla, daha büyük veri işlemeye başladığınızda, daha fazla gaza ihtiyacınız olacak (kelime oyunu) ve eğer evin etrafında özel bir sunucunuz yoksa, o zaman bir sanal makine üzerinde bir sanal makine kullanmanız gerekecektir. bulut bilişim platformu.
Bu sadece merkezi olmakla kalmıyor, aynı zamanda verinin hesaplama makinesinden bilinmeyen bir uzaklıkta olması nedeniyle verimsiz ve hızlı bir şekilde maliyetli olabiliyor. Bunun için GPU işleme sunan herhangi bir ücretsiz katmanlı bulut bilgi işlem hizmeti bulamadım (birisi kripto madenciliği yasaklarını mı söyledi..?) ve planlar ayda > 400 ABD dolarından geldi (teşekkür etmeyin).
Bacalhau!
Neyse ki bu sorunlar Bacalhau'nun çözmeye çalıştığı sorunlardan bazıları. Bacalhau'da veri işleme ve hesaplamayı açık ve herkes için kullanılabilir hale getirmek ve işlem sürelerini hızlandırmak, ilk olarak birden fazla düğümde toplu işlemeyi kullanarak ve ikinci olarak işleme düğümlerini verinin bulunduğu yere yerleştirerek mümkündür!
Bacalhau, daha geniş anlamda IPFS, Filecoin ve Web3'e özgü merkezi olmayan değerlerden vazgeçmeden veriler üzerinde zincir dışı hesaplamayı mümkün kılarak veri işlemenin geleceğini demokratikleştirmeye yardımcı olmayı amaçlıyor.
Bacalhau , kullanıcıların Docker kapsayıcılarını veya Web Montaj görüntülerini IPFS'de (ve yakında Filecoin) depolanan veriler de dahil olmak üzere herhangi bir veriye karşı görev olarak çalıştırabilecekleri genel, şeffaf ve isteğe bağlı olarak doğrulanabilir hesaplama süreçleri için bir platform sağlayan, eşler arası bir açık hesaplama ağıdır. Hatta GPU işleri için desteği bile var ve fiyatı 400 ABD Doları veya daha fazla değil!
Komut dosyasını Bacalhau'da çalıştırma
Bu betiği çalıştırmak için onu Bacalhau'da kullanmak üzere Dockerize edebiliriz. Bunu yapmayı öğrenmek istiyorsanız buradaki öğreticiyi takip edebilirsiniz. Daha sonra bunu yalnızca bir satır kodla Bacalhau CLI ile çalıştırabiliriz ( Bacalhau'yu başka bir tek satırlık kodla yükledikten sonra):
bacalhau docker run --gpu 1 ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1 -- python main.py --o ./outputs --p "Rainbow Unicorn"
Ancak bu örnekte, beni bu dockerleştirilmiş kararlı difüzyon betiğine bağlayan bir HTTP uç noktası kullanacağım ve bunu size Entegrasyonlar bölümünde göstereceğim!
Ancak burada bunun veri hesaplama süreçlerini çalıştırmanın güçlü ve esnek bir yolu olduğunu ve aynı zamanda web3 dostu olduğunu da belirtmeliyim; biz sadece bu küçük modelle sınırlı değiliz.
Neyse NFT betiğine geçelim! :)
Akıllı Sözleşme
NFT Akıllı Sözleşmesi , Open Zeppelin'in ERC721 uygulamasını temel alır ancak meta veri standart uzantılarını içeren ERC721URIStorage sürümünü kullanır (böylece NFT.Storage'a kaydedeceğimiz IPFS adresli meta verilerimizi sözleşmeye aktarabiliriz) .
Bu temel sözleşme ayrıca bize, bizim için zaten uygulanmış olan mint() ve transfer() gibi işlevlerle bir NFT sözleşmesinin genel işlevselliğini de sağlar.
Ayrıca ön ucum için veri almak üzere birkaç alıcı işlevi ve her yeni bir NFT basıldığında zincir üzerinde yayınlanacak bir Etkinlik eklediğimi fark edeceksiniz. Bu, DApp'ten zincir içi olayları dinleme olanağı sağlar.
💡 Remikste deneyin ve bu bağlantıya tıklayarak mevcut tüm işlevleri görün! 💡
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]; }
Gereksinimler
Bu sözleşmeyi Filecoin Sanal Makine Hiperuzay Testnet'ine dağıtacağım, ancak bu sözleşmeyi Polygon, BSC, Optimism, Arbitrum, Avalanche ve daha fazlası dahil olmak üzere EVM uyumlu herhangi bir zincire dağıtabilirsiniz. Çok zincirli bir NFT oluşturmak için ön ucunuzda bile ince ayar yapabilirsiniz (ipucu: bu repo )!
Hyperspace Testnet'e dağıtmak için şunları yapmamız gerekir:
Akıllı Sözleşmeyi Hardhat ile Dağıtma
Bu sözleşmeyi Hyperspace test ağına dağıtmak için kask kullanıyorum.
🛸 Hyperspace RPC ve BlockExplorer Seçenekleri:
Genel RPC Uç Noktaları
BlockExplorer'ın
https://filecoin-hyperspace.chainstacklabs.com/rpc/v0
https://hyperspace.filfox.info/rpc/v0
https://fvm.starboard.ventures/contracts/
https://rpc.ankr.com/filecoin_testnet
https://explorer.glif.io/?network=hyperspacenet
API'yi açın : beryx.zondax.ch
Yapılandırma kurulumu için mevcut genel RPC uç noktalarından herhangi birini seçebiliriz.
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;
Ve akıllı sözleşmeyi dağıtmak için bir dağıtım komut dosyası oluşturuyoruz - burada imzalayan (sahip) olarak özellikle Cüzdan adresini ayarladığımı unutmayın - yazım sırasında FEVM'de hala çalışılmakta olan birkaç haritalama hatası var. bazı tuhaf davranışlar.
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; });
Dağıtmak için, aşağıdaki kodu kullanarak yukarıdaki betiği terminalde çalıştırın (Not: yapılandırmamızda varsayılan ağı filecoinHyperspace olarak ayarladığımızdan, aşağıda gösterilse de ağ için bir bayrak iletmeye gerek yoktur)
> cd ./pages/hardhat/deploy/
npx hardhat run ./deployBacalhauFRC721.ts --network filecoinHyperspace
Kutlamak! NFT sözleşmemizi Filecoin hiperuzay test ağına dağıttık!
Wooo güzel kısma... ve ayrıca hepsini bir arada tutan yapıştırıcıya :)
Ön ucu oluşturmak için NextJS ve Typescript kullanıyorum. Ancak dürüst olmak gerekirse, NextJS'nin SSR (sunucu tarafı oluşturma) özelliklerinden hiçbirinden faydalanmıyorum ve sayfa yönlendirmelerini bile kullanmıyorum (çünkü tek sayfalık bir Dapp), yani gerçekten gidebilirsiniz Vanilya React kurulumuyla (veya elbette seçtiğiniz herhangi bir çerçeveyle!).
Daktiloya gelince... bunu biraz aceleyle yaptım ve itiraf etmeliyim ki bu çok iyi bir TypeScript örneği değil - yine de değişkenler mutlu görünüyor... ;)
Neyse - bu bölümün asıl amacı size bir ön ucun nasıl kodlanacağını göstermek değil, akıllı sözleşme, Bacalhau (kararlı difüzyon ML modelimizle) ve tabii ki NFT.Storage ile nasıl etkileşim kuracağınızı göstermektir. - # NotOnIPFSNotYourNFT.
[yapılacak şey: bir akış şeması diyagramı oluşturun]
Güzel - bunu kodda nasıl uygulayacağımızı görelim!
Bacalhau için ön uç API uç noktasının oluşturulması, mühendis Luke Marsden tarafından bu proje raporunda belgelenmiştir.
API şu anda yalnızca bu blogda belgelenen kararlı dağıtım komut dosyalarına doğrudan ulaşmaktadır; ancak ekip, örneklerden herhangi birini ve ayrıca kendi dağıtılan komut dosyalarınızı bir HTTP'den çağırabilmeniz için onu daha genel bir API'ye genişletme sürecindedir. REST API'si. Bunu buradan veya FilecoinProject slack'teki #bacalhau kanalından takip edin.
>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; };
Bu işlev, aşağıdaki gibi bir klasör yapısına sahip bir IPFS CID (içerik tanımlayıcı) döndürecektir. Görüntü daha sonra /outputs/image0.png
altında bulunabilir.
Ahhh gökkuşağı tek boynuzlu atları... sevilmeyecek ne var ki!
NFT.Storage, NFT Meta Verilerinin bir javascript veya HTTP SDK ile IPFS ve Filecoin üzerinde sürekli olarak saklanmasını kolaylaştıran bir kamu yararıdır (ücretsiz olarak da bilinir).
NFT Meta Verileri, doğrudan Open Zeppelin belgelerinden alınan aşağıdaki örneğe benzeyen bir JSON belgesidir:
NFT'ler oluştururken, meta verileri zincir üzerinde saklamadığınız sürece (ki bu büyük dosyalar için aşırı derecede pahalı olabilir), bir tokenın 'değiştirilemezliğine' uyum sağlamak için, şuna dikkat etmeniz önemlidir: kalıcı, güvenilir ve değişmez.
NFT'niz yukarıdaki örnek gibi konuma dayalı bir adrese sahipse, bu konum yolunun bir satıştan sonra değiştirilmesi oldukça basittir; bu, satın aldığınızı düşündüğünüz NFT'nin tamamen farklı bir şeye veya bu durumda gerçek anlamda bir halı çekme haline geldiği anlamına gelir. NFT yaratıcısının halı resimleri yerine sanat görsellerini değiştirdiği yerin altında.
Açık Zeplin'in bile uyardığı bir şey!
NFT.Storage kullanmak, meta verilerimiz için yalnızca IPFS'ye sabitlenmekle kalmayıp aynı zamanda kalıcılık için Filecoin'de saklanan değişmez bir IPFS dosyası CID'si ( içerik - konum değil - kimlik tanımlayıcı) elde ettiğimiz anlamına gelir. NFT.Storage'a gidin ve bunun için bir API anahtarı alın (.env dosyanıza kaydetmek için).
.env example
NEXT_PUBLIC_NFT_STORAGE_API_KEY=xxx
Ayrıca düzgün biçimlendirilmiş bir Meta Veri JSON oluşturduğumuzdan da emin olmalıyız - çünkü FVM'de (henüz!) NFT Pazaryerleri olmasa da... benimsendiğinde NFT'mizin yine de standarda uygun olmasını umduğumuzdan emin olmak istiyoruz .
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; };
Şimdi bu meta verileri NFT.Storage'a kaydedelim!
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 - Bacalhau'dan görselimiz var, meta verilerimizi NFT.Strorage ile değişmez ve kalıcı bir şekilde kaydettik, şimdi NFT'mizi oluşturalım!
💡 Kısa İpucu 💡NFT.Storage ayrıca, CID'nin IPFS sabitleme ve Filecoin depolama fırsatlarını döndüren status() işlevinin yanı sıra, StoreCar & StoreDirectory gibi bir dizi başka API çağrıları da sunar -> bu, aşağıdakiler için oldukça hoş bir eklenti olabilir: NFT'lerin durumunu kontrol etmek için bir FEVM DApp (veya FEVM ana ağ sürümüne ulaştığında FEVM'de NFT uygulaması).
Burada 3 tür etkileşim var (ve birkaç FEVM özelliği var - beta teknolojisi her zaman bazı ilginç hata özelliklerine sahip olacak!)
Zincirdeki verileri değiştirmeden almak için salt okunur çağrılar
İmzalamak ve benzin ödemek için bir cüzdan gerektiren aramalar yazın. NFT'yi basmak gibi zincirin durumunu değiştiren işlevler!
olay dinleyicileri - sözleşmeden yayılan olayları dinleyenler
Tüm bu işlevler için, sözleşmemize bağlanmak ve ona çağrılar gerçekleştirmek amacıyla Ethereum API'si için hafif bir sarmalayıcı olan eters.js kitaplığını kullanacağız.
Sözleşmeye genel bir RPC ile okuma modunda bağlanma:
//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 );
Sözleşmedeki olayları dinlemek. Bu salt okunur (get) bir olay olduğundan, zincirdeki olay emisyonlarını dinlemek için genel RPC'yi kullanabiliriz.
//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 } );
Sözleşmeye yazma modunda bağlanma - bu, kullanıcının bir işlemi imzalayabilmesi ve gaz için ödeme yapabilmesi için Ethereum nesnesinin bir cüzdan tarafından web tarayıcısına enjekte edilmesini gerektirir - bu yüzden bir window.ethereum olup olmadığını kontrol ediyoruz. nesne.
//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); }
Yazma bağlantılı sözleşmeyi kullanarak mint İşlevinin çağrılması.
Öncelikle kullanıcıdan bir cüzdan adresi aldığımızdan ve FVM Hyperspace zincirinde olduğumuzdan emin olun. ChainId'nin nasıl kontrol edileceği ve Hyperspace ağının Metamask/cüzdan'a programlı olarak nasıl ekleneceği de dahil olmak üzere isteyebileceğiniz birkaç yararlı cüzdan işlevini burada bulabilirsiniz. Doğrudan Ethereum nesnesini kullanarak veya eters.js kullanarak cüzdanlarla etkileşim kurabilirsiniz.
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); }); } };
Yazma modunda sözleşme nane işlevini çağırın....
// 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 Basıldı!! Tek boynuzlu at dansı modu zamanı!
Bacalhau, veriler üzerinde tekrarlayan, deterministik işleme işlerini gerçekleştirme konusunda oldukça başarılıdır.
ETL Süreçleri
Makine Öğrenimi ve Yapay Zeka
IoT veri entegrasyonu
Toplu İşleme şunları içerir:
Video ve Görüntü işleme - yaratıcılar için harika
Bacalhau belgelerinde yukarıdakilerden bazılarının nasıl elde edileceğine dair çok sayıda örnek vardır.
Bacalhau, FEVM Akıllı sözleşmelerinden doğrudan Bacalhau'yu aramak için bir entegrasyon oluşturmakla meşgul olsa da Bacalhau x FVM işbirlikleri hakkında bazı düşünceler:
Şu anda Bacalhau'yu doğrudan akıllı sözleşmelerinizden çalıştırmanın bir yolunu geliştiriyoruz!!!! Bu projeye Project Frog / Project Lilypad adı veriliyor ve FEVM akıllı sözleşmelerinden Bacalhau işlerinin çağrılmasını sağlayacak bir entegrasyon katmanı olacak.
Bültenimize kaydolarak veya aşağıdaki sosyal medyaya katılarak bu konudaki ilerlemeyi takip edin.
Eğer sonuna kadar okuduysanız tebrikler!!!
Bu sizin için yararlı olduysa beğenir, yorum yapar, takip eder veya paylaşırsanız sevinirim! <3
Bacalhau'yla iletişiminizi sürdürün!
♥️ DeveloperAlly ile
Sponsor olarak Alison Haire'ı destekleyin. Herhangi bir miktar takdir edilir!
Hashnode Sponsorları hakkında daha fazla bilgi edinin
Burada da yayınlandı.