18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[NFT] Pythonで独自コントラクトを作成しアイコンをmint(NFT化)してみた

Posted at

今回の記事の目的

OpenSeaなどのNFTマーケットを利用すれば簡単にNFT化できるが、今回はPython(一部solidity)で独自にやってみることによってNFT化とはどんなことをやっているか理解する
※詳しい用語の説明はしてませんリンクを貼っていますので詳しく知りたかたはそちらを参考にしてください!

記事の流れ

  • NFTとは
  • NFTにしたい画像とmetadataをIPFSにアップロードする
  • 独自コントラクトを作成する
  • mintする
  • OpenSeaからmintしたNFTを確認する

NFTとは

NFTとは、「Non Fungible Token(ノン ファンジブル トークン: 非代替性トークン)」という意味でブロックチェーン技術を使用した、替えが効かない、唯一無二と証明されたトークンのことです。
このトークンとデジタルデータを紐づけることで唯一無二のデジダルデータの証明ができるということです。

NFTとは

さっそくやってみる

まずはNFTにしたい画像とmetadataをIPFSにアップロードします

  • IPFSとは非中央集権の分散型ファイルシステムのことです。
    • NFTにする画像の保存場所はどこでも(S3等)でもいいのですがweb3のdecentralized的な考え方でいうとIPFSに保存するのが一般的です。

IPFSとは

  • IPFSに画像をアップロードするために今回はpinataというサービスを使用します。
    • pinataにサインアップ後NFTにしたい画像をアップロードします。
    • 画像のURIが発行されます
    • 次にmetadataを作成します
      • 実際にブロックチェーン上に画像を書き込むわけではなくmetadataの中に画像のURIを記載しそのmetadataのURIをブロックチェーン上に書き込みます。
      • 今回作成したmetadataは下記です。
        {
            "name": "harrier's icon",
            "image": "https://gateway.pinata.cloud/ipfs/QmZKRfNt52ZdQtBpz3C33ujB6XUKsn1oj3WrJjK8BdJAiX",
            "description": "Hyper NFT Creator harrier's icon"
        }
        
      • 作成したmetadataをpinataにアップロードします。
      • metadataのURIが発行されるのでこれを後ほどブロックチェーン上に記録します。(https://gateway.pinata.cloud/ipfs/QmbuizjxTp7oLTzAwC2RDc9KLHEvaz6883bk3E9tqfrmSn)

pinata
pinataの使いかた

続いて独自コントラクトを作成する

  • 独自コントラクトとは

    • ユーザーが独自に作ったスマートコントラクトのことを独自コントラクトと言います。
      • OpenSeaで画像をアップロードしてNFTを作成する場合はOpenSeaの共有コントラクトを使うことになります。
    • スマートコントラクトとはブロックチェーン上で動く契約(プログラム)のこと。
      • 独自コントラクトを作成するとこの契約(プログラム)を自由に書くことができる
  • solidityを使ってコントラクトを作成

    • solidityとはブロックチェーン上で動くスマートコントラクトを作成するための言語です
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// ここではopenzeppelinというライブラリを使用しERC721の規格に即したコントラクトを作成しています。
contract CONTRACT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("HARRIER's Contract", "HAR") {}

    // スマートコントラクトとしてmintという関数を実装しています。
    // この中でtoAddressに対してNFTを発行しています
    function mint(address toAddress, string memory matadataURI)
        public onlyOwner
        returns (uint256)
    {
        uint256 newTokenId = _tokenIds.current();
        _mint(sendAddress, newTokenId);
        _setTokenURI(newTokenId, matadataURI);

        _tokenIds.increment();
        return newTokenId;
    }
}
  • solidityのコードをpythonでコンパイルして実行
import os
from logging import getLogger, INFO, basicConfig

from web3 import Web3
from web3.middleware import geth_poa_middleware
from solcx import compile_source, install_solc
from dotenv import load_dotenv

basicConfig(level=INFO)
logger = getLogger(__name__)
load_dotenv()


if __name__ == "__main__":
    # infuraというサービスのurl
    # https://infura.io/
    INFURA_URL = os.getenv("INFURA_URL")
    # 自分のwalletのaddress(コントラクトの作成にwalletが必要になります。今回はmetamaskを使用してます。)
    WALLET_ADDRESS = os.getenv("WALLET_ADDRESS")
    # metamaskのsecret_key(絶対に外部に漏らしてはいけません)
    SECRET_KEY = os.getenv("SECRET_KEY")

    w3 = Web3(Web3.HTTPProvider(INFURA_URL))
    wallet_address = w3.toChecksumAddress(WALLET_ADDRESS)
    w3.middleware_onion.inject(geth_poa_middleware, layer=0)

    # 先ほど作成したsolidityのコードを読み込む
    with open("contract.sol", "r") as file:
        source = file.read()
        install_solc("0.8.14")
        compiled_sol = compile_source(
            source,
            output_values=["abi", "bin"],
            solc_version="0.8.14",
            base_path="node_modules",
        )
    # コンパイルしたデータからコントラクトIDとコントラクタインターフェイスを取得
    contract_id = next(iter(compiled_sol))
    contract_interface = compiled_sol[contract_id]
    # コントラクタを作成するためのガス代と上限の設定
    base_tx = {
        "nonce": w3.eth.get_transaction_count(wallet_address),
        "gas": 10000000,
        "gasPrice": w3.toWei("2", "gwei"),
    }
        # コントラクトの作成
    contract = contract = w3.eth.contract(
        abi=contract_interface["abi"], bytecode=contract_interface["bin"]
    )
    # トランザクション作成
    contract_tx = contract.constructor().buildTransaction(base_tx)
    # 秘密鍵を使用してトランザクションに署名
    signed_tx = w3.eth.account.sign_transaction(contract_tx, private_key=SECRET_KEY)
    # トランザクション送信
    w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    tx_hash = w3.toHex(w3.keccak(signed_tx.rawTransaction))
        # トランザクション結果を取得
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    contract_address = receipt["contractAddress"]
    logger.info(f"contract_address: {contract_address}")

  • これでコントラクトの作成が完了です
    • 出力結果はこのようになってます contract_address: 0xc34a91401f25A81E18D58DFFC8feC5fAE5D39116
    • https://mumbai.polygonscan.com/ こちらで出力されたcontract_addressを入れると作成されてるのがわかります
      スクリーンショット 2022-06-07 15.21.18.png

次にmintしていきます!

  • mintとはNFTを発行する事です

  • pythonでmintする

import os
from logging import getLogger, INFO, basicConfig

from web3 import Web3
from web3.middleware import geth_poa_middleware
from solcx import compile_source, install_solc
from dotenv import load_dotenv

basicConfig(level=INFO)
logger = getLogger(__name__)
load_dotenv()


if __name__ == "__main__":
    INFURA_URL = os.getenv("INFURA_URL")
    # NFTを発行したいwalletのアドレス
    WALLET_ADDRESS = os.getenv("WALLET_ADDRESS")
    # walletのsecret_key
    SECRET_KEY = os.getenv("SECRET_KEY")
    # pinataにアップロードしたmetadataのURI
    METADATA_URI = os.getenv("METADATA_URI")
    # 先ほど出力されたcontract_address
    CONTRACT_ADDRESS = os.getenv("CONTRACT_ADDRESS")

    w3 = Web3(Web3.HTTPProvider(INFURA_URL))
    wallet_address = w3.toChecksumAddress(WALLET_ADDRESS)
    w3.middleware_onion.inject(geth_poa_middleware, layer=0)
    # solidityのコードを読み込む
    with open("contract.sol", "r") as file:
        source = file.read()
        install_solc("0.8.14")
        compiled_sol = compile_source(
            source,
            output_values=["abi", "bin"],
            solc_version="0.8.14",
            base_path="node_modules",
        )
    # コンパイルしたデータからコントラクトIDとコントラクタインターフェイスを取得
    contract_id = next(iter(compiled_sol))
    contract_interface = compiled_sol[contract_id]
    # コントラクタを作成するためのガス代と上限の設定
    base_tx = {
        "nonce": w3.eth.get_transaction_count(wallet_address),
        "gas": 10000000,
        "gasPrice": w3.toWei("2", "gwei"),
    }
    # コントラクトの取得
    contract_address = w3.toChecksumAddress(CONTRACT_ADDRESS)
    contract = w3.eth.contract(
        address=contract_address,
        abi=contract_interface["abi"],
        bytecode=contract_interface["bin"],
    )
    # スマートコントラクトとして登録したmint関数を呼び出しmintする
    mint_tx = contract.functions.mint(wallet_address, METADATA_URI).buildTransaction(
        base_tx
    )
    # トランザクションに署名
    sign_mint_tx = w3.eth.account.sign_transaction(mint_tx, private_key=SECRET_KEY)
    # トランザクション送信
    w3.eth.send_raw_transaction(sign_mint_tx.rawTransaction)
    tx_hash = w3.toHex(w3.keccak(sign_mint_tx.rawTransaction))
    # トランザクションが終わるのを待つ
    w3.eth.wait_for_transaction_receipt(tx_hash)
    # Done
    logger.info("Mint complete.")

  • これでmintが完了になります。
  • OpenSeaからmintができているか確認してみます。
    •  https://testnets.opensea.io/
    •  コントラクトアドレスで検索をかけてみると作成したHARRIERs Contractの中にharrier's iconが登録されてるのが確認できます。
      スクリーンショット 2022-06-07 15.41.34.png

最後に

現状NFTアートの使い道としては売買、投機、コレクション的な目的しか有りませんが、Twitter, Instagram, FacebookがNFTアイコンを採用すると発表してますのでその際はぜひ皆さんもmintしてみてはいがでしょうか!

参考

18
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?