Как создать NFT (цифровой предмет коллекционирования) для благодарных фанатов

Как создать NFT (цифровой предмет коллекционирования) для благодарных фанатов

21 марта 2023 г.

<цитата>

Давайте рассмотрим процесс создания цифрового предмета коллекционирования (или NFT) на Ethereum с использованием Infura и Truffle.

В течение многих лет создатели находились во власти платформ. Исторически сложилось так, что эти платформы не только забирали 50 % (или более) дохода авторов, но и блокировали общение между авторами и их поклонниками.

В этой статье мы рассмотрим способ обойти этот централизованный контроль и связаться с фанатами без посредника. Использование Infura, Ethereum/смарт-контрактов и Далее мы создадим приложение, которое позволит авторам получать советы от поклонников, а затем напрямую вознаграждать этих поклонников собственным "Спасибо" ” NFT (невзаимозаменяемый токен) и цифровой предмет коллекционирования, который можно отображать, обменивать, продавать и собирать.

Это отличный способ вознаградить фанатов и сохранить контроль в руках создателей! Начнем строить.

Что мы создаем

Это руководство разделено на две части.

В первой части мы рассмотрим пошаговое руководство и развернем смарт-контракт с помощью Solidity, Infura и Truffle, которые создадут цифровой предмет коллекционирования на тесте Goerli Blockchain Ethereum. сеть. (Мы будем использовать тестовую сеть, чтобы сэкономить на расходах, но ее можно легко развернуть в основной сети, когда вы будете готовы.)

Во второй части мы развернем приложение Next, которое взаимодействует с нашим контрактом. Это позволит любому человеку в мире пожертвовать деньги (используя кошелек MetaMask) в обмен на предмет коллекционирования «Спасибо».

Этот предмет коллекционирования можно покупать, продавать и обменивать на популярных торговых площадках NFT, таких как OpenSea.

Если вам нужно введение в любую из этих концепций или учебник по программированию в Solidity, у ConsenSys есть хорошая коллекция руководств для начинающих. .

Часть 1. Создание смарт-контракта с помощью Infura и Truffle

Шаг 1. Установите npm и Node

Мы создадим наш проект, используя Node и npm. Если они не установлены на вашем локальном компьютере, вы можете сделать это здесь.

Чтобы убедиться, что все работает правильно, выполните следующую команду:

$ node -v

Если все пойдет хорошо, вы должны увидеть номер версии узла.

Шаг 2. Зарегистрируйте учетную запись Infura

Чтобы развернуть наш контракт в сети Goerli (и, в конечном итоге, в основной сети), нам потребуется учетная запись Infura. Infura предоставляет нам доступ к конечным точкам RPC, которые, в свою очередь, обеспечивают быстрый, надежный и простой доступ к выбранной нами цепочке блоков (в нашем случае Ethereum).

Зарегистрируйте бесплатную учетную запись здесь. Создав учетную запись, перейдите на панель инструментов и выберите Создать новый ключ

Для сети выберите Web3 API и назовите его Спасибо NFT.

Как только вы нажмете Создать, Infura сгенерирует для вас ключ API и автоматически предоставит вам конечные точки RPC для Ethereum, L2 и L1 без EVM (и соответствующие тестовые сети).

В этом руководстве нас интересует только конечная точка Ethereum Goerli RPC. Этот URL-адрес имеет вид https://goerli.infura.io/v3/←API. →

Шаг 3. Создайте криптокошелек MetaMask и получите goerliETH из крана

Чтобы развернуть наш контракт, нам необходимо иметь кошелек с достаточным количеством токенов для оплаты gas. Газ используется для оплаты транзакционных сборов в Ethereum — в данном случае для развертывания нашего контракта в блокчейне. В основной сети вам понадобится криптовалюта ETH. Но так как мы развертываем в тестовой сети, мы можем использовать goerliETH — это бесплатно.

Если вы еще этого не сделали, установите расширение MetaMask в своем любимом браузере и внимательно следуйте инструкциям, чтобы настроить новый кошелек. MetaMask — самый популярный и простой в использовании цифровой кошелек с самостоятельным хранением в мире.

В рамках настройки вам будет предоставлена ​​мнемоника из 12 слов для обеспечения безопасности вашего кошелька (а также для создания закрытых ключей для ваших кошельков). Держите это под рукой. Он понадобится нам позже.

Наконец, приобретите несколько тестовых токенов goerliETH из сборщика Infura. Как только это будет сделано, вы сможете увидеть небольшое количество goerliETH в своем кошельке MetaMask при переключении на тестовую сеть Goerli.

Шаг 4. Создайте проект Node и установите зависимости

Давайте настроим пустой репозиторий проекта, выполнив следующие команды:

$ 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 на Ethereum.

$ npm install @openzeppelin/contracts

Чтобы позволить Truffle использовать наш кошелек MetaMask, подписывать транзакции и оплачивать комиссию за газ от нашего имени, нам потребуется еще один пакет под названием hdwalletprovider. Установите его с помощью следующей команды:

$ npm install @truffle/hdwallet-provider

Наконец, чтобы защитить наш конфиденциальный кошелек и информацию о конечной точке RPC, мы будем использовать пакет dotenv

$ npm install dotenv

Шаг 5. Создайте свою коллекцию NFT

Теперь давайте создадим смарт-контракт, который управляет нашим созданием NFT. Откройте репозиторий проекта в вашем любимом редакторе кода (например, VS Code). В папке contracts создайте новый файл с именем thankyounft.sol.

(небольшое примечание: ConsenSys/Infura только что выпустила новый API NFT, который упрощает создание NFT и может заменить некоторые из эти шаги)

Мы собираемся написать контракт ERC-721 со следующими функциями:

  1. Возможность для любого чеканить NFT в обмен на сумму пожертвования (больше или равную определенному минимуму).
  2. Возможность для владельца (или создателя) снимать любые деньги, отправленные на контракт в качестве пожертвования.
  3. Изображение SVG и связанные с ним метаданные всех благодарностей NFT, хранящихся в сети.

Добавьте следующий код в СпасибоNft.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 и просмотреть его в реальном времени.

Поздравляем! Вы успешно развернули контракт в Goerli.

Теперь давайте развернем внешний интерфейс Next, который взаимодействует с контрактом и позволяет любому вызвать функцию mintthankyou, чтобы сделать пожертвование и отчеканить цифровую коллекционную вещь для себя.

Часть 2. Внешний интерфейс

Шаг 1. Загрузите шаблонный код и установите зависимости

У нас уже есть стандартный репозиторий, который вы можете загрузить. Это реализует определенные стандартные функции, такие как подключение кошелька.

Загрузите или клонируйте репозиторий отсюда: https://github.com/rounakbanik/ty-nft-frontend< /p>

Затем откройте репозиторий в своем терминале и выполните:

npm install

Это установит все необходимые зависимости и настроит для вас приложение Next.

Шаг 2. Добавьте файл ABI и константы

Откройте этот репозиторий в своем любимом редакторе кода и создайте новую папку с именем contracts

.

Вернитесь в репозиторий из первой части и скопируйте файл thankyounft.json из папки build, а затем вставьте его в вышеупомянутую папку contracts . Это контрактный ABI (по сути, интерфейс), который будет важен при вызове на нем функций.

Затем создайте папку с именем data и в ней создайте файл с именем constants.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), создатели могут получать советы со всего мира, снимать эти суммы в любое время, не платить комиссионные и создавать цифровые предметы коллекционирования, которые напрямую связывают их с их фанаты.

И это было только начало! Ознакомьтесь с дополнительной информацией об Ethereum, смарт-контракты и цифровой предмет коллекционирования/мир NFT.


Также опубликовано Здесь


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE