توانمندسازی سازندگان دیجیتال: راهنمای فنی در مورد نحوه ایجاد یک NFT (مجموعه دیجیتال) برای گفتن “متشکرم”
بیایید روند ایجاد یک کلکسیون دیجیتال (یا NFT) در اتریوم با استفاده از Infura و Truffle را طی کنیم.
معرفی
سالهاست که سازندگان در دست پلتفرمها بودهاند. از لحاظ تاریخی، این پلتفرمها نه تنها 50 درصد (یا بیشتر) از درآمد یک سازندگان را گرفتهاند، بلکه ارتباطات بین سازندگان و طرفدارانشان را نیز قفل کردهاند.
در این مقاله به روشی برای دور زدن این کنترل متمرکز و ارتباط با فن ها بدون واسطه خواهیم پرداخت. با استفاده از Infura، Ethereum/Smart Contracts، و Next، برنامهای ایجاد میکنیم که به سازندگان اجازه میدهد نکاتی را از طرفداران بپذیرند و سپس مستقیماً به آن طرفداران NFT (توکن غیرقابل تعویض) و مجموعه دیجیتالی شما پاداش میدهند. نمایش داده شود، معامله شود، فروخته شود و جمع آوری شود.
این یک راه عالی برای پاداش دادن به طرفداران و حفظ کنترل در دست سازندگان است! بیایید شروع به ساختن کنیم.
چیزی که ما می سازیم
این آموزش به دو بخش تقسیم شده است.
در بخش اول، یک راهنمای گام به گام را طی می کنیم و یک قرارداد هوشمند را با استفاده از Solidity، Infura و Truffle اجرا می کنیم که کلکسیون دیجیتالی را در شبکه آزمایشی Ethereum Blockchain Goerli ایجاد می کند. (ما از شبکه آزمایشی برای صرفه جویی در هزینه ها استفاده خواهیم کرد – اما وقتی آماده باشید می توان آن را به راحتی در شبکه اصلی مستقر کرد.)
در بخش دوم، ما یک برنامه Next را که با قرارداد ما ارتباط دارد، مستقر خواهیم کرد. این به هر کسی در جهان این امکان را می دهد که در ازای مجموعه “متشکرم” پول (با استفاده از کیف پول MetaMask) اهدا کند. این مجموعه را می توان در بازارهای محبوب NFT مانند OpenSea خرید، فروخت و معامله کرد.
اگر به معرفی هر یک از این مفاهیم یا مقدمه ای در مورد نحوه کدنویسی در Solidity نیاز دارید، ConsenSys مجموعه خوبی از آموزش های مبتدی دارد.
قسمت 1: قرارداد هوشمند با Infura و Truffle بسازید
مرحله 1: npm و Node را نصب کنید
ما پروژه خود را با استفاده از Node و npm می سازیم. اگر اینها را روی دستگاه محلی خود نصب نکرده اید، می توانید این کار را در اینجا انجام دهید.
برای اطمینان از اینکه همه چیز به درستی کار می کند، دستور زیر را اجرا کنید:
$ node -v
اگر همه چیز خوب پیش برود، باید یک شماره نسخه برای node ببینید.
مرحله 2: برای یک حساب Infura ثبت نام کنید
برای استقرار قرارداد خود در شبکه Goerli (و در نهایت به یک شبکه اصلی)، به یک حساب Infura نیاز داریم. Infura به ما امکان دسترسی به نقاط پایانی RPC را می دهد که به نوبه خود امکان دسترسی سریع، قابل اعتماد و آسان به بلاک چین مورد نظر ما (در مورد ما، اتریوم) را فراهم می کند.
برای یک حساب کاربری رایگان در اینجا ثبت نام کنید. هنگامی که حساب خود را ایجاد کردید، به داشبورد بروید و انتخاب کنید کلید جدید ایجاد کنید.
برای شبکه، انتخاب کنید Web3 API و اسمش را بگذار متشکرم NFT.
پس از کلیک بر روی ايجاد كردن، Infura یک کلید API برای شما ایجاد می کند و به طور خودکار به شما نقاط پایانی RPC را به اتریوم، L2 و L1های غیر EVM (و شبکه های آزمایشی مربوط به آنها) می دهد.
برای این آموزش، ما فقط به نقطه پایانی Ethereum Goerli RPC علاقه مند هستیم. این URL به شکل https://goerli.infura.io/v3/←API KEY →
مرحله 3: یک کیف پول رمزنگاری MetaMask ایجاد کنید و goerliETH را از یک شیر آب دریافت کنید
برای استقرار قرارداد خود، باید کیف پولی با ژتون های کافی برای پرداخت هزینه بنزین داشته باشیم. از گاز برای پرداخت هزینه های تراکنش در اتریوم استفاده می شود – در این مورد، استقرار قرارداد ما در بلاک چین است. در شبکه اصلی، به ارز دیجیتال ETH نیاز دارید. اما از آنجایی که ما در یک شبکه آزمایشی مستقر میشویم، میتوانیم از goerliETH استفاده کنیم – که رایگان است.
اگر قبلاً این کار را نکرده اید، افزونه MetaMask را در مرورگر مورد علاقه خود نصب کنید و دستورالعمل ها را به دقت دنبال کنید تا کیف پول جدید خود را تنظیم کنید. MetaMask محبوبترین و آسانترین کیف پول دیجیتال خودسرپرست در جهان است.
به عنوان بخشی از تنظیمات، به شما یک یادگاری 12 کلمه ای داده می شود تا کیف پول خود را ایمن نگه دارید (و همچنین برای ایجاد کلیدهای خصوصی برای کیف پول خود). این را دستی نگه دارید. در مرحله بعد به آن نیاز خواهیم داشت.
در نهایت، تعدادی توکن تست goerliETH را از شیر آب Infura تهیه کنید. هنگامی که این کار انجام شد، باید بتوانید مقدار کمی از goerliETH را در کیف پول MetaMask خود هنگام تغییر به شبکه آزمایشی Goerli مشاهده کنید.
مرحله 4: یک پروژه Node ایجاد کنید و Dependencies را نصب کنید
بیایید با اجرای دستورات زیر یک مخزن پروژه خالی راه اندازی کنیم:
$ mkdir ty-nft && cd ty-nft
$ npm init -y
ما از Truffle، یک محیط توسعه کلاس جهانی و چارچوب آزمایشی برای قراردادهای هوشمند EVM، برای ساخت و استقرار قرارداد هوشمند خود استفاده خواهیم کرد. Truffle را با اجرا نصب کنید:
$ npm install —save truffle
اکنون می توانیم با اجرای دستور زیر یک پروژه Truffle بدون استخوان ایجاد کنیم:
$ npx truffle init
برای بررسی اینکه آیا همه چیز به درستی کار می کند، اجرا کنید:
$ npx truffle test
ما اکنون Truffle را با موفقیت پیکربندی کرده ایم. در مرحله بعد، اجازه دهید بسته قراردادهای OpenZeppelin را نصب کنیم. این بسته به ما امکان دسترسی به پیاده سازی پایه ERC-721 و همچنین چند قابلیت مفید اضافی را می دهد. ERC-721 استاندارد باز است که نحوه ساخت NFT ها را بر روی اتریوم تعریف می کند.
$ npm install @openzeppelin/contracts
برای اینکه به Truffle اجازه دهیم از کیف پول MetaMask ما استفاده کند، تراکنشها را امضا کند و هزینههای گاز را از طرف ما بپردازد، به بسته دیگری به نام نیاز داریم. ارائه دهنده hdwallet. با استفاده از دستور زیر آن را نصب کنید:
$ npm install @truffle/hdwallet-provider
در نهایت، به منظور محافظت از کیف پول حساس و اطلاعات نقطه پایانی RPC، از بسته dotenv استفاده خواهیم کرد.
$ npm install dotenv
مرحله 5: مجموعه NFT خود را ایجاد کنید
اکنون بیایید قرارداد هوشمندی را ایجاد کنیم که ایجاد NFT ما را کنترل می کند. مخزن پروژه را در ویرایشگر کد مورد علاقه خود باز کنید (به عنوان مثال VS Code). در contracts
پوشه، یک فایل جدید به نام ایجاد کنید ThankYouNft.sol
.
(توجه سریع – ConsenSys/Infura به تازگی یک NFT API جدید منتشر کرده است که ایجاد NFT ها را بسیار آسان می کند و می تواند جایگزین برخی از این مراحل شود)
ما قصد داریم یک قرارداد ERC-721 بنویسیم که دارای عملکرد زیر باشد:
- توانایی برای هر کسی که یک NFT در ازای مبلغ اهدایی (بیشتر یا مساوی از حداقل معین) برش دهد.
- توانایی مالک (یا پدیدآورنده) برای برداشت پولی که به عنوان کمک مالی به قرارداد ارسال شده است.
- تصویر SVG و ابرداده مربوط به همه NFT های متشکرم که در زنجیره ذخیره می شوند.
کد زیر را به آن اضافه کنید ThankYouNft.sol
.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract ThankYouNft is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
// Define a Donation object
struct Donation {
uint id;
uint amount;
address donor;
string donorName;
}
Donation[] donations;
constructor() ERC721("Thank You NFT", "TYN") {}
// Donate money and mint thank you NFT
function mintThankYou(string memory donorName) public payable {
require(msg.value >= 0.001 ether, "Smallest donation is 0.001 ETH");
string memory metadata = generateMetadata(_tokenIds.current(), donorName);
donations.push(Donation(_tokenIds.current(), msg.value, msg.sender, donorName));
_mintSingleNft(metadata);
}
// Generate NFT metadata
function generateMetadata(uint tokenId, string memory donorName) public pure returns (string memory) {
string memory svg = string(abi.encodePacked(
"<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio='xMinyMin meet' viewBox='0 0 350 350'>",
"<style>.base { fill: white; font-family: serif; font-size: 25px; }</style>",
"<rect width="100%" height="100%" fill="orange" />",
"<text x='50%' y='40%' class="base" dominant-baseline="middle" text-anchor="middle">",
"<tspan y='40%' x='50%'>Thank You for Donating!</tspan>",
"<tspan y='50%' x='50%'>",
donorName,
"</tspan></text></svg>"
));
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "Thank You NFT #',
Strings.toString(tokenId),
'", "description": "A token of thanks for donating!", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(svg)),
'", "attributes": [{"trait_type": "Donor", "value": "',
donorName,
'"}]}'
)
)
)
);
string memory metadata = string(
abi.encodePacked("data:application/json;base64,", json)
);
return metadata;
}
// Mint a single NFT with on-chain metadata
function _mintSingleNft(string memory _tokenURI) private {
uint newTokenID = _tokenIds.current();
_safeMint(msg.sender, newTokenID);
_setTokenURI(newTokenID, _tokenURI);
_tokenIds.increment();
}
// Get tokens of an owner
function tokensOfOwner(address _owner) external view returns (uint[] memory) {
uint tokenCount = balanceOf(_owner);
uint[] memory tokensId = new uint256[](tokenCount);
for (uint i = 0; i < tokenCount; i++) {
tokensId[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokensId;
}
// Withdraw ether donations made
function withdraw() public payable onlyOwner {
uint balance = address(this).balance;
require(balance > 0, "No ether left to withdraw");
(bool success, ) = (msg.sender).call{value: balance}("");
require(success, "Transfer failed.");
}
// The following functions are overrides required by Solidity.
function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
internal
override(ERC721, ERC721Enumerable)
{
super._beforeTokenTransfer(from, to, tokenId, batchSize);
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
با اجرای زیر مطمئن شوید که قرارداد به درستی کامپایل شده است:
npx truffle compile
مرحله 6: پیکربندی Truffle را به روز کنید و یک فایل .env ایجاد کنید
یک فایل جدید در دایرکتوری ریشه پروژه ایجاد کنید به نام .env
و مطالب زیر را اضافه کنید:
INFURA_API_KEY = "https://goerli.infura.io/v3/<Your-API-Key>"
MNEMONIC = "<Your-MetaMask-Secret-Recovery-Phrase>"
در مرحله بعد، بیایید اطلاعاتی درباره کیف پول خود، نقطه پایانی Infura RPC و شبکه Goerli را به فایل پیکربندی Truffle خود اضافه کنیم. محتویات را جایگزین کنید truffle.config.js با موارد زیر:
require('dotenv').config();
const HDWalletProvider = require('@truffle/hdwallet-provider');
const { INFURA_API_KEY, MNEMONIC } = process.env;
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*"
},
goerli: {
provider: () => new HDWalletProvider(MNEMONIC, INFURA_API_KEY),
network_id: '5',
}
}
};
مرحله 7: قرارداد را مستقر کنید
اجازه دهید اکنون یک اسکریپت بنویسیم تا قرارداد خود را در بلاک چین Goerli مستقر کنیم.
در migrations
پوشه، یک فایل جدید به نام ایجاد کنید 1_deploy_contract.js
و کد زیر را اضافه کنید:
// Get instance of the NFT contract
const nftContract = artifacts.require("ThankYouNft");
module.exports = function (deployer) {
// Deploy the contract
deployer.deploy(nftContract);
};
ما آماده ایم! قرارداد را با اجرای دستور زیر مستقر کنید:
truffle migrate --network goerli
اگر همه چیز خوب پیش برود، باید خروجی (شامل آدرس قرارداد) را ببینید که چیزی شبیه به این است:
Starting migrations...
======================
> Network name: 'goerli'
> Network id: 5
> Block gas limit: 30000000 (0x1c9c380)
1_deploy_contract.js
====================
Deploying 'ThankYouNft'
-----------------------
> transaction hash: 0x0a4fabe13a2c62e335486dc8359eecbe8b1432e5ab7a162e6bd9a167036cdcd4
> Blocks: 2 Seconds: 33
> contract address: 0x4EBC03568822c4Af39ca02002C3771Ae4e8bb3ED
> block number: 8641567
> block timestamp: 1678616928
> account: 0xc361Fc33b99F88612257ac8cC2d852A5CEe0E217
> balance: 0.734846151579135017
> gas used: 4108239 (0x3eafcf)
> gas price: 29.254126274 gwei
> value sent: 0 ETH
> total cost: 0.120182942469771486 ETH
> Saving artifacts
-------------------------------------
> Total cost: 0.120182942469771486 ETH
Summary
=======
> Total deployments: 1
> Final cost: 0.120182942469771486 ETH
می توانید آدرس قرارداد خود را در Goerli etherscan جستجو کنید و آن را به صورت زنده مشاهده کنید.
تبریک می گویم! شما با موفقیت قرارداد را با گوئرلی پیاده کردید.
اکنون بیایید یک Next Frontend را مستقر کنیم که با قرارداد ارتباط برقرار می کند و به هر کسی اجازه می دهد با آن تماس بگیرد نعنا متشکرم این کار را انجام می دهند تا کمک مالی کنند و مجموعه دیجیتالی را برای خودشان برش دهند.
قسمت 2: جبهه
مرحله 1: کد Boilerplate را دانلود کرده و Dependencies را نصب کنید
ما قبلاً یک مخزن دیگ بخار برای دانلود در دسترس شما داریم. این عملکرد استاندارد خاصی مانند کیف پول اتصال را پیاده سازی می کند.
مخزن را از اینجا دانلود یا شبیه سازی کنید: https://github.com/rounakbanik/ty-nft-frontend
سپس مخزن را در ترمینال خود باز کنید و اجرا کنید:
npm install
با این کار تمام وابستگی های لازم نصب می شود و برنامه Next برای شما راه اندازی می شود.
مرحله 2: فایل ABI و Constants را اضافه کنید
این مخزن را در ویرایشگر کد مورد علاقه خود باز کنید و یک پوشه جدید به نام ایجاد کنید قراردادها
به مخزن قسمت 1 برگردید و آن را کپی کنید ThankYouNft.json فایل موجود در ساختن پوشه، سپس آن را در قسمت فوق الذکر قرار دهید قراردادها پوشه این قرارداد ABI (اساساً رابط) است که هنگام فراخوانی توابع روی آن مهم خواهد بود.
سپس یک پوشه به نام ایجاد کنید داده ها و در آن فایلی به نام ایجاد کنید ثابت ها.js با اطلاعات زیر:
const apiKey = "<-- INFURA API KEY –>";
const ownerAddress = "<-- Wallet address –>";
const contractAddress = "<-- Address of deployed NFT contract from Part 1 –>";
export { apiKey, ownerAddress, contractAddress }
مرحله 3: فایل index.js را پر کنید
اکنون میتوانیم اصل برنامه خود را در آن بنویسیم index.js فایلی که باعث ایجاد NFT می شود.
هنگامی که کاربران کیف پول خود را به برنامه ما متصل کردند، می توانند فرمی را ببینند که در آن از آنها خواسته می شود نام و مبلغ کمک مالی خود را وارد کنند. پس از انجام این کار، میتوانند فرآیند اهدا و نعناع NFT را آغاز کنند.
کد زیر را اضافه کنید:
// Standard Next and CSS imports
import Head from "next/head";
import { Fragment, useState, useEffect } from "react";
import styles from "../styles/mainpage.module.css";
import { useRouter } from "next/router";
// Imports from the constants.js file
import { apiKey, contractAddress } from "@/data/constants";
// Wagmi import for connected wallet info
import { useAccount } from "wagmi";
// Ethers for invoking functions on smart contract
import { ethers } from 'ethers';
// Contract ABI import
import contract from '@/contracts/ThankYouNft.json';
// Extract ABI from the ABI JSON file
const abi = contract.abi;
export default function Home() {
// Standard Next router definition
const router = useRouter();
// Get connected wallet address and connection status
const { address, isConnected } = useAccount();
// Donor name
const [donorName, setDonorName] = useState(null);
// Tip amount
const [amount, setAmount] = useState(null);
// Page mounting info to prevent hydration errors
const [hasMounted, setHasMounted] = useState(false);
// Minting state
const [isMinting, setIsMinting] = useState(false);
// Flag to check if minting has succeeded
const [success, setSuccess] = useState(false);
// Form error message
const [formError, setFormError] = useState(null);
// Mounting fix to avoid hydration errors
useEffect(() => {
setHasMounted(true);
}, []);
// Do not render until entire UI is mounted
if (!hasMounted) return null;
// Redirect to Connect page if wallet is not connected
if (!isConnected) {
router.replace('/connect');
}
// Handlers for form inputs
const amountHandler = (e) => {
setAmount(e.target.value);
}
const nameHandler = (e) => {
setDonorName(e.target.value);
}
// Mint function invoked when form is submitted
const mintNft = async (e) => {
e.preventDefault();
setFormError(false);
// Basic check for correctness of data
if (donorName.length === 0 || parseFloat(amount) < 0.001) {
console.log("Incorrect form input");
setFormError(true);
return;
}
try {
// Get MetaMask Ethereum instance
const { ethereum } = window;
if (ethereum) {
// Reset states
setIsMinting(true);
setFormError(false);
setSuccess(false);
// Define provider, signer, and an instance of the contract
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const nftContract = new ethers.Contract(contractAddress, abi, signer);
// Call the mint function
console.log("Initialize payment");
let nftTxn = await nftContract.mintThankYou("Satoshi", { value: ethers.utils.parseEther('0.001') });
console.log("Mining... please wait");
await nftTxn.wait();
console.log(`Mined, see transaction: https://goerli.etherscan.io/tx/${nftTxn.hash}`);
// Set final states
setIsMinting(false);
setSuccess(true);
setDonorName(null);
setAmount(null)
} else {
console.log("Ethereum object does not exist");
}
} catch (err) {
// Something wrong has happened. Set error and minting states
setIsMinting(false);
setFormError(true);
console.log(err);
}
}
return (
<Fragment>
<Head>
<title>Tip and Mint a Thank You NFT!</title>
</Head>
<div className={styles.jumbotron}>
<h1>Tip and Mint a Thank You NFT!</h1>
{/* Main Form */}
<form onSubmit={mintNft} className={styles.mint_form}>
<input type="text" id="name" name="name" placeholder="Your Name" onChange={nameHandler} value={donorName} />
<input type="number" id="amount" name="amount" min={0.001} placeholder="Donation Amount in ETH (min 0.001 ETH)" onChange={amountHandler} value={amount} step={0.001} />
<button type="submit">
Tip
</button>
</form>
{/* Helpful messages for end user to know what's going on */}
{isMinting && <p>Your NFT is minting...</p>}
{success && <p>Thank you for your donation! Check out your NFT on OpenSea!</p>}
{formError && <p>Something went wrong! Try again.</p>}
</div>
</Fragment>
)
}
ما آماده ایم! بیایید این برنامه را با اجرا در لوکال هاست مستقر کنیم:
npm run dev
هنگامی که کیف پول خود را متصل کردید و فرآیند ضرب کردن را کامل کردید، باید یک پیام موفقیت آمیز را در پایین مشاهده کنید که چیزی شبیه به این است.
شما اکنون NFT خود را دارید! اکنون می توانید آدرس کیف پول خود یا آدرس قرارداد را در پلتفرم NFT OpenSea جستجو کنید. این به شما امکان می دهد NFT و مجموعه مربوطه را مشاهده کنید.
نتیجه
مجموعههای دیجیتال (NFT) پتانسیل عظیمی برای متحول کردن اقتصاد سازندگان دارند و قدرت را مستقیماً در دست سازندگان و طرفداران آنها قرار میدهند.
با استفاده از برنامهای که با مجموعه ConsenSys ساختهایم (Infura، Truffle و MetaMask)، سازندگان میتوانند نکاتی را از سراسر جهان دریافت کنند، آن مبالغ را در هر زمان برداشت کنند، کمیسیون پرداخت نکنند، و یک مجموعه دیجیتالی ایجاد کنند که مستقیماً آنها را به آنها متصل کند. طرفداران
و این فقط یک شروع بود! اطلاعات بیشتر در مورد اتریوم، قراردادهای هوشمند و دنیای دیجیتالی کلکسیونی/NFT را بررسی کنید.