// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @openzeppelin/contracts/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// File: contracts/core/assets/OpenAvatarGen0AssetsCanvasIdStore.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title OpenAvatarGen0AssetsCanvasIdStore
 * @dev This contracts stores a canvas ID.
 */
abstract contract OpenAvatarGen0AssetsCanvasIdStore {
  /// @notice The canvas ID.
  uint8 public canvasId;

  constructor(uint8 canvasId_) {
    canvasId = canvasId_;
  }

  /**
   * @notice Get the canvas ID.
   * @return The canvas ID.
   */
  function getCanvasId() external view returns (uint8) {
    return canvasId;
  }
}

// File: contracts/core/dependencies/Base64.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

// Adapted from:
// https://github.com/Brechtpd/base64/blob/4d85607b18d981acff392d2e99ba654305552a97/base64.sol

// solhint-disable no-empty-blocks

/// @title Base64
/// @author Brecht Devos - <brecht@loopring.org>
/// @notice Provides functions for encoding/decoding base64
library Base64 {
  string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  bytes internal constant TABLE_DECODE =
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'00000000000000000000003e0000003f3435363738393a3b3c3d000000000000'
    hex'00000102030405060708090a0b0c0d0e0f101112131415161718190000000000'
    hex'001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000';

  function encode(bytes memory data) internal pure returns (bytes memory) {
    if (data.length == 0) return '';

    // load the table into memory
    string memory table = TABLE_ENCODE;

    // multiply by 4/3 rounded up
    uint encodedLen = 4 * ((data.length + 2) / 3);

    // add some extra buffer at the end required for the writing
    bytes memory result = new bytes(encodedLen + 32);

    assembly {
      // set the actual output length
      mstore(result, encodedLen)

      // prepare the lookup table
      let tablePtr := add(table, 1)

      // input ptr
      let dataPtr := data
      let endPtr := add(dataPtr, mload(data))

      // result ptr, jump over length
      let resultPtr := add(result, 32)

      // run over the input, 3 bytes at a time
      for {

      } lt(dataPtr, endPtr) {

      } {
        // read 3 bytes
        dataPtr := add(dataPtr, 3)
        let input := mload(dataPtr)

        // write 4 characters
        mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
        resultPtr := add(resultPtr, 1)
        mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
        resultPtr := add(resultPtr, 1)
        mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
        resultPtr := add(resultPtr, 1)
        mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
        resultPtr := add(resultPtr, 1)
      }

      // padding with '='
      switch mod(mload(data), 3)
      case 1 {
        mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
      }
      case 2 {
        mstore(sub(resultPtr, 1), shl(248, 0x3d))
      }
    }

    return result;
  }

  function decode(string memory _data) internal pure returns (bytes memory) {
    bytes memory data = bytes(_data);

    if (data.length == 0) return new bytes(0);
    require(data.length % 4 == 0, 'invalid base64 decoder input');

    // load the table into memory
    bytes memory table = TABLE_DECODE;

    // every 4 characters represent 3 bytes
    uint decodedLen = (data.length / 4) * 3;

    // add some extra buffer at the end required for the writing
    bytes memory result = new bytes(decodedLen + 32);

    assembly {
      // padding with '='
      let lastBytes := mload(add(data, mload(data)))
      if eq(and(lastBytes, 0xFF), 0x3d) {
        decodedLen := sub(decodedLen, 1)
        if eq(and(lastBytes, 0xFFFF), 0x3d3d) {
          decodedLen := sub(decodedLen, 1)
        }
      }

      // set the actual output length
      mstore(result, decodedLen)

      // prepare the lookup table
      let tablePtr := add(table, 1)

      // input ptr
      let dataPtr := data
      let endPtr := add(dataPtr, mload(data))

      // result ptr, jump over length
      let resultPtr := add(result, 32)

      // run over the input, 4 characters at a time
      for {

      } lt(dataPtr, endPtr) {

      } {
        // read 4 characters
        dataPtr := add(dataPtr, 4)
        let input := mload(dataPtr)

        // write 3 bytes
        let output := add(
          add(
            shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),
            shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))
          ),
          add(
            shl(6, and(mload(add(tablePtr, and(shr(8, input), 0xFF))), 0xFF)),
            and(mload(add(tablePtr, and(input, 0xFF))), 0xFF)
          )
        )
        mstore(resultPtr, shl(232, output))
        resultPtr := add(resultPtr, 3)
      }
    }

    return result;
  }
}

// File: contracts/core/dependencies/ens/registry/ENS.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.4;

interface ENS {
  /**
   * @dev Returns the address that owns the specified node.
   * @param node The specified node.
   * @return address of the owner.
   */
  function owner(bytes32 node) external view returns (address);
}

// File: contracts/core/dependencies/ens/reverseRegistrar/IReverseRegistrar.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.4;

interface IReverseRegistrar {
  /**
   * @dev Transfers ownership of the reverse ENS record associated with the
   *      calling account.
   * @param owner The address to set as the owner of the reverse record in ENS.
   * @return The ENS node hash of the reverse record.
   */
  function claim(address owner) external returns (bytes32);

  /**
   * @dev Sets the `name()` record for the reverse ENS record associated with
   * the calling account. First updates the resolver to the default reverse
   * resolver if necessary.
   * @param name The name to set for this address.
   * @return The ENS node hash of the reverse record.
   */
  function setName(string memory name) external returns (bytes32);
}

// File: contracts/core/dependencies/IERC634.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

/**
 * @title IERC634: Text Data Interface
 * @dev This is the same as EIP-634 with different variable names
 * - rename node -> dna
 * - +calldata for key in text()
 * - +external for text()
 * - +memory for return value of text()
 */
interface IERC634 {
  /**
   * @notice Returns the text data associated with a DNA
   * @param dna A DNA to lookup text data for
   * @param key A key to lookup text data for
   * @return text The text data
   */
  function text(bytes32 dna, string calldata key) external view returns (string memory text);
}

// File: contracts/core/interfaces/assets/IOpenAvatarGen0AssetsCanvasStore.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

struct CanvasHeader {
  uint8 id;
  uint8 width;
  uint8 height;
}

/**
 * @title IOpenAvatarGen0AssetsCanvasStoreRead
 * @dev This interface reads canvas headers.
 */
interface IOpenAvatarGen0AssetsCanvasStoreRead {
  function hasCanvas(uint8 id) external view returns (bool);

  function getCanvasHeader(uint8 id) external view returns (CanvasHeader memory);

  function getNumCanvasIds() external view returns (uint);

  function getCanvasIds() external view returns (uint8[] memory);

  function getCanvasHeight(uint8 id) external view returns (uint8);

  function getCanvasWidth(uint8 id) external view returns (uint8);

  function getCanvasNumBytes(uint8 id) external view returns (uint);

  function getCanvasNumPixels(uint8 id) external view returns (uint);
}

/**
 * @title IOpenAvatarGen0AssetsCanvasStoreWrite
 * @dev This interface writes canvas headers
 */
interface IOpenAvatarGen0AssetsCanvasStoreWrite {
  function addCanvas(CanvasHeader calldata header) external;
}

/**
 * @title IOpenAvatarGen0AssetsCanvasStore
 * @dev This interface reads and writes canvas headers
 */
interface IOpenAvatarGen0AssetsCanvasStore is
  IOpenAvatarGen0AssetsCanvasStoreRead,
  IOpenAvatarGen0AssetsCanvasStoreWrite
{

}

// File: contracts/core/interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title IOpenAvatarGen0AssetsPaletteStoreRead
 * @dev This interface allows reading from the palette store.
 */
interface IOpenAvatarGen0AssetsPaletteStoreRead {
  /////////////////////////////////////////////////////////////////////////////
  // Constants
  /////////////////////////////////////////////////////////////////////////////

  function hasAlphaChannel() external view returns (bool);

  function getBytesPerPixel() external view returns (uint8);

  /////////////////////////////////////////////////////////////////////////////
  // Palettes
  /////////////////////////////////////////////////////////////////////////////

  function getNumPaletteCodes() external view returns (uint);

  function getNumPalettes(uint8 code) external view returns (uint);

  function getPalette(uint8 code, uint8 index) external view returns (bytes4[] memory);
}

struct UploadPaletteInput {
  uint8 code;
  uint8 index;
  bytes4[] palette;
}

struct UploadPaletteBatchInput {
  uint8 code;
  uint8 fromIndex;
  bytes4[][] palettes;
}

/**
 * @title IOpenAvatarGen0AssetsPaletteStoreWrite
 * @dev This interface allows writing to the palette store.
 */
interface IOpenAvatarGen0AssetsPaletteStoreWrite {
  function uploadPalette(UploadPaletteInput calldata input) external;

  function uploadPaletteBatch(UploadPaletteBatchInput calldata input) external;

  function uploadPaletteBatches(UploadPaletteBatchInput[] calldata input) external;
}

/**
 * @title IOpenAvatarGen0AssetsPaletteStore
 * @dev This interface allows reading from and writing to the palette store.
 */
interface IOpenAvatarGen0AssetsPaletteStore is
  IOpenAvatarGen0AssetsPaletteStoreRead,
  IOpenAvatarGen0AssetsPaletteStoreWrite
{

}

// File: contracts/core/interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {IOpenAvatarGen0AssetsCanvasStore, IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsCanvasStoreWrite} from './IOpenAvatarGen0AssetsCanvasStore.sol';
import {IOpenAvatarGen0AssetsPaletteStoreRead, IOpenAvatarGen0AssetsPaletteStoreWrite, IOpenAvatarGen0AssetsPaletteStore} from './IOpenAvatarGen0AssetsPaletteStore.sol';

struct PatternHeader {
  /// @dev width of the pattern
  uint8 width;
  /// @dev height of the pattern
  uint8 height;
  /// @dev x offset of the pattern within the canvas
  uint8 offsetX;
  /// @dev y offset of the pattern within the canvas
  uint8 offsetY;
  /// @dev the palette code for the pattern
  uint8 paletteCode;
}

struct OptionalPatternHeader {
  /// @dev true if the header exists
  bool exists;
  /// @dev the pattern header
  /// @dev all zeroes is valid header
  PatternHeader header;
}

struct PatternBlob {
  /// @dev the pattern header
  PatternHeader header;
  /// @dev the pattern data
  bytes data;
}

struct UploadPatternInput {
  /// @dev the canvas id
  uint8 canvasId;
  /// @dev index of the layer within the canvas
  uint8 layer;
  /// @dev index of the pattern within the layer
  uint8 index;
  /// @dev width of the pattern
  uint8 width;
  /// @dev height of the pattern
  uint8 height;
  /// @dev x offset of the pattern within the canvas
  uint8 offsetX;
  /// @dev y offset of the pattern within the canvas
  uint8 offsetY;
  /// @dev the palette code for the pattern
  uint8 paletteCode;
  /// @dev the pattern data
  bytes data;
}

/**
 * @title IOpenAvatarGen0AssetsPatternStoreRead
 * @dev This interface reads pattern data
 */
interface IOpenAvatarGen0AssetsPatternStoreRead is IOpenAvatarGen0AssetsCanvasStoreRead {
  /////////////////////////////////////////////////////////////////////////////
  // Layers
  /////////////////////////////////////////////////////////////////////////////

  function getNumLayers(uint8 canvasId) external view returns (uint);

  /////////////////////////////////////////////////////////////////////////////
  // Patterns
  /////////////////////////////////////////////////////////////////////////////

  function getNumPatterns(uint8 canvasId, uint8 layer) external view returns (uint);

  function getPatternHeader(
    uint8 canvasId,
    uint8 layer,
    uint8 index
  ) external view returns (OptionalPatternHeader memory);

  function getPatternData(uint8 canvasId, uint8 layer, uint8 index) external view returns (bytes memory);
}

/**
 * @title IOpenAvatarGen0AssetsPatternStoreWrite
 * @dev This interface writes pattern data
 */
interface IOpenAvatarGen0AssetsPatternStoreWrite is IOpenAvatarGen0AssetsCanvasStoreWrite {
  /////////////////////////////////////////////////////////////////////////////
  // Layers
  /////////////////////////////////////////////////////////////////////////////

  function addLayer(uint8 canvasId, uint8 layer) external;

  function addLayers(uint8 canvasId, uint8[] calldata layers) external;

  /////////////////////////////////////////////////////////////////////////////
  // Patterns
  /////////////////////////////////////////////////////////////////////////////

  function uploadPattern(UploadPatternInput calldata input) external;

  function uploadPatterns(UploadPatternInput[] calldata inputs) external;
}

/**
 * @title IOpenAvatarGen0AssetsPatternStore
 * @dev This interface reads and writes pattern data
 */
interface IOpenAvatarGen0AssetsPatternStore is
  IOpenAvatarGen0AssetsPatternStoreRead,
  IOpenAvatarGen0AssetsPatternStoreWrite,
  IOpenAvatarGen0AssetsCanvasStore
{

}

// File: contracts/core/interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {PatternHeader} from '../assets/IOpenAvatarGen0AssetsPatternStore.sol';

struct LayerPatternPalette {
  uint8 layer;
  uint8 pattern;
  uint8 palette;
}

/**
 * @title IOpenAvatarGen0AssetsCanvasLayerCompositor
 * @dev This contract composes palettized patterns into a single image.
 */
interface IOpenAvatarGen0AssetsCanvasLayerCompositor {
  function createLayerComposition(
    uint8 canvasId,
    // is view function, so not concerned about calldata being cheaper
    // need memory because this function is called by other functions that
    // may compute layers dynamically in memory
    LayerPatternPalette[] memory layerPatternPalette
  ) external view returns (bytes memory);

  function drawLayerComposition(
    bytes memory out,
    uint8 canvasId,
    // is view function, so not concerned about calldata being cheaper
    // need memory because this function is called by other functions that
    // may compute layers dynamically in memory
    LayerPatternPalette[] memory layerPatternPalette
  ) external view returns (bytes memory);

  function drawLayer(
    bytes memory image,
    uint8 canvasId,
    uint8 layer,
    uint8 pattern,
    uint8 palette
  ) external view returns (bytes memory);

  function drawMaskedLayer(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    uint8 layer,
    uint8 pattern,
    uint8 palette
  ) external view returns (bytes memory);

  function drawPattern(
    bytes memory image,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) external view returns (bytes memory);

  function drawMaskedPattern(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) external view returns (bytes memory);
}

// File: contracts/core/interfaces/render/IOpenAvatarGen0CanvasRenderer.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

interface IOpenAvatarGen0CanvasRenderer {
  function drawOpenAvatar(uint8 canvasId, bytes32 dna) external view returns (bytes memory);

  function drawOpenAvatarOverlay(bytes memory image, uint8 canvasId, bytes32 dna) external view returns (bytes memory);
}

// File: contracts/core/lib/Adler32.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title Adler32
 * @notice This contract implements the Adler32 checksum algorithm.
 */
library Adler32 {
  function adler32(bytes memory self, uint offset, uint end) internal pure returns (uint32) {
    unchecked {
      uint32 a = 1;
      uint32 b = 0;

      // Process each byte of the data in order
      for (uint i = offset; i < end; i++) {
        a = (a + uint32(uint8(self[i]))) % 65521;
        b = (b + a) % 65521;
      }

      // The Adler-32 checksum is stored as a 4-byte value
      return (b << 16) | a;
    }
  }
}

// File: contracts/core/lib/CRC32.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title CRC32
 * @notice This contract implements the CRC32 checksum algorithm.
 */
library CRC32 {
  // CRC32 algorithm: https://en.wikipedia.org/wiki/Cyclic_redundancy_check
  /**
   * @dev Calculates the CRC32 checksum of a chunk of data.
   * @param self The data to calculate the checksum of.
   * @param start The start index of the data.
   * @param end The end index of the data.
   * @return checksum The CRC32 checksum of the data.
   */
  function crc32(bytes memory self, uint start, uint end) internal pure returns (uint32 checksum) {
    // Initialize the checksum to 0xffffffff
    checksum = 0xffffffff;

    // Loop through each byte of the chunk data
    for (uint i = start; i < end; i++) {
      // XOR the byte with the checksum
      checksum = checksum ^ uint8(self[i]);
      // Loop through each bit of the byte
      for (uint j = 0; j < 8; j++) {
        // If the LSB of the checksum is 1
        if ((checksum & 1) == 1) {
          // 0xEDB88320 is the CRC-32 polynomial in reversed bit order
          // this translates to the polynomial with equation
          // x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
          // which is the same as the one used in the PNG specification
          checksum = (checksum >> 1) ^ 0xedb88320;
        }
        // If the LSB of the checksum is 0
        else {
          // Shift the checksum right by 1 bit
          checksum = (checksum >> 1);
        }
      }
    }

    // Return the inverted checksum
    return ~checksum;
  }
}

// File: contracts/core/lib/DNA.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 *  @title DNA
 *  @notice This library implements the DNA as defined by OpenAvatar.
 *  @dev The DNA string is a 32-byte hex string. The DNA string is immutable.
 *  The bytes represent the following:
 *  ZZZZ YYYY XXXX WWWW VVVV UUUU TTTT SSSS
 *  0000 0000 0000 0000 0000 0000 0000 0000
 *
 *    Bytes  |  Chars  | Description
 *  ---------|---------|-------------
 *   [0:1]   | [0:3]   |  body
 *   [2:3]   | [4:7]   |  tattoos
 *   [4:5]   | [8:11]  |  makeup
 *   [6:7]   | [12:15] |  left eye
 *   [8:9]   | [16:19] |  right eye
 *   [10:11] | [20:23] |  bottomwear
 *   [12:13] | [24:27] |  footwear
 *   [14:15] | [28:31] |  topwear
 *   [16:17] | [32:35] |  handwear
 *   [18:19] | [36:39] |  outerwear
 *   [20:21] | [40:43] |  jewelry
 *   [22:23] | [44:47] |  facial hair
 *   [24:25] | [48:51] |  facewear
 *   [26:27] | [52:55] |  eyewear
 *   [28:29] | [56:59] |  hair
 *   [30:31] | [60:63] |  reserved
 *
 *  Each 2-byte section is a struct of the following:
 *    [0] | [0:1] |  pattern
 *    [1] | [2:3] |  palette
 *
 * The pattern is an index into the pattern array.
 * The palette is an index into the palette array.
 */
library DNA {
  /////////////////////////////////////////////////////////////////////////////
  /// Body
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [0:1]

  /// @notice Returns the body pattern index from the DNA.
  function bodyPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[0]);
  }

  /// @notice Returns the body palette index from the DNA.
  function bodyPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[1]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Tattoos
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [2:3]

  /// @notice Returns the tattoos pattern index from the DNA.
  function tattoosPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[2]);
  }

  /// @notice Returns the tattoos palette index from the DNA.
  function tattoosPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[3]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Makeup
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [4:5]

  /// @notice Returns the makeup pattern index from the DNA.
  function makeupPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[4]);
  }

  /// @notice Returns the makeup palette index from the DNA.
  function makeupPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[5]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Left Eye
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [6:7]

  /// @notice Returns the left eye pattern index from the DNA.
  function leftEyePattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[6]);
  }

  /// @notice Returns the left eye palette index from the DNA.
  function leftEyePalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[7]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Right Eye
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [8:9]

  /// @notice Returns the right eye pattern index from the DNA.
  function rightEyePattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[8]);
  }

  /// @notice Returns the right eye palette index from the DNA.
  function rightEyePalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[9]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Bottomwear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [10:11]

  /// @notice Returns the bottomwear pattern index from the DNA.
  function bottomwearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[10]);
  }

  /// @notice Returns the bottomwear palette index from the DNA.
  function bottomwearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[11]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Footwear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [12:13]

  /// @notice Returns the footwear pattern index from the DNA.
  function footwearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[12]);
  }

  /// @notice Returns the footwear palette index from the DNA.
  function footwearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[13]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Topwear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [14:15]

  /// @notice Returns the topwear pattern index from the DNA.
  function topwearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[14]);
  }

  /// @notice Returns the topwear palette index from the DNA.
  function topwearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[15]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Handwear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [16:17]

  /// @notice Returns the handwear pattern index from the DNA.
  function handwearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[16]);
  }

  /// @notice Returns the handwear palette index from the DNA.
  function handwearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[17]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Outerwear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [18:19]

  /// @notice Returns the outerwear pattern index from the DNA.
  function outerwearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[18]);
  }

  /// @notice Returns the outerwear palette index from the DNA.
  function outerwearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[19]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Jewelry
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [20:21]

  /// @notice Returns the jewelry pattern index from the DNA.
  function jewelryPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[20]);
  }

  /// @notice Returns the jewelry palette index from the DNA.
  function jewelryPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[21]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Facial Hair
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [22:23]

  /// @notice Returns the facial hair pattern index from the DNA.
  function facialHairPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[22]);
  }

  /// @notice Returns the facial hair palette index from the DNA.
  function facialHairPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[23]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Facewear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [24:25]

  /// @notice Returns the facewear pattern index from the DNA.
  function facewearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[24]);
  }

  /// @notice Returns the facewear palette index from the DNA.
  function facewearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[25]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Eyewear
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [26:27]

  /// @notice Returns the eyewear pattern index from the DNA.
  function eyewearPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[26]);
  }

  /// @notice Returns the eyewear palette index from the DNA.
  function eyewearPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[27]);
  }

  /////////////////////////////////////////////////////////////////////////////
  /// Hair
  /////////////////////////////////////////////////////////////////////////////
  /// bytes [28:29]

  /// @notice Returns the hair pattern index from the DNA.
  function hairPattern(bytes32 self) internal pure returns (uint8) {
    return uint8(self[28]);
  }

  /// @notice Returns the hair palette index from the DNA.
  function hairPalette(bytes32 self) internal pure returns (uint8) {
    return uint8(self[29]);
  }
}

// File: contracts/core/lib/ENSReverseClaimer.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {ENS} from '../dependencies/ens/registry/ENS.sol';
import {IReverseRegistrar} from '../dependencies/ens/reverseRegistrar/IReverseRegistrar.sol';

/**
 * @title ENSReverseClaimer
 * @dev This contract is used to claim reverse ENS records.
 */
abstract contract ENSReverseClaimer is Ownable {
  /// @dev The namehash of 'addr.reverse', the domain at which reverse records
  ///      are stored in ENS.
  bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;

  /**
   * @dev Transfers ownership of the reverse ENS record associated with the
   *      contract.
   * @param ens The ENS registry.
   * @param claimant The address to set as the owner of the reverse record in
   *                 ENS.
   * @return The ENS node hash of the reverse record.
   */
  function claimReverseENS(ENS ens, address claimant) external onlyOwner returns (bytes32) {
    return IReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)).claim(claimant);
  }

  /**
   * @dev Sets the reverse ENS record associated with the contract.
   * @param ens The ENS registry.
   * @param name The name to set as the reverse record in ENS.
   * @return The ENS node hash of the reverse record.
   */
  function setReverseENS(ENS ens, string calldata name) external onlyOwner returns (bytes32) {
    return IReverseRegistrar(ens.owner(ADDR_REVERSE_NODE)).setName(name);
  }
}

// File: contracts/core/lib/ImageEncoder.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';
import {Base64} from '../dependencies/Base64.sol';
import {PNG} from './PNG.sol';

/**
 * @title ImageEncoder
 * @dev A library for encoding images as PNG or SVG.
 */
contract ImageEncoder is PNG {
  /**
   * @notice Encodes the image as a Base64-encoded PNG.
   * @param data The raw image data.
   * @param width Width of the image data, in pixels.
   * @param height Height of the image data, in pixels.
   * @param alpha Whether the image has an alpha channel.
   * @return The encoded Base64-encoded PNG.
   */
  function encodeBase64PNG(bytes memory data, uint width, uint height, bool alpha) public pure returns (bytes memory) {
    bytes memory png = encodePNG(data, width, height, alpha);
    return Base64.encode(png);
  }

  /**
   * @notice Encodes the image as an SVG.
   * @param data The raw image data.
   * @param width Width of the image data, in pixels.
   * @param height Height of the image, in pixels.
   * @param alpha Whether the image has an alpha channel.
   * @param svgWidth Width of the scaled SVG, in pixels.
   * @param svgHeight Height of the scaled SVG, in pixels.
   * @return The encoded SVG.
   */
  function encodeSVG(
    bytes memory data,
    uint width,
    uint height,
    bool alpha,
    uint svgWidth,
    uint svgHeight
  ) public pure returns (bytes memory) {
    bytes memory base64PNG = encodeBase64PNG(data, width, height, alpha);
    string memory svgWidthStr = Strings.toString(svgWidth);
    string memory svgHeightStr = Strings.toString(svgHeight);
    return
      abi.encodePacked(
        '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ',
        svgWidthStr,
        ' ',
        svgHeightStr,
        '">\n\t<foreignObject width="',
        svgWidthStr,
        '" height="',
        svgHeightStr,
        '">\n\t\t<img xmlns="http://www.w3.org/1999/xhtml" width="',
        svgWidthStr,
        '" height="',
        svgHeightStr,
        '" style="image-rendering: pixelated;" src="data:image/png;base64,',
        base64PNG,
        '"/>\n\t</foreignObject>\n</svg>'
      );
  }

  /**
   * @notice Encodes the image as a Base64-encoded SVG.
   * @param data The raw image data.
   * @param width Width of the image data, in pixels.
   * @param height Height of the image, in pixels.
   * @param alpha Whether the image has an alpha channel.
   * @param svgWidth Width of the scaled SVG, in pixels.
   * @param svgHeight Height of the scaled SVG, in pixels.
   * @return The encoded Base64-encoded SVG.
   */
  function encodeBase64SVG(
    bytes memory data,
    uint width,
    uint height,
    bool alpha,
    uint svgWidth,
    uint svgHeight
  ) public pure returns (bytes memory) {
    bytes memory svg = encodeSVG(data, width, height, alpha, svgWidth, svgHeight);
    return Base64.encode(svg);
  }
}

// File: contracts/core/lib/KeepAlive.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

/**
 * @title KeepAlive
 * @dev KeepAlive is a contract designed to maintain its onchain presence in
 * case of state expiration by using payable receive() and fallback() functions,
 * while allow the owner to still withdraw funds.
 * fallback():
 *   - Default function that gets executed when no other function in the contract
 *     matches the provided function signature, or when the contract receives
 *     Ether along with data
 *   - Can be payable or non-payable
 *   - Must be marked external
 * receive():
 *   - Introduced in Solidity 0.6.0
 *   - Special function that is executed when a contract receives Ether without
 *     any data
 *   - Must be payable
 *   - Must be marked external
 *   - Makes it easier to differentiate between intended Ether transfers and
 *     other function calls
 */
contract KeepAlive is Ownable {
  /**
   * @notice Fallback function.
   * @dev fallback():
   *  - Default function that gets executed when no other function in the contract
   *    matches the provided function signature, or when the contract receives
   *    Ether along with data
   *  - Can be payable or non-payable
   *  - Must be marked external
   */
  // solhint-disable-next-line no-empty-blocks
  fallback() external payable {}

  /**
   * @notice Receive funds.
   * @dev receive():
   *   - Introduced in Solidity 0.6.0
   *   - Special function that is executed when a contract receives Ether without
   *     any data
   *   - Must be payable
   *   - Must be marked external
   *   - Makes it easier to differentiate between intended Ether transfers and
   *     other function calls
   */
  // solhint-disable-next-line no-empty-blocks
  receive() external payable {}

  /**
   * @notice Withdraw funds from the contract.
   */
  function withdraw(uint amount) external onlyOwner {
    payable(msg.sender).transfer(amount);
  }
}

// File: contracts/core/lib/PNG.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {Adler32} from './Adler32.sol';
import {CRC32} from './CRC32.sol';

/**
 * @title PNG
 * @dev PNG is a contract for generating PNG images from raw image data in Solidity.
 * It includes functions for encoding and decoding PNG images, as well as for
 * calculating Adler32 and CRC32 checksums.
 *
 * This contract is based on the PNG specification:
 *
 * http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
 *
 * It supports only 8 bit images and supports RGB or RGBA color formats.
 * It uses compression method 0, filter method 0, and interlace method 0.
 */
contract PNG {
  using Adler32 for bytes;
  using CRC32 for bytes;

  /**
   * The PNG signature is a fixed eight-byte sequence:
   * 89 50 4e 47 0d 0a 1a 0a
   */
  bytes public constant PNG_SIGNATURE = hex'89504e470d0a1a0a';

  /**
   * The IEND chunk marks the end of the PNG datastream.
   * It contains no data.
   *
   * The IEND chunk must appear last.
   * It is an error to place any data after the IEND chunk.
   *
   * The IEND chunk is always equal to 12 bytes
   * 00 00 00 00 49 45 4e 44 ae 42 60 82
   */
  bytes public constant IEND = hex'0000000049454e44ae426082';

  /**
   * @notice Encodes a PNG image from raw image data
   * @param data Raw image data
   * @param width  The width of the image, in pixels
   * @param height The height of the image, in pixels
   * @param alpha  Whether the image has an alpha channel
   * @return PNG image
   */
  function encodePNG(bytes memory data, uint width, uint height, bool alpha) public pure returns (bytes memory) {
    unchecked {
      // Determine the width of each pixel
      uint pixelWidth = (alpha) ? 4 : 3;

      // Check that the length of the data is correct
      require(data.length == pixelWidth * width * height, 'Invalid image data length');

      // Create the IHDR chunk
      bytes memory chunkIHDR = encodeIHDR(width, height, alpha);

      // Create the IDAT chunk
      bytes memory chunkIDAT = encodeIDAT(data, width, height, alpha);

      // Concatenate the chunks into a single bytes array.
      return abi.encodePacked(PNG_SIGNATURE, chunkIHDR, chunkIDAT, IEND);
    }
  }

  /**
   * @dev Generates an IHDR chunk for a PNG image with the given width and height.
   * This function generates an IHDR chunk according to the PNG specification
   * (http://libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR).
   *
   * @param width The width of the image.
   * @param height The height of the image.
   * @param alpha Whether the image has alpha transparency.
   * @return A bytes memory array containing the IDAT chunk data.
   */
  function encodeIHDR(uint width, uint height, bool alpha) public pure returns (bytes memory) {
    // Create the IHDR chunk
    // The IHDR chunk length is 13 bytes (0x0000000d in hex)
    // The IHDR type 49 48 44 52 (IHDR)
    //
    // The IHDR chunk data consists of the following fields:
    // 4 bytes: width
    // 4 bytes: height
    // 1 byte: bit depth (8)
    // 1 byte: color type (2 for RGB, 6 for RGBA)
    // 1 byte: compression method (0)
    // 1 byte: filter method (0)
    // 1 byte: interlace method (0)
    //
    // 4 bytes: CRC32 checksum
    bytes memory chunkIHDR = hex'0000000d494844520000000000000000080200000000000000';
    // Set the width and height of the image in the chunk data
    chunkIHDR[8] = bytes1(uint8(width >> 24));
    chunkIHDR[9] = bytes1(uint8(width >> 16));
    chunkIHDR[10] = bytes1(uint8(width >> 8));
    chunkIHDR[11] = bytes1(uint8(width));
    chunkIHDR[12] = bytes1(uint8(height >> 24));
    chunkIHDR[13] = bytes1(uint8(height >> 16));
    chunkIHDR[14] = bytes1(uint8(height >> 8));
    chunkIHDR[15] = bytes1(uint8(height));

    // Set the color type of the image in the chunk data
    if (alpha) {
      // truecolor image with alpha channel
      chunkIHDR[17] = hex'06';
    } else {
      // truecolor image without alpha channel
      chunkIHDR[17] = hex'02';
    }

    // Calculate and set the CRC32 checksum of the chunk
    uint32 checksum = chunkIHDR.crc32(4, 21);
    chunkIHDR[21] = bytes1(uint8(checksum >> 24));
    chunkIHDR[22] = bytes1(uint8(checksum >> 16));
    chunkIHDR[23] = bytes1(uint8(checksum >> 8));
    chunkIHDR[24] = bytes1(uint8(checksum));

    return chunkIHDR;
  }

  /**
   * @dev Interlaces a given bytes array of image data.
   * @param data The bytes array of image data.
   * @param width The width of the image, in pixels.
   * @param height The height of the image, in pixels.
   * @param alpha Whether the image has an alpha channel.
   * @return The interlaced bytes array.
   */
  function interlace(bytes memory data, uint width, uint height, bool alpha) internal pure returns (bytes memory) {
    unchecked {
      uint pixelWidth = alpha ? 4 : 3;

      // IDAT chunk
      // The IDAT chunk contains the actual image data.
      // The layout and total size of this raw data are determined by the fields of IHDR.
      // The filtered data is then compressed using the method specified by the IHDR chunk.

      // Since our image has no filtering,
      // the filter type byte for each scanline would be 0x00 (no filtering).
      // Interlacing method 0 is used, so pixels are stored sequentially from left to right,
      // and scanlines sequentially from top to bottom (no interlacing).
      uint rowWidth = pixelWidth * width;
      uint rowWidthPadded = rowWidth + 1;
      // Declare a bytes array to hold the interlaced data.
      bytes memory interlacedData = new bytes(rowWidthPadded * height);

      // Loop over the scanlines.
      for (uint row = 0; row < height; row++) {
        // Calculate the starting index for the current scanline.
        uint startIndex = rowWidthPadded * row;

        // Set the filter type byte for the current scanline.
        interlacedData[startIndex] = 0x00; // Filter type 0 (no filtering)

        // Copy the scanline data into the interlaced data array.
        // No filtering is used, so the scanline data starts at index 1.
        for (uint j = 0; j < rowWidth; j++) {
          interlacedData[startIndex + 1 + j] = data[row * rowWidth + j];
        }
      }
      return interlacedData;
    }
  }

  /**
   * @dev Generates a zlib-compressed version of the given image data using the Deflate algorithm.
   * This function generates a zlib-compressed version of the given image data using the Deflate algorithm,
   * as specified in the PNG specification (http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html).
   * The resulting data is suitable for storage in an IDAT chunk of a PNG file.
   *
   * @param data The image data to be compressed.
   * @param width The width of the image, in pixels.
   * @param height The height of the image, in pixels.
   * @param alpha Whether the image has alpha transparency.
   * @return A bytes array containing the zlib-compressed image data.
   */
  function zlibCompressDeflate(
    bytes memory data,
    uint width,
    uint height,
    bool alpha
  ) internal pure returns (bytes memory) {
    unchecked {
      // Generate Deflate-compressed data
      bytes memory deflateCompressedData = interlace(data, width, height, alpha);

      // Calculate Adler-32 checksum of Deflate-compressed data
      uint32 blockAdler32 = deflateCompressedData.adler32(0, deflateCompressedData.length);

      // zlib block header (BFINAL = 1, BTYPE = 0)
      bytes memory zlibBlockHeader = hex'01';

      // LEN is the length of the data
      bytes32 len = bytes32(deflateCompressedData.length);

      // Generate zlib-compressed data
      bytes memory result = abi.encodePacked(
        // zlib header
        // CM = 8 (deflate), CINFO = 7 (32K window size)
        hex'78',
        // FCHECK = 0 (no check)
        // FDICT = 0 (no preset dictionary)
        // FLEVEL = 0 (fastest compression)
        hex'01',
        // block header (BFINAL = 1, BTYPE = 0)
        zlibBlockHeader,
        // LEN (2 bytes) (length of the data)
        len[31],
        len[30],
        // NLEN (2 bytes) (one's complement of LEN)
        ~len[31],
        ~len[30],
        // Deflate-compressed data
        deflateCompressedData,
        // block footer (adler32 checksum)
        blockAdler32
      );

      return result;
    }
  }

  /**
   * @dev Generates an IDAT chunk for a PNG image with the given width and height.
   * This function generates an IDAT chunk according to the PNG specification
   * (http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html).
   *
   * @param data The filtered image data.
   * @param width The width of the image.
   * @param height The height of the image.
   * @param alpha Whether the image has alpha transparency.
   * @return A bytes memory array containing the IDAT chunk data.
   */
  function encodeIDAT(bytes memory data, uint width, uint height, bool alpha) internal pure returns (bytes memory) {
    unchecked {
      // The IDAT data is compressed using the deflate algorithm.
      bytes memory compressed = zlibCompressDeflate(data, width, height, alpha);
      // The compressed data stream is then stored in the IDAT chunk.
      bytes memory typedata = abi.encodePacked(
        hex'49444154', // Chunk type: "IDAT" in ASCII
        compressed
      );

      // CRC calculated from the chunk type and chunk data
      uint32 crc = typedata.crc32(0, typedata.length);

      // Append the CRC32 checksum to the end of the chunk
      return abi.encodePacked(uint32(compressed.length), typedata, crc);
    }
  }
}

// File: contracts/extensions/OpenAvatarGen0ProfilePictureRenderer.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
import {OpenAvatarGen0AssetsCanvasIdStore} from '../core/assets/OpenAvatarGen0AssetsCanvasIdStore.sol';
import {IOpenAvatarGen0CanvasRenderer} from '../core/interfaces/render/IOpenAvatarGen0CanvasRenderer.sol';
import {ENSReverseClaimer} from '../core/lib/ENSReverseClaimer.sol';
import {ImageEncoder} from '../core/lib/ImageEncoder.sol';
import {KeepAlive} from '../core/lib/KeepAlive.sol';
import {IOpenAvatarGen0AssetsRead} from '../IOpenAvatarGen0Assets.sol';
import {IOpenAvatarGen0Renderer} from '../IOpenAvatarGen0Renderer.sol';
import {IOpenAvatarGen0TextRecords} from '../IOpenAvatarGen0TextRecords.sol';
import {IOpenAvatarGen0TokenDNA} from '../IOpenAvatarGen0Token.sol';
import {OpenAvatarGenerationZero} from '../OpenAvatarGenerationZero.sol';
import {DNA} from '../core/lib/DNA.sol';
import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../core/interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol';

struct OpenAvatarProfilePictureSettings {
  /// @dev Whether or not to use a solid background color.
  bool overrideBackground;
  /// @dev The background color.
  bytes3 backgroundColor;
  /// @dev Whether or not to mask below the neck for a "floating head" effect.
  bool maskBelowTheNeck;
}

/**
 * @title OpenAvatarGen0ProfilePictureRenderer
 * @author Cory Gabrielsen (cory.eth)
 *
 * @notice A contract for rendering OpenAvatar Gen0 profile pictures.
 * @dev This contract renders an Avatar against a background image.
 *
 * ----------------------------------------------------------------------------
 * 'pfp' Renderer
 * ----------------------------------------------------------------------------
 * A Profile Picture Renderer is provided with the key "pfp".
 *
 * The 'pfp' Renderer renders the Avatar with a configurable background color
 * determined by the 'gen0.renderer.pfp.background-color' text record, which
 * should be a valid RGB hex color code (e.g. "#ff0000" for red).
 *
 * Further, the 'pfp' Renderer provides the option of masking the Avatar
 * below the neck to create a "floating head" effect. This can be configured
 * by setting the `gen0.renderer.pfp.mask` text record to "below-the-neck".
 *
 * The Profile Picture Renderer is a demonstration of combining the OpenAvatar
 * building blocks together to create dynamic, owner-customizable behavior. It
 * utilizes onchain assets, onchain rendering, and onchain text records to
 * render a customizable Avatar pfp.
 *
 * This pattern is permissionless.
 *
 *
 * ----------------------------------------------------------------------------
 * Background Image
 * ----------------------------------------------------------------------------
 * The background image is by default a white/light gray checkerboard of 8x8
 * squares arranged in a 4x4 grid.
 *
 * The background image can be changed by the token owner by setting the
 * 'gen0.renderer.pfp.background-image' text record to a valid RGB hex color
 * code (e.g. "#ff0000" for red).
 */
contract OpenAvatarGen0ProfilePictureRenderer is
  IOpenAvatarGen0Renderer,
  OpenAvatarGenerationZero,
  OpenAvatarGen0AssetsCanvasIdStore,
  ImageEncoder,
  ENSReverseClaimer,
  KeepAlive
{
  using DNA for bytes32;

  /// @dev Emitted when the fuse is burned to make the background image immutable.
  event FuseBurnedChangeBackgroundImage();

  /// @dev Error when a component is already initialized.
  error AlreadyInitialized();
  /// @dev Error when the fuse is already burned.
  error FuseBurned();
  /// @dev Error when the required ERC-165 interfaces are not supported.
  error InterfaceUnsupported(address contractAddress, bytes4 interfaceId);
  /// @dev Error when the provided canvas has an invalid number of bytes per pixel.
  error InvalidCanvasBytesPerPixel();
  /// @dev Error when calling write-protection function on a non-owned DNA.
  error NotOwner(bytes32 dna);

  /// @dev The ERC-165 interface id for the OpenAvatarGen0Renderer (for clients).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_RENDERER = 0xb93e4881;
  /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsRead (dependency).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ = 0x67bf31d1;
  /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsCanvasLayerCompositor (dependency).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR = 0x2638c94b;
  /// @dev The ERC-165 interface id for the OpenAvatarGen0TokenDNA (dependency).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA = 0x2717336f;
  /// @dev The ERC-165 interface id for the OpenAvatarGen0TextRecords ERC-634 text() (dependency).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_TEXT = 0x59d1d43c;

  /// @dev The canvas id.
  uint8 public constant CANVAS_ID = 0;
  /// @dev The pixel width of the canvas.
  uint8 public constant CANVAS_WIDTH = 32;
  /// @dev The pixel height of the canvas.
  uint8 public constant CANVAS_HEIGHT = 32;
  /// @dev The number of bytes in the canvas - 32 * 32 * 4.
  uint16 public constant CANVAS_NUM_BYTES = 4096;

  /// @dev The layer index for the body layer.
  uint8 public constant LAYER_INDEX_BODY = 10;
  /// @dev The layer index for the tattoos layer.
  uint8 public constant LAYER_INDEX_TATTOOS = 20;
  /// @dev The layer index for the makeup layer.
  uint8 public constant LAYER_INDEX_MAKEUP = 30;
  /// @dev The layer index for the left eye layer.
  uint8 public constant LAYER_INDEX_LEFT_EYE = 40;
  /// @dev The layer index for the right eye layer.
  uint8 public constant LAYER_INDEX_RIGHT_EYE = 50;
  /// @dev The layer index for the bottomwear layer.
  uint8 public constant LAYER_INDEX_BOTTOMWEAR = 60;
  /// @dev The layer index for the footwear layer.
  uint8 public constant LAYER_INDEX_FOOTWEAR = 70;
  /// @dev The layer index for the topwear layer.
  uint8 public constant LAYER_INDEX_TOPWEAR = 80;
  /// @dev The layer index for the handwear layer.
  uint8 public constant LAYER_INDEX_HANDWEAR = 90;
  /// @dev The layer index for the outerwear layer.
  uint8 public constant LAYER_INDEX_OUTERWEAR = 100;
  /// @dev The layer index for the jewelry layer.
  uint8 public constant LAYER_INDEX_JEWELRY = 110;
  /// @dev The layer index for the facial hair layer.
  uint8 public constant LAYER_INDEX_FACIAL_HAIR = 120;
  /// @dev The layer index for the facewear layer.
  uint8 public constant LAYER_INDEX_FACEWEAR = 130;
  /// @dev The layer index for the eyewear layer.
  uint8 public constant LAYER_INDEX_EYEWEAR = 140;
  /// @dev The layer index for the hair layer.
  uint8 public constant LAYER_INDEX_HAIR = 150;

  /// @dev Scale the SVG by this amount.
  uint public constant SVG_SCALE = 10;

  /// @dev The "only head" mask which masks below the neck.
  bytes public constant BELOW_THE_NECK_MASK =
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0000000000000000000000000000000000000000000000000000000000000000'
    hex'0101010101010101010101010000000000000000010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101'
    hex'0101010101010101010101010101010101010101010101010101010101010101';

  /// @dev The background image, an 4x4 grid of 8x8 checkerboard squares.
  ///      Every four lines is one row of the image.
  ///      The pattern switches every 8 rows, or 32 lines
  ///
  ///      This value is constant even though it isn't declared constant,
  ///      because declaring it as a constant makes renderURI fail
  ///      with out-of-gas?
  bytes public backgroundImage =
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff'
    hex'EEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEffEEEEEEff'
    hex'DDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDffDDDDDDff';

  /// @dev The text key for the background color of the avatar pfp.
  string public constant TEXT_KEY_PFP_BACKGROUND_COLOR = 'gen0.renderer.pfp.background-color';
  /// @dev The text key for the mask of the avatar pfp.
  string public constant TEXT_KEY_PFP_MASK = 'gen0.renderer.pfp.mask';
  /// @dev The text value for a pfp rendering that only displays the head.
  string public constant TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK = 'below-the-neck';
  /// @dev The keccak26 hash 'below-the-neck', for use in the contract.
  bytes32 private constant TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK_HASH =
    keccak256(abi.encodePacked(TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK));

  /////////////////////////////////////////////////////////////////////////////
  // State variables
  /////////////////////////////////////////////////////////////////////////////

  /// @dev fuse can be burned to make the background image immutable
  bool public fuseBurnedChangeBackgroundImage = false;

  /// @dev The OpenAvatarGen0AssetsRead dependency.
  IOpenAvatarGen0AssetsRead public openAvatarGen0AssetsRead;
  /// @dev The OpenAvatarGen0AssetsCanvasLayerCompositor dependency.
  IOpenAvatarGen0AssetsCanvasLayerCompositor public openAvatarGen0AssetsCanvasLayerCompositor;
  /// @dev The OpenAvatarDNA dependency.
  IOpenAvatarGen0TokenDNA public openAvatarGen0Token;
  /// @dev The OpenAvatarGen0TextRecords dependency.
  IOpenAvatarGen0TextRecords public openAvatarGen0TextRecords;

  // solhint-disable-next-line no-empty-blocks
  constructor(address ownerProxy) OpenAvatarGen0AssetsCanvasIdStore(CANVAS_ID) {
    // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner
    // using a proxy allows for using same bytecode in test and prod

    address wantOwner = Ownable(ownerProxy).owner();
    if (owner() != wantOwner) {
      transferOwnership(wantOwner);
    }
  }

  /////////////////////////////////////////////////////////////////////////////
  // ERC-165: Standard Interface Detection
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Checks if the contract supports an interface.
   * @param interfaceId The interface identifier, as specified in ERC-165.
   * @return True if the contract supports interfaceID, false otherwise.
   */
  function supportsInterface(bytes4 interfaceId) public pure override(OpenAvatarGenerationZero) returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // IOpenAvatar
      interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration.
      interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel.
      interfaceId == 0x86953eb4 || // ERC165 interface ID for IOpenAvatar.
      // renderer
      interfaceId == INTERFACE_ID_OPENAVATAR_GEN0_RENDERER;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Initialize Dependencies
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @dev Initialize the contract.
   * @param openAvatarGen0Assets_ The address of the asset store read interface contract.
   * @param openAvatarAssesCanvasLayerCompositor_ The address of the canvas layer compositor contract.
   * @param openAvatarGen0Token_ The address of the OpenAvatar token contract.
   * @param openAvatarGen0TextRecords_ The address of the text records contract.
   */
  function initialize(
    address openAvatarGen0Assets_,
    address openAvatarAssesCanvasLayerCompositor_,
    address openAvatarGen0Token_,
    address openAvatarGen0TextRecords_
  ) external onlyOwner {
    // helpers enforce only-once invariant and check ERC-165 supportsInterface()
    setOpenAvatarGen0Assets(openAvatarGen0Assets_);
    setOpenAvatarGen0AssetsCanvasLayerCompositor(openAvatarAssesCanvasLayerCompositor_);
    setOpenAvatarGen0Token(openAvatarGen0Token_);
    setOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_);
  }

  /**
   * @notice Check if the contract has been initialized.
   * @return True if the contract has been initialized, false otherwise.
   */
  function isInitialized() external view returns (bool) {
    return address(openAvatarGen0AssetsRead) != address(0);
  }

  /**
   * @notice Get the asset store.
   * @return The address of the asset store read interface contract.
   */
  function getOpenAvatarGen0Assets() external view returns (address) {
    return address(openAvatarGen0AssetsRead);
  }

  /**
   * @notice Set the asset store.
   * @param openAvatarGen0Assets_ The address of the asset store read interface contract.
   */
  function setOpenAvatarGen0Assets(address openAvatarGen0Assets_) internal {
    // only set once
    if (address(openAvatarGen0AssetsRead) != address(0)) revert AlreadyInitialized();

    // check ERC-165 support
    // only read interface is required
    if (!IERC165(openAvatarGen0Assets_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ)) {
      revert InterfaceUnsupported(openAvatarGen0Assets_, INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ);
    }

    // set
    openAvatarGen0AssetsRead = IOpenAvatarGen0AssetsRead(openAvatarGen0Assets_);

    // sanity check
    if (openAvatarGen0AssetsRead.getBytesPerPixel() != 4) revert InvalidCanvasBytesPerPixel();
  }

  /**
   * @notice Get the IOpenAvatarGen0AssetsCanvasLayerCompositor.
   * @return The address of the IOpenAvatarGen0AssetsCanvasLayerCompositor interface contract.
   */
  function getOpenAvatarGen0AssetsCanvasLayerCompositor() external view returns (address) {
    return address(openAvatarGen0AssetsCanvasLayerCompositor);
  }

  /**
   * @notice Set the IOpenAvatarGen0AssetsCanvasLayerCompositor.
   * @param openAvatarAssesCanvasLayerCompositor_ The address of the
   * IOpenAvatarGen0AssetsCanvasLayerCompositor interface contract.
   */
  function setOpenAvatarGen0AssetsCanvasLayerCompositor(address openAvatarAssesCanvasLayerCompositor_) internal {
    // only set once
    if (address(openAvatarGen0AssetsCanvasLayerCompositor) != address(0)) revert AlreadyInitialized();

    // check ERC-165 support
    //
    // we don't need use renderURI from OpenAvatarGen0Renderer, which is too high level here
    //
    // instead we use the compositor interface to manually layer assets on top of each other
    // allowing us to modify the image layers before encoding as a PNG/SVG
    if (
      !IERC165(openAvatarAssesCanvasLayerCompositor_).supportsInterface(
        INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR
      )
    ) {
      revert InterfaceUnsupported(
        openAvatarAssesCanvasLayerCompositor_,
        INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR
      );
    }

    // set
    openAvatarGen0AssetsCanvasLayerCompositor = IOpenAvatarGen0AssetsCanvasLayerCompositor(
      openAvatarAssesCanvasLayerCompositor_
    );
  }

  /**
   * @notice Get the OpenAvatar token.
   * @return The address of the OpenAvatar token read interface contract.
   */
  function getOpenAvatarGen0Token() external view returns (address) {
    return address(openAvatarGen0Token);
  }

  /**
   * @notice Set the OpenAvatar token address.
   * @param openAvatarGen0Token_ The address of the OpenAvatar token contract.
   */
  function setOpenAvatarGen0Token(address openAvatarGen0Token_) internal {
    // only set once
    if (address(openAvatarGen0Token) != address(0)) revert AlreadyInitialized();

    // check ERC-165 support
    // only DNA interface is required
    if (!IERC165(openAvatarGen0Token_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA)) {
      revert InterfaceUnsupported(openAvatarGen0Token_, INTERFACE_ID_OPENAVATAR_GEN0_TOKEN_DNA);
    }

    // set
    openAvatarGen0Token = IOpenAvatarGen0TokenDNA(openAvatarGen0Token_);
  }

  /**
   * @notice Get the OpenAvatarGen0TextRecords.
   * @return The address of the OpenAvatarGen0TextRecords.
   */
  function getOpenAvatarGen0TextRecords() external view returns (address) {
    return address(openAvatarGen0TextRecords);
  }

  /**
   * @notice Set the OpenAvatarGen0TextRecords address.
   * @param openAvatarGen0TextRecords_ The address of the OpenAvatarGen0TextRecords contract.
   */
  function setOpenAvatarGen0TextRecords(address openAvatarGen0TextRecords_) internal {
    // only set once
    if (address(openAvatarGen0TextRecords) != address(0)) revert AlreadyInitialized();

    // check ERC-165 support
    // only text() interface is required
    if (!IERC165(openAvatarGen0TextRecords_).supportsInterface(INTERFACE_ID_OPENAVATAR_GEN0_TEXT)) {
      revert InterfaceUnsupported(openAvatarGen0TextRecords_, INTERFACE_ID_OPENAVATAR_GEN0_TEXT);
    }

    // set
    openAvatarGen0TextRecords = IOpenAvatarGen0TextRecords(openAvatarGen0TextRecords_);
  }

  /////////////////////////////////////////////////////////////////////////////
  // Modifiers
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Modifier to check that the caller is the owner of the token.
   */
  modifier onlyTokenOwner(bytes32 dna) {
    if (msg.sender != openAvatarGen0Token.ownerOfDNA(dna)) {
      revert NotOwner(dna);
    }
    _;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Fuses
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @dev Get the fuse burned status for changing the background image.
   */
  function getFuseBurnedChangeBackgroundImage() external view returns (bool) {
    return fuseBurnedChangeBackgroundImage;
  }

  /**
   * @dev Burn the fuse to prevent changing the background image.
   */
  function burnFuseChangeBackgroundImage() external onlyOwner {
    if (!fuseBurnedChangeBackgroundImage) {
      fuseBurnedChangeBackgroundImage = true;
      emit FuseBurnedChangeBackgroundImage();
    }
  }

  /////////////////////////////////////////////////////////////////////////////
  // Background Image
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @dev Get the default background image.
   */
  function getBackgroundImage() external view returns (bytes memory) {
    return backgroundImage;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Profile Picture Settings
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Return the background settings for the given DNA.
   * @param dna The DNA to set the background color for.
   * @return The background settings.
   */
  function getProfilePictureSettings(bytes32 dna) external view returns (OpenAvatarProfilePictureSettings memory) {
    string memory pfpBackground = openAvatarGen0TextRecords.text(dna, TEXT_KEY_PFP_BACKGROUND_COLOR);

    // pfpBackground is a RGB hexstring like #AABBCC
    // we need to decompose into a bytes3 0xAABBCC
    bool overrideBackground = bytes(pfpBackground).length == 7;
    bytes3 backgroundColor = _hexStrToBytes3(pfpBackground);

    // pfpMask is a string like 'below-the-neck' or ''
    string memory pfpMask = openAvatarGen0TextRecords.text(dna, TEXT_KEY_PFP_MASK);
    // security (gas) - don't compute hash if incorrect length
    bool validLength = bytes(pfpMask).length == 14; // 'below-the-neck' is 14 bytes
    bool maskBelowTheNeck = false;
    if (validLength) {
      maskBelowTheNeck = keccak256(abi.encodePacked(pfpMask)) == TEXT_KEY_PFP_MASK_VALUE_BELOW_THE_NECK_HASH;
    }

    return OpenAvatarProfilePictureSettings(overrideBackground, backgroundColor, maskBelowTheNeck);
  }

  /**
   * @notice Convert an RGB hextring #AABBCC to a bytes3 0xAABBCC.
   * @param _str The string to convert.
   * @return The bytes3 value
   * @dev if the string is not length-7 or leading # is missing, return 0x000000
   * @dev if a pair of characters is not a valid hex string, return 0x00 for that byte
   */
  function _hexStrToBytes3(string memory _str) internal pure returns (bytes3) {
    bytes memory b = bytes(_str);
    if (b.length != 7) return 0x000000;

    // the first character should be '#'
    if (b[0] != 0x23) return 0x000000;

    bytes3 rgb;
    for (uint i = 0; i < 3; i++) {
      rgb |= bytes3(_safeParseByte(_str, 2 * i + 1)) >> (8 * i);
    }
    return rgb;
  }

  /**
   * @notice Safely parse a byte.
   * @param _str The string to parse.
   * @param _start The start index.
   * @return The parsed byte, or 0x00 if the byte is invalid.
   */
  function _safeParseByte(string memory _str, uint _start) private pure returns (bytes1) {
    bytes1 b1 = _safeParseHexDigit(bytes(_str)[_start]);
    bytes1 b2 = _safeParseHexDigit(bytes(_str)[_start + 1]);
    return (b1 << 4) | b2;
  }

  /**
   * @dev Safely parse a hex digit.
   * @param _hex The hex digit to parse.
   * @return The parsed digit, or 0x00 if the digit is invalid.
   */
  function _safeParseHexDigit(bytes1 _hex) private pure returns (bytes1) {
    if (_hex >= '0' && _hex <= '9') {
      return bytes1(uint8(_hex) - 48);
    }
    if (_hex >= 'a' && _hex <= 'f') {
      return bytes1(uint8(_hex) - 97 + 10);
    }
    if (_hex >= 'A' && _hex <= 'F') {
      return bytes1(uint8(_hex) - 65 + 10);
    }
    return 0;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Rendering - external
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Render the given DNA as a base64-encoded SVG URI.
   * @param dna The DNA to render.
   * @return The SVG URI.
   */
  function renderURI(bytes32 dna) external view override returns (string memory) {
    return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVG(dna)));
  }

  /**
   * @notice Render the given DNA as a base64-encoded SVG URI with the provided settings.
   * @param dna The DNA to render.
   * @param pfpSettings The background settings to use.
   * @return The SVG URI.
   */
  function renderURIWithSettings(
    bytes32 dna,
    OpenAvatarProfilePictureSettings memory pfpSettings
  ) external view returns (string memory) {
    return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVGWithSettings(dna, pfpSettings)));
  }

  /////////////////////////////////////////////////////////////////////////////
  // Rendering - helpers
  /////////////////////////////////////////////////////////////////////////////

  function createBackgroundImage(
    OpenAvatarProfilePictureSettings memory pfpSettings
  ) internal view returns (bytes memory, uint8, uint8) {
    bytes memory image = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId));

    uint8 width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId);
    uint8 height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId);

    if (pfpSettings.overrideBackground) {
      uint numPixels = uint(width) * uint(height);
      // override default background with solid color
      for (uint i = 0; i < numPixels; ) {
        uint offset = i * 4;
        image[offset] = pfpSettings.backgroundColor[0];
        image[offset + 1] = pfpSettings.backgroundColor[1];
        image[offset + 2] = pfpSettings.backgroundColor[2];
        image[offset + 3] = 0xff;
        unchecked {
          ++i;
        }
      }
    } else {
      // copy default background
      for (uint i = 0; i < image.length; ) {
        image[i] = backgroundImage[i];
        unchecked {
          ++i;
        }
      }
    }
    return (image, width, height);
  }

  /// @dev kind of a hack but doesn't matter. seems like a one-off so just put it here
  function _drawNeckLine(bytes memory image) internal pure returns (bytes memory) {
    // the bottom of the head is an 8-pixel line representing the chinline/neck
    // by default, this is will be the "dark" color for that body color palette,
    // which is not black but rather a dark skin tone color.
    //
    // but if we are drawing just the head without the body, we actually want to have
    // the head outlined in black because that's the boundary shape.
    //
    // so we draw 8 black pixels over top of the "dark" pixels
    //
    // another design considered was uploading the "head" pattern separately to
    // the assets contract, for the same canvas, but on a "off" layer (11?) (body=10)
    // which fits with the intended design of how to use the assets contract
    //
    // but
    //
    // this was a one-off for now, so we are just doing the 8 pixel difference here

    // 8 pixels across

    // 1st pixel
    image[2864] = 0x00;
    image[2865] = 0x00;
    image[2866] = 0x00;
    image[2867] = 0xff;
    // 2nd pixel
    image[2868] = 0x00;
    image[2869] = 0x00;
    image[2870] = 0x00;
    image[2871] = 0xff;
    // 3rd pixel
    image[2872] = 0x00;
    image[2873] = 0x00;
    image[2874] = 0x00;
    image[2875] = 0xff;
    // 4th pixel
    image[2876] = 0x00;
    image[2877] = 0x00;
    image[2878] = 0x00;
    image[2879] = 0xff;
    // 5th pixel
    image[2880] = 0x00;
    image[2881] = 0x00;
    image[2882] = 0x00;
    image[2883] = 0xff;
    // 6th pixel
    image[2884] = 0x00;
    image[2885] = 0x00;
    image[2886] = 0x00;
    image[2887] = 0xff;
    // 7th pixel
    image[2888] = 0x00;
    image[2889] = 0x00;
    image[2890] = 0x00;
    // 8th pixel
    image[2891] = 0xff;
    image[2892] = 0x00;
    image[2893] = 0x00;
    image[2894] = 0x00;
    image[2895] = 0xff;
    return image;
  }

  /**
   * @dev Draw the layers of the avatar.
   * @param image The image to draw on.
   * @param mask The mask to apply before drawing.
   * @param canvasId The canvas ID to draw on.
   * @param dna The DNA to use.
   * @param pfpSettings The background settings to use.
   * @return The image with the layers drawn.
   */
  function _drawLayers(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    bytes32 dna,
    OpenAvatarProfilePictureSettings memory pfpSettings
  ) internal view returns (bytes memory) {
    // body
    uint8 bodyPattern = dna.bodyPattern();
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_BODY,
      bodyPattern,
      dna.bodyPalette()
    );

    // the neck is normally drawn on top of the body with a skin tone "neck"
    // but for this we want black border around the head not the body
    //
    // transparent bodies don't have a neck line
    if (pfpSettings.maskBelowTheNeck && bodyPattern != 0) {
      image = _drawNeckLine(image);
    }

    // tattoos
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_TATTOOS,
      dna.tattoosPattern(),
      dna.tattoosPalette()
    );
    // makeup
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_MAKEUP,
      dna.makeupPattern(),
      dna.makeupPalette()
    );
    // eyes
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_LEFT_EYE,
      dna.leftEyePattern(),
      dna.leftEyePalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_RIGHT_EYE,
      dna.rightEyePattern(),
      dna.rightEyePalette()
    );
    // clothes
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_BOTTOMWEAR,
      dna.bottomwearPattern(),
      dna.bottomwearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_FOOTWEAR,
      dna.footwearPattern(),
      dna.footwearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_TOPWEAR,
      dna.topwearPattern(),
      dna.topwearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_HANDWEAR,
      dna.handwearPattern(),
      dna.handwearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_OUTERWEAR,
      dna.outerwearPattern(),
      dna.outerwearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_JEWELRY,
      dna.jewelryPattern(),
      dna.jewelryPalette()
    );
    // facial hair - no mask
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawLayer(
      image,
      canvasId,
      LAYER_INDEX_FACIAL_HAIR,
      dna.facialHairPattern(),
      dna.facialHairPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_FACEWEAR,
      dna.facewearPattern(),
      dna.facewearPalette()
    );
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawMaskedLayer(
      image,
      mask,
      canvasId,
      LAYER_INDEX_EYEWEAR,
      dna.eyewearPattern(),
      dna.eyewearPalette()
    );
    // hair - no mask
    image = openAvatarGen0AssetsCanvasLayerCompositor.drawLayer(
      image,
      canvasId,
      LAYER_INDEX_HAIR,
      dna.hairPattern(),
      dna.hairPalette()
    );
    return image;
  }

  /**
   * @notice Render the given DNA as a base64-encoded SVG.
   * @param dna The DNA to render.
   * @return The base64-encoded SVG.
   */
  function renderBase64SVG(bytes32 dna) public view returns (bytes memory) {
    return renderBase64SVGWithSettings(dna, this.getProfilePictureSettings(dna));
  }

  /**
   * @notice Render the given DNA as a base64-encoded SVG.
   * @param dna The DNA to render.
   * @param pfpSettings The settings to use for the profile picture.
   * @return The base64-encoded SVG.
   */
  function renderBase64SVGWithSettings(
    bytes32 dna,
    OpenAvatarProfilePictureSettings memory pfpSettings
  ) public view returns (bytes memory) {
    (bytes memory image, uint8 width, uint8 height) = createBackgroundImage(pfpSettings);

    // copy mask into memory
    bytes memory mask = new bytes(openAvatarGen0AssetsRead.getCanvasNumPixels(canvasId));
    if (pfpSettings.maskBelowTheNeck) {
      uint length = mask.length;
      for (uint i = 0; i < length; ) {
        mask[i] = BELOW_THE_NECK_MASK[i];
        unchecked {
          ++i;
        }
      }
    }
    image = _drawLayers(image, mask, canvasId, dna, pfpSettings);
    return
      encodeBase64SVG(
        image,
        width,
        height,
        openAvatarGen0AssetsRead.hasAlphaChannel(),
        SVG_SCALE * width,
        SVG_SCALE * height
      );
  }
}

// File: contracts/IOpenAvatar.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title IOpenAvatarSentinel
 * @dev An interface for the OpenAvatar sentinel.
 */
interface IOpenAvatarSentinel {
  /// @dev Returns true
  function openAvatar() external view returns (bool);
}

/**
 * @title IOpenAvatarGeneration
 * @dev An interface for the OpenAvatar generation.
 */
interface IOpenAvatarGeneration {
  /// @dev Returns the generation of the OpenAvatar
  function openAvatarGeneration() external view returns (uint);
}

/**
 * @title IOpenAvatar
 * @dev The OpenAvatar interface.
 */
interface IOpenAvatar is IOpenAvatarSentinel, IOpenAvatarGeneration {

}

// File: contracts/IOpenAvatarGen0Assets.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {IOpenAvatarGen0AssetsCanvasStore, IOpenAvatarGen0AssetsCanvasStoreRead, IOpenAvatarGen0AssetsCanvasStoreWrite} from './core/interfaces/assets/IOpenAvatarGen0AssetsCanvasStore.sol';
import {IOpenAvatarGen0AssetsPaletteStoreRead, IOpenAvatarGen0AssetsPaletteStoreWrite, IOpenAvatarGen0AssetsPaletteStore} from './core/interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol';
import {IOpenAvatarGen0AssetsPatternStoreRead, IOpenAvatarGen0AssetsPatternStoreWrite, IOpenAvatarGen0AssetsPatternStore} from './core/interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol';

/**
 * @title IOpenAvatarGen0AssetsRead
 * @dev This interface reads asset data
 */
interface IOpenAvatarGen0AssetsRead is
  IOpenAvatarGen0AssetsCanvasStoreRead,
  IOpenAvatarGen0AssetsPatternStoreRead,
  IOpenAvatarGen0AssetsPaletteStoreRead
{

}

/**
 * @title IOpenAvatarGen0AssetsWrite
 * @dev This interface writes asset data
 */
interface IOpenAvatarGen0AssetsWrite is
  IOpenAvatarGen0AssetsCanvasStoreWrite,
  IOpenAvatarGen0AssetsPatternStoreWrite,
  IOpenAvatarGen0AssetsPaletteStoreWrite
{

}

/**
 * @title IOpenAvatarGen0Assets
 * @dev This interface reads and writes asset data
 */
interface IOpenAvatarGen0Assets is IOpenAvatarGen0AssetsRead, IOpenAvatarGen0AssetsWrite {

}

// File: contracts/IOpenAvatarGen0Renderer.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

/**
 * @title IOpenAvatarGen0Renderer
 * @dev The primary interface for rendering an Avatar.
 */
interface IOpenAvatarGen0Renderer {
  function renderURI(bytes32 dna) external view returns (string memory);
}

/**
 * @title IOpenAvatarGen0RendererDecorator
 * @dev The IOpenAvatarGen0RendererDecorator interface.
 */
interface IOpenAvatarGen0RendererDecorator is IOpenAvatarGen0Renderer {
  function renderHex(bytes32 dna) external view returns (bytes memory);

  function renderPNG(bytes32 dna) external view returns (bytes memory);

  function renderBase64PNG(bytes32 dna) external view returns (bytes memory);

  function renderSVG(bytes32 dna) external view returns (bytes memory);

  function renderBase64SVG(bytes32 dna) external view returns (bytes memory);
}

// File: contracts/IOpenAvatarGen0TextRecords.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;
import {IERC634} from './core/dependencies/IERC634.sol';

/**
 * @title Text Record
 * @notice A key-value string pair.
 */
struct TextRecord {
  string key;
  string value;
}

/**
 * @title DNA Text Record
 * @notice A DNA and a key-value string pair.
 */
struct DNATextRecord {
  bytes32 dna;
  string key;
  string value;
}

/**
 * @title IOpenAvatarGen0TextRecords
 * @dev The TextStore contract stores text data by 32-byte OpenAvatar DNA.
 *
 * This contract is based on ERC-634 originally developed for ENS.
 */
interface IOpenAvatarGen0TextRecords is IERC634 {
  function setText(bytes32 dna, string calldata key, string calldata value) external;

  function setText2(
    bytes32 dna,
    string calldata key,
    string calldata value,
    string calldata key2,
    string calldata value2
  ) external;

  function setText3(
    bytes32 dna,
    string calldata key,
    string calldata value,
    string calldata key2,
    string calldata value2,
    string calldata key3,
    string calldata value3
  ) external;

  function setText4(
    bytes32 dna,
    string calldata key,
    string calldata value,
    string calldata key2,
    string calldata value2,
    string calldata key3,
    string calldata value3,
    string calldata key4,
    string calldata value4
  ) external;

  function setTexts(bytes32 dna, TextRecord[] calldata records) external;

  function setTextBatch(DNATextRecord[] calldata records) external;
}

// File: contracts/IOpenAvatarGen0Token.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {IOpenAvatar} from './IOpenAvatar.sol';

/**
 * @title OpenAvatarGen0TokenMetadata
 * @dev The OpenAvatar metadata.
 */
struct OpenAvatarGen0TokenMetadata {
  uint generation;
  uint tokenId;
  bytes32 dna;
  address creator;
  address renderer;
}

/**
 * @title IOpenAvatarGen0TokenMetadata
 * @dev An interface for the OpenAvatar metadata.
 */
interface IOpenAvatarGen0TokenMetadata {
  function getOpenAvatarGen0TokenMetadataByDNA(bytes32 dna) external view returns (OpenAvatarGen0TokenMetadata memory);

  function getOpenAvatarGen0TokenMetadataByTokenId(
    uint tokenId
  ) external view returns (OpenAvatarGen0TokenMetadata memory);

  function openAvatarURI(bytes32 dna) external view returns (string memory);
}

/**
 * @title IOpenAvatarGen0TokenDNA
 * @dev An interface for the OpenAvatar DNA.
 */
interface IOpenAvatarGen0TokenDNA {
  function getDNAByTokenId(uint tokenId) external view returns (bytes32);

  function getDNAsByTokenIds(uint[] calldata tokenIds) external view returns (bytes32[] memory);

  function getTokenIdByDNA(bytes32 dna) external view returns (uint);

  function getTokenIdsByDNAs(bytes32[] calldata dnas) external view returns (uint[] memory);

  function creatorOf(uint tokenId) external view returns (address);

  function creatorOfDNA(bytes32 dna) external view returns (address);

  function ownerOfDNA(bytes32 dna) external view returns (address);

  function ownerOfDNAs(bytes32[] calldata dnas) external view returns (address[] memory);
}

/*
| Function                  | Mint State | Payment       | Batch  | Specify Recipient |
|---------------------------|------------|---------------|--------|-------------------|
| mint(dna)                 | Public     | mintPrice     | No     | No                |
| mintTo(to, dna)           | Public     | mintPrice     | No     | Yes               |
| mintBatch(dnas)           | Public     | mintPrice * N | Yes    | No                |
| mintBatchTo(to, dnas)     | Public     | mintPrice * N | Yes    | Yes               |
*/

/**
 * @title IOpenAvatarGen0TokenMintRead
 * @notice An interface for reading OpenAvatar minting state.
 */
interface IOpenAvatarGen0TokenMintRead {
  /////////////////////////////////////////////////////////////////////////////
  // Supply
  /////////////////////////////////////////////////////////////////////////////
  function supplySoftCap() external view returns (uint16);

  function supplyHardCap() external view returns (uint16);

  /////////////////////////////////////////////////////////////////////////////
  // Mint Price
  /////////////////////////////////////////////////////////////////////////////

  function getMintPrice() external view returns (uint);

  /////////////////////////////////////////////////////////////////////////////
  // Mint State
  /////////////////////////////////////////////////////////////////////////////

  function isMinted(bytes32 dna) external view returns (bool);

  function isMintedEach(bytes32[] calldata dnas) external view returns (bool[] memory);
}

/**
 * @title IOpenAvatarGen0TokenMintWrite
 * @notice An interface for minting OpenAvatars.
 */
interface IOpenAvatarGen0TokenMintWrite {
  /////////////////////////////////////////////////////////////////////////////
  // Mint
  /////////////////////////////////////////////////////////////////////////////
  function mint(bytes32 dna) external payable;

  function mintTo(address to, bytes32 dna) external payable;

  function mintBatch(bytes32[] calldata dnas) external payable;

  function mintBatchTo(address to, bytes32[] calldata dnas) external payable;
}

/**
 * @title IOpenAvatarGen0TokenMintAdmin
 * @notice An interface allowing the public mint price to be updated.
 */
interface IOpenAvatarGen0TokenMintAdmin {
  /////////////////////////////////////////////////////////////////////////////
  // Mint Price
  /////////////////////////////////////////////////////////////////////////////
  function setMintPrice(uint val) external;
}

/**
 * @title IOpenAvatarGen0TokenMint
 * @notice The mint interfaces for OpenAvatarGen0Token.
 */
interface IOpenAvatarGen0TokenMint is
  IOpenAvatarGen0TokenMintRead,
  IOpenAvatarGen0TokenMintWrite,
  IOpenAvatarGen0TokenMintAdmin
{

}

/**
 * @title IOpenAvatar
 * @dev The OpenAvatar interface.
 */
interface IOpenAvatarGen0Token is
  IOpenAvatar,
  IOpenAvatarGen0TokenMetadata,
  IOpenAvatarGen0TokenDNA,
  IOpenAvatarGen0TokenMint
{

}

// File: contracts/OpenAvatarGenerationZero.sol



// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.20;

import {IOpenAvatar} from './IOpenAvatar.sol';

/**
 * @title IOpenAvatarGeneration
 * @dev OpenAvatar Generation 0 common definitions.
 */
abstract contract OpenAvatarGenerationZero is IOpenAvatar {
  /// @dev OpenAvatar Generation 0.
  uint public constant OPENAVATAR_GENERATION_ZERO = 0;

  /////////////////////////////////////////////////////////////////////////////
  // IOpenAvatarGeneration
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Returns 0, in reference to Open Avatar Generation 0.
   * @return 0 (zero).
   */
  function openAvatarGeneration() external pure returns (uint) {
    return OPENAVATAR_GENERATION_ZERO;
  }

  /////////////////////////////////////////////////////////////////////////////
  // IOpenAvatarSentinel
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Returns true.
   * @dev This is a sentinel function to indicate that this contract is an
   * OpenAvatar contract.
   * @return True.
   */
  function openAvatar() public pure returns (bool) {
    return true;
  }

  /////////////////////////////////////////////////////////////////////////////
  // ERC-165: Standard Interface Detection
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Checks if the contract supports an interface.
   * @param interfaceId The interface identifier, as specified in ERC-165.
   * @return True if the contract supports interfaceID, false otherwise.
   */
  function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // IOpenAvatar
      interfaceId == 0xfdf02ac8 || // ERC165 interface ID for IOpenAvatarGeneration.
      interfaceId == 0x7b65147c || // ERC165 interface ID for IOpenAvatarSentinel.
      interfaceId == 0x86953eb4; // ERC165 interface ID for IOpenAvatar.
  }
}