Creator Fee Enforcement

Seaport supports creator earnings enforcement for ERC721-C and ERC1155-C tokens through the use of Seaport Hooks.

By specifying a zone that can serve as an authorizer, Seaport is then able to hook into a transfer validator contract set on the 721-C or 1155-C. The transfer validator contract will attest to the inclusion of creator fees on the associated Seaport order.

OpenSea currently supports a single cross-chain zone for this purpose, the SignedZone. This zone is assigned as an authorizer on the StrictAuthorizedTransferSecurityRegistry and calls into the registry before and after the NFT in question is transferred — this call sets (and unsets) a flag for approving a specific operator, token id, or token id & amount to be transferred. Finally, the NFT itself will also call this registry during the transfer, and the registry will cause the transfer to revert if the corresponding flag has not been successfully set.

Creator Token Standard

Creators who wish to utilize fee enforcement via Seaport should first implement the following functionality on their NFT:

interface ICreatorToken {
    event TransferValidatorUpdated(address oldValidator, address newValidator);

    function getTransferValidator() external view returns (address validator);
    
    function getTransferValidationFunction() external view
        returns (bytes4 functionSignature, bool isViewFunction);

    function setTransferValidator(address validator) external;
}

This interface is implemented by ERC721SeaDrop, ERC1155SeaDrop, and the LimitBreak creator contracts, or can be implemented manually in your own token contracts. Once the owner of the NFT in question sets the StrictAuthorizedTransferSecurityRegistry as their transfer validator, their collection becomes eligible for creator earnings enforcement via Seaport. This can be performed by navigating to the creator earnings tab of your contracts on OpenSea Studio (note that any custom lists or security policies set on the previous transfer validator will need to be reconfigured on the StrictAuthorizedTransferSecurityRegistry validator).

In addition to implementing the above ICreatorToken interface, the NFT contract should also implement one of the following transfer validation functions in their _beforeTokenTransfer hook, with the call being performed to the assigned transfer validator (if one is set) and with getTransferValidationFunction() returning the corresponding function selector:

  /// Ensure that a transfer has been authorized for a specific tokenId
  /// (Recommended for ERC-721 tokens)
  function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;

  /// Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and
  /// reduce the transferable amount remaining. (Recommended for ERC-1155 tokens)
  function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;

  /// Ensure that a specific operator has been authorized to transfer tokens
  function validateTransfer(address caller, address from, address to) external view;

  /// Legacy alias for validateTransfer (address caller, address from, address to)
  function applyCollectionTransferPolicy(address caller, address from, address to) external view;

Finally, an order type of either FULL_RESTRICTED or PARTIAL_RESTRICTED and a zone equal to the SignedZone must be supplied when constructing a Seaport order involving a token that has been configured to the above. Then, the order must be fulfilled using fulfillAdvancedOrder, fulfillAvailableAdvancedOrders, or matchAdvancedOrders along with extraData that adheres to the SIP-7 specification, with a substandard that depends on the choice of function for validating the transfer:

  • Substandard 7: validateTransfer(address caller, address from, address to) or applyCollectionTransferPolicy(address caller, address from, address to)
  • Substandard 8: validateTransfer(address caller, address from, address to, uint256 tokenId)
  • Substandard 9: validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount)

This extraData, including the required server signature, is returned from the OpenSea API Fulfill Listing and Fulfill Offer endpoints.


Function signatures for getTransferValidationFunction():

0x7c1e14b4: bytes4(keccak256("validateTransfer(address,address,address)"))
0x285fb8c8: bytes4(keccak256("applyCollectionTransferPolicy(address,address,address)"))
0xcaee23ea: bytes4(keccak256("validateTransfer(address,address,address,uint256)"))
0x1854b241: bytes4(keccak256("validateTransfer(address,address,address,uint256,uint256)"))