This repository has been archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #80 from balajmarius/updatable-operator-filterer-u…
…pgradable feat: added UpdatableOperatorFiltererUpgradeable contract & tests
- Loading branch information
Showing
3 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
src/example/upgradeable/UpdatableExampleERC721Upgradeable.sol
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {ERC721Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; | ||
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol"; | ||
|
||
import {UpdatableOperatorFiltererUpgradeable} from | ||
"../../upgradeable/UpdatableOperatorFiltererUpgradeable.sol"; | ||
|
||
/** | ||
* @title UpdatableExampleERC721Upgradeable | ||
* @author qed.team, abarbatei, balajmarius | ||
* @notice This example contract is configured to use the UpdatableOperatorFiltererUpgradeable, which registers the | ||
* token and subscribes it to a give register filter. | ||
* Adding the onlyAllowedOperator modifier to the setApprovalForAll, approve, transferFrom, safeTransferFrom (both version) methods ensures that | ||
* the msg.sender (operator) is allowed by the OperatorFilterRegistry. | ||
*/ | ||
abstract contract UpdatableExampleERC721Upgradeable is | ||
ERC721Upgradeable, | ||
UpdatableOperatorFiltererUpgradeable, | ||
OwnableUpgradeable | ||
{ | ||
function initialize(address _registry, address subscriptionOrRegistrantToCopy, bool subscribe) public initializer { | ||
__ERC721_init("Example", "EXAMPLE"); | ||
__Ownable_init(); | ||
__UpdatableOperatorFiltererUpgradeable_init(_registry, subscriptionOrRegistrantToCopy, subscribe); | ||
} | ||
|
||
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); | ||
} | ||
|
||
function owner() | ||
public | ||
view | ||
virtual | ||
override (OwnableUpgradeable, UpdatableOperatorFiltererUpgradeable) | ||
returns (address) | ||
{ | ||
return OwnableUpgradeable.owner(); | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
src/upgradeable/UpdatableOperatorFiltererUpgradeable.sol
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {OperatorFiltererUpgradeable} from "./OperatorFiltererUpgradeable.sol"; | ||
import {IOperatorFilterRegistry} from "../IOperatorFilterRegistry.sol"; | ||
|
||
/** | ||
* @title Upgradeable storage layout for UpdatableOperatorFiltererUpgradeable. | ||
* @author qed.team, abarbatei, balajmarius | ||
* @notice Upgradeable contracts must use a storage layout that can be used across upgrades. | ||
* Only append new variables to the end of the layout. | ||
*/ | ||
library UpdatableOperatorFiltererUpgradeableStorage { | ||
struct Layout { | ||
/// @dev Address of the opensea filter register contract | ||
address _operatorFilterRegistry; | ||
} | ||
|
||
/// @dev The EIP-1967 specific storage slot for the layout | ||
bytes32 internal constant STORAGE_SLOT = | ||
bytes32(uint256(keccak256(bytes("UpdatableOperatorFiltererUpgradeable.contracts.storage"))) - 1); | ||
|
||
/// @dev The layout of the storage. | ||
function layout() internal pure returns (Layout storage l) { | ||
bytes32 slot = STORAGE_SLOT; | ||
assembly { | ||
l.slot := slot | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @title UpdatableOperatorFiltererUpgradeable | ||
* @author qed.team, abarbatei, balajmarius | ||
* @notice Abstract contract whose init function automatically registers and optionally subscribes to or copies another | ||
* registrant's entries in the OperatorFilterRegistry. This contract allows the Owner to update the | ||
* OperatorFilterRegistry address via updateOperatorFilterRegistryAddress, including to the zero address, | ||
* which will bypass registry checks. | ||
* Note that OpenSea will still disable creator earnings enforcement if filtered operators begin fulfilling orders | ||
* on-chain, eg, if the registry is revoked or bypassed. | ||
* @dev This smart contract is meant to be inherited by token contracts so they can use the following: | ||
* - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods. | ||
* - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods. | ||
* Use updateOperatorFilterRegistryAddress function to change registry address if needed | ||
*/ | ||
abstract contract UpdatableOperatorFiltererUpgradeable is OperatorFiltererUpgradeable { | ||
using UpdatableOperatorFiltererUpgradeableStorage for UpdatableOperatorFiltererUpgradeableStorage.Layout; | ||
|
||
/// @notice Emitted when someone other than the owner is trying to call an only owner function. | ||
error OnlyOwner(); | ||
|
||
/// @notice Emitted when the operator filter registry address is changed by the owner of the contract | ||
event OperatorFilterRegistryAddressUpdated(address newRegistry); | ||
|
||
/** | ||
* @notice Initialization function in accordance with the upgradable pattern | ||
* @dev The upgradeable initialize function specific to proxied contracts | ||
* @param _registry Registry address to which to register to for blocking operators that do not respect royalties | ||
* @param subscriptionOrRegistrantToCopy Subscription address to use as a template for when | ||
* imitating/copying blocked addresses and codehashes | ||
* @param subscribe If to subscribe to the subscriptionOrRegistrantToCopy address or just copy entries from it | ||
*/ | ||
function __UpdatableOperatorFiltererUpgradeable_init( | ||
address _registry, | ||
address subscriptionOrRegistrantToCopy, | ||
bool subscribe | ||
) internal onlyInitializing { | ||
UpdatableOperatorFiltererUpgradeableStorage.layout()._operatorFilterRegistry = _registry; | ||
IOperatorFilterRegistry registry = IOperatorFilterRegistry(_registry); | ||
// 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(registry).code.length > 0) { | ||
if (subscribe) { | ||
registry.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy); | ||
} else { | ||
if (subscriptionOrRegistrantToCopy != address(0)) { | ||
registry.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy); | ||
} else { | ||
registry.register(address(this)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @notice Update the address that the contract will make OperatorFilter checks against. When set to the zero | ||
* address, checks will be bypassed. OnlyOwner. | ||
* @custom:event OperatorFilterRegistryAddressUpdated | ||
* @param newRegistry The address of the registry that will be used for this contract | ||
*/ | ||
function updateOperatorFilterRegistryAddress(address newRegistry) public virtual { | ||
if (msg.sender != owner()) { | ||
revert OnlyOwner(); | ||
} | ||
UpdatableOperatorFiltererUpgradeableStorage.layout()._operatorFilterRegistry = newRegistry; | ||
emit OperatorFilterRegistryAddressUpdated(newRegistry); | ||
} | ||
|
||
/** | ||
* @dev Helper function to return the value of the currently used registry address | ||
*/ | ||
function operatorFilterRegistry() public view returns (address) { | ||
return address(UpdatableOperatorFiltererUpgradeableStorage.layout()._operatorFilterRegistry); | ||
} | ||
|
||
/** | ||
* @dev Assume the contract has an owner, but leave specific Ownable implementation up to inheriting contract | ||
*/ | ||
function owner() public view virtual returns (address); | ||
|
||
/** | ||
* @dev A helper function to check if the operator is allowed | ||
*/ | ||
function _checkFilterOperator(address operator) internal view virtual override { | ||
IOperatorFilterRegistry registry = | ||
IOperatorFilterRegistry(UpdatableOperatorFiltererUpgradeableStorage.layout()._operatorFilterRegistry); | ||
// Check registry code length to facilitate testing in environments without a deployed registry. | ||
if (address(registry) != address(0) && address(registry).code.length > 0) { | ||
// under normal circumstances, this function will revert rather than return false, but inheriting or | ||
// upgraded contracts may specify their own OperatorFilterRegistry implementations, which may behave | ||
// differently | ||
if (!registry.isOperatorAllowed(address(this), operator)) { | ||
revert OperatorNotAllowed(operator); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.