🍉
OpenSeaでfee設定に必要なoperator-filter-registryを読んでみる
OpenSeaが11月10日に発表したツイートでは、
新しいコレクションに対してクリエイターフィーを認識するためには、オンチェーンでの執行ツールを採用する必要があります。
私たちの新しいレジストリはそのようなツールの1つですが、他にもありますし、私たちはそれらもサポートしています。
と記載されており、feeを受け取るにはロイヤリティを強制するコントラクトを使用しなければならないと書かれています。
今回は「このコントラクトでは何が強制されているのか」を読み解いていきます。
リンク集
🔗 実装例(Azukiエンジニア: cygaar)
🔗 operator-filter-registry
実装例
- ERC721
transferFrom
,safeTransferFrom(byteあり,なし)
をoverrideして、onlyAllowedOperator(from)
のmodifierを追加すれば良さそうです。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "operator-filter-registry/src/OperatorFilterer.sol";
contract SampleNFT721 is ERC721, Ownable, OperatorFilterer {
constructor() ERC721("SampleNFT", "SAMPLE") OperatorFilterer(address(0), false) {}
/* Add minting logic here */
/* Add metadata logic here */
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);
}
}
onlyAllowedOperatorを読む
では、実際に何を行っているのかを確認していきます。
OperatorFilterer.sol
modifier onlyAllowedOperator(address from) virtual {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(operatorFilterRegistry).code.length > 0) {
// Allow spending tokens from addresses with balance
// Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
// from an EOA.
// 通常のTransfer(fromが自分)の場合は制限を受けない
if (from == msg.sender) {
_;
return;
}
// msg.senderとfromの両方が許可されていればOK,そうでなければrevert
if (
!(
operatorFilterRegistry.isOperatorAllowed(address(this), msg.sender)
&& operatorFilterRegistry.isOperatorAllowed(address(this), from)
)
) {
revert OperatorNotAllowed(msg.sender);
}
}
_;
}
ちなみにisOperatorAllowed
を見てみると、制限されたコントラクト(ブラックリスト)はrevert
、それ以外はTrue
を返すようになっています。
※制限されたコントラクト(ブラックリスト)はこちら
OperatorFilterRegistry.sol
/**
* @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;
}
Discussion