How do EIP-2891 royalties work?

Hi everyone :wave:t4:

I want to add secondary royalties to my smart contract for OpenSea and Rarible and also have royalties for the artist after every mint. I've been reading up on royalties implementations, specifically EIP 2891.

I want to know if this is possible, and if i'm going about it the right way.

The article i'm reading

My contract

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "./@rarible/royalties/contracts/impl/RoyaltiesV2Impl.sol";
import "./@rarible/royalties/contracts/LibPart.sol";
import "./@rarible/royalties/contracts/LibRoyaltiesV2.sol";

contract EpicTest is ERC721, Ownable, RoyaltiesV2Impl {
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdTracker;

    uint256 public constant RESERVE_GIVEAWAYS = 50;
    uint256 public constant PRICE = 0 ether;
    uint256 public constant MAX_ELEMENTS = 1000;
    bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
    uint256 private startSales = 1634124766;
    address public constant artistAddress = 0x0x0x0x0x0x0x0x0x0x0x0x0x0;

    constructor() ERC721("EpicTest", "ET") {}
    function mint(address _to) public onlyOwner {
        super._mint(_to, _tokenIdTracker.current());
        setRoyalties(_tokenIdTracker.current(), payable (artistAddress), 2000);
    modifier saleIsOpen {
        require(_totalSupply() <= MAX_ELEMENTS, "Sale end");
        if (_msgSender() != owner()) {
            require(salesIsOpen(), "Sales not open");

    function salesIsOpen() public view returns (bool){
        return block.timestamp >= startSales;

    function totalMint() public view returns (uint256) {
        return _totalSupply();

    function _totalSupply() internal view returns (uint256) {
        return _tokenIdTracker.current();

    function mintBatch(address _to, uint256 _count) public payable saleIsOpen {
        uint256 total = _totalSupply();
        require(total + _count <= MAX_ELEMENTS, "Max limit");
        require(msg.value >= price(_count), "Value below price");

        for (uint256 i = 0; i < _count; i++) {

    function price(uint256 _count) public pure returns (uint256) {
        return PRICE.mul(_count);

    function setRoyalties(uint _tokenId, address payable _royaltiesReceipientAddress, uint96 _percentageBasisPoints) public onlyOwner {
        LibPart.Part[] memory _royalties = new LibPart.Part[](1);
        _royalties[0].value = _percentageBasisPoints;
        _royalties[0].account = _royaltiesReceipientAddress;
        _saveRoyalties(_tokenId, _royalties);
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) {
        LibPart.Part[] memory _royalties = royalties[_tokenId];
        if(_royalties.length > 0) {
            return (_royalties[0].account, (_salePrice * _royalties[0].value)/10000);
        return (address(0), 0);
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) {
        if(interfaceId == LibRoyaltiesV2._INTERFACE_ID_ROYALTIES) {
            return true;
        if(interfaceId == _INTERFACE_ID_ERC2981) {
            return true;
        return super.supportsInterface(interfaceId);

    function _baseURI() internal view virtual override returns (string memory) {
        return "";

Everything works, but I don't see the royalties being reflected on the address. I have deployed my contract here rinkeby etherscan

Am I missing something? Or is their a better way of approaching this, any guidance would be greatly appreciated! Thank you.

EIP-2891 doesn't pay royalties automatically. From the spec:

This standard allows contracts [...] to signal a royalty amount [...] The royalty payment must be voluntary

A marketplace would read the royalty info and trigger the royalty payment "manually". You can read more about this in the EIP, in particular the section Optional royalty payments:

It is impossible to know which NFT transfers are the result of sales, and which are merely wallets moving or consolidating their NFTs. Therefore, we cannot force every transfer function, such as transferFrom() in ERC-721, to involve a royalty payment as not every transfer is a sale that would require such payment. We believe the NFT marketplace ecosystem will voluntarily implement this royalty payment standard to provide ongoing funding for artists/creators. NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions.