// SPDX-License-Identifier: GPL-3.0

/**
  @title CapsuleToken

  @author peri

  @notice Each Capsule token has a unique color and a custom text rendered as a SVG. The text and font for a Capsule can be updated at any time by its owner.

  @dev `bytes3` type is used to store the RGB hex-encoded color that is unique to each Capsule. 

  `bytes32[8]` type is used to store 8 lines of 16 text characters, where each line contains 16 2-byte hexadecimal unicodes packed into a bytes32 value. 2 bytes is large enough to hex-encode the unicode value of every character in the Basic Multilingual Plane (BMP).

  To avoid high gas costs, text isn't validated when minting or editing, meaning Capsule text could contain characters that are unsupported by the Capsules Typeface. Instead, we rely on the Renderer contract to render a safe image even if the Capsule text is invalid.
 */

pragma solidity ^0.8.8;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./ERC721A.sol";
import "./interfaces/ICapsuleMetadata.sol";
import "./interfaces/ICapsuleRenderer.sol";
import "./interfaces/ICapsuleToken.sol";
import "./interfaces/ITypeface.sol";

/*                                                                                */
/*              000    000   0000    0000  0   0  0      00000   0000             */
/*             0   0  0   0  0   0  0      0   0  0      0      0                 */
/*             0      00000  0000    000   0   0  0      0000    000              */
/*             0   0  0   0  0          0  0   0  0      0          0             */
/*              000   0   0  0      0000    000   00000  00000  0000              */
/*                                                                                */

error CapsuleLocked();
error ColorAlreadyMinted(uint256 capsuleId);
error InvalidColor();
error InvalidFontForRenderer(address renderer);
error InvalidRenderer();
error NotCapsuleOwner(address owner);
error NotCapsulesTypeface();
error PureColorNotAllowed();
error ValueBelowMintPrice();

contract CapsuleToken is
    ICapsuleToken,
    ERC721A,
    IERC2981,
    Ownable,
    Pausable,
    ReentrancyGuard
{
    /* -------------------------------------------------------------------------- */
    /*       0   0   000   0000   00000  00000  00000  00000  0000    0000        */
    /*       00 00  0   0  0   0    0    0        0    0      0   0  0            */
    /*       0 0 0  0   0  0   0    0    00000    0    00000  0000    000         */
    /*       0   0  0   0  0   0    0    0        0    0      0 0        0        */
    /*       0   0   000   0000   00000  0      00000  00000  0  0   0000         */
    /* -------------------------------------------------------------------------- */
    /* -------------------------------- MODIFIERS ------------------------------- */
    /* -------------------------------------------------------------------------- */

    /// @notice Require that the value sent is at least MINT_PRICE.
    modifier requireMintPrice() {
        if (msg.value < MINT_PRICE) revert ValueBelowMintPrice();
        _;
    }

    /// @notice Require that the font is valid for a given renderer.
    modifier onlyValidFontForRenderer(Font memory font, address renderer) {
        if (!isValidFontForRenderer(font, renderer))
            revert InvalidFontForRenderer(renderer);
        _;
    }

    /// @notice Require that the font is valid for a given renderer.
    modifier onlyValidRenderer(address renderer) {
        if (!isValidRenderer(renderer)) revert InvalidRenderer();
        _;
    }

    /// @notice Require that the color is valid and unminted.
    modifier onlyMintableColor(bytes3 color) {
        uint256 capsuleId = tokenIdOfColor[color];
        if (_exists(capsuleId)) revert ColorAlreadyMinted(capsuleId);
        if (!isValidColor(color)) revert InvalidColor();
        _;
    }

    /// @notice Require that the color is not pure.
    modifier onlyImpureColor(bytes3 color) {
        if (isPureColor(color)) revert PureColorNotAllowed();
        _;
    }

    /// @notice Require that the sender is the CapsulesTypeface contract.
    modifier onlyCapsulesTypeface() {
        if (msg.sender != capsulesTypeface) revert NotCapsulesTypeface();
        _;
    }

    /// @notice Require that the Capsule is unlocked.
    modifier onlyUnlockedCapsule(uint256 capsuleId) {
        if (isLocked(capsuleId)) revert CapsuleLocked();
        _;
    }

    /// @notice Require that the sender owns the Capsule.
    modifier onlyCapsuleOwner(uint256 capsuleId) {
        address owner = ownerOf(capsuleId);
        if (owner != msg.sender) revert NotCapsuleOwner(owner);
        _;
    }

    /* -------------------------------------------------------------------------- */
    /*  000    000   0   0   0000  00000  0000   0   0   000   00000  000   0000  */
    /* 0   0  0   0  00  0  0        0    0   0  0   0  0   0    0   0   0  0   0 */
    /* 0      0   0  0 0 0   000     0    0000   0   0  0        0   0   0  0000  */
    /* 0   0  0   0  0  00      0    0    0  0   0   0  0   0    0   0   0  0  0  */
    /*  000    000   0   0  0000     0    0   0   000    000     0    000   0   0 */
    /* -------------------------------------------------------------------------- */
    /* ------------------------------- CONSTRUCTOR ------------------------------ */
    /* -------------------------------------------------------------------------- */

    constructor(
        address _capsulesTypeface,
        address _defaultRenderer,
        address _capsuleMetadata,
        address _feeReceiver,
        bytes3[] memory _pureColors,
        uint256 _royalty
    ) ERC721A("Capsules", "CAPS") {
        capsulesTypeface = _capsulesTypeface;

        _setDefaultRenderer(_defaultRenderer);
        _setCapsuleMetadata(_capsuleMetadata);
        _setFeeReceiver(_feeReceiver);

        pureColors = _pureColors;
        emit SetPureColors(_pureColors);

        _setRoyalty(_royalty);

        _pause();
    }

    /* -------------------------------------------------------------------------- */
    /*       0   0   000   0000   00000   000   0000   0      00000   0000        */
    /*       0   0  0   0  0   0    0    0   0  0   0  0      0      0            */
    /*       0   0  00000  0000     0    00000  0000   0      0000    000         */
    /*        0 0   0   0  0  0     0    0   0  0   0  0      0          0        */
    /*         0    0   0  0   0  00000  0   0  0000   00000  00000  0000         */
    /* -------------------------------------------------------------------------- */
    /* -------------------------------- VARIABLES ------------------------------- */
    /* -------------------------------------------------------------------------- */

    /// Price to mint a Capsule
    uint256 public constant MINT_PRICE = 1e16; // 0.01 ETH

    /// CapsulesTypeface address
    address public immutable capsulesTypeface;

    /// Default CapsuleRenderer address
    address public defaultRenderer;

    /// CapsuleMetadata address
    address public capsuleMetadata;

    /// Capsule ID of a minted color
    mapping(bytes3 => uint256) public tokenIdOfColor;

    /// Array of pure colors
    bytes3[] public pureColors;

    /// Address to receive fees
    address public feeReceiver;

    /// Royalty amount out of 1000
    uint256 public royalty;

    /// Validity of a renderer address
    mapping(address => bool) internal _validRenderers;

    /// Text of a Capsule ID
    mapping(uint256 => bytes32[8]) internal _textOf;

    /// Color of a Capsule ID
    mapping(uint256 => bytes3) internal _colorOf;

    /// Font of a Capsule ID
    mapping(uint256 => Font) internal _fontOf;

    /// Renderer address of a Capsule ID
    mapping(uint256 => address) internal _rendererOf;

    /// Locked state of a Capsule ID
    mapping(uint256 => bool) internal _locked;

    /* -------------------------------------------------------------------------- */
    /*           00000  0   0  00000  00000  0000   0   0   000   0               */
    /*           0       0 0     0    0      0   0  00  0  0   0  0               */
    /*           0000     0      0    0000   0000   0 0 0  00000  0               */
    /*           0       0 0     0    0      0  0   0  00  0   0  0               */
    /*           00000  0   0    0    00000  0   0  0   0  0   0  00000           */
    /* -------------------------------------------------------------------------- */
    /* --------------------------- EXTERNAL FUNCTIONS --------------------------- */
    /* -------------------------------------------------------------------------- */

    /// @notice Mints a Capsule to sender, saving gas by not setting text.
    /// @param color Color of Capsule.
    /// @param font Font of Capsule.
    /// @return capsuleId ID of minted Capsule.
    function mint(bytes3 color, Font calldata font)
        external
        payable
        requireMintPrice
        onlyImpureColor(color)
        nonReentrant
        returns (uint256)
    {
        return _mintCapsule(msg.sender, color, font);
    }

    /// @notice Mint a Capsule to sender while setting its text.
    /// @param color Color of Capsule.
    /// @param font Font of Capsule.
    /// @param text Text of Capsule.
    /// @return capsuleId ID of minted Capsule.
    function mintWithText(
        bytes3 color,
        Font calldata font,
        bytes32[8] calldata text
    )
        external
        payable
        whenNotPaused
        requireMintPrice
        onlyMintableColor(color)
        onlyImpureColor(color)
        onlyValidFontForRenderer(font, defaultRenderer)
        nonReentrant
        returns (uint256 capsuleId)
    {
        address to = msg.sender;

        _mint(to, 1, new bytes(0), false);

        capsuleId = _storeNewCapsuleData(color, font);

        _textOf[capsuleId] = text;

        emit MintCapsule(capsuleId, to, color);
    }

    /// @notice Allows the CapsulesTypeface to mint a pure color Capsule.
    /// @param to Address to receive Capsule.
    /// @param font Font of Capsule.
    /// @return capsuleId ID of minted Capsule.
    function mintPureColorForFont(address to, Font calldata font)
        external
        onlyCapsulesTypeface
        nonReentrant
        returns (uint256)
    {
        return _mintCapsule(to, pureColorForFontWeight(font.weight), font);
    }

    /// @notice Return token URI for Capsule, using the CapsuleMetadata contract.
    /// @param capsuleId ID of Capsule token.
    /// @return metadata Metadata string for Capsule.
    function tokenURI(uint256 capsuleId)
        public
        view
        override
        returns (string memory)
    {
        require(_exists(capsuleId), "ERC721A: URI query for nonexistent token");

        return
            ICapsuleMetadata(capsuleMetadata).metadataOf(
                capsuleOf(capsuleId),
                svgOf(capsuleId)
            );
    }

    /// @notice Return SVG image from the Capsule's renderer.
    /// @param capsuleId ID of Capsule token.
    /// @return svg Encoded SVG image of Capsule.
    function svgOf(uint256 capsuleId) public view returns (string memory) {
        return
            ICapsuleRenderer(rendererOf(capsuleId)).svgOf(capsuleOf(capsuleId));
    }

    /// @notice Returns all data for Capsule.
    /// @param capsuleId ID of Capsule.
    /// @return capsule Data for Capsule.
    function capsuleOf(uint256 capsuleId) public view returns (Capsule memory) {
        bytes3 color = _colorOf[capsuleId];

        return
            Capsule({
                id: capsuleId,
                font: _fontOf[capsuleId],
                text: _textOf[capsuleId],
                color: color,
                isPure: isPureColor(color),
                isLocked: _locked[capsuleId]
            });
    }

    /// @notice Check if color is pure.
    /// @param color Color to check.
    /// @return true True if color is pure.
    function isPureColor(bytes3 color) public view returns (bool) {
        bytes3[] memory _pureColors = pureColors;

        unchecked {
            for (uint256 i; i < _pureColors.length; i++) {
                if (color == _pureColors[i]) return true;
            }
        }

        return false;
    }

    /// @notice Returns the pure color matching a specific font weight.
    /// @param fontWeight Font weight to return pure color for.
    /// @return color Color for font weight.
    function pureColorForFontWeight(uint256 fontWeight)
        public
        view
        returns (bytes3)
    {
        // 100 == pureColors[0]
        // 200 == pureColors[1]
        // 300 == pureColors[2]
        // etc...
        return pureColors[(fontWeight / 100) - 1];
    }

    /// @notice Returns the color of Capsule.
    /// @param capsuleId ID of Capsule.
    /// @return color Color of Capsule.
    function colorOf(uint256 capsuleId) public view returns (bytes3) {
        return _colorOf[capsuleId];
    }

    /// @notice Returns the text of Capsule.
    /// @param capsuleId ID of Capsule.
    /// @return text Text of Capsule.
    function textOf(uint256 capsuleId) public view returns (bytes32[8] memory) {
        return _textOf[capsuleId];
    }

    /// @notice Returns the font of Capsule.
    /// @param capsuleId ID of Capsule.
    /// @return font Font of Capsule.
    function fontOf(uint256 capsuleId) public view returns (Font memory) {
        return _fontOf[capsuleId];
    }

    /// @notice Returns renderer of Capsule. If the Capsule has no renderer set, the default renderer is used.
    /// @param capsuleId ID of Capsule.
    /// @return renderer Address of renderer.
    function rendererOf(uint256 capsuleId) public view returns (address) {
        if (_rendererOf[capsuleId] != address(0)) return _rendererOf[capsuleId];

        return defaultRenderer;
    }

    /// @notice Check if Capsule is locked.
    /// @param capsuleId ID of Capsule.
    /// @return locked True if Capsule is locked.
    function isLocked(uint256 capsuleId) public view returns (bool) {
        return _locked[capsuleId];
    }

    /// @notice Check if font is valid for a CapsuleRenderer contract.
    /// @param renderer CapsuleRenderer contract address.
    /// @param font Font to check validity of.
    /// @return true True if font is valid.
    function isValidFontForRenderer(Font memory font, address renderer)
        public
        view
        returns (bool)
    {
        return ICapsuleRenderer(renderer).isValidFont(font);
    }

    /// @notice Check if address is a valid CapsuleRenderer contract.
    /// @param renderer Renderer address to check.
    /// @return true True if renderer is valid.
    function isValidRenderer(address renderer) public view returns (bool) {
        return _validRenderers[renderer];
    }

    /// @notice Check if color is valid.
    /// @dev A color is valid if all 3 bytes are divisible by 5 AND at least one byte == 255.
    /// @param color Color to check validity of.
    /// @return true True if color is valid.
    function isValidColor(bytes3 color) public pure returns (bool) {
        // At least one byte must equal 0xff (255)
        if (color[0] < 0xff && color[1] < 0xff && color[2] < 0xff) {
            return false;
        }

        // All bytes must be divisible by 5
        unchecked {
            for (uint256 i; i < 3; i++) {
                if (uint8(color[i]) % 5 != 0) return false;
            }
        }

        return true;
    }

    /// @notice Check if Capsule text is valid.
    /// @dev Checks validity using Capsule's renderer contract.
    /// @param capsuleId ID of Capsule.
    /// @return true True if Capsule text is valid.
    function isValidCapsuleText(uint256 capsuleId)
        external
        view
        returns (bool)
    {
        return
            ICapsuleRenderer(rendererOf(capsuleId)).isValidText(
                textOf(capsuleId)
            );
    }

    /// @notice Withdraws balance of this contract to the feeReceiver address.
    function withdraw() external nonReentrant {
        uint256 balance = address(this).balance;

        payable(feeReceiver).transfer(balance);

        emit Withdraw(feeReceiver, balance);
    }

    /// @notice EIP2981 royalty standard
    function royaltyInfo(uint256, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount)
    {
        return (payable(this), (salePrice * royalty) / 1000);
    }

    /// @notice EIP2981 standard Interface return. Adds to ERC721A Interface returns.
    /// @dev See {IERC165-supportsInterface}.
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(IERC165, ERC721A)
        returns (bool)
    {
        return
            interfaceId == type(IERC2981).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /// @dev Allows contract to receive ETH
    receive() external payable {}

    /* -------------------------------------------------------------------------- */
    /*                      000   0   0  0   0  00000  0000                       */
    /*                     0   0  0   0  00  0  0      0   0                      */
    /*                     0   0  0   0  0 0 0  0000   0000                       */
    /*                     0   0  0 0 0  0  00  0      0  0                       */
    /*                      000    0 0   0   0  00000  0   0                      */
    /* -------------------------------------------------------------------------- */
    /* ------------------------ CAPSULE OWNER FUNCTIONS ------------------------- */
    /* -------------------------------------------------------------------------- */

    /// @notice Allows Capsule owner to edit the Capsule text, font, and locked state.
    /// @param capsuleId ID of Capsule.
    /// @param text New text for Capsule.
    /// @param font New font for Capsule.
    /// @param lock Lock capsule, preventing any future edits.
    function editCapsule(
        uint256 capsuleId,
        bytes32[8] calldata text,
        Font calldata font,
        bool lock
    ) public {
        _editCapsule(capsuleId, text, font, lock);
    }

    /// @notice Allows Capsule owner to lock a Capsule, preventing any future edits.
    /// @param capsuleId ID of Capsule to lock.
    function lockCapsule(uint256 capsuleId) external {
        _lockCapsule(capsuleId);
    }

    /// @notice Allows Capsule owner to set its renderer contract. If renderer is the zero address, the Capsule will use the default renderer.
    /// @dev Does not check validity of the current Capsule text or font with the new renderer.
    /// @param capsuleId ID of Capsule.
    /// @param renderer Address of new renderer.
    function setRendererOf(uint256 capsuleId, address renderer)
        external
        onlyCapsuleOwner(capsuleId)
        onlyValidRenderer(renderer)
    {
        _rendererOf[capsuleId] = renderer;

        emit SetRendererOf(capsuleId, renderer);
    }

    /// @notice Burns a Capsule.
    /// @param capsuleId ID of Capsule to burn.
    function burn(uint256 capsuleId) external onlyCapsuleOwner(capsuleId) {
        _burn(capsuleId);
    }

    /* -------------------------------------------------------------------------- */
    /*                      000   0000   0   0  00000  0   0                      */
    /*                     0   0  0   0  00 00    0    00  0                      */
    /*                     00000  0   0  0 0 0    0    0 0 0                      */
    /*                     0   0  0   0  0   0    0    0  00                      */
    /*                     0   0  0000   0   0  00000  0   0                      */
    /* -------------------------------------------------------------------------- */
    /* ---------------------------- ADMIN FUNCTIONS ----------------------------- */
    /* -------------------------------------------------------------------------- */

    /// @notice Allows the owner of this contract to update the defaultCapsuleRenderer contract.
    /// @param renderer Address of new default defaultCapsuleRenderer contract.
    function setDefaultRenderer(address renderer) external onlyOwner {
        _setDefaultRenderer(renderer);
    }

    /// @notice Allows the owner of this contract to update the CapsuleMetadata contract.
    /// @param _capsuleMetadata Address of new default CapsuleMetadata contract.
    function setCapsuleMetadata(address _capsuleMetadata) external onlyOwner {
        _setCapsuleMetadata(_capsuleMetadata);
    }

    /// @notice Allows the owner of this contract to update the feeReceiver address.
    /// @param newFeeReceiver Address of new feeReceiver.
    function setFeeReceiver(address newFeeReceiver) external onlyOwner {
        _setFeeReceiver(newFeeReceiver);
    }

    /// @notice Allows the owner of this contract to update the royalty amount.
    /// @param royaltyAmount New royalty amount.
    function setRoyalty(uint256 royaltyAmount) external onlyOwner {
        _setRoyalty(royaltyAmount);
    }

    /// @notice Allows the contract owner to pause the contract.
    /// @dev Can only be called by the owner when the contract is unpaused.
    function pause() external override onlyOwner {
        _pause();
    }

    /// @notice Allows the contract owner to unpause the contract.
    /// @dev Can only be called by the owner when the contract is paused.
    function unpause() external override onlyOwner {
        _unpause();
    }

    /* -------------------------------------------------------------------------- */
    /*           00000  0   0  00000  00000  0000   0   0   000   0               */
    /*             0    00  0    0    0      0   0  00  0  0   0  0               */
    /*             0    0 0 0    0    0000   0000   0 0 0  00000  0               */
    /*             0    0  00    0    0      0  0   0  00  0   0  0               */
    /*           00000  0   0    0    00000  0   0  0   0  0   0  00000           */
    /* -------------------------------------------------------------------------- */
    /* --------------------------- INTERNAL FUNCTIONS --------------------------- */
    /* -------------------------------------------------------------------------- */

    /// @notice ERC721A override to start tokenId at 1 instead of 0.
    function _startTokenId() internal pure override returns (uint256) {
        return 1;
    }

    /// @notice Mints a Capsule.
    /// @param to Address to receive capsule.
    /// @param color Color of Capsule.
    /// @param font Font of Capsule.
    /// @return capsuleId ID of minted Capsule.
    function _mintCapsule(
        address to,
        bytes3 color,
        Font calldata font
    )
        internal
        whenNotPaused
        onlyMintableColor(color)
        onlyValidFontForRenderer(font, defaultRenderer)
        returns (uint256 capsuleId)
    {
        _mint(to, 1, new bytes(0), false);

        capsuleId = _storeNewCapsuleData(color, font);

        emit MintCapsule(capsuleId, to, color);
    }

    function _editCapsule(
        uint256 capsuleId,
        bytes32[8] calldata text,
        Font calldata font,
        bool lock
    )
        internal
        onlyCapsuleOwner(capsuleId)
        onlyUnlockedCapsule(capsuleId)
        onlyValidFontForRenderer(font, rendererOf(capsuleId))
    {
        _textOf[capsuleId] = text;
        _fontOf[capsuleId] = font;

        emit EditCapsule(capsuleId);

        if (lock) _lockCapsule(capsuleId);
    }

    function _lockCapsule(uint256 capsuleId)
        internal
        onlyCapsuleOwner(capsuleId)
        onlyUnlockedCapsule(capsuleId)
    {
        _locked[capsuleId] = true;

        emit LockCapsule(capsuleId);
    }

    /// @notice Check if all lines of text are empty.
    /// @param text Text to check.
    /// @return true if text is empty.
    function _isEmptyText(bytes32[8] memory text) internal pure returns (bool) {
        for (uint256 i; i < 8; i++) {
            if (!_isEmptyLine(text[i])) return false;
        }
        return true;
    }

    /// @notice Check if line is empty.
    /// @dev Returns true if every byte of text is 0x00.
    /// @param line line to check.
    /// @return true if line is empty.
    function _isEmptyLine(bytes32 line) internal pure returns (bool) {
        bytes2[16] memory _line = _bytes32ToBytes2Array(line);
        for (uint256 i; i < 16; i++) {
            if (_line[i] != 0) return false;
        }
        return true;
    }

    /// @notice Format bytes32 type as array of bytes2
    /// @param b bytes32 value to convert to array
    /// @return a Array of bytes2
    function _bytes32ToBytes2Array(bytes32 b)
        internal
        pure
        returns (bytes2[16] memory a)
    {
        for (uint256 i; i < 16; i++) {
            a[i] = bytes2(abi.encodePacked(b[i * 2], b[i * 2 + 1]));
        }
    }

    function _setDefaultRenderer(address renderer) internal {
        defaultRenderer = renderer;

        _validRenderers[renderer] = true;

        emit SetDefaultRenderer(renderer);
    }

    function _setRoyalty(uint256 royaltyAmount) internal {
        require(royaltyAmount <= 1000, "Amount too high");

        royalty = royaltyAmount;

        emit SetRoyalty(royaltyAmount);
    }

    function _setFeeReceiver(address newFeeReceiver) internal {
        feeReceiver = newFeeReceiver;

        emit SetFeeReceiver(newFeeReceiver);
    }

    function _setCapsuleMetadata(address _capsuleMetadata) internal {
        capsuleMetadata = _capsuleMetadata;

        emit SetCapsuleMetadata(_capsuleMetadata);
    }

    function _storeNewCapsuleData(bytes3 color, Font memory font)
        private
        returns (uint256 capsuleId)
    {
        capsuleId = _currentIndex - 1;

        tokenIdOfColor[color] = capsuleId;
        _colorOf[capsuleId] = color;
        _fontOf[capsuleId] = font;
    }
}
[{"inputs":[{"internalType":"address","name":"_capsulesTypeface","type":"address"},{"internalType":"address","name":"_defaultRenderer","type":"address"},{"internalType":"address","name":"_capsuleMetadata","type":"address"},{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"bytes3[]","name":"_pureColors","type":"bytes3[]"},{"internalType":"uint256","name":"_royalty","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"CapsuleLocked","type":"error"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"ColorAlreadyMinted","type":"error"},{"inputs":[],"name":"InvalidColor","type":"error"},{"inputs":[{"internalType":"address","name":"renderer","type":"address"}],"name":"InvalidFontForRenderer","type":"error"},{"inputs":[],"name":"InvalidRenderer","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"NotCapsuleOwner","type":"error"},{"inputs":[],"name":"NotCapsulesTypeface","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"PureColorNotAllowed","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"ValueBelowMintPrice","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"EditCapsule","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"LockCapsule","type":"event"},{"anonymous":false,"inputs":[],"name":"LockRenderer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"bytes3","name":"color","type":"bytes3"}],"name":"MintCapsule","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"metadata","type":"address"}],"name":"SetCapsuleMetadata","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"renderer","type":"address"}],"name":"SetDefaultRenderer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"SetFeeReceiver","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes3[]","name":"colors","type":"bytes3[]"}],"name":"SetPureColors","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"renderer","type":"address"}],"name":"SetRendererOf","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"royalty","type":"uint256"}],"name":"SetRoyalty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MINT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"capsuleMetadata","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"capsuleOf","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes3","name":"color","type":"bytes3"},{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"},{"internalType":"bytes32[8]","name":"text","type":"bytes32[8]"},{"internalType":"bool","name":"isPure","type":"bool"},{"internalType":"bool","name":"isLocked","type":"bool"}],"internalType":"struct Capsule","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"capsulesTypeface","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"colorOf","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultRenderer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"},{"internalType":"bytes32[8]","name":"text","type":"bytes32[8]"},{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"},{"internalType":"bool","name":"lock","type":"bool"}],"name":"editCapsule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"fontOf","outputs":[{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"color","type":"bytes3"}],"name":"isPureColor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"isValidCapsuleText","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"color","type":"bytes3"}],"name":"isValidColor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"},{"internalType":"address","name":"renderer","type":"address"}],"name":"isValidFontForRenderer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"renderer","type":"address"}],"name":"isValidRenderer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"lockCapsule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes3","name":"color","type":"bytes3"},{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"}],"name":"mintPureColorForFont","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes3","name":"color","type":"bytes3"},{"components":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"string","name":"style","type":"string"}],"internalType":"struct Font","name":"font","type":"tuple"},{"internalType":"bytes32[8]","name":"text","type":"bytes32[8]"}],"name":"mintWithText","outputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fontWeight","type":"uint256"}],"name":"pureColorForFontWeight","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pureColors","outputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"rendererOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"royalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_capsuleMetadata","type":"address"}],"name":"setCapsuleMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"renderer","type":"address"}],"name":"setDefaultRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeReceiver","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"},{"internalType":"address","name":"renderer","type":"address"}],"name":"setRendererOf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"name":"setRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"svgOf","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"textOf","outputs":[{"internalType":"bytes32[8]","name":"","type":"bytes32[8]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes3","name":"","type":"bytes3"}],"name":"tokenIdOfColor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"capsuleId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]