Secondary Royalties on Rarible
Rarible works by using an on-chain OrderMatcher, which internally queries the token for secondary sales royalties. This is written, in code, over here:
https://github.com/rarible/protocol-contracts/blob/master/royalties/contracts/RoyaltiesV2.solā
We could install the Rarible Royalties Package, but at the time of writing, the package is written for Solidity < 0.8, which makes it incompatible.
Instead, we will mimic the interface, so that the Rarible DAO gets the right information!
Create a new folder structure ā@rarible/royalties/contractsā in the contracts folder. Then add the following files:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LibPart.sol";
interface IRoyaltiesProvider {
function getRoyalties(address token, uint tokenId) external returns (LibPart.Part[] memory);
}
Then add another folder inside called āimplā and add the following files in there:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../LibPart.sol";
abstract contract AbstractRoyalties {
mapping (uint256 => LibPart.Part[]) public royalties;
function _saveRoyalties(uint256 _id, LibPart.Part[] memory _royalties) internal {
for (uint i = 0; i < _royalties.length; i++) {
require(_royalties[i].account != address(0x0), "Recipient should be present");
require(_royalties[i].value != 0, "Royalty value should be positive");
royalties[_id].push(_royalties[i]);
}
_onRoyaltiesSet(_id, _royalties);
}
function _updateAccount(uint256 _id, address _from, address _to) internal {
uint length = royalties[_id].length;
for(uint i = 0; i < length; i++) {
if (royalties[_id][i].account == _from) {
royalties[_id][i].account = payable(address(uint160(_to)));
}
}
}
function _onRoyaltiesSet(uint256 _id, LibPart.Part[] memory _royalties) virtual internal;
}
If you add everything into the project, then it should look something like this:
It is the same structure as in their official Github repositoryā. But their contracts are, at the time of writing, solidity <0.8. So, it mimics the official structure with the hope that the contracts are updated to sol >=0.8 at some point, but having the same structure as before.
If you have a look at the RoyaltiesRegistryā then you see that on every token transfer, the Rarible Protocol will āaskā the token, if there are any royalties to be paid out. That is done by calling getRaribleV2Royalties
. So, all our contract needs to do is expose this function. And it does it if we are extending from RoyaltiesV2Impl. Letās improve our token:
//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/Counters.sol";
import "./@rarible/royalties/contracts/impl/RoyaltiesV2Impl.sol";
import "./@rarible/royalties/contracts/LibPart.sol";
import "./@rarible/royalties/contracts/LibRoyaltiesV2.sol";
contract MinimalERC721 is ERC721, Ownable, RoyaltiesV2Impl {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdTracker;
constructor() ERC721("Minimal", "MIN") {}
function mint(address _to) public onlyOwner {
super._mint(_to, _tokenIdTracker.current());
_tokenIdTracker.increment();
}
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 supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) {
if(interfaceId == LibRoyaltiesV2._INTERFACE_ID_ROYALTIES) {
return true;
}
return super.supportsInterface(interfaceId);
}
}
If you open this again on the truffle developer console, you can test it:
truffle develop
migrate
let token = await MinimalERC721.deployed()
token.mint(accounts[0]);
token.setRoyalties(0, accounts[0], 1000)
!!! info Percentage Basis Points The Percentage is given in percentage basis points. That means, 2.5% is 250
If you now execute
token.getRaribleV2Royalties(0);
It should output that account[0] gets 10% or 1000 basis points:
Perfect, but what about Mintable? Letās do that next!