Skip to content
This repository was archived by the owner on Sep 1, 2023. It is now read-only.

Commit bce3a35

Browse files
Merge branch 'main' into oz-n01
2 parents d14f148 + dab3eba commit bce3a35

30 files changed

+748
-121
lines changed

README.md

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,40 @@
22

33
## **Note on Grace Period for Non-Ethereum Chains**
44

5-
Currently, OpenSea only requires creator fee enforcement on Ethereum Mainnet and Goerli for collections to be eligible for creator fees. However, starting **January 2nd, 2023**, Opensea will begin validating creator fee enforcement on all supported EVM chains.
5+
Currently, OpenSea only requires creator earnings enforcement on Ethereum Mainnet and Goerli for collections to be eligible for creator earnings. However, starting **January 2nd, 2023**, Opensea will begin validating creator earnings enforcement on all supported EVM chains.
66

77
## Introduction
88

9-
This repository contains a number of tools to help token contracts manage the operators allowed to transfer tokens on behalf of users - including the smart contracts and delegates of marketplaces that do not respect creator fees.
9+
This repository contains a number of tools to help token contracts manage the operators allowed to transfer tokens on behalf of users - including the smart contracts and delegates of marketplaces that do not respect creator earnings.
1010

11-
This is not a foolproof approach - but it makes bypassing creator fees less liquid and easy at scale.
11+
This is not a foolproof approach - but it makes bypassing creator earnings less liquid and easy at scale.
1212

1313
## How it works
1414

1515
Token smart contracts may register themselves (or be registered by their "owner") with the `OperatorFilterRegistry`. Token contracts or their "owner"s may then curate lists of operators (specific account addresses) and codehashes (smart contracts deployed with the same code) that should not be allowed to transfer tokens on behalf of users.
1616

17-
## Creator Fee Enforcement
17+
## Creator Earnings Enforcement
1818

19-
OpenSea will enforce creator fees for smart contracts that make best efforts to filter transfers from operators known to not respect creator fees.
19+
OpenSea will enforce creator earnings for smart contracts that make best efforts to filter transfers from operators known to not respect creator earnings.
2020

2121
This repository facilitates that process by providing smart contracts that interface with the registry automatically, including automatically subscribing to OpenSea's list of filtered operators.
2222

2323
When filtering operators, use of this registry is not required, nor is it required for a token contract to "subscribe" to OpenSea's list within this registry. Subscriptions can be changed or removed at any time. Filtered operators and codehashes may likewise be added or removed at any time.
2424

25-
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.
25+
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 earnings enforcement on OpenSea.
2626

2727
## Note on [EIP-2981](https://eips.ethereum.org/EIPS/eip-2981)
2828

29-
Implementing EIP-2981 is not sufficient for a token to be eligible for creator fees on OpenSea.
29+
Implementing EIP-2981 is not sufficient for a token to be eligible for creator earnings on OpenSea.
3030

31-
While sometimes described as "on-chain," EIP-2981 only provides a method to determine what the appropriate creator fee should be for a sale. EIP-2981 does not provide any mechanism of on-chain enforcement of those fees.
31+
While sometimes described as "on-chain," EIP-2981 only provides a method to determine what the appropriate creator earnings should be for a sale. EIP-2981 does not provide any mechanism of on-chain enforcement of those earnings.
3232

3333
## Filtered addresses
3434

3535
Entries in this list are added according to the following criteria:
3636

37-
- If the application most commonly used to interface with the contract gives buyers and sellers the ability to bypass creator fees when a similar transaction for the same item would require creator fee payment on OpenSea.io
38-
- If the contract is facilitating the evasion of on-chain creator fee enforcement measures. For example, the contract uses a wrapper contract to bypass fee enforcement.
37+
- If the application most commonly used to interface with the contract gives buyers and sellers the ability to bypass creator earnings when a similar transaction for the same item would require creator earnings payment on OpenSea.io
38+
- If the contract is facilitating the evasion of on-chain creator earnings enforcement measures. For example, the contract uses a wrapper contract to bypass earnings enforcement.
3939

4040
<table>
4141
<tr>
@@ -79,11 +79,18 @@ Ethereum Mainnet
7979
<table>
8080
<tr>
8181
<th>Network</th>
82+
<th>CORI Subscription TimelockController</th>
8283
<th>OperatorFilterRegistry</th>
83-
<th>OpenSea Curated Subscription Address</th>
84+
<th>CORI Curated Subscription Address</th>
8485
</tr>
8586

86-
<tr><td>Ethereum</td><td rowspan="20">
87+
<tr><td>Ethereum</td>
88+
<td>
89+
90+
0x178AD648e66815E1B01791eBBdbF7b2D7C5B1626
91+
92+
</td>
93+
<td rowspan="20">
8794

