🐙

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

2022/11/14に公開

本記事ではOpenSeaが発表したコントラクト強制ソリューションの要件であるコントラクトを読んでいきます。
その1にNFTへの実装方法などありますのでまずはそちらを読んでいただくと理解がしやすいかと。

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

前回までで実際に実装する側から見ていきました。全体の構造としてはロイヤリティを強制しないオペレーターを弾くリストを管理するコントラクトとなります。実際に管理するところを見ていきましょう。

コードはこちらにありますので適宜ダウンロードして読み合わせてください

https://github.com/ProjectOpenSea/operator-filter-registry

さてここからOperatorFilterRegistry.solを読んでいくのですが、こちらのコントラクトはOpenSeaのゼロベース作成ではなく QQL Art が作成したコントラクトを流用している旨がnoticeとして記載されています。

QQLはNFT作成側の自衛手段でX2Y2で売ることを止めたようですね。私もこまかいことはわかっておりませんが、コントラクトで叩き合うのはアリです。そういう契約でとコードでかいてあるものですからね。

ではコードをみていきましょう。なかなか長いですからInterfaceをみて全体を俯瞰しましょう。

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

import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";

interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator) external returns (bool);
    function register(address registrant) external;
    function registerAndSubscribe(address registrant, address subscription) external;
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;
    function updateOperator(address registrant, address operator, bool filtered) external;
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
    function subscribe(address registrant, address registrantToSubscribe) external;
    function unsubscribe(address registrant, bool copyExistingEntries) external;
    function subscriptionOf(address addr) external returns (address registrant);
    function subscribers(address registrant) external returns (address[] memory);
    function subscriberAt(address registrant, uint256 index) external returns (address);
    function copyEntriesOf(address registrant, address registrantToCopy) external;
    function isOperatorFiltered(address registrant, address operator) external returns (bool);
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
    function filteredOperators(address addr) external returns (address[] memory);
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
    function isRegistered(address addr) external returns (bool);
    function codeHashOf(address addr) external returns (bytes32);
}

さて関数名をながめていきましょうか。

  • register
  • subscribe
  • operator
  • codeHash
  • filtered

この辺りがキーワードであることがわかります。OpenSeaの文章の解説でもこれらを混ぜ合わせて説明がなされていました。私はOpenSeaの文章を読んだだけだとうむ?と思いましたが、コードを読んで納得しました。

こちらのコントラクトの動きはこうです。

register

こちらは自分のNFTのコントラクトを登録します。自分のコントラクトに紐ついてブラックリストを作れる仕組みとなっているわけです。ただそれだと面倒ですよね?

subscribe

で用意されているのが subscribeです。これを使うと誰かが作ったリストを自分にも適用しますという運用がかのです。実はOpenSeaがすでにレジストリーをしていてそれを使ってくださいと言っているのです。

参照: https://github.com/ProjectOpenSea/operator-filter-registry

その1で記載していますが自分のNFTには、DefaultOperatorFilterer.solを読み込みます。

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

import {OperatorFilterer} from "./OperatorFilterer.sol";

contract DefaultOperatorFilterer is OperatorFilterer {
    address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);

    constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
}

そのコントラクトの中でOpenSeaのリストをSubsucribeすることがコントラクトのデブロイでおこなわているわけです。

今回のOpenSeaはこう言った形で使っています。上記のインターフェイスを見てもらうとみれるかと思いますが、updateOperator()の関数もあり、OpenSeaは排除するオペレーターの変更をすることができます。ところでいまはどこを登録しているのでしょうか?GitHubのレポジトリーの説明では6箇所でしたが。

参照: https://github.com/ProjectOpenSea/operator-filter-registry

コントラクトで運用されているので誰でも設定をRead関数を叩くことでみることができます。Etherscanで叩いた時の結果です。6個のアドレスが見れますね。

OperatorFilterRegistryのコードでは CodeHashと呼ばれるコード自体でブラックリストを管理できる構造も含まれていますが、多分OpenSeaでは使っていません。CodeHashでのリストだとデブロイしたアドレスが変わってもコードが同じならブラックリストですよということができそうです。ちょっと変えたら変わる気もするので対応はされやすい気もします。

operator-filter-registry一通り読んでみましたが、ブラックリストをしっかり、サブスクできる形で管理する以上のことはコードからよみとれませんでした。逐一解説するところもないかなという感じではあります。

ということでfunctionの解説は冗長となるのでやめておこうと思います。このあたりは歯応えがない感じではあります。ただ、ライブにデブロイされているのでEtherscanでコントラクトを叩いて様子を見たり、どれくらいの人がすでにレジストリーしたのかEventLogを見るのはおもしろいかもしれませんね。

https://etherscan.io/address/0x000000000000AAeB6D7670E522A718067333cd4E#readContract

こちらの関数を叩くとOpenSeaのレジストリーに対して122のサブスクライバーがいます。これが多いのか少ないのか。どうでしょうか。

既存のNFTに対しては変わらずロイヤリティの徴収をすることをOpenSeaは宣言しています。厳しい戦いがOpenSeaには待っていそうです。ちょっとブラックリスト的な対応はそれぞれのNFT取引所と軋轢を生みそうだなと思っていますがどうでしょうかね。

NFTのコントラクト自体にもうロイヤリティを取る仕組みを入れられるようにするのがいい気もしますが、P2P取引の場合の扱いが難しいのでしょう。この辺りの発明が望まれます。

ではでは。

Discussion