Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change default operator filter subscription #93

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions contracts/erc721/OperatorFilterOS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
pragma solidity >=0.8.0 <0.9.0;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {DefaultOperatorFilterer} from "operator-filter-registry/src/DefaultOperatorFilterer.sol";
import {OperatorFilterer} from "operator-filter-registry/src/OperatorFilterer.sol";
import {ERC721A, ERC721ACommon} from "./ERC721ACommon.sol";

address constant BLUR_SUBSCRIPTION = 0x9dC5EE2D52d014f8b81D662FA8f4CA525F27cD6b;

/**
* @notice ERC721ACommon extension that adds Opensea's operator filtering.
*/
abstract contract OperatorFilterOS is ERC721ACommon, DefaultOperatorFilterer {
abstract contract OperatorFilterOS is ERC721ACommon, OperatorFilterer(BLUR_SUBSCRIPTION, true) {
using Address for address;

/**
Expand Down Expand Up @@ -51,28 +53,33 @@ abstract contract OperatorFilterOS is ERC721ACommon, DefaultOperatorFilterer {
super.approve(operator, tokenId);
}

function transferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override onlyAllowedOperator(from) {
function transferFrom(address from, address to, uint256 tokenId)
public
payable
virtual
override
onlyAllowedOperator(from)
{
super.transferFrom(from, to, tokenId);
}

function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override onlyAllowedOperator(from) {
function safeTransferFrom(address from, address to, uint256 tokenId)
public
payable
virtual
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId);
}

function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public payable virtual override onlyAllowedOperator(from) {
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
public
payable
virtual
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, data);
}
}
117 changes: 31 additions & 86 deletions tests/erc721/OperatorFilterOS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,27 @@ import {Test} from "../TestLib.sol";
import {IERC721A} from "erc721a/contracts/IERC721A.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {OperatorFilterRegistryErrorsAndEvents, OperatorFilterRegistry} from "operator-filter-registry/src/OperatorFilterRegistry.sol";
import {
OperatorFilterRegistryErrorsAndEvents,
OperatorFilterRegistry
} from "operator-filter-registry/src/OperatorFilterRegistry.sol";
import {OperatorFilterer} from "operator-filter-registry/src/OperatorFilterer.sol";
import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, CANONICAL_CORI_SUBSCRIPTION} from "operator-filter-registry/src/lib/Constants.sol";
import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "operator-filter-registry/src/lib/Constants.sol";

import {ERC721ACommon} from "../../contracts/erc721/ERC721ACommon.sol";
import {OperatorFilterOS} from "../../contracts/erc721/OperatorFilterOS.sol";
import {BLUR_SUBSCRIPTION, OperatorFilterOS} from "../../contracts/erc721/OperatorFilterOS.sol";

contract TestableToken is OperatorFilterOS {
constructor(address admin, address steerer)
ERC721ACommon(
admin,
steerer,
"NAME",
"SYM",
payable(address(0xFEE)),
750
)
ERC721ACommon(admin, steerer, "NAME", "SYM", payable(address(0xFEE)), 750)
{} // solhint-disable-line no-empty-blocks

function mint(address to, uint256 num) public {
_mint(to, num);
}
}

contract ERC721ATransferRestrictedGeneralTest is
Test,
OperatorFilterRegistryErrorsAndEvents
{
contract ERC721ATransferRestrictedGeneralTest is Test, OperatorFilterRegistryErrorsAndEvents {
using Address for address;

TestableToken public token;
Expand All @@ -46,13 +39,10 @@ contract ERC721ATransferRestrictedGeneralTest is
OperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);

function setUp() public virtual {
vm.etch(
CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS,
address(new OperatorFilterRegistry()).code
);
vm.etch(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS, address(new OperatorFilterRegistry()).code);

vm.prank(CANONICAL_CORI_SUBSCRIPTION);
registry.register(CANONICAL_CORI_SUBSCRIPTION);
vm.prank(BLUR_SUBSCRIPTION);
registry.register(BLUR_SUBSCRIPTION);

token = new TestableToken(admin, steerer);
}
Expand All @@ -64,49 +54,33 @@ contract ERC721ATransferRestrictedGeneralTest is

vm.startPrank(vandal, steerer);

vm.expectRevert(
missingRoleError(vandal, token.DEFAULT_STEERING_ROLE())
);
vm.expectRevert(missingRoleError(vandal, token.DEFAULT_STEERING_ROLE()));
token.callOperatorFilterRegistry(hex"");
}

function testForwardUnsubscribe(bool copyExistingEntries) public {
assertEq(
registry.subscriptionOf(address(token)),
CANONICAL_CORI_SUBSCRIPTION
);
assertEq(registry.subscriptionOf(address(token)), BLUR_SUBSCRIPTION);

vm.prank(steerer);
token.callOperatorFilterRegistry(
abi.encodeWithSelector(
OperatorFilterRegistry.unsubscribe.selector,
address(token),
copyExistingEntries
)
abi.encodeWithSelector(OperatorFilterRegistry.unsubscribe.selector, address(token), copyExistingEntries)
);
assertEq(registry.subscriptionOf(address(token)), address(0));
}

function testForwardSubscribe(address newSubscription) public {
vm.assume(newSubscription != address(0));
vm.assume(newSubscription != address(token));
vm.assume(newSubscription != CANONICAL_CORI_SUBSCRIPTION);
vm.assume(newSubscription != BLUR_SUBSCRIPTION);

vm.prank(newSubscription);
registry.register(newSubscription);

assertEq(
registry.subscriptionOf(address(token)),
CANONICAL_CORI_SUBSCRIPTION
);
assertEq(registry.subscriptionOf(address(token)), BLUR_SUBSCRIPTION);

vm.prank(steerer);
token.callOperatorFilterRegistry(
abi.encodeWithSelector(
OperatorFilterRegistry.subscribe.selector,
address(token),
newSubscription
)
abi.encodeWithSelector(OperatorFilterRegistry.subscribe.selector, address(token), newSubscription)
);
assertEq(registry.subscriptionOf(address(token)), newSubscription);
}
Expand All @@ -115,20 +89,16 @@ contract ERC721ATransferRestrictedGeneralTest is
vm.expectRevert(CannotSubscribeToZeroAddress.selector);
vm.prank(steerer);
token.callOperatorFilterRegistry(
abi.encodeWithSelector(
OperatorFilterRegistry.subscribe.selector,
address(token),
address(0)
)
abi.encodeWithSelector(OperatorFilterRegistry.subscribe.selector, address(token), address(0))
);
}

function _updateOperator(address operator, bool filtered) internal {
address[] memory list = new address[](1);
list[0] = operator;

vm.prank(CANONICAL_CORI_SUBSCRIPTION);
registry.updateOperators(CANONICAL_CORI_SUBSCRIPTION, list, filtered);
vm.prank(BLUR_SUBSCRIPTION);
registry.updateOperators(BLUR_SUBSCRIPTION, list, filtered);
}

enum TransferType {
Expand Down Expand Up @@ -158,12 +128,8 @@ contract ERC721ATransferRestrictedGeneralTest is
token.mint(tt.from, 1);
_updateOperator(tt.blockedOperator, true);

bytes memory err = abi.encodeWithSelector(
AddressFiltered.selector,
tt.blockedOperator
);
bool blocked = (tt.operator == tt.blockedOperator &&
tt.operator != tt.from);
bytes memory err = abi.encodeWithSelector(AddressFiltered.selector, tt.blockedOperator);
bool blocked = (tt.operator == tt.blockedOperator && tt.operator != tt.from);

if (tt.operator != tt.from) {
// can't approve self
Expand Down Expand Up @@ -201,36 +167,15 @@ contract ERC721ATransferRestrictedGeneralTest is
uint8 approvalType;
}

function _toTestCase(FuzzParams memory fuzz)
internal
pure
returns (TransferTest memory)
{
return
TransferTest({
from: fuzz.from,
to: fuzz.to,
blockedOperator: fuzz.blockedOperator,
operator: fuzz.operator,
transferType: TransferType(
uint8(
_bound(
fuzz.transferType,
0,
uint256(type(TransferType).max)
)
)
),
approvalType: ApprovalType(
uint8(
_bound(
fuzz.approvalType,
0,
uint256(type(ApprovalType).max)
)
)
)
});
function _toTestCase(FuzzParams memory fuzz) internal pure returns (TransferTest memory) {
return TransferTest({
from: fuzz.from,
to: fuzz.to,
blockedOperator: fuzz.blockedOperator,
operator: fuzz.operator,
transferType: TransferType(uint8(_bound(fuzz.transferType, 0, uint256(type(TransferType).max)))),
approvalType: ApprovalType(uint8(_bound(fuzz.approvalType, 0, uint256(type(ApprovalType).max))))
});
}

function testTransfer(FuzzParams memory fuzz) public {
Expand Down
Loading