8895
[0x000000000000AAeB6D7670E522A718067333cd4E](https://etherscan.io/address/0x000000000000AAeB6D7670E522A718067333cd4E#code)
8996

@@ -93,8 +100,17 @@ Ethereum Mainnet
93100

94101
</td></tr>
95102

96-
<tr><td>Goerli</td></tr>
97-
<tr><td>Polygon</td></tr>
103+
<tr>
104+
<td>Polygon</td>
105+
106+
<td>
107+
0x87bCD4735CbCF9CE98ea2822fBf3F05F2ce10f96
108+
</td>
109+
<td></td>
110+
<td></td>
111+
</tr>
112+
113+
<tr><td>Goerli</td><td rowspan="20">0xe3A6CD067a1193b903143C36dA00557c9d95C41e</td></tr>
98114
<tr><td>Mumbai</td></tr>
99115
<tr><td>Optimism</td></tr>
100116
<tr><td>Optimism Goerli</td></tr>
@@ -111,6 +127,8 @@ Ethereum Mainnet
111127

112128
</table>
113129

130+
To mitigate abuse of the CORI curated subscription of filtered operators and codehashes, the CORI curated subscription is owned by a `TimelockController`, which is in turn owned by a multi-signature wallet. Any update to CORI's list of filtered operators must be approved by at least two members of the Creator Ownership Research Institute, and is then subject to a minimum 24-hour delay before being executed. During this time, updates may be reviewed and revoked.
131+
114132
## Usage
115133

116134
Token contracts that wish to manage lists of filtered operators and restrict transfers from them may integrate with the registry easily with tokens using the [`OperatorFilterer`](src/OperatorFilterer.sol) and [`DefaultOperatorFilterer`](src/DefaultOperatorFilterer.sol) contracts. These contracts provide modifiers (`onlyAllowedOperator` and `onlyAllowedOperatorApproval`) which can be used on the token's transfer methods to restrict transfers from or approvals of filtered operators.
@@ -210,7 +228,7 @@ This method will toggle filtering for an operator for a given registrant. If `fi
210228

211229
### updateCodeHash(address registrant, bytes32 codeHash, bool filtered)
212230

213-
This method will toggle filtering on code hashes of operators given registrant. If an operator's `EXTCODEHASH` matches a filtered code hash, `isOperatorAllowed` will return `true`. Otherwise, `isOperatorAllowed` will return `false`. This can filter smart contract operators with different addresess but the same code.
231+
This method will toggle filtering on code hashes of operators given registrant. If an operator's `EXTCODEHASH` matches a filtered code hash, `isOperatorAllowed` will return `true`. Otherwise, `isOperatorAllowed` will return `false`. This can filter smart contract operators with different addresses but the same code.
214232

215233
## `OperatorFilterer`
216234

@@ -225,6 +243,8 @@ On construction, it takes three parameters:
225243
- `address subscriptionOrRegistrantToCopy`: the address of the registrant the contract will either subscribe to, or do a one-time copy of that registrant's filters. If the zero address is provided, no subscription or copies will be made.
226244
- `bool subscribe`: if true, subscribes to the previous address if it was not the zero address. If false, copies existing filtered addresses and codeHashes without subscribing to future updates.
227245

246+
Please note that if your token contract does not provide an owner with [EIP-173](https://eips.ethereum.org/EIPS/eip-173), it must provide administration methods on the contract itself to interact with the registry otherwise the subscription will be locked to the options set during construction.
247+
228248
### `onlyAllowedOperator(address operator)`
229249

230250
This modifier will revert if the `operator` or its code hash is filtered by the `OperatorFilterRegistry` contract.
@@ -233,15 +253,17 @@ This modifier will revert if the `operator` or its code hash is filtered by the
233253

234254
This smart contract extends `OperatorFilterer` and automatically configures the token contract that inherits it to subscribe to OpenSea's list of filtered operators and code hashes. This subscription can be updated at any time by the owner by calling `updateSubscription` on the `OperatorFilterRegistry` contract.
235255

256+
Please note that if your token contract does not provide an owner with [EIP-173](https://eips.ethereum.org/EIPS/eip-173), it must provide administration methods on the contract itself to interact with the registry otherwise the subscription will be locked to the options set during construction.
257+
236258
## `OwnedRegistrant`
237259

238260
This `Ownable` smart contract is meant as a simple utility to enable subscription addresses that can easily be transferred to a new owner for administration. For example: an EOA curates a list of filtered operators and code hashes, and then transfers ownership of the `OwnedRegistrant` to a multisig wallet.
239261

240262
# Validation
241263

242-
When the first token is minted on an NFT smart contract, OpenSea checks if the filtered operators on that network (Ethereum Mainnet, Goerli, Polygon, etc.) are allowed to transfer the token. If they are, OpenSea will mark the collection as ineligible for creator fees. Otherwise, OpenSea will enforce creator fees on the collection.
264+
When the first token is minted on an NFT smart contract, OpenSea checks if the filtered operators on that network (Ethereum Mainnet, Goerli, Polygon, etc.) are allowed to transfer the token. If they are, OpenSea will mark the collection as ineligible for creator earnings. Otherwise, OpenSea will enforce creator earnings on the collection.
243265

244-
If at a later point, OpenSea detects orders being fulfilled by filtered operators, OpenSea will mark the collection as ineligible for creator fees going forward.
266+
If at a later point, OpenSea detects orders being fulfilled by filtered operators, OpenSea will mark the collection as ineligible for creator earnings going forward.
245267

246268
The included [validation test](test/validation/Validation.t.sol) runs the same checks that OpenSea does when first creating a collection page, and can be extended with custom setup for your token contract.
247269

src/DefaultOperatorFilterer.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ import {OperatorFilterer} from "./OperatorFilterer.sol";
66
/**
77
* @title DefaultOperatorFilterer
88
* @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
9+
* @dev Please note that if your token contract does not provide an owner with EIP-173, it must provide
10+
* administration methods on the contract itself to interact with the registry otherwise the subscription
11+
* will be locked to the options set during construction.
912
*/
1013
abstract contract DefaultOperatorFilterer is OperatorFilterer {
14+
/// @dev The default OpenSea subscription address
1115
address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
1216

17+
/// @dev The constructor that is called when the contract is being deployed.
1318
constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
1419
}

src/IOperatorFilterRegistry.sol

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,138 @@
22
pragma solidity ^0.8.13;
33

44
interface IOperatorFilterRegistry {
5+
/**
6+
* @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
7+
* true if supplied registrant address is not registered.
8+
*/
59
function isOperatorAllowed(address registrant, address operator) external view returns (bool);
10+
11+
/**
12+
* @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
13+
*/
614
function register(address registrant) external;
15+
16+
/**
17+
* @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
18+
*/
719
function registerAndSubscribe(address registrant, address subscription) external;
20+
21+
/**
22+
* @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
23+
* address without subscribing.
24+
*/
825
function registerAndCopyEntries(address registrant, address registrantToCopy) external;
26+
27+
/**
28+
* @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
29+
* Note that this does not remove any filtered addresses or codeHashes.
30+
* Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
31+
*/
932
function unregister(address addr) external;
33+
34+
/**
35+
* @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
36+
*/
1037
function updateOperator(address registrant, address operator, bool filtered) external;
38+
39+
/**
40+
* @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
41+
*/
1142
function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
43+
44+
/**
45+
* @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
46+
*/
1247
function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
48+
49+
/**
50+
* @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
51+
*/
1352
function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
53+
54+
/**
55+
* @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
56+
* subscription if present.
57+
* Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
58+
* subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
59+
* used.
60+
*/
1461
function subscribe(address registrant, address registrantToSubscribe) external;
62+
63+
/**
64+
* @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
65+
*/
1566
function unsubscribe(address registrant, bool copyExistingEntries) external;
67+
68+
/**
69+
* @notice Get the subscription address of a given registrant, if any.
70+
*/
1671
function subscriptionOf(address addr) external returns (address registrant);
72+
73+
/**
74+
* @notice Get the set of addresses subscribed to a given registrant.
75+
* Note that order is not guaranteed as updates are made.
76+
*/
1777
function subscribers(address registrant) external returns (address[] memory);
78+
79+
/**
80+
* @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
81+
* Note that order is not guaranteed as updates are made.
82+
*/
1883
function subscriberAt(address registrant, uint256 index) external returns (address);
84+
85+
/**
86+
* @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
87+
*/
1988
function copyEntriesOf(address registrant, address registrantToCopy) external;
89+
90+
/**
91+
* @notice Returns true if operator is filtered by a given address or its subscription.
92+
*/
2093
function isOperatorFiltered(address registrant, address operator) external returns (bool);
94+
95+
/**
96+
* @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
97+
*/
2198
function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
99+
100+
/**
101+
* @notice Returns true if a codeHash is filtered by a given address or its subscription.
102+
*/
22103
function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
104+
105+
/**
106+
* @notice Returns a list of filtered operators for a given address or its subscription.
107+
*/
23108
function filteredOperators(address addr) external returns (address[] memory);
109+
110+
/**
111+
* @notice Returns the set of filtered codeHashes for a given address or its subscription.
112+
* Note that order is not guaranteed as updates are made.
113+
*/
24114
function filteredCodeHashes(address addr) external returns (bytes32[] memory);
115+
116+
/**
117+
* @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
118+
* its subscription.
119+
* Note that order is not guaranteed as updates are made.
120+
*/
25121
function filteredOperatorAt(address registrant, uint256 index) external returns (address);
122+
123+
/**
124+
* @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
125+
* its subscription.
126+
* Note that order is not guaranteed as updates are made.
127+
*/
26128
function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
129+
130+
/**
131+
* @notice Returns true if an address has registered
132+
*/
27133
function isRegistered(address addr) external returns (bool);
134+
135+
/**
136+
* @dev Convenience method to compute the code hash of an arbitrary contract
137+
*/
28138
function codeHashOf(address addr) external returns (bytes32);
29139
}

0 commit comments

Comments
 (0)