NOTE: This has been deprecated. See this blog post for more details.
projectopensea / operator-filter-registry Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
NOTE: This has been deprecated. See this blog post for more details.
The example used in documentation blocked listing on x2y2 but not looksrare on ethereum mainnet. I thought the correct address might be 0xf42aa99F011A1fA7CDA90E5E98b277E306BcA83e (this is the contract you setApprovalForAll on looksrare listings) not 0x59728544b08ab483533076417fbbb2fd0b17ce3a which is listed in the documentation, though when I ran UpdateOperator, it still didn't block looksrare.
This filter is only able to filter out specific operator addresses and codehashes, both of which can be made dynamic for (future) marketplaces. For example, a marketplace can be designed that deploys an operator contract for each user, or even for each order, with a slightly different codehash via a pseudo-random salt in creation code. It would be infeasible for this registry to denylist every dynamically created operator contract (with intentionally different codehashes), rendering it useless in the long run (likely only a couple of months).
Also, I want to note that I very much appreciate the open and honest conversations on this topic from you all ❤️
operator-filter-registry/src/OperatorFilterer.sol
Lines 39 to 51 in 529ccee
The cost of comparing from
and msg.sender
is way less than the cost to go check the code size of an external address.
For the sake of not wasting gas on direct transfers (and imo a better flow) this code could be rewritten into
if (from != msg.sender) {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), msg.sender)) {
revert OperatorNotAllowed(msg.sender);
}
}
}
_;
this would also allow to put the checking snippet in its own internal
function _checkOperatorFilter(address operator) internal virtual {
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
revert OperatorNotAllowed(operator);
}
}
}
and have a cleaner code reuse
modifier onlyAllowedOperator(address from) virtual {
if (from != msg.sender) {
_checkOperatorFilter(msg.sender);
}
_;
}
modifier onlyAllowedOperatorApproval(address operator) virtual {
_checkOperatorFilter(operator);
_;
}
function _checkOperatorFilter(address operator) internal virtual {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
revert OperatorNotAllowed(operator);
}
}
}
}
This would also allow to facilitate overrides from implementers, for example to add a switchs to enable/disable the OperatorFilter when all this drama of not wanting to respect creator fees/royalties will stop
Adding ~150gas at execution, but makes it hell of easier to override
Hey Deploy the contract on Goerli network.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {DefaultOperatorFilterer} from "./DefaultOperatorFilterer.sol";
contract NFTee is ERC721, DefaultOperatorFilterer, Ownable {
constructor() ERC721("Example", "EXAMPLE") {
_mint("YOUR_TREASURY_ADDRESS",1);
}
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 tokenURI(uint256) public pure override returns (string memory) {
return "";
}
}
Hi,
your package is not working with erc721A because the transfers functions and setApproval are payable.
please update your package to allow erc721A as well.
thanks
If a collection contract has enforcement disabled by default, 1X is minted, then enforcement is enabled, OS still does not see this being enabled and does not allow setting royalties.
Happened with this collection https://opensea.io/collection/strawberryjuice as an example
Add a recheck eligibility button next to creator fees?
For people implementing these filters on approvals rather than directly on transfers, they should block the sudoswap PairFactory contract (0xb16c1342E617A5B6E4b631EB114483FDB289c0A4) in addition to the PairRouter. Might as well add this to the list you have going, as it's just one additional address
I don't think it's necessary to have a separate filterer class for 1155. I presume the goal of OperatorFilterer1155.sol was to demonstrate what a 1155 filterer would look like, but the current example doesn't showcase anything different than the 721 filterer. Looping multiple times here without using the ids
parameter seems unnecessary: https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/example/OperatorFilterer1155.sol#L42. Additionally, the id
parameter here is unused: https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/example/OperatorFilterer1155.sol#L29.
I'm also trying to think of a scenario in which a certain 1155 id would need to be blocked, but another wouldn't be.
According to the code, the subscribe
method pushes an address onto the subscription stack (
import "https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/OperatorFilterer.sol";
import "https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/RevokableOperatorFilterer.sol";
contract ExampleNFT is ERC721, ERC721Enumerable, RevokableOperatorFilterer {
address public currentOperatorFiltererSubscriber = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
constructor() ERC721("ExampleNFT", "EXAMPLE") OperatorFilterer(currentOperatorFiltererSubscriber, true){}
function setOperatorFiltererSubscriber(address _subscription) public onlyOwner {
OPERATOR_FILTER_REGISTRY.unregister(address(this));
OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), _subscription);
currentOperatorFiltererSubscriber = _subscription;
}
/**
* Overrides
*/
function setApprovalForAll(address operator, bool approved) public override(IERC721, ERC721) onlyAllowedOperatorApproval(operator) {
super.setApprovalForAll(operator, approved);
}
function approve(address operator, uint256 tokenId) public override(IERC721, ERC721) onlyAllowedOperatorApproval(operator) {
super.approve(operator, tokenId);
}
function transferFrom(address from, address to, uint256 tokenId) public override(IERC721, ERC721) onlyAllowedOperator(from) {
super.transferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public override(IERC721, ERC721) onlyAllowedOperator(from) {
super.safeTransferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
public
override(IERC721, ERC721)
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, data);
}
function owner() public view virtual override (Ownable, RevokableOperatorFilterer) returns (address) {
return Ownable.owner();
}
}
Hello OpenSea, thank you for sharing the contracts. They are another interesting contribution to the NFT space and another learning opportunity. This issue concerns this part of the README.
Contract owners may implement their own filtering outside of this registry, or they may use this registry to curate their own lists of filtered operators. However, there are certain contracts that are filtered by the default subscription, and must be filtered in order to be eligible for creator fee enforcement on OpenSea.
The example in this repository has an NFT implementing a blacklist of transfers from the default OpenSea registry.
operator-filter-registry/src/OperatorFilterer.sol
Lines 29 to 37 in 0871c3a
Name | Address | Network |
---|---|---|
Blur.io ExecutionDelegate | 0x00000000000111AbE46ff893f3B2fdF1F759a8A8 | Ethereum Mainnet |
LooksRareExchange | 0x59728544b08ab483533076417fbbb2fd0b17ce3a | Ethereum Mainnet |
X2Y2 ERC721Delegate | 0xf849de01b080adc3a814fabe1e2087475cf2e354 | Ethereum Mainnet |
SudoSwap LSSVMPairRouter | 0x2b2e8cda09bba9660dca5cb6233787738ad68329 | Ethereum Mainnet |
As an NFT creator, I receive significant volume on my collections from several of these blacklisted exchanges. While these exchanges support optional creator fees, many of my community members still choose to pay creator fees. Ultimately it is their choice and I would like to respect that.
As an NFT creator, I also appreciate all the support OpenSea can give me. I would like for my NFT to ideally balance both my own expectations of profit from secondary sales and the experiences of my community on their platform of choice.
When my community chooses to use OpenSea as their trading platform, I would like to opt in to enforcing creator fees. I do not, however, want to negatively impact the experience of my community members who choose to use any other exchange and therefore do not want to blacklist the use of those exchanges. In short, I find the present situation as it stands right now ideal and would prefer a way to maintain that situation for my NFTs and my community.
I understand the business rationale behind combining these use cases, but as a creator would prefer if OpenSea gave me the option to opt in to enforced creator fees without also requiring that I blacklist any other exchanges.
First, i want to thank to OpenSea about enforce creator fees. I have some problems with the function subscribe
:
/**
* @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);
}
According to the function comments, if the registrant calls this function to subscribe a new subscription. Is that means the _filteredOperators[registrant]
should contains _filteredOperators[newSubscription]
, but there is no copy
code. And there is function copyEntriesOf
, why not copy the entries of new subscription into registrant's filtered operators when call subscribe
?
Hi OpenSea team! We're deploying a system that uses a factory contract to deploy ERC1155 clones. Our implementation contract inherits DefaultOperatorFilterUpgradeable
and supports EIP-2981.
Our verification fork tests pass, creator fees are enforced in the OpenSea testnet UI, and the mainnet contracts look like they are correctly registered with the operator filter, but we are still seeing optional creator fees on mainnet OpenSea. Implementation contract bytecode is the same on Goerli and mainnet. Any idea what might be going on? We were surprised by this in production, since fork tests and testnet deployments both looked OK.
Here's an example deployment tx from mainnet for one of the clone contracts.
Currently Operator Filter Registry is just deployed on Ethereum and Goerli at 0x000000000000aaeb6d7670e522a718067333cd4e.
We could deploy our own registries in different networks, but it makes sense to have one unique registry address (0x000000000000aaeb6d7670e522a718067333cd4e) deployed across different blockchains.
It would be great to have full support (including filtered addresses) on the networks you already support: Arbitrum, Avalanche, Klaytn, Optimism, Polygon, Solana.
But at least and empty deployment on EVM Blockchains would ease things for multichain projects like ours, where we can share code across different blockchains and not having to think about different addresses on different blockchains and keeping our own registries.
We've implement it from the very first day at https://nfts2me.com/ (https://twitter.com/nfts2me/status/1589215568942747648) as we think this is a great option for creators, but support for other blockchain is really a need!
Hello!
I've read through most of the existing issues, but can't quite figure out why my contract is being shown as optional for creator fees...
Chain: Polygon Testnet (Mumbai)
Token Type: ERC1155
Here's my Smart Contract code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
contract Clones is ERC1155, ERC2981, DefaultOperatorFilterer, Ownable {
// Contract name
string public name;
// Contract symbol
string public symbol;
constructor(
string memory _name,
string memory _symbol,
string memory someUri
) ERC1155("someBaseUri") {
name = _name;
symbol = _symbol;
_setURI(someUri);
_setDefaultRoyalty(owner(), 500);
}
function airdropClone(uint256 tokenId, address to) public onlyOwner {
_mint(to, tokenId, 1, "");
}
function setDefaultRoyalty(address receiver, uint96 feeNumerator) public onlyOwner {
_setDefaultRoyalty(receiver, feeNumerator);
}
/// @dev Set royalty fee for specific token
/// @param _tokenId The tokenId where to add the royalty
/// @param _receiver The royalty receiver
/// @param _feeNumerator the fee for specific tokenId
function setTokenRoyalty(
uint256 _tokenId,
address _receiver,
uint96 _feeNumerator
) public onlyOwner {
_setTokenRoyalty(_tokenId, _receiver, _feeNumerator);
}
/// @dev Allow owner to delete the default royalty for all collection
function deleteDefaultRoyalty() external onlyOwner {
_deleteDefaultRoyalty();
}
/// @dev Reset specific royalty
/// @param tokenId The token id where to reset the royalty
function resetTokenRoyalty(uint256 tokenId) external onlyOwner {
_resetTokenRoyalty(tokenId);
}
//=======================================================================
// [public/override/onlyAllowedOperatorApproval] for OperatorFilter
//=======================================================================
function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) { super.setApprovalForAll(operator, approved); }
function safeTransferFrom(address from, address to, uint256 tokenId, uint256 amount, bytes memory data) public override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, amount, data); }
function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override onlyAllowedOperator(from) { super.safeBatchTransferFrom(from, to, ids, amounts, data); }
//=======================================================================
// [public/override]
//=======================================================================
function supportsInterface(bytes4 interfaceId) public view override( ERC1155, ERC2981 ) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
Editing the contract on OpenSea shows the following:
I've seen some other people saying that they had to hit a refresh button, but I don't have access to a refresh button to run the check again. I've also minted a few NFTs from the contract, as it says in the documentation that the enforcement check is run whenever the first NFT is minted.
Any ideas as to why this isn't being enforced?
Thanks,
Matt
It would be helpful to add the specific justification for inclusion of each marketplace contract on the table.
For example from what I can see LooksRare support via ERC2981, why are they on the list of excluded contracts?
Has anyone deployed a NFT smart contract with OperatorFilterer to set up the allowed Operators to make token transfers yet?
Please let us know the contract address.
Or if someone could provide an example code it would be a tremendous help.
Thank you
Is there an API or a way for me to get the information of the wallet that receives the commission (creator earnings) when successfully selling 1 NFT? I would like to get information to know which NFT commission is earned?
Summary:
The use of msg.sender in the operator-filter registry makes it impossible to inherit from these classes and override msg.sender. This can be a problem in contexts where the sender may be different than anticipated, such as with EIP-725X.
As a solution, it is recommended to follow the example of OpenZeppelin's contracts and use _msgSender() instead. This will make it easier to consume the operator-filter registry without having to fork the code.
Reference: https://forum.openzeppelin.com/t/why-use-msgsender-rather-than-msg-sender/4370
After trying to get royalty fees working on görli, I realized that a valid tokenURI is required to get the controls for creator fees working.
The examples here return a blank tokenURI and it could be replaced with an example token such as https://www.721.so/api/example/metadata/1
.
https://goerli.etherscan.io/address/0xceeab5b8852e267a27f51799d892bad86e3baadd#code
I have been using code from 72b5d50d8e63d1be48a23a482aa3b165195587ab
but my ERC721A-upgradable contract is still not eligible for creator fees on goerli
I have a simple ERC1155 contract that extends DefaultOperatorFilterer.
It also define the contract URI function.
I can deploy and mint correctly.
I can sell it on opensea, but when I try to buy on testnet i recieve this message 'Execution reverted. Please reach out to the collection owner to troubleshoot.'.
Do I need to configure something more to work correctly?
Does anyone know if this works with TinyERC721 (https://github.com/tajigen/tiny-erc721).
I tried deploying with it on goerli and it still showed as unable to assign creator fees. But then when I switched to erc721a it worked.
Any ideas?
TinyERC721 is very gas efficient which is why I like using it
I am trying to deploy a contract with the creator earnings embedded into the contractURI
function. I have implemented the DefaultOperatorFilter properly. The contractURI
address is reachable as OpenSea is picking up the collection name and description and everything but not the seller_fee_basis_points
and fee_recepient
. It was working perfectly before Nov 8, but after implementing the DefaultOperatorFilter
it is not working anymore. What could be the issue?
Here is a contract I have deployed to goerli and the creator earning informations are not being picked up.
https://goerli.etherscan.io/address/0x16e8255c88d0e24d99b5503467e03db9db722a63#code
Hey there, I think that backwards compatibility could be achieved with an intermediate contract. This is a quick and dirty proof of concept:
contract ERC721OperatorFiltererAdapter is DefaultOperatorFilterer, Ownable {
address public target;
constructor(address _target) {
target = _target;
}
function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator onlyApprovedOperator {
IERC721(target).transferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator onlyApprovedOperator {
IERC721(target).safeTransferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
public
override
onlyAllowedOperator
onlyApprovedOperator
{
IERC721(target).safeTransferFrom(from, to, tokenId, data);
}
mapping(address => bool) private _operatorApprovals;
function setApprovalForAll(address operator, bool approved) public virtual override onlyOwner {
_operatorApprovals[operator] = approved;
}
modifier onlyApprovedOperator {
require(true == _operatorApprovals[msg.sender]);
_;
}
}
On Goerli It looks like the OS curated filter are the operator addresses for the filtered mainnet operator contracts:
[
0x00000000000111AbE46ff893f3B2fdF1F759a8A8,
0x59728544B08AB483533076417FbBB2fD0B17CE3a,
0xFED24eC7E22f573c2e08AEF55aA6797Ca2b3A051,
0x2B2e8cDA09bBA9660dCA5cB6233787738Ad68329,
0xf42aa99F011A1fA7CDA90E5E98b277E306BcA83e
]
However, at least for LooksRare their Transfer Manager on Goerli is deployed too: 0xf8c81f3ae82b6efc9154c69e3db57fd4da57ab6e
The testnet subscription should probably match their corresponding filtered testnet operators for full E2E testing.
I have followed the ReadMe and added the required methods. This is the verified contract: https://goerli.etherscan.io/address/0x714965eAA0A9c44Fb741aEB690a565471Ba637a0#code
And I have added the contract address as the creator fees receiver in the settings.
But when I try to list an item for sale, below error happened:
RRNLRequestError: Relay request for `useHandleBlockchainActionsCreateOrderMutation` failed by the following reasons:
1. [500] [500] Error occurred while processing your request. Please try again later.
Error id: a505e5cba87d419885e84678461b6885
Error id: a505e5cba87d419885e84678461b6885
^^^
This is my NFT collection on Opensea: https://testnets.opensea.io/collection/kits-og1sesicwa
You forced us to implement this last minute, and yet people can buy our NFTs with 0% fees on X2Y2.
We almost rescheduled the mint, because without [operator-filter-registry](https://github.com/ProjectOpenSea/operator-filter-registry)
, OpenSea won't enforce creator fees after January 2. So we were kinda forced to implement this, otherwise no one will pay fees.
Then as it turned out, it meant nothing, people can still buy/sell on X2Y2 with 0% creator fees. When I think it can't be worse, OpenSea proves there is always worse, and you charge you a ridiculous 2.5% fee for providing this unacceptable infrastructure. And if I want access to the API, fill a fckn google form and pray...
It seems some new operator addresses were added for LooksRare (#7), however it appears that listing, selling, and buying still works on LooksRare.
tx: https://etherscan.io/tx/0x40d78b8381f03b8b6176bdc2277680d005679c0d2d78e1548f65268db96b057e
I am using the example upgradeable contact, and modify some code to add the ERC721EnumerableUpgradeable, like as blow:
abstract contract ExampleERC721Upgradeable is ERC721Upgradeable, DefaultOperatorFiltererUpgradeable, OwnableUpgradeable, ERC721EnumerableUpgradeable
and I override the functions of ERC721EnumerableUpgradeable accordingly. Then I will get the error message:
The detail error message is :
`TypeError: Function needs to specify overridden contracts "ERC721Upgradeable" and "IERC721Upgradeable".
--> contracts/TEST.sol:50:72:
|
50 | function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval {
| ^^^^^^^^
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:20:1:
|
20 | contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol:11:1:
|
11 | interface IERC721Upgradeable is IERC165Upgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
TypeError: Function needs to specify overridden contracts "ERC721Upgradeable" and "IERC721Upgradeable".
--> contracts/TEST.sol:54:64:
|
54 | function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
| ^^^^^^^^
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:20:1:
|
20 | contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol:11:1:
|
11 | interface IERC721Upgradeable is IERC165Upgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
TypeError: Function needs to specify overridden contracts "ERC721Upgradeable" and "IERC721Upgradeable".
--> contracts/TEST.sol:58:77:
|
58 | function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
| ^^^^^^^^
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:20:1:
|
20 | contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol:11:1:
|
11 | interface IERC721Upgradeable is IERC165Upgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
TypeError: Function needs to specify overridden contracts "ERC721Upgradeable" and "IERC721Upgradeable".
--> contracts/TEST.sol:62:81:
|
62 | function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
| ^^^^^^^^
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:20:1:
|
20 | contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol:11:1:
|
11 | interface IERC721Upgradeable is IERC165Upgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
TypeError: Function needs to specify overridden contracts "ERC721Upgradeable" and "IERC721Upgradeable".
--> contracts/TEST.sol:68:9:
|
68 | override
| ^^^^^^^^
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol:20:1:
|
20 | contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: This contract:
--> @openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol:11:1:
|
11 | interface IERC721Upgradeable is IERC165Upgradeable {
| ^ (Relevant source part starts here and spans across multiple lines).
TypeError: Wrong argument count for modifier invocation: 0 arguments given but expected 1.
--> contracts/TEST.sol:50:81:
|
50 | function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
`
Could somebody help me fix this? thx in advance!
The example implementations for ERC721
and ERC1155
are restricting safeTransferFrom
and safeBatchTransferFrom
like this:
function safeTransferFrom(address from, address to, uint256 tokenId, uint256 amount, bytes memory data)
public
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, amount, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override onlyAllowedOperator(from) {
super.safeBatchTransferFrom(from, to, ids, amounts, data);
}
is there a reason to not restrict via _beforeTokenTransfer
?
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint[] memory ids,
uint[] memory amounts,
bytes memory data
) internal virtual override onlyAllowedOperator(from) {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
Without a package, I'm left trying to copy these in. Are these contracts at the point they can be packaged up an hosted at npm?
According to the Default operator registry code, the current owner of the smart contract or the smart contract itself can send request to update operators as shown in code below:
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)) } } } } _; }
The Ownable(addr) checks for the current owner, however, when I call the updateOperator with me being the owner, it throws an error. Am I missing something?
After taking a look at this tweet:
https://twitter.com/pandajackson42/status/1620081518575235073
It seems they did with their own conduit, and the seem to be honouring Royalties.
But, what stops Blur from not honoring Royalties? Using their own conduit allows them to remove or introduce any arbitrary fees as long as the user list the NFT under their conduit.
But, on the other hand a new conduit is a new contract that could be blocked, right? Will you? Or being royalties complain on this conduit is enough to follow the rules as long as they honour royalties?
EDIT: I've done some more research and realized that they actually are using OpenSea's conduit. Then, there's no way to forbid it, right?
The current implementation of the operator filtering logic allows token owners (also real users) to be blocked indefinitely from moving their tokens. See also #4 for a failing test.
Was this intentional? If so, what was the reasoning for it?
Even though it is the contract owners decision in the end to leave this in, I think this should not be allowed in the reference implementation.
Hello.
Does this operator filter registry affect non-marketplace contracts? For example our own NFT staking contract where user approves and it moves the NFT (minting) contract tokens to the staking contract. Or other related operations/use cases?
Please add a prettierrc or a linter or any conf you want so IDEs can follow the maintainers configuration
How can I upgrade my NFT smart contract to implement Operator Filter Registry? I know there's they release an upgradeable OFR, but it requires to be initialized. I'm just wondering how are you going to initialize it when you already deployed the smart contract and you're just upgrading it?
Confused af .......
I upgraded my ERC1155 contract yesterday but OpenSea still say "This collection is not eligible for creator fees" in the collection edit page.
I followed the example : https://github.com/ProjectOpenSea/operator-filter-registry/blob/main/src/example/upgradeable/ExampleERC1155Upgradeable.sol and added the following methods into my old contract
function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
super.setApprovalForAll(operator, approved);
}
function safeTransferFrom(address from, address to, uint256 tokenId, uint256 amount, bytes memory data)
public
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, amount, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override onlyAllowedOperator(from) {
super.safeBatchTransferFrom(from, to, ids, amounts, data);
}
After upgrading my old contract on Ethereum Mainnet, nothing changed in my collection editing page, it still say "This collection is not eligible for creator fees".
It is necessary to add support for upgradeable smart contracts
Since the OperatorFilterRegistry
is a new and un-audited contract, there is no guarantee there won't be any bugs and/or vulnerabilities discovered in future.
Because of that, hardcoding the address to this contract seems risky:
IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
To de-risk this should the address be updatable via a setOperatorFilterRegistry
method?
Readme talks about updateOperatorAddress method to employ, but I am not seeing this method in the src code?
Hello to the OpenSea development team.
we have incorporated the DefaultOperatorFilterer into ERC1155 and deployed it to Goerli network.
we verified the sales on OpenSea's test net and was able to edit "Creatoer earnings".
Below is the verification code.
https://goerli.etherscan.io/address/0x3aa59d510abed17216c4c7da676862d8381e634b#code
Below is the OpenSea(testet) collection page.
https://testnets.opensea.io/ja/collection/unidentified-contract-baemdyveyr
Below is the transaction that was sold and successfully traded with the commission edited to 0%.
https://goerli.etherscan.io/tx/0xfd13270c2977b588b8256d6f719e4124b73a1d729b9bc1d3b10944c21331bd76
How can we disable the "creator earnings" edit when ERC1155 is offered for sale?
(When we tried with ERC721, the "Creatoer fearnigs" edits were disabled as expected.)
Thank you in advance for your help.
Hey! I made a simple 1155 erc contract but it seems like creator fee are not available , obv with the DefaultOperatorFilterer.
Here my contract ( i'm gonna strip some functions here)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "./DefaultOperatorFilterer.sol";
interface IERC2981Royalties {
function royaltyInfo(uint256 _tokenId, uint256 _value)
external
view
returns (address _receiver, uint256 _royaltyAmount);
}
abstract contract ERC2981Base is ERC165, IERC2981Royalties {
struct RoyaltyInfo {
address recipient;
uint24 amount;
}
/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override
returns (bool)
{
return
interfaceId == type(IERC2981Royalties).interfaceId ||
super.supportsInterface(interfaceId);
}
}
/// @custom:security-contact [email protected]
contract DversoTickets is ERC1155, Ownable, ERC1155Supply , Pausable, ERC1155Burnable , ERC2981Base , DefaultOperatorFilterer {
using ECDSA for bytes32;
mapping(uint256 => bool) public tokenEnabled;
mapping(uint256 => bool) public tokenWhitelist;
mapping(uint256 => uint256) public maxTokenPW;
mapping(uint256 => uint256) public costs;
mapping(uint256 => uint256) public tokenSupplies;
mapping(uint256 => mapping(address => uint256)) private mintedBalances;
mapping(uint256 => string) public cids;
RoyaltyInfo private _royalties;
constructor() ERC1155("") {
_setRoyalties(0x5211063C82D0CD0aB516a063206D50AA919eac75 , 200);
setTokenIndex( 1, //token Id
1, //max per wallet
1000, // supply
true, // whitelist verification enabled
true, // token enabled
"", //cid
0 ether); // cost
}
function setTokenIndex(uint256 tokenId,uint256 maxPerWallet,uint256 supply,bool whitelist,bool _enabled,string memory cid,uint256 cost) public onlyOwner {
maxTokenPW[tokenId] = maxPerWallet;
tokenEnabled[tokenId] = _enabled;
tokenWhitelist[tokenId] = whitelist;
tokenSupplies[tokenId] = supply;
cids[tokenId] = cid;
costs[tokenId] = cost;
}
function contractURI() public pure returns (string memory) {
return "https://example.com/contract.json";
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function _baseURI(uint256 tokenId) internal view virtual returns (string memory) {
return string(abi.encodePacked("ipfs://", cids[tokenId]));
}
function mintedBalanceOf(address account, uint256 id) public view returns (uint256) {
require(account != address(0), "ERC1155: balance query for the zero address");
return mintedBalances[id][account];
}
function mint(bytes calldata signature, uint256 id, bytes memory data) public payable
{
require(tokenSupplies[id] > 0, "Token is not mintable");
require(tokenEnabled[id], "Token is not mintable");
require(mintedBalanceOf(msg.sender,id) < maxTokenPW[id], "Reached max mint for token");
require(totalSupply(id) < tokenSupplies[id], "Reached max supply");
require(msg.value >= costs[id], "Value should not be lower than than cost");
mintedBalances[id][msg.sender] += 1;
_mint(msg.sender, id, 1, data);
}
function mintOwner(address account,uint256 id, uint256 amount, bytes memory data)
public
onlyOwner
{
require(totalSupply(id) + (amount - 1) < tokenSupplies[id], "Reached max supply");
require(tokenEnabled[id], "Token is not mintable");
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyOwner
{
for (uint256 i = 0; i < ids.length; i++) {
require(totalSupply(ids[i]) + (amounts[i] - 1) < tokenSupplies[ids[i]], "Reached max supply");
require(tokenEnabled[ids[i]], "Token is not mintable");
}
_mintBatch(to, ids, amounts, data);
}
function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
super.setApprovalForAll(operator, approved);
}
function safeTransferFrom(address from, address to, uint256 tokenId, uint256 amount, bytes memory data)
public
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, amount, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override onlyAllowedOperator(from) {
super.safeBatchTransferFrom(from, to, ids, amounts, data);
}
// The following functions are overrides required by Solidity.
function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
internal
whenNotPaused
override(ERC1155, ERC1155Supply)
{
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
function uri(uint _id) public override view returns (string memory) {
return string(abi.encodePacked(
_baseURI(_id), "/", Strings.toString(_id),".json"
));
}
function _setRoyalties(address recipient, uint256 value) internal {
require(value <= 10000, 'ERC2981Royalties: Too high');
_royalties = RoyaltyInfo(recipient, uint24(value));
}
function royaltyInfo(uint256, uint256 value)
external
view
override
returns (address receiver, uint256 royaltyAmount)
{
RoyaltyInfo memory royalties = _royalties;
receiver = royalties.recipient;
royaltyAmount = (value * royalties.amount) / 10000;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, ERC2981Base) returns (bool) {
return super.supportsInterface(interfaceId);
}
}
Here is the contract code: https://etherscan.io/address/0x06d56899b9ffb9dcac279a51924643f0411623ac#code,
I add the operator-filter-registry into this contract, but I still cannot set creator fees.
Currently contracts are imporing from 'openzeppelin-contracts' instead of '@openzeppelin/contracts' which breaks smart contract verification as hardhat verification task can't find 'openzeppelin-contracts'
When I try to inherit , below message pop-up.
Shoud I just delete "function owner()" at UpdatableOperatorFiltere.sol ?
Note: Definition in "Ownable":
--> @openzeppelin/contracts/access/Ownable.sol:43:5:
|
43 | function owner() public view virtual returns (address) {
| ^ (Relevant source part starts here and spans across multiple lines).
Note: Definition in "UpdatableOperatorFilterer":
--> contracts/RevokableOperatorFilterRegistry/UpdatableOperatorFilterer.sol:72:5:
|
72 | function owner() public view virtual returns (address);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is my contract address: 0x1b63Be0847C57f72cc8e093C99628D625C1F4baA on goerli network.
I checked [0x000000000000AAeB6D7670E522A718067333cd4E] and found my contract was registered the filter.
But the creator fee is still not eligible.
what's going on next?
I have implemented openSea royalty reforcement and normally it enforces royalties on opensea but now i'm using "erc1967 proxy UUPSUpgradeable" and then using proxy I mint nfts and when I check in openSea it doesnot enforces royalty. What is issue there I don't understand.Need help.
the proxy is calling transfer function when selling on opensea and as well as it identifies onlyAllowedOperator modifier but still I don't understand why it does not enforce royalties
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.