NFT発行アプリ作ってみた

こんにちは。サイオステクノロジーの和田です。今回はNFTの発行ができる簡単なアプリを作成したので共有したいと思います。

今回作るもの

今回はNFTを発行することができるWebアプリケーションを作成し、動作確認までしたいと思います。具体的にはウォレットアドレスとトークンIDを入力することで、NFTを発行できるようにしたいと思います。それではいきましょう。

作成手順

以下の手順で進めていきたいと思います。

  1. スマートコントラクトの作成
  2. バックエンドのコード作成
  3. フロントエンドのコード作成
  4. 動作確認

スマートコントラクト作成

今回はRemixを使ってコントラクトを作成していきます。

以下のようにコントラクトを作成しました。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC721, Ownable {
    constructor(address initialOwner)
        ERC721("MyToken", "MTK")
        Ownable(initialOwner)
    {}

    function safeMint(address to, uint256 tokenId) public onlyOwner {
        _safeMint(to, tokenId);
    }
}

こちらのファイルをRemixの画面で作成します。まず、アイコンパネルからファイルを選択し、contractsフォルダの中に上記内容のファイルを作成します。

続いてファイルをコンパイルしていきます。コンパイルのアイコンをクリックしてコンパイル画面を表示します。青色のボタンをクリックすることで、コンパイルが完了します。

コンパイルが完了すると以下のように表示が追加されるので、ABIをコピーしておいてください。バックエンド実装で後に使用します。

続いてデプロイを行います。デプロイのアイコンをクリックしてデプロイ画面に行きます。ENVIRONMENTをMetaMaskに設定し、ネットワークはSepoliaにデプロイします。デプロイ時の初期値として自分のウォレットアドレスを入れておきます。

デプロイが完了すると、以下のようにDeployed Contractsが表示されるので、コントラクトアドレスをコピーしておきます。こちらも後でバックエンドの実装で使用します。

バックエンド実装

次に、バックエンドの実装をしていきたいと思います。今回はNest.jsを使用して作成しました。Nest.jsはNode.js用のフレームワークで、APIの作成を容易にすることができます。ControllerとServiceがあり、Controllerはルーティングを定義し、Serviceは実際の処理を記述します。今回のケースでは、NFTのMint処理を記述することになります。

nest generateコマンドでControllerとServiceを作ったので、以下に記載します。

ディレクトリ構造

nft
├── nft.controller.spec.ts
├── nft.controller.ts
├── nft.service.spec.ts
└── nft.service.ts

nft.controller.ts

import { Controller, Post, Body } from '@nestjs/common';
import { NftService } from './nft.service';

@Controller('nft')
export class NftController {
  constructor(private readonly nftService: NftService) {}

  @Post()
  async mint(
    @Body('accountAddress') accountAddress: string,
    @Body('tokenId') tokenId: string,
  ) {
    return await this.nftService.mintNft(accountAddress, tokenId);
  }
}


nft.service.ts

import { Injectable } from '@nestjs/common';
import Web3 from 'web3';
import 'dotenv/config';

@Injectable()
export class NftService {
  async mintNft(accountAddress: string, tokenId: string) {
    const network = process.env.ETHEREUM_NETWORK;
    const web3 = new Web3(
      new Web3.providers.HttpProvider(
        `https://${network}.infura.io/v3/${process.env.INFURA_API_KEY}`,
      ),
    );
    
		const contractABI = [...]; // 作成したコントラクトのABI
    const contractAddress = String(process.env.CONTRACT_ADDRESS); // スマートコントラクトのアドレス
    const myContract = new web3.eth.Contract(contractABI, contractAddress);
    const privateKey = String(process.env.PRIVATE_KEY); // あなたのプライベートキー

    const data = myContract.methods
      .safeMint(accountAddress, tokenId)
      .encodeABI();

    const tx = {
      from: accountAddress,
      to: contractAddress,
      data: data,
      gas: 100000,
      gasPrice: web3.utils.toWei('250', 'gwei'),
    };

    try {
      const signed = await web3.eth.accounts.signTransaction(tx, privateKey);
      const receipt = await web3.eth.sendSignedTransaction(
        signed.rawTransaction,
      );
      console.log('Transaction receipt:', receipt);
      return receipt;
    } catch (error) {
      console.error('NFTの発行に失敗しました:', error);
      throw error;
    }
  }
}


