Last active
June 29, 2023 21:28
-
-
Save shobhitic/2dec2f5369328d2bbf346112af89807d to your computer and use it in GitHub Desktop.
Working version of OpenSea operator filter registry - https://www.youtube.com/watch?v=4spapOTVpNA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) {} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.4; | |
import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol"; | |
import "@openzeppelin/[email protected]/token/ERC721/extensions/ERC721URIStorage.sol"; | |
import "@openzeppelin/[email protected]/access/Ownable.sol"; | |
import "@openzeppelin/[email protected]/utils/Counters.sol"; | |
import "./DefaultOperatorFilterer.sol"; | |
contract MyToken is ERC721, ERC721URIStorage, Ownable, DefaultOperatorFilterer { | |
using Counters for Counters.Counter; | |
Counters.Counter private _tokenIdCounter; | |
constructor() ERC721("MyToken", "MTK") {} | |
function safeMint(address to, string memory uri) public onlyOwner { | |
uint256 tokenId = _tokenIdCounter.current(); | |
_tokenIdCounter.increment(); | |
_safeMint(to, tokenId); | |
_setTokenURI(tokenId, uri); | |
} | |
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); | |
} | |
// The following functions are overrides required by Solidity. | |
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { | |
super._burn(tokenId); | |
} | |
function tokenURI(uint256 tokenId) | |
public | |
view | |
override(ERC721, ERC721URIStorage) | |
returns (string memory) | |
{ | |
return super.tokenURI(tokenId); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol"; | |
contract OperatorFilterer { | |
error OperatorNotAllowed(address operator); | |
IOperatorFilterRegistry constant operatorFilterRegistry = | |
IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E); | |
constructor(address subscriptionOrRegistrantToCopy, bool subscribe) { | |
// If an inheriting token contract is deployed to a network without the registry deployed, the modifier | |
// will not revert, but the contract will need to be registered with the registry once it is deployed in | |
// order for the modifier to filter addresses. | |
if (address(operatorFilterRegistry).code.length > 0) { | |
if (subscribe) { | |
operatorFilterRegistry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy); | |
} else { | |
if (subscriptionOrRegistrantToCopy != address(0)) { | |
operatorFilterRegistry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy); | |
} else { | |
operatorFilterRegistry.register(address(this)); | |
} | |
} | |
} | |
} | |
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); | |
} | |
} | |
_; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol"; | |
import {Ownable} from "@openzeppelin/[email protected]/access/Ownable.sol"; | |
import {EnumerableSet} from "@openzeppelin/[email protected]/utils/structs/EnumerableSet.sol"; | |
import {OperatorFilterRegistryErrorsAndEvents} from "./OperatorFilterRegistryErrorsAndEvents.sol"; | |
/** | |
* @title OperatorFilterRegistry | |
* @notice Borrows heavily from the QQL BlacklistOperatorFilter contract: | |
* https://github.com/qql-art/contracts/blob/main/contracts/BlacklistOperatorFilter.sol | |
* @notice This contracts allows tokens or token owners to register specific addresses or codeHashes that may be | |
* * restricted according to the isOperatorAllowed function. | |
*/ | |
contract OperatorFilterRegistry is IOperatorFilterRegistry, OperatorFilterRegistryErrorsAndEvents { | |
using EnumerableSet for EnumerableSet.AddressSet; | |
using EnumerableSet for EnumerableSet.Bytes32Set; | |
/// @dev initialized accounts have a nonzero codehash (see https://eips.ethereum.org/EIPS/eip-1052) | |
/// Note that this will also be a smart contract's codehash when making calls from its constructor. | |
bytes32 constant EOA_CODEHASH = keccak256(""); | |
mapping(address => EnumerableSet.AddressSet) private _filteredOperators; | |
mapping(address => EnumerableSet.Bytes32Set) private _filteredCodeHashes; | |
mapping(address => address) private _registrations; | |
mapping(address => EnumerableSet.AddressSet) private _subscribers; | |
/** | |
* @notice restricts method caller to the address or EIP-173 "owner()" | |
*/ | |
modifier onlyAddressOrOwner(address addr) { | |
if (msg.sender != addr) { | |
try Ownable(addr).owner() returns (address owner) { | |
if (msg.sender != owner) { | |
revert OnlyAddressOrOwner(); | |
} | |
} catch (bytes memory reason) { | |
if (reason.length == 0) { | |
revert NotOwnable(); | |
} else { | |
/// @solidity memory-safe-assembly | |
assembly { | |
revert(add(32, reason), mload(reason)) | |
} | |
} | |
} | |
} | |
_; | |
} | |
/** | |
* @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; | |
} | |
////////////////// | |
// AUTH METHODS // | |
////////////////// | |
/** | |
* @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner. | |
*/ | |
function register(address registrant) external onlyAddressOrOwner(registrant) { | |
if (_registrations[registrant] != address(0)) { | |
revert AlreadyRegistered(); | |
} | |
_registrations[registrant] = registrant; | |
emit RegistrationUpdated(registrant, true); | |
} | |
/** | |
* @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner. | |
* Note that this does not remove any filtered addresses or codeHashes. | |
* Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes. | |
*/ | |
function unregister(address registrant) external onlyAddressOrOwner(registrant) { | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
_subscribers[registration].remove(registrant); | |
emit SubscriptionUpdated(registrant, registration, false); | |
} | |
_registrations[registrant] = address(0); | |
emit RegistrationUpdated(registrant, false); | |
} | |
/** | |
* @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes. | |
*/ | |
function registerAndSubscribe(address registrant, address subscription) external onlyAddressOrOwner(registrant) { | |
address registration = _registrations[registrant]; | |
if (registration != address(0)) { | |
revert AlreadyRegistered(); | |
} | |
if (registrant == subscription) { | |
revert CannotSubscribeToSelf(); | |
} | |
address subscriptionRegistration = _registrations[subscription]; | |
if (subscriptionRegistration == address(0)) { | |
revert NotRegistered(subscription); | |
} | |
if (subscriptionRegistration != subscription) { | |
revert CannotSubscribeToRegistrantWithSubscription(subscription); | |
} | |
_registrations[registrant] = subscription; | |
_subscribers[subscription].add(registrant); | |
emit RegistrationUpdated(registrant, true); | |
emit SubscriptionUpdated(registrant, subscription, true); | |
} | |
/** | |
* @notice Registers an address with the registry and copies the filtered operators and codeHashes from another | |
* address without subscribing. | |
*/ | |
function registerAndCopyEntries(address registrant, address registrantToCopy) | |
external | |
onlyAddressOrOwner(registrant) | |
{ | |
if (registrantToCopy == registrant) { | |
revert CannotCopyFromSelf(); | |
} | |
address registration = _registrations[registrant]; | |
if (registration != address(0)) { | |
revert AlreadyRegistered(); | |
} | |
address registrantRegistration = _registrations[registrantToCopy]; | |
if (registrantRegistration == address(0)) { | |
revert NotRegistered(registrantToCopy); | |
} | |
_registrations[registrant] = registrant; | |
emit RegistrationUpdated(registrant, true); | |
_copyEntries(registrant, registrantToCopy); | |
} | |
/** | |
* @notice Update an operator address for a registered address - when filtered is true, the operator is filtered. | |
*/ | |
function updateOperator(address registrant, address operator, bool filtered) | |
external | |
onlyAddressOrOwner(registrant) | |
{ | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
revert CannotUpdateWhileSubscribed(registration); | |
} | |
EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant]; | |
if (!filtered) { | |
bool removed = filteredOperatorsRef.remove(operator); | |
if (!removed) { | |
revert AddressNotFiltered(operator); | |
} | |
} else { | |
bool added = filteredOperatorsRef.add(operator); | |
if (!added) { | |
revert AddressAlreadyFiltered(operator); | |
} | |
} | |
emit OperatorUpdated(registrant, operator, filtered); | |
} | |
/** | |
* @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered. | |
*/ | |
function updateCodeHash(address registrant, bytes32 codeHash, bool filtered) | |
external | |
onlyAddressOrOwner(registrant) | |
{ | |
if (codeHash == EOA_CODEHASH) { | |
revert CannotFilterEOAs(); | |
} | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
revert CannotUpdateWhileSubscribed(registration); | |
} | |
EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant]; | |
if (!filtered) { | |
bool removed = filteredCodeHashesRef.remove(codeHash); | |
if (!removed) { | |
revert CodeHashNotFiltered(codeHash); | |
} | |
} else { | |
bool added = filteredCodeHashesRef.add(codeHash); | |
if (!added) { | |
revert CodeHashAlreadyFiltered(codeHash); | |
} | |
} | |
emit CodeHashUpdated(registrant, codeHash, filtered); | |
} | |
/** | |
* @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates. | |
*/ | |
function updateOperators(address registrant, address[] calldata operators, bool filtered) | |
external | |
onlyAddressOrOwner(registrant) | |
{ | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
revert CannotUpdateWhileSubscribed(registration); | |
} | |
EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant]; | |
uint256 operatorsLength = operators.length; | |
unchecked { | |
if (!filtered) { | |
for (uint256 i = 0; i < operatorsLength; ++i) { | |
address operator = operators[i]; | |
bool removed = filteredOperatorsRef.remove(operator); | |
if (!removed) { | |
revert AddressNotFiltered(operator); | |
} | |
} | |
} else { | |
for (uint256 i = 0; i < operatorsLength; ++i) { | |
address operator = operators[i]; | |
bool added = filteredOperatorsRef.add(operator); | |
if (!added) { | |
revert AddressAlreadyFiltered(operator); | |
} | |
} | |
} | |
} | |
emit OperatorsUpdated(registrant, operators, filtered); | |
} | |
/** | |
* @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates. | |
*/ | |
function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) | |
external | |
onlyAddressOrOwner(registrant) | |
{ | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
revert CannotUpdateWhileSubscribed(registration); | |
} | |
EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant]; | |
uint256 codeHashesLength = codeHashes.length; | |
unchecked { | |
if (!filtered) { | |
for (uint256 i = 0; i < codeHashesLength; ++i) { | |
bytes32 codeHash = codeHashes[i]; | |
bool removed = filteredCodeHashesRef.remove(codeHash); | |
if (!removed) { | |
revert CodeHashNotFiltered(codeHash); | |
} | |
} | |
} else { | |
for (uint256 i = 0; i < codeHashesLength; ++i) { | |
bytes32 codeHash = codeHashes[i]; | |
if (codeHash == EOA_CODEHASH) { | |
revert CannotFilterEOAs(); | |
} | |
bool added = filteredCodeHashesRef.add(codeHash); | |
if (!added) { | |
revert CodeHashAlreadyFiltered(codeHash); | |
} | |
} | |
} | |
} | |
emit CodeHashesUpdated(registrant, codeHashes, filtered); | |
} | |
/** | |
* @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous | |
* subscription if present. | |
* Note that accounts with subscriptions may go on to subscribe to other accounts - in this case, | |
* subscriptions will not be forwarded. Instead the former subscription's existing entries will still be | |
* used. | |
*/ | |
function subscribe(address registrant, address newSubscription) external onlyAddressOrOwner(registrant) { | |
if (registrant == newSubscription) { | |
revert CannotSubscribeToSelf(); | |
} | |
if (newSubscription == address(0)) { | |
revert CannotSubscribeToZeroAddress(); | |
} | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration == newSubscription) { | |
revert AlreadySubscribed(newSubscription); | |
} | |
address newSubscriptionRegistration = _registrations[newSubscription]; | |
if (newSubscriptionRegistration == address(0)) { | |
revert NotRegistered(newSubscription); | |
} | |
if (newSubscriptionRegistration != newSubscription) { | |
revert CannotSubscribeToRegistrantWithSubscription(newSubscription); | |
} | |
if (registration != registrant) { | |
_subscribers[registration].remove(registrant); | |
emit SubscriptionUpdated(registrant, registration, false); | |
} | |
_registrations[registrant] = newSubscription; | |
_subscribers[newSubscription].add(registrant); | |
emit SubscriptionUpdated(registrant, newSubscription, true); | |
} | |
/** | |
* @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes. | |
*/ | |
function unsubscribe(address registrant, bool copyExistingEntries) external onlyAddressOrOwner(registrant) { | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration == registrant) { | |
revert NotSubscribed(); | |
} | |
_subscribers[registration].remove(registrant); | |
_registrations[registrant] = registrant; | |
emit SubscriptionUpdated(registrant, registration, false); | |
if (copyExistingEntries) { | |
_copyEntries(registrant, registration); | |
} | |
} | |
/** | |
* @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr. | |
*/ | |
function copyEntriesOf(address registrant, address registrantToCopy) external onlyAddressOrOwner(registrant) { | |
if (registrant == registrantToCopy) { | |
revert CannotCopyFromSelf(); | |
} | |
address registration = _registrations[registrant]; | |
if (registration == address(0)) { | |
revert NotRegistered(registrant); | |
} | |
if (registration != registrant) { | |
revert CannotUpdateWhileSubscribed(registration); | |
} | |
address registrantRegistration = _registrations[registrantToCopy]; | |
if (registrantRegistration == address(0)) { | |
revert NotRegistered(registrantToCopy); | |
} | |
_copyEntries(registrant, registrantToCopy); | |
} | |
/// @dev helper to copy entries from registrantToCopy to registrant and emit events | |
function _copyEntries(address registrant, address registrantToCopy) private { | |
EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrantToCopy]; | |
EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrantToCopy]; | |
uint256 filteredOperatorsLength = filteredOperatorsRef.length(); | |
uint256 filteredCodeHashesLength = filteredCodeHashesRef.length(); | |
unchecked { | |
for (uint256 i = 0; i < filteredOperatorsLength; ++i) { | |
address operator = filteredOperatorsRef.at(i); | |
bool added = _filteredOperators[registrant].add(operator); | |
if (added) { | |
emit OperatorUpdated(registrant, operator, true); | |
} | |
} | |
for (uint256 i = 0; i < filteredCodeHashesLength; ++i) { | |
bytes32 codehash = filteredCodeHashesRef.at(i); | |
bool added = _filteredCodeHashes[registrant].add(codehash); | |
if (added) { | |
emit CodeHashUpdated(registrant, codehash, true); | |
} | |
} | |
} | |
} | |
////////////////// | |
// VIEW METHODS // | |
////////////////// | |
/** | |
* @notice Get the subscription address of a given registrant, if any. | |
*/ | |
function subscriptionOf(address registrant) external view returns (address subscription) { | |
subscription = _registrations[registrant]; | |
if (subscription == address(0)) { | |
revert NotRegistered(registrant); | |
} else if (subscription == registrant) { | |
subscription = address(0); | |
} | |
} | |
/** | |
* @notice Get the set of addresses subscribed to a given registrant. | |
* Note that order is not guaranteed as updates are made. | |
*/ | |
function subscribers(address registrant) external view returns (address[] memory) { | |
return _subscribers[registrant].values(); | |
} | |
/** | |
* @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant. | |
* Note that order is not guaranteed as updates are made. | |
*/ | |
function subscriberAt(address registrant, uint256 index) external view returns (address) { | |
return _subscribers[registrant].at(index); | |
} | |
/** | |
* @notice Returns true if operator is filtered by a given address or its subscription. | |
*/ | |
function isOperatorFiltered(address registrant, address operator) external view returns (bool) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredOperators[registration].contains(operator); | |
} | |
return _filteredOperators[registrant].contains(operator); | |
} | |
/** | |
* @notice Returns true if a codeHash is filtered by a given address or its subscription. | |
*/ | |
function isCodeHashFiltered(address registrant, bytes32 codeHash) external view returns (bool) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredCodeHashes[registration].contains(codeHash); | |
} | |
return _filteredCodeHashes[registrant].contains(codeHash); | |
} | |
/** | |
* @notice Returns true if the hash of an address's code is filtered by a given address or its subscription. | |
*/ | |
function isCodeHashOfFiltered(address registrant, address operatorWithCode) external view returns (bool) { | |
bytes32 codeHash = operatorWithCode.codehash; | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredCodeHashes[registration].contains(codeHash); | |
} | |
return _filteredCodeHashes[registrant].contains(codeHash); | |
} | |
/** | |
* @notice Returns true if an address has registered | |
*/ | |
function isRegistered(address registrant) external view returns (bool) { | |
return _registrations[registrant] != address(0); | |
} | |
/** | |
* @notice Returns a list of filtered operators for a given address or its subscription. | |
*/ | |
function filteredOperators(address registrant) external view returns (address[] memory) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredOperators[registration].values(); | |
} | |
return _filteredOperators[registrant].values(); | |
} | |
/** | |
* @notice Returns the set of filtered codeHashes for a given address or its subscription. | |
* Note that order is not guaranteed as updates are made. | |
*/ | |
function filteredCodeHashes(address registrant) external view returns (bytes32[] memory) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredCodeHashes[registration].values(); | |
} | |
return _filteredCodeHashes[registrant].values(); | |
} | |
/** | |
* @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or | |
* its subscription. | |
* Note that order is not guaranteed as updates are made. | |
*/ | |
function filteredOperatorAt(address registrant, uint256 index) external view returns (address) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredOperators[registration].at(index); | |
} | |
return _filteredOperators[registrant].at(index); | |
} | |
/** | |
* @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or | |
* its subscription. | |
* Note that order is not guaranteed as updates are made. | |
*/ | |
function filteredCodeHashAt(address registrant, uint256 index) external view returns (bytes32) { | |
address registration = _registrations[registrant]; | |
if (registration != registrant) { | |
return _filteredCodeHashes[registration].at(index); | |
} | |
return _filteredCodeHashes[registrant].at(index); | |
} | |
/// @dev Convenience method to compute the code hash of an arbitrary contract | |
function codeHashOf(address a) external view returns (bytes32) { | |
return a.codehash; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
contract OperatorFilterRegistryErrorsAndEvents { | |
error CannotFilterEOAs(); | |
error AddressAlreadyFiltered(address operator); | |
error AddressNotFiltered(address operator); | |
error CodeHashAlreadyFiltered(bytes32 codeHash); | |
error CodeHashNotFiltered(bytes32 codeHash); | |
error OnlyAddressOrOwner(); | |
error NotRegistered(address registrant); | |
error AlreadyRegistered(); | |
error AlreadySubscribed(address subscription); | |
error NotSubscribed(); | |
error CannotUpdateWhileSubscribed(address subscription); | |
error CannotSubscribeToSelf(); | |
error CannotSubscribeToZeroAddress(); | |
error NotOwnable(); | |
error AddressFiltered(address filtered); | |
error CodeHashFiltered(address account, bytes32 codeHash); | |
error CannotSubscribeToRegistrantWithSubscription(address registrant); | |
error CannotCopyFromSelf(); | |
event RegistrationUpdated(address indexed registrant, bool indexed registered); | |
event OperatorUpdated(address indexed registrant, address indexed operator, bool indexed filtered); | |
event OperatorsUpdated(address indexed registrant, address[] operators, bool indexed filtered); | |
event CodeHashUpdated(address indexed registrant, bytes32 indexed codeHash, bool indexed filtered); | |
event CodeHashesUpdated(address indexed registrant, bytes32[] codeHashes, bool indexed filtered); | |
event SubscriptionUpdated(address indexed registrant, address indexed subscription, bool indexed subscribed); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol"; | |
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; | |
/** | |
* @title OwnedRegistrant | |
* @notice Ownable contract that registers itself with the OperatorFilterRegistry and administers its own entries, | |
* to facilitate a subscription whose ownership can be transferred. | |
*/ | |
contract OwnedRegistrant is Ownable2Step { | |
address constant registry = 0x000000000000AAeB6D7670E522A718067333cd4E; | |
constructor(address _owner) { | |
IOperatorFilterRegistry(registry).register(address(this)); | |
transferOwnership(_owner); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment