NFTコントラクトを作成するOpenSeaのチュートリアルをやってみたメモ

NFTコントラクトを作成し、OpenSeaにリストするまでの流れを実際にやってみたいと思い、以下のOpenSeaのチュートリアルをやってみたメモです。

https://docs.opensea.io/docs/creating-an-nft-contract

このチュートリアルでは、OpenZeppelinを使ったNFTコントラクトを作成し、Goerli Testnetにデプロイ、NFTをmintして、[OpenSeaのテストネット](https://testnets.opensea.io/)で確認するまでを行います。

# 結論
- OpenZeppelinを使ったNFTコントラクトを、Goerli Testnetにデプロイできました。
  - 途中でデプロイ先がなぜかRinkeby Testnetになっている箇所がありますが、Goerli Testnetにデプロイしました。
- Goerli Testnetにデプロイ後、[OpenSeaのテストネットのコレクションページ](https://testnets.opensea.io/collections)にアクセスし、mintしたNFTが表示されていることを確認できました。
  - デプロイ時に使用したMetaMaskアカウントでOpenSeaにログインすることで、mintしたNFTコレクションが表示されました。独自コントラクトのNFTをOpenSeaにリストするには、コントラクトアドレスをマニュアルで登録する必要があると勝手に思い込んでいました。
- 基本は一般的なNFT開発のチュートリアルと同じですが、[OpenSeaのロイヤリティ有効化の実装](https://docs.opensea.io/docs/setting-up-your-smart-contract-project#enabling-creator-fees)は学びがありました。

# メモ
## ロイヤリティの有効化

ロイヤリティとは、NFTの転売時に、代金の一定割合をNFTの発行者に収益として還元する制度であり、NFTが注目されている理由の一つです。
最近、OpenSeaのロイヤリティの仕組みに変更があり、2023年1月2日午後12時 (米国東部標準時) 以降に作成されたコレクションで、ロイヤリティを有効化するためには、[オペレーターフィルター](https://github.com/ProjectOpenSea/operator-filter-registry)を実装する必要があります。これが含まれていないと、ロイヤリティが発生しません。

オペレーターフィルターは、NFT販売をロイヤリティを実施するマーケットプレイスだけに制限するコントラクトです。

背景として、最近はロイヤリティの支払いをスキップできるNFTマーケットプレイスが登場し、使用するユーザーが増加しています。OpenSeaはその動きを問題視し、今回の変更が行われました。

operator-filter-registryをnpmでinstallします。

```shell
npm i operator-filter-registry
```
DefaultOperatorFiltererをimportし、DefaultOperatorFiltererをextendし、ERC721コントラクトのメソッドをオーバーライドします。

```solidity:NFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";

contract NFT is ERC721, DefaultOperatorFilterer {
    using Counters for Counters.Counter;
    Counters.Counter private currentTokenId;
    
    constructor() ERC721("NFTTutorial", "NFT") {}
    
    function mintTo(address recipient)
        public
        returns (uint256)
    {
        currentTokenId.increment();
        uint256 newItemId = currentTokenId.current();
        _safeMint(recipient, newItemId);
        return newItemId;
    }
  
    function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
        super.setApprovalForAll(operator, approved);
    }

    function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
        super.approve(operator, tokenId);
    }

    function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
        super.transferFrom(from, to, tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
        super.safeTransferFrom(from, to, tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
        public
        override
        onlyAllowedOperator(from)
    {
        super.safeTransferFrom(from, to, tokenId, data);
    }
}
```

ロイヤリティの問題について、詳しくは以下の記事をご覧ください。

https://opensea.io/blog/announcements/on-creator-fees/

OpenSeaの発表です。

https://note.com/miin_nft/n/n539724ca1e6b

ロイヤリティの問題について解説されている記事です。