contractABIには先ほどスマートコントラクトの作成でコピーしたものを張り付けてください。

また、プロジェクトルートに.envファイルを配置して、以下のように設定しました。

.env

PRIVATE_KEY="あなたのアカウントのプライベートキー"
INFURA_API_KEY="インフラのAPIキー"
ETHEREUM_NETWORK="sepolia"
CONTRACT_ADDRESS="作成したスマートコントラクトのアドレス"

今回はInfuraを使ってネットワークにアクセスしました。 Infuraでプロジェクトを作成し、APIキーを取得して貼り付けてください。ネットワークはスマートコントラクトをデプロイしたsepoliaとしています。

フロントエンド実装

続いて、フロントエンドの実装をしていきます。今回はNext.jsを用いてフロントを作成しました。下記のようなアドレスとトークンIDを受け取ってNFTを発行できるフォームを作りました。

"use client";
import React, { useState } from "react";
import { Button, TextField, Container, Box, Typography } from "@mui/material";

export default function FormComponent() {
  const [address, setAddress] = useState("");
  const [tokenId, setTokenId] = useState("");
  const [result, setResult] = useState("");

  const handleSubmit = async (e: { preventDefault: () => void }) => {
    e.preventDefault();
    try {
      const response = await fetch("<http://localhost:5000/nft>", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          accountAddress: address,
          tokenId: tokenId,
        }),
      });

      if (!response.ok) {
        throw new Error(`Error: ${response.status}`);
      }

      // レスポンスデータを取得
      const mintResult = await response.json();
      console.log(mintResult);

      // 成功した場合、入力フィールドをクリアし、結果メッセージを設定
      setAddress("");
      setTokenId("");
      setResult("登録成功");
    } catch (error) {
      // エラー処理
      console.error("Error minting NFT:", error);
      setResult("登録失敗");
    }
  };

  return (
    <Container maxWidth="sm">
      <Box display="flex" flexDirection="column" alignItems="center" justifyContent="center" height="100vh">
        <Typography variant="h4" component="h1" gutterBottom>
          トークンの登録
        </Typography>
        <form onSubmit={handleSubmit} style={{ width: "100%" }}>
          <Box mb={2}>
            <TextField
              fullWidth
              label="アドレス"
              variant="outlined"
              value={address}
              onChange={(e: { target: { value: React.SetStateAction<string> } }) => setAddress(e.target.value)}
              required
            />
          </Box>
          <Box mb={2}>
            <TextField
              fullWidth
              label="トークンID"
              variant="outlined"
              value={tokenId}
              onChange={(e: { target: { value: React.SetStateAction<string> } }) => setTokenId(e.target.value)}
              required
            />
          </Box>
          <Box width="100%" display="flex" justifyContent="center" mt={2}>
            {result}
          </Box>
          <Box width="100%" display="flex" justifyContent="center" mt={2}>
            <Button type="submit" variant="contained" color="primary" size="large">
              登録
            </Button>
          </Box>
        </form>
      </Box>
    </Container>
  );
}

それでは実際に動作確認していきたいと思います。フロントとバックをそれぞれ立ち上げて接続してみます。

以下のようにアドレスとトークンIDを入力します。ここで入力するアドレスはウォレットのアドレスを入力してください。

実行した結果、以下のように登録成功しました。

アカウントにNFTが発行されているのか、Metamaskで確認したいと思います。

画像のようにNFTをインポートをクリックして、NFTをインポートします。NFTをインポートする際に入れるアドレスはスマートコントラクトのアドレスを入れてください。インポートをクリックすると、NFTが無事追加されたことが確認できました。

まとめ

今回はNFTを発行することができる簡単なWebアプリケーションを作成してみました。実際に実装してみることで、NFTの仕組みをより深く理解することができました。今回は実装しませんでしたが、今後NFTの画像も表示できるように拡張してみたいと思います。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です