VibeERC721.sol

VibeERC721 Documentation

VibeERC721 is a Solidity contract that implements a customizable ERC721 token with additional features, such as royalties enforcement on OpenSea (using the DefaultOperatorFilterer by OpenSea), as well as implementing the ERC2981 and IDerivativeLicense interfaces. Vibe Labs is the creator of the IDerivativeLicense interface.

Inherited Contracts

DefaultOperatorFilterer

The DefaultOperatorFilterer by OpenSea allows for the enforcement of royalties on OpenSea but disallows listing on zero-fee exchanges. NFTs with this functionality will be able to freely be listed on SeaPort and other royalty-enforcing exchanges, but they might not take advantage of other proprietary functions of non-enforcing exchanges.

Roles

Minter

The minter role allows addresses to mint new tokens. Only the contract owner can assign or remove the minter role from an address. Minting is restricted to minters through the onlyMinter modifier.

Owner

The owner role is inherited from OpenZeppelin's Ownable contract. The owner can change the minter status of an address, set royalty information, and change the base URI.

Access Control

Access control is implemented using OpenZeppelin's Ownable contract for the owner role, and a custom mapping (isMinter) with the onlyMinter modifier for the minter role.

Interfaces

ERC2981

The ERC2981 interface is used for handling royalty payments on NFTs. VibeERC721 implements royaltyInfo function, allowing users to query royalty information for a specific token.

IDerivativeLicense

The IDerivativeLicense interface is an invention of Vibe Labs. It is used for handling derivative royalties on NFTs. VibeERC721 implements the derivativeRoyaltyInfo function, allowing users to query royalty information for a specific token when used as a derivative.

Key Functions

setMinter

The setMinter function allows the contract owner to grant or revoke the minter role from an address.

function setMinter(address minter, bool status) external onlyOwner {
    isMinter[minter] = status;
    emit LogMinterChange(minter, status);
}

renounceMinter

The renounceMinter function allows an address with the minter role to renounce it.

function renounceMinter() external {
    isMinter[msg.sender] = false;
    emit LogMinterChange(msg.sender, false);
}

init

The init function initializes the contract with a given name, symbol, and base URI.

function init(bytes calldata data) public payable override {
    (
        string memory _name,
        string memory _symbol,
        string memory baseURI_
    ) = abi.decode(data, (string, string, string));
    require(bytes(baseURI).length == 0 && bytes(baseURI_).length != 0);
    _transferOwnership(msg.sender);
    name = _name;
    symbol = _symbol;
    baseURI = baseURI_;
}

mint

The mint function allows an address with the minter role to mint a new token to a specified address.

function mint(address to) external onlyMinter returns (uint256 tokenId) {
    tokenId = totalSupply++;
    _mint(to, tokenId);
}

mintWithId

The mintWithId function allows an address with the minter role to mint a new token with a specific tokenId to a specified address.

function mintWithId(address to, uint256 tokenId) external onlyMinter {
    _mint(to, tokenId);
}

setApprovalForAll, approve, transferFrom, safeTransferFrom

These functions override the corresponding ERC721 functions to ensure that the operator is allowed by the OperatorFilterRegistry.

function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
    super.setApprovalForAll(operator, approved);
}

/**
 * @dev See {ERC721-approve}.
 *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
 */
function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
    super.approve(operator, tokenId);
}

/**
 * @dev See {ERC721-transferFrom}.
 *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
 */
function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
    super.transferFrom(from, to, tokenId);
}

/**
 * @dev See {ERC721-safeTransferFrom}.
 */
function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
    super.safeTransferFrom(from, to, tokenId);
}

burn

The burn function allows a token owner or an approved operator to burn a token.

function burn(uint256 id) external {
    address oldOwner = _ownerOf[id];

    require(
        msg.sender == oldOwner ||
            msg.sender == getApproved[id] ||
            isApprovedForAll[oldOwner][msg.sender],
        "NOT_AUTHORIZED"
    );

    _burn(id);
}

tokenURI

The tokenURI function returns the URI of a specific token.

function tokenURI(uint256 id) public view override returns (string memory) {
    return
        bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, id.toString()))
            : "";
}

royaltyInfo

The royaltyInfo function returns the royalty receiver and royalty amount for a specific token and sale price.

function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
    external
    view
    override
    returns (address receiver, uint256 royaltyAmount)
{
    return (
        royaltyInformation.royaltyReceiver,
        (_salePrice * royaltyInformation.royaltyRate) / BPS
    );
}

derivativeRoyaltyInfo

The derivativeRoyaltyInfo function returns the derivative royalty receiver and derivative royalty amount for a specific token and sale price, if derivatives are allowed.

function derivativeRoyaltyInfo(uint256 _tokenId, uint256 _salePrice)
    external
    view
    override
    returns (address receiver, uint256 royaltyAmount)
{
    require(royaltyInformation.isDerivativeAllowed);
    return (
        royaltyInformation.royaltyReceiver,
        (_salePrice * royaltyInformation.derivativeRoyaltyRate) / BPS
    );
}

setRoyalty

The setRoyalty function allows the contract owner to set royalty information, including the royalty receiver, royalty rate, derivative rate, and whether derivatives are allowed.

function setRoyalty(
    address royaltyReceiver_,
    uint16 royaltyRate_,
    uint16 derivativeRate,
    bool isDerivativeAllowed
) external onlyOwner {
    require(royaltyReceiver_ != address(0));
    // If Derivative Works were turned on in the past, this can not be retracted in the future
    isDerivativeAllowed = royaltyInformation.isDerivativeAllowed
        ? true
        : isDerivativeAllowed;
    royaltyInformation = RoyaltyData(
        royaltyReceiver_,
        royaltyRate_,
        derivativeRate,
        isDerivativeAllowed
    );
    emit LogSetRoyalty(
        royaltyRate_,
        royaltyReceiver_,
        derivativeRate,
        isDerivativeAllowed
    );
}

changeBaseURI

The changeBaseURI function allows the contract owner to change the base URI and its immutability.

function changeBaseURI(string memory baseURI_, bool immutability_)
    external
    onlyOwner
{
    require(immutability == false, "Immutable");
    immutability = immutability_;
    baseURI = baseURI_;

    emit LogChangeBaseURI(baseURI_, immutability_);
}

supportsInterface

The supportsInterface function checks if the contract supports a specific interface.

function supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721, IERC165)
    returns (bool)
{
    return
        interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
        interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
        interfaceId == 0x2a55205a || // ERC165 Interface ID for IERC2981
        interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
        (interfaceId == 0x15a779f5 &&
            royaltyInformation.isDerivativeAllowed);
}