Hướng dẫn đầy đủ để xây dựng, chạy và triển khai DApp với tập lệnh Chuyển văn bản thành hình ảnh của riêng bạn để đúc các NFT nghệ thuật do AI tạo trên FVM Hyperspace Testnet!
Blog này sẽ hướng dẫn bạn cách
Xây dựng tập lệnh chuyển văn bản thành hình ảnh dựa trên python mã nguồn mở dựa trên Tensorflow (bạn cũng có thể chỉ cần sử dụng điểm cuối HTTP Bacalhau nếu điều này không khiến bạn quan tâm)
Chạy tập lệnh này trên Bacalhau (một nền tảng điện toán ngoài chuỗi p2p mở)
Tạo Hợp đồng NFT trong Solidity (dựa trên hợp đồng Open Zeppelin ERC721)
Triển khai Hợp đồng NFT cho Mạng thử nghiệm siêu không gian của Máy ảo Filecoin (FVM) với Hardhat
Tương tác giao diện người dùng - Cách tương tác với tập lệnh chuyển văn bản thành hình ảnh Bacalhau và hợp đồng NFT của bạn trong React
Cách lưu Siêu dữ liệu NFT của bạn vào NFT.Storage
Cách triển khai DApp Front-End của bạn cho Fleek
Tôi đã cố tình chọn sử dụng càng nhiều công nghệ mã nguồn mở và phi tập trung càng tốt trong ngăn xếp này.
Blog này sẽ khá dài (này - tôi muốn cung cấp TẤT CẢ THÔNG TIN và đảm bảo rằng chúng tôi thân thiện với người mới bắt đầu và toàn diện!) - vì vậy, vui lòng bỏ qua các phần hữu ích cho bạn trong bảng của nội dung <3
(hiểu rồi - đó là chồng bánh kếp #sorrynotsorry)
Nguồn mở & có giá trị Web3 ngay từ đầu :)
💡 Mẹo TLDR 💡
Tập lệnh này đã có sẵn để sử dụng thông qua Bacalhau thông qua CLI và điểm cuối HTTP, vì vậy vui lòng bỏ qua phần này.
Giới thiệu nhanh về khuếch tán ổn định
Khuếch tán ổn định hiện là Mô hình học máy hàng đầu để thực hiện xử lý văn bản thành hình ảnh (& là mô hình tương tự mà Dall-E sử dụng). Đó là một loại Học sâu - một tập hợp con của Học máy tự dạy để thực hiện một tác vụ cụ thể - trong trường hợp này là chuyển đổi đầu vào văn bản thành đầu ra hình ảnh.
Trong ví dụ này, chúng tôi đang sử dụng mô hình xác suất khuếch tán sử dụng máy biến áp để tạo hình ảnh từ văn bản.
Mặc dù vậy, đừng lo lắng - chúng ta không cần phải đào tạo một mô hình học máy cho việc này (mặc dù này - nếu đó là việc của bạn - bạn hoàn toàn có thể làm được!)
Thay vào đó, chúng tôi sẽ sử dụng một mô hình được đào tạo trước từ thư viện Máy học mã nguồn mở TensorFlow của Google trong tập lệnh python của chúng tôi vì các trọng số ML đã được tính toán trước cho chúng tôi.
Chính xác hơn, chúng tôi đang sử dụng nhánh triển khai Keras/TensorFlow được tối ưu hóa của mô hình ML ban đầu.
Tập lệnh Python
🦄 Bạn có thể tìm thấy hướng dẫn đầy đủ về cách xây dựng và Dockerise tập lệnh chuyển văn bản thành hình ảnh này và chạy tập lệnh đó trên Bacalhau trong cả tài liệu Bacalhau và trong video YouTube @BacalhauProject này .🦄 Bạn cũng có thể chạy tập lệnh này trong Google Collabs Notebook
Đây là tập lệnh python đầy đủ!
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")
Tập lệnh ở trên chỉ cần nhập đối số đầu vào dấu nhắc văn bản và một số tham số tùy chọn khác, sau đó gọi thư viện TensorFlow đã rẽ nhánh để tạo (các) hình ảnh và lưu chúng vào tệp đầu ra.
Tất cả những công việc nặng nhọc được thực hiện ở đây đều diễn ra trong phần bên dưới - đây là nơi Mô hình Học máy thực hiện điều kỳ diệu của nó. 🪄
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, )
Tuyệt vời, chúng tôi có thể tạo hình ảnh từ lời nhắc văn bản, nhưng ừm... chạy tập lệnh yêu cầu GPU này ở đâu..... 🤔🤔
Nếu có một điều mà công nghệ chuỗi khối vốn không hoạt động tốt, thì đó là xử lý dữ liệu lớn. Điều này là do chi phí tính toán trên một hệ thống phân tán để cung cấp các thuộc tính mạnh mẽ khác như tính tin cậy và khả năng chống kiểm duyệt.
Có thể sử dụng máy cục bộ của bạn cho các ví dụ nhỏ - trên thực tế, tôi đã cố gắng để ví dụ cụ thể này hoạt động trên máy Mac M1 (rất không hài lòng về nó) của mình, tuy nhiên, phải chờ kết quả rất lâu (trò chơi bóng bàn có ai không?) vì vậy, một khi bạn bắt đầu xử lý dữ liệu lớn hơn, bạn sẽ cần nhiều gas hơn (ý định chơi chữ) và nếu bạn không có máy chủ chuyên dụng nằm quanh nhà, thì bạn sẽ cần sử dụng máy ảo trên nền tảng điện toán đám mây.
Điều đó không chỉ tập trung mà còn không hiệu quả - do dữ liệu ở một khoảng cách không xác định so với máy tính và nó có thể trở nên tốn kém nhanh chóng. Tôi không tìm thấy bất kỳ dịch vụ điện toán đám mây cấp miễn phí nào cung cấp khả năng xử lý GPU cho việc này (có ai nói lệnh cấm khai thác tiền điện tử không..?) và các gói có mức phí > 400 USD một tháng (không, cảm ơn bạn).
Bacalhau!
Tuy nhiên, may mắn thay, những vấn đề này là một số vấn đề mà Bacalhau đang cố gắng giải quyết. Trước hết, Bacalhau có thể làm cho việc xử lý và tính toán dữ liệu trở nên mở và sẵn có cho mọi người cũng như tăng tốc thời gian xử lý - bằng cách sử dụng xử lý hàng loạt trên nhiều nút và thứ hai bằng cách đặt các nút xử lý vào nơi dữ liệu tồn tại!
Bacalhau đang hướng tới mục tiêu giúp dân chủ hóa tương lai của việc xử lý dữ liệu bằng cách cho phép tính toán dữ liệu ngoài chuỗi mà không từ bỏ các giá trị phân cấp vốn có của IPFS, Filecoin & Web3 một cách rộng rãi hơn.
Bacalhau là một mạng tính toán mở ngang hàng cung cấp nền tảng cho các quy trình tính toán công khai, minh bạch và có thể kiểm chứng tùy chọn, nơi người dùng có thể chạy bộ chứa Docker hoặc hình ảnh Web Assembly dưới dạng tác vụ đối với bất kỳ dữ liệu nào kể cả dữ liệu được lưu trữ trong IPFS (và sắp có Filecoin). Nó thậm chí còn hỗ trợ cho các công việc GPU chứ không phải ở mức 400 đô la Mỹ trở lên!
Chạy tập lệnh trên Bacalhau
Để chạy tập lệnh này, chúng tôi có thể Dockerise nó để sử dụng trên Bacalhau. Bạn có thể làm theo hướng dẫn tại đây nếu muốn học cách làm điều đó. Sau đó, chúng tôi có thể chạy nó với Bacalhau CLI chỉ với một dòng mã (sau khi cài đặt Bacalhau với một lớp lót khác):
bacalhau docker run --gpu 1 ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1 -- python main.py --o ./outputs --p "Rainbow Unicorn"
Tuy nhiên, trong ví dụ này, tôi sẽ sử dụng một điểm cuối HTTP để kết nối tôi với tập lệnh khuếch tán ổn định dockerised này, mà tôi sẽ chỉ cho bạn trong phần Tích hợp!
Tuy nhiên, tôi sẽ lưu ý ở đây rằng đây là một cách mạnh mẽ và linh hoạt để chạy các quy trình tính toán dữ liệu cũng thân thiện với web3 - chúng tôi không chỉ giới hạn ở một mô hình nhỏ này.
Tuy nhiên, hãy chuyển sang tập lệnh NFT! :)
Hợp đồng thông minh
Hợp đồng thông minh NFT dựa trên việc triển khai ERC721 của Open Zeppelin nhưng sử dụng phiên bản ERC721URIStorage, bao gồm các tiện ích mở rộng tiêu chuẩn siêu dữ liệu (vì vậy chúng tôi có thể chuyển siêu dữ liệu có địa chỉ IPFS của mình - chúng tôi sẽ lưu trên NFT.Storage, vào hợp đồng) .
Ngoài ra, hợp đồng cơ sở này cung cấp cho chúng tôi chức năng chung của hợp đồng NFT với các chức năng như mint() và transfer() đã được triển khai cho chúng tôi.
Bạn sẽ nhận thấy rằng tôi cũng đã thêm một vài hàm getter để tìm nạp dữ liệu cho giao diện người dùng của mình cũng như một Sự kiện sẽ được phát ra trên chuỗi mỗi khi tạo ra một NFT mới. Điều này mang lại khả năng lắng nghe các sự kiện trên chuỗi từ DApp.
💡 Hãy dùng thử bản phối lại và xem tất cả các chức năng có sẵn bằng cách nhấp vào liên kết này! 💡
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]; }
Yêu cầu
Tôi sẽ triển khai hợp đồng này cho Mạng thử nghiệm siêu không gian máy ảo Filecoin , nhưng bạn có thể triển khai hợp đồng này cho bất kỳ chuỗi nào tương thích với EVM bao gồm Polygon, BSC, Optimism, Arbitrum, Avalanche, v.v. Bạn thậm chí có thể điều chỉnh giao diện người dùng của mình để tạo NFT đa chuỗi (gợi ý: repo này )!
Để triển khai lên Hyperspace Testnet, chúng tôi sẽ cần
Triển khai Hợp đồng thông minh với Hardhat
Tôi đang sử dụng hardhat để triển khai hợp đồng này tới mạng thử nghiệm Hyperspace.
🛸 Tùy chọn RPC siêu không gian & BlockExplorer:
Điểm cuối RPC công khai
BlockExplorer
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
Mở API : beryx.zondax.ch
Đối với thiết lập cấu hình, chúng tôi có thể chọn từ bất kỳ điểm cuối RPC công khai nào có sẵn.
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;
Và để triển khai hợp đồng thông minh, chúng tôi tạo một tập lệnh triển khai - lưu ý rằng tôi đang đặt cụ thể địa chỉ Ví ở đây với tư cách là người ký (chủ sở hữu) - có một số lỗi ánh xạ vẫn đang được xử lý trong FEVM tại thời điểm viết bài có thể gây ra một số hành vi kỳ quặc.
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; });
Để triển khai, hãy chạy tập lệnh trên trong thiết bị đầu cuối bằng cách sử dụng mã sau (Lưu ý: vì chúng tôi đã đặt mạng mặc định thành filecoinHyperspace trong cấu hình của mình, không cần thiết phải chuyển cờ cho mạng mặc dù điều này được hiển thị bên dưới)
> cd ./pages/hardhat/deploy/
npx hardhat run ./deployBacalhauFRC721.ts --network filecoinHyperspace
Kỉ niệm! Chúng tôi vừa triển khai hợp đồng NFT của mình cho mạng thử nghiệm siêu không gian Filecoin!
Wooo vào phần xinh đẹp ... và cũng là chất keo gắn kết tất cả lại với nhau ở đây :)
Để xây dựng giao diện người dùng, tôi đang sử dụng NextJS và Typescript. Mặc dù vậy, thành thật mà nói - tôi không tận dụng bất kỳ tính năng SSR (kết xuất phía máy chủ) nào của NextJS và tôi thậm chí không sử dụng định tuyến trang của họ (vì đó là Dapp một trang), vì vậy bạn thực sự có thể đi tất nhiên là với một React vanilla được thiết lập (hoặc bất kỳ framework nào bạn chọn!).
Đối với bản đánh máy ... tốt, tôi đã xây dựng cái này hơi vội vàng và phải thừa nhận rằng đây không phải là một ví dụ hay về Bản đánh máy - mặc dù vậy, các vars có vẻ hài lòng ...;)
Dù sao đi nữa - điểm chính của phần này không phải là chỉ cho bạn cách viết mã giao diện người dùng mà là chỉ cho bạn cách tương tác với hợp đồng thông minh, Bacalhau (với mô hình ML khuếch tán ổn định của chúng tôi) và tất nhiên, NFT.Storage - # NotOnIPFSNotYourNFT.
[việc cần làm: xây dựng sơ đồ dòng chảy]
Tốt - hãy xem cách chúng tôi triển khai điều này trong mã!
Việc tạo điểm cuối API giao diện người dùng cho Bacalhau được kỹ sư Luke Marsden ghi lại trong báo cáo dự án này .
API hiện chỉ truy cập trực tiếp vào các tập lệnh khuếch tán ổn định được ghi lại trong blog này, tuy nhiên, nhóm đang trong quá trình mở rộng nó thành một API chung hơn để bạn có thể gọi bất kỳ ví dụ nào và cả các tập lệnh được triển khai của riêng bạn từ HTTP API REST. Hãy theo dõi điều này tại đây hoặc trong kênh #bacalhau trong 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; };
Hàm này sẽ trả về một IPFS CID (mã định danh nội dung) với cấu trúc thư mục như bên dưới. Sau đó, bạn có thể tìm thấy hình ảnh trong /outputs/image0.png
.
Ahhh kỳ lân cầu vồng... có gì mà không thích!
NFT.Storage là hàng hóa công cộng (còn gọi là miễn phí) giúp dễ dàng lưu trữ vĩnh viễn Siêu dữ liệu NFT trên IPFS & Filecoin bằng javascript hoặc HTTP SDK.
Siêu dữ liệu NFT là một tài liệu JSON trông giống như ví dụ bên dưới - được lấy trực tiếp từ tài liệu Open Zeppelin:
Khi tạo NFT, điều quan trọng cần lưu ý là trừ khi bạn đang lưu trữ siêu dữ liệu trên chuỗi (có thể trở nên cực kỳ tốn kém đối với các tệp lớn), thì để phù hợp với 'tính không thể thay thế' của mã thông báo, bạn cần có dung lượng lưu trữ phù hợp bền bỉ, đáng tin cậy và bất biến.
Nếu NFT của bạn có địa chỉ dựa trên vị trí như ví dụ trên, thì việc thay đổi đường dẫn vị trí này sau khi bán là khá đơn giản, nghĩa là NFT mà bạn nghĩ mình đã mua sẽ trở thành một thứ hoàn toàn khác - hoặc một tấm thảm theo nghĩa đen kéo vào hộp bên dưới, nơi người tạo NFT đã chuyển các hình ảnh nghệ thuật thành hình ảnh của những tấm thảm.
Một cái gì đó thậm chí Open Zeppelin cảnh báo về!
Sử dụng NFT.Storage có nghĩa là chúng tôi nhận được CID tệp IPFS bất biến ( nội dung - không phải vị trí - id mã hóa) cho siêu dữ liệu của chúng tôi, siêu dữ liệu này không chỉ được ghim vào IPFS mà còn được lưu trữ vào Filecoin để duy trì lâu dài. Bạn chỉ cần đăng ký để sử dụng NFT.Storage và nhận khóa API (để lưu trong tệp .env của bạn) cho khóa này.
.env example
NEXT_PUBLIC_NFT_STORAGE_API_KEY=xxx
Chúng tôi cũng cần đảm bảo rằng chúng tôi đã tạo JSON Siêu dữ liệu được định dạng chính xác - bởi vì trong khi FVM không (chưa!) có Thị trường NFT... chúng tôi muốn đảm bảo rằng khi nó được thông qua, NFT của chúng tôi hy vọng vẫn tuân thủ tiêu chuẩn .
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; };
Bây giờ, hãy lưu trữ siêu dữ liệu này vào 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 - chúng tôi có hình ảnh của mình từ Bacalhau, chúng tôi đã lưu siêu dữ liệu của mình một cách bất biến và liên tục với NFT.Strorage, bây giờ hãy đúc NFT của chúng tôi!
💡 Mẹo nhanh 💡NFT.Storage cũng cung cấp một loạt các lệnh gọi API khác như storeCar & storeDirectory cũng như hàm status() - trả về giao dịch ghim IPFS và lưu trữ Filecoin của CID -> đây có thể là một bổ sung khá thú vị cho một FEVM DApp (hoặc triển khai NFT trên FEVM sau khi FEVM đạt bản phát hành mạng chính) để kiểm tra trạng thái NFT.
Có 3 loại tương tác ở đây (và một số lỗi của FEVM - công nghệ beta luôn có một số tính năng lỗi kỳ quặc!)
lệnh gọi chỉ đọc để truy xuất dữ liệu từ chuỗi mà không làm thay đổi chuỗi
viết các cuộc gọi yêu cầu ví để ký và trả tiền gas tức là. các chức năng thay đổi trạng thái của chuỗi, chẳng hạn như đúc NFT!
người nghe sự kiện - lắng nghe các sự kiện phát ra từ hợp đồng
Đối với tất cả các chức năng này, chúng tôi sẽ sử dụng thư viện ethers.js - một trình bao bọc nhẹ cho API Ethereum, để kết nối với hợp đồng của chúng tôi và thực hiện các lệnh gọi tới nó.
Kết nối với hợp đồng ở chế độ đọc với RPC công khai:
//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 );
Lắng nghe các sự kiện trong hợp đồng. Vì đây là sự kiện chỉ đọc (nhận), nên chúng tôi có thể sử dụng RPC công khai để lắng nghe phát thải sự kiện trên chuỗi.
//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 } );
Kết nối với hợp đồng ở chế độ ghi - điều này yêu cầu đối tượng Ethereum được đưa vào trình duyệt web bằng ví để người dùng có thể ký giao dịch và thanh toán gas - đó là lý do tại sao chúng tôi đang kiểm tra window.ethereum sự vật.
//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); }
Gọi Hàm đúc bằng cách sử dụng hợp đồng được kết nối ghi.
Trước tiên, hãy đảm bảo rằng chúng tôi có địa chỉ ví từ người dùng và chúng tôi đang ở trên chuỗi FVM Hyperspace. Dưới đây là một số chức năng ví hữu ích mà bạn có thể muốn, bao gồm cách kiểm tra chainId và cách lập trình thêm mạng Hyperspace vào Metamask/ví. Bạn có thể tương tác trực tiếp với ví bằng cách sử dụng đối tượng Ethereum hoặc sử dụng 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); }); } };
Gọi hàm đúc hợp đồng ở chế độ ghi....
// 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 Minted!! Thời gian chế độ nhảy lân!
Bacalhau rất phù hợp để thực hiện các công việc xử lý xác định, lặp đi lặp lại trên dữ liệu.
Quy trình ETL
Máy học & Trí tuệ nhân tạo
Tích hợp dữ liệu IoT
Xử lý hàng loạt bao gồm cho
Xử lý video & hình ảnh - tuyệt vời cho quảng cáo
Có nhiều ví dụ trong tài liệu Bacalhau về cách đạt được một số điều trên.
Trong khi Bacalhau đang bận rộn xây dựng tích hợp để gọi trực tiếp Bacalhau từ hợp đồng Thông minh FEVM, đây là một số suy nghĩ về sự hợp tác Bacalhau x FVM:
Chúng tôi hiện đang xây dựng một cách để bạn chạy Bacalhau trực tiếp từ các hợp đồng thông minh của mình!!!! Dự án này được gọi là Dự án Frog / Dự án Lilypad - và sẽ là một lớp tích hợp cho phép gọi các công việc Bacalhau từ các hợp đồng thông minh FEVM.
Theo dõi tiến độ của việc này bằng cách đăng ký nhận bản tin của chúng tôi hoặc tham gia các mạng xã hội bên dưới.
Xin chúc mừng nếu bạn đã đọc hết!!!
Tôi đánh giá cao một lượt thích, bình luận, theo dõi hoặc chia sẻ nếu điều này hữu ích cho bạn! <3
Giữ liên lạc với Bacalhau!
Với ♥️ DeveloperAlly
Hỗ trợ Alison Haire bằng cách trở thành nhà tài trợ. Bất kỳ số lượng được đánh giá cao!
Tìm hiểu thêm về Nhà tài trợ Hashnode
Cũng được xuất bản ở đây .