Hi there!
I’m looking to deploy an override contract to the royalty registry.
Essentially, we need to deploy a new contract with some simple royalty rules which is also a payment splitter. We’ve had advice from other sources that although the registry supports multiple recipients, it’s still best to try and follow EIP-2981 (which is why we’re using a splitter).
Anyway, here is what I’ve come up with:
import "@openzeppelin/contracts/finance/PaymentSplitter.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
/**
* Contract for use with the Royalty Registry as an override supporting EIP2981.
*
* This contract is also a payment splitter so multiple payees can be supported.
*
* Version: 1.0
*/
contract RegistrySplitterEIP2981 is PaymentSplitter, ERC165 {
constructor(address[] memory _payees, uint256[] memory _bps)
payable
PaymentSplitter(_payees, _bps)
{}
bytes4 private constant _INTERFACE_ID_ROYALTIES_EIP2981 = 0x2a55205a;
function supportsInterface(bytes4 interfaceId) public view override(ERC165) returns (bool) {
return interfaceId == _INTERFACE_ID_ROYALTIES_EIP2981 || super.supportsInterface(interfaceId);
}
/**
* ERC-2981
*/
function royaltyInfo(uint256 /* tokenId */, uint256 value) external view virtual returns (address, uint256)
{
return (address(this), (totalShares() * value) / 10000);
}
}
So the idea here is that this contract is both itself a payment splitter but also implements the 2981 spec.
I’ve tried this experimentally with the royalty registry and it seems to work.
My question is this: is there any problem with having a single contract which is both itself a payment splitter and a 2981?
Is it weird or problematic that when anyone asks “where should I send royalties?” this contract will return itself (its own address)?
I’m thinking here about some subtle Solidity/EVM behaviour that maybe I’m not aware of.
I guess this is an inheritances vs. composition question. E.g. an alternative could have been something like:
contract RegistrySplitterEIP2981 is ERC165 {
PaymentSplitterImpl paymentSplitter;
constructor(address[] memory _payees, uint256[] memory _bps)
{
paymentSplitter = PaymentSplitterImpl(_payees, _bps);
}
...
function royaltyInfo(uint256 /* tokenId */, uint256 value) external view virtual returns (address, uint256)
{
return (address(paymentSplitter), (paymentSplitter.totalShares() * value) / 10000);
}
}
I did also have a play with the second approach but I struggled to get the inner contract verified on etherscan, so at the moment I slightly prefer the first approach.
Does anyone have any guidance about this?
Thanks!