😎

OpenSeaのコントラクト強制ソリューション1 OperatorFilterRegistryを読んでみる その1

2022/11/08に公開

さて2022年11月7日にOpenSeaから声明のあったロイヤリティ強制ソリューションのツールとしてリリースされた OperatorFilterRegistryを読んだので少し噛み砕いて説明しておこうとおもう。

今回説明するコードを導入しないNFTに対してはOpenSeaはロイヤリティ徴収を強制しないことを言っています。OpenSeaでロイヤリティをゲットしたかったらこの機能をNFTにつけてねと言っているわけです。

最初のオンチェーン ツールは、作成者が将来の NFT コントラクトや既存のアップグレード可能なコントラクトに追加できるシンプルなコード スニペットです。このコードは、NFT の販売をクリエイター料金を強制するマーケットプレイスに制限します。11 月 8 日火曜日の東部標準時午後 12 時から、OpenSea は新しいコレクションをチェックして、作成者料金を強制しないマーケットプレイスでアイテムを販売できるかどうかを確認します。OpenSea は、オンチェーンの強制ツールを使用する新しいコレクションに対してクリエーター料金を強制します。OpenSea は、オンチェーンの施行を実装していない新しいコレクションに対してクリエーター料金を施行しません。

*正確な内容は下記原文を参照ください。

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

さてコードをみていきます。私も勘違いしている部分もあろうかと思いますので有識者の方は何かありましたらぜひ、アドバイスいただけますと嬉しいです。

コードはここhttps://github.com/ProjectOpenSea/operator-filter-registry

さて新しいNFTが変更するポイントが例で出ています。これをみるだけで、ポイントは抑えられます。

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

import {ERC721} from "openzeppelin-contracts/token/ERC721/ERC721.sol";
import {DefaultOperatorFilterer} from "../DefaultOperatorFilterer.sol";

/**
 * @title  ExampleERC721
 * @notice This example contract is configured to use the DefaultOperatorFilterer, which automatically registers the
 *         token and subscribes it to OpenSea's curated filters.
 *         Adding the onlyAllowedOperator modifier to the transferFrom and both safeTransferFrom methods ensures that
 *         the msg.sender (operator) is allowed by the OperatorFilterRegistry.
 */
contract ExampleERC721 is ERC721("Example", "EXAMPLE"), DefaultOperatorFilterer {
    function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator {
        super.transferFrom(from, to, tokenId);
    }

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

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

    function tokenURI(uint256) public pure override returns (string memory) {
        return "";
    }
}

この例ではERC721のコントラクト自体はOpenZeppelinから引用してきており基本はそのままだけど、下記の転送用の関数に制限をつけましょうということです。

ransferFrom(address from, address to, uint256 tokenId)
function safeTransferFrom(address from, address to, uint256 tokenId)
safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)

これら3つの関数の定義には元の関数には無い onlyAllowedOperator という言葉がついています。こちらについてさらにコードをみてみましょう。

modifier onlyAllowedOperator() virtual {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(operatorFilterRegistry).code.length > 0) {
   if (!operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)) {
   revert OperatorNotAllowed(msg.sender);
   }
}
_;
}

関数がわかりやすい名前になっているのでおよそ読めるかと思いますが、 isOperatorAllowedというところがポイントで ! が最初についているのがみれると思いますが、これは意味を反対にすることをする修飾文字になりますので 許可されていないOperatorの場合は revert しますという動きをします。 revertはトランザクションをそこで終了し無かったことにするものです。なのでNFTの転送ができなくなるリミッターとなります。

isOperatorAllowed(address(this), msg.sender)をみてきましょうか。


    /**
    * @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
    *         true if supplied registrant address is not registered.
    */
   function isOperatorAllowed(address registrant, address operator) external view returns (bool) {
       address registration = _registrations[registrant];
       if (registration != address(0)) {
           EnumerableSet.AddressSet storage filteredOperatorsRef;
           EnumerableSet.Bytes32Set storage filteredCodeHashesRef;

           filteredOperatorsRef = _filteredOperators[registration];
           filteredCodeHashesRef = _filteredCodeHashes[registration];

           if (filteredOperatorsRef.contains(operator)) {
               revert AddressFiltered(operator);
           }
           if (operator.code.length > 0) {
               bytes32 codeHash = operator.codehash;
               if (filteredCodeHashesRef.contains(codeHash)) {
                   revert CodeHashFiltered(operator, codeHash);
               }
           }
       }
       return true;
   }
   

isOperatorAllowed(address(this), msg.sender)
isOperatorAllowed(address registrant, address operator)

上がNFTから呼び出す側に書いてあるもの
下が呼ばれるもの。

ここで引数の名前が変わっています。 address(this)はNFTのコントラクトアドレス。そして msg.senderはトランザクションを発生させたアドレスになります。NFTのマーケットはNFTの所有者からpermissionを得てNFT
を売買成立時にtransferします。なので msg.senderはNFTマーケットのアドレスになるわけですね。

で今回は上記のここをみてください

** if (filteredOperatorsRef.contains(operator)) {
revert AddressFiltered(operator);
}**

ここで該当するoperatorだとトランザクションを終了させるわけですね。

ということで本日はここまでにしておきます。また第2弾でもう少しサブスクリプションなどを含め説明していきます。

Discussion