// 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/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/PixelBlender.sol



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

/**
 * @title PixelBlender
 * @dev This contract blends pixels together.
 */
contract PixelBlender {
  /**
   * @notice Blend two pixels together.
   * @param foreground The foreground pixel color value.
   * @param background The background pixel color value.
   * @param foregroundAlpha The alpha of the foreground pixel.
   * @return The blended pixel color value.
   */
  function blendPixel(uint8 foreground, uint8 background, uint8 foregroundAlpha) internal pure returns (uint8) {
    return uint8((uint(foreground) * uint(foregroundAlpha) + uint(background) * (255 - uint(foregroundAlpha))) / 255);
  }

  /**
   * @notice Blend two alpha values together.
   * @param foregroundAlpha The foreground alpha value.
   * @param backgroundAlpha The background alpha value.
   * @return The blended alpha value.
   */
  function blendAlpha(uint8 foregroundAlpha, uint8 backgroundAlpha) internal pure returns (uint8) {
    if (foregroundAlpha == 255) return 255;
    if (backgroundAlpha == 255) return 255;
    return uint8(uint(foregroundAlpha) + (uint(backgroundAlpha) * (255 - uint(foregroundAlpha))) / 255);
  }
}

// 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/core/render/OpenAvatarGen0AssetsCanvasLayerCompositor.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 {IOpenAvatarGen0AssetsPaletteStoreRead} from '../interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol';
import {OptionalPatternHeader, PatternHeader} from '../interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol';
import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol';
import {KeepAlive} from '../lib/KeepAlive.sol';
import {PixelBlender} from '../lib/PixelBlender.sol';
import {IOpenAvatarGen0AssetsRead} from '../../IOpenAvatarGen0Assets.sol';

/**
 * @title OpenAvatarGen0AssetsCanvasLayerCompositor
 * @dev This contract composes layer patterns into a single image.
 * @dev A pattern is a 2d byte array with a corresponding color palette.
 * @dev The values of the pattern array are indexes into the color palette.
 */
contract OpenAvatarGen0AssetsCanvasLayerCompositor is
  KeepAlive,
  PixelBlender,
  IOpenAvatarGen0AssetsCanvasLayerCompositor
{
  /// @dev Error reverted when the component is already initialized.
  error AlreadyInitialized();
  /// @dev Error reverted when provided address does not support the required interface.
  error InterfaceUnsupported(address contractAddress, bytes4 interfaceId);
  /// @dev Error reverted when the canvas bytes per pixel is not 4.
  error InvalidCanvasBytesPerPixel();
  /// @dev Error reverted when the canvas size is invalid.
  error InvalidCanvasSize(uint8 canvasId, uint invalidNumBytes);
  /// @dev Error reverted when the mask length is invalid.
  error InvalidMaskLength(uint maskLength, uint canvasSize);

  /// @dev The transparent alpha value.
  bytes1 public constant TRANSPARENT = 0x00;
  /// @dev The opaque alpha value.
  bytes1 public constant OPAQUE = 0xff;

  /// @dev The
  IOpenAvatarGen0AssetsRead public openAvatarGen0AssetsRead;

  /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsCanvasLayerCompositor (for clients).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR = 0x2638c94b;
  /// @dev The ERC-165 interface id for the OpenAvatarGen0AssetsRead (dependency).
  bytes4 private constant INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_READ = 0x67bf31d1;

  constructor(address ownerProxy) {
    // will be deployed by ImmutableCreate2Factory and then transferred to the
    // configured owner.
    // using a proxy allows for using same constructor args and thus same
    // bytecode for all instances of this contract.

    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 virtual returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      interfaceId == INTERFACE_ID_OPENAVATAR_GEN0_ASSETS_CANVAS_LAYER_COMPOSITOR;
  }

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

  /**
   * @dev Initialize the contract.
   * @param openAvatarGen0Assets_ The address of the asset store read interface
   * contract.
   */
  function initialize(address openAvatarGen0Assets_) external onlyOwner {
    setOpenAvatarGen0Assets(openAvatarGen0Assets_);
  }

  /**
   * @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);
  }

  /**
   * @dev Get the OpenAvatarGen0Assets address.
   * @return The OpenAvatarGen0Assets address.
   */
  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();
  }

  /////////////////////////////////////////////////////////////////////////////
  // Layer Composition
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @dev Create a layer composition.
   * @param canvasId The id of the canvas to use
   * @param layerPatternPalette The layer, pattern, and palette to use for each
   * layer
   */
  function createLayerComposition(
    uint8 canvasId,
    LayerPatternPalette[] memory layerPatternPalette
  ) external view override returns (bytes memory) {
    bytes memory out = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId));
    _drawLayerComposition(out, canvasId, layerPatternPalette);
    return out;
  }

  /**
   * @dev Draw a layer composition onto the image
   * @param out The image to draw the layer composition onto
   * @param canvasId The id of the canvas to use
   * @param layerPatternPalette The layer, pattern, and palette to use for each
   * layer
   */
  function drawLayerComposition(
    bytes memory out,
    uint8 canvasId,
    LayerPatternPalette[] memory layerPatternPalette
  ) public view override returns (bytes memory) {
    _drawLayerComposition(out, canvasId, layerPatternPalette);
    return out;
  }

  /**
   * @dev Draw a layer composition onto the image
   * @param out The image to draw the layer composition onto
   * @param canvasId The id of the canvas to use
   * @param layerPatternPalette The layer, pattern, and palette to use for each
   * layer
   */
  function _drawLayerComposition(
    bytes memory out,
    uint8 canvasId,
    LayerPatternPalette[] memory layerPatternPalette
  ) internal view {
    // sanity check so we don't out of bounds for bad input
    if (out.length != openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId)) {
      revert InvalidCanvasSize(canvasId, out.length);
    }

    uint length = layerPatternPalette.length;
    for (uint i = 0; i < length; ) {
      LayerPatternPalette memory lpp = layerPatternPalette[i];
      _drawLayer(out, canvasId, lpp.layer, lpp.pattern, lpp.palette);
      unchecked {
        ++i;
      }
    }
  }

  /**
   * @dev Overlay a layer on top of the image
   * @param image The image to overlay the layer on top of
   * @param canvasId The id of the canvas to use
   * @param layer The index of the layer to overlay
   * @param pattern The index of the pattern to use for the layer
   * @param palette The index of the palette to use for the layer/pattern
   */
  function drawLayer(
    bytes memory image,
    uint8 canvasId,
    uint8 layer,
    uint8 pattern,
    uint8 palette
  ) public view override returns (bytes memory) {
    _drawLayer(image, canvasId, layer, pattern, palette);
    return image;
  }

  /**
   * @dev Overlay a layer on top of the image
   * @param image The image to overlay the layer on top of
   * @param canvasId The id of the canvas to use
   * @param layer The index of the layer to overlay
   * @param pattern The index of the pattern to use for the layer
   * @param palette The index of the palette to use for the layer/pattern
   */
  function _drawLayer(bytes memory image, uint8 canvasId, uint8 layer, uint8 pattern, uint8 palette) internal view {
    OptionalPatternHeader memory optionalHeader = openAvatarGen0AssetsRead.getPatternHeader(canvasId, layer, pattern);
    // gracefully handle missing pattern
    if (optionalHeader.exists) {
      bytes memory patternData = openAvatarGen0AssetsRead.getPatternData(canvasId, layer, pattern);
      // transparent patterns will exist and have length 0
      if (patternData.length > 0) {
        bytes4[] memory paletteData = openAvatarGen0AssetsRead.getPalette(optionalHeader.header.paletteCode, palette);
        // gracefully handle missing palette
        if (paletteData.length > 0) {
          _drawPattern(image, canvasId, optionalHeader.header, patternData, paletteData);
        }
      }
    }
  }

  /**
   * @dev Overlay a layer on top of the image
   * @param image The image to overlay the layer on top of
   * @param mask The mask to apply to the pattern before overlaying it
   * @param canvasId The id of the canvas to use
   * @param layer The index of the layer to overlay
   * @param pattern The index of the pattern to use for the layer
   * @param palette The index of the palette to use for the layer/pattern
   */
  function drawMaskedLayer(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    uint8 layer,
    uint8 pattern,
    uint8 palette
  ) public view override returns (bytes memory) {
    _drawMaskedLayer(image, mask, canvasId, layer, pattern, palette);
    return image;
  }

  /**
   * @dev Overlay a layer on top of the image
   * @param image The image to overlay the layer on top of
   * @param mask The mask to apply to the pattern before overlaying it
   * @param canvasId The id of the canvas to use
   * @param layer The index of the layer to overlay
   * @param pattern The index of the pattern to use for the layer
   * @param palette The index of the palette to use for the layer/pattern
   */
  function _drawMaskedLayer(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    uint8 layer,
    uint8 pattern,
    uint8 palette
  ) internal view {
    OptionalPatternHeader memory optionalHeader = openAvatarGen0AssetsRead.getPatternHeader(canvasId, layer, pattern);
    // gracefully handle missing pattern
    if (optionalHeader.exists) {
      bytes memory patternData = openAvatarGen0AssetsRead.getPatternData(canvasId, layer, pattern);
      // transparent patterns will exist and have length 0
      if (patternData.length > 0) {
        bytes4[] memory paletteData = openAvatarGen0AssetsRead.getPalette(optionalHeader.header.paletteCode, palette);
        // gracefully handle missing palette
        if (paletteData.length > 0) {
          _drawMaskedPattern(image, mask, canvasId, optionalHeader.header, patternData, paletteData);
        }
      }
    }
  }

  /**
   * @dev Overlay a pattern on top of the image
   * @param image The image to overlay the layer on top of
   * @param header The header of the pattern to use for the layer
   * @param pattern The pattern to use for the layer
   * @param palette The palette to use for the layer/pattern
   */
  function drawPattern(
    bytes memory image,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) public view returns (bytes memory) {
    _drawPattern(image, canvasId, header, pattern, palette);
    return image;
  }

  /**
   * @dev Overlay a pattern on top of the image
   * @param image The image to overlay the layer on top of
   * @param header The header of the pattern to use for the layer
   * @param pattern The pattern to use for the layer
   * @param palette The palette to use for the layer/pattern
   */
  function _drawPattern(
    bytes memory image,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) internal view {
    _drawMaskedPattern(
      image,
      // this is wasteful
      new bytes(openAvatarGen0AssetsRead.getCanvasNumPixels(canvasId)),
      canvasId,
      header,
      pattern,
      palette
    );
  }

  /**
   * @dev Overlay a pattern on top of the image
   * @param image The image to overlay the layer on top of
   * @param header The header of the pattern to use for the layer
   * @param pattern The pattern to use for the layer
   * @param palette The palette to use for the layer/pattern
   */
  function drawMaskedPattern(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) public view returns (bytes memory) {
    _drawMaskedPattern(image, mask, canvasId, header, pattern, palette);
    return image;
  }

  /**
   * @dev Overlay a pattern on top of the image, applying a mask
   * @param image The image to overlay the layer on top of
   * @param mask The mask to apply to the pattern before overlaying it
   * @param header The header of the pattern to use for the layer
   * @param pattern The pattern to use for the layer
   * @param palette The palette to use for the layer/pattern
   */
  function _drawMaskedPattern(
    bytes memory image,
    bytes memory mask,
    uint8 canvasId,
    PatternHeader memory header,
    bytes memory pattern,
    bytes4[] memory palette
  ) internal view {
    unchecked {
      uint8 canvasWidth = openAvatarGen0AssetsRead.getCanvasWidth(canvasId);
      // loop through pixels in the image
      for (uint y = 0; y < header.height; ) {
        uint colY = y + header.offsetY;
        for (uint x = 0; x < header.width; ) {
          uint rowX = x + header.offsetX;
          uint imagePixel = colY * canvasWidth + rowX;
          if (imagePixel < mask.length && mask[imagePixel] != 0) {
            // skip transparent pixels
            ++x;
            continue;
          }

          // calculate the offset of the pixel in the pattern
          // get the color index from the pattern
          uint8 colorIndex = uint8(pattern[y * header.width + x]);
          // colorIndex == 0 means transparent
          if (colorIndex > 0) {
            // get the color from the palette
            bytes4 rgba = palette[colorIndex];

            // calculate the offset of the pixel in the image
            uint offset = 4 * imagePixel;
            if (rgba[3] == OPAQUE) {
              image[offset] = rgba[0];
              image[offset + 1] = rgba[1];
              image[offset + 2] = rgba[2];
              image[offset + 3] = OPAQUE;

              // solhint-disable-next-line no-empty-blocks
            } else if (rgba[3] == TRANSPARENT) {
              // do nothing
            } else {
              // blend the pixel with the existing pixel
              // again there are two subcases based on whether the existing
              // pixel is transparent or not
              if (image[offset + 3] == TRANSPARENT) {
                // CASE 1: existing pixel is transparent
                // so just copy the pixel exactly
                // including the semi-transparent alpha value
                image[offset] = rgba[0];
                image[offset + 1] = rgba[1];
                image[offset + 2] = rgba[2];
                image[offset + 3] = rgba[3];
              } else {
                // CASE 2: existing pixel is not transparent
                // we need to blend
                image[offset] = bytes1(blendPixel(uint8(rgba[0]), uint8(image[offset]), uint8(rgba[3])));
                image[offset + 1] = bytes1(blendPixel(uint8(rgba[1]), uint8(image[offset + 1]), uint8(rgba[3])));
                image[offset + 2] = bytes1(blendPixel(uint8(rgba[2]), uint8(image[offset + 2]), uint8(rgba[3])));
                image[offset + 3] = bytes1(blendAlpha(uint8(rgba[3]), uint8(image[offset + 3])));
              }
            }
          }
          ++x;
        }
        ++y;
      }
    }
  }
}

// File: contracts/core/render/OpenAvatarGen0CanvasRenderer.sol



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

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IOpenAvatarGen0RendererDecorator} from '../../IOpenAvatarGen0Renderer.sol';
import {OpenAvatarGen0AssetsCanvasIdStore} from '../assets/OpenAvatarGen0AssetsCanvasIdStore.sol';
import {IOpenAvatarGen0AssetsCanvasLayerCompositor, LayerPatternPalette} from '../interfaces/render/IOpenAvatarGen0AssetsCanvasLayerCompositor.sol';
import {IOpenAvatarGen0CanvasRenderer} from '../interfaces/render/IOpenAvatarGen0CanvasRenderer.sol';
import {ImageEncoder} from '../lib/ImageEncoder.sol';
import {DNA} from '../lib/DNA.sol';
import {OpenAvatarGen0AssetsCanvasLayerCompositor} from './OpenAvatarGen0AssetsCanvasLayerCompositor.sol';

/**
 * @title OpenAvatarGen0CanvasRenderer
 * @dev This contract renders a DNA as an image in a variety of formats.
 */
contract OpenAvatarGen0CanvasRenderer is
  IOpenAvatarGen0CanvasRenderer,
  IOpenAvatarGen0RendererDecorator,
  ImageEncoder,
  OpenAvatarGen0AssetsCanvasIdStore,
  OpenAvatarGen0AssetsCanvasLayerCompositor
{
  using DNA for bytes32;

  /// @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 base PNG to an SVG by this factor
  uint public constant SVG_SCALE = 10;

  constructor(
    address ownerProxy,
    uint8 canvasId_
  )
    OpenAvatarGen0AssetsCanvasIdStore(canvasId_)
    OpenAvatarGen0AssetsCanvasLayerCompositor(ownerProxy)
  // solhint-disable-next-line no-empty-blocks
  {

  }

  /////////////////////////////////////////////////////////////////////////////
  // 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 override(OpenAvatarGen0AssetsCanvasLayerCompositor) returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // IOpenAvatarGen0AssetsCanvasLayerCompositor
      interfaceId == 0xb93e4881 || // ERC165 interface ID for IOpenAvatarGen0Renderer.
      interfaceId == 0x00a663b1 || // ERC165 interface ID for IOpenAvatarGen0RendererDecorator.
      interfaceId == 0x13247985 || // ERC165 interface ID for IOpenAvatarGen0CanvasRenderer.
      interfaceId == 0x2638c94b; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasLayerCompositor.
  }

  /////////////////////////////////////////////////////////////////////////////
  // Drawing
  /////////////////////////////////////////////////////////////////////////////

  function drawOpenAvatar(uint8 canvasId, bytes32 dna) external view returns (bytes memory) {
    return _drawOpenAvatar(canvasId, dna);
  }

  function _drawOpenAvatar(uint8 canvasId, bytes32 dna) internal view returns (bytes memory) {
    bytes memory image = new bytes(openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId));
    return _drawOpenAvatarOverlay(image, canvasId, dna);
  }

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

  /**
   * @notice Compose the given DNA into a single image, on top of the given
   * base image.
   * @param image The base image.
   * @param canvasId The canvas ID. Between [0, 11]. Behavior for 12 or
   * higher undefined.
   * @param dna The DNA to compose.
   * @return The image.
   */
  function _drawOpenAvatarOverlay(
    bytes memory image,
    uint8 canvasId,
    bytes32 dna
  ) internal view returns (bytes memory) {
    uint expectedNumBytes = openAvatarGen0AssetsRead.getCanvasNumBytes(canvasId);
    // sanity check the provided image array
    // must be at least as long as the expected image length
    if (image.length < expectedNumBytes) {
      revert InvalidCanvasSize(canvasId, image.length);
    }

    /**
     * A 32 byte hex string
     * @dev The DNA string is a 32 byte hex string.
     * @dev The DNA string is immutable.
     * @dev The bytes represent the following:
     * 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
     */
    LayerPatternPalette[] memory layers = new LayerPatternPalette[](15);
    layers[0] = LayerPatternPalette(LAYER_INDEX_BODY, dna.bodyPattern(), dna.bodyPalette());
    layers[1] = LayerPatternPalette(LAYER_INDEX_TATTOOS, dna.tattoosPattern(), dna.tattoosPalette());
    layers[2] = LayerPatternPalette(LAYER_INDEX_MAKEUP, dna.makeupPattern(), dna.makeupPalette());
    layers[3] = LayerPatternPalette(LAYER_INDEX_LEFT_EYE, dna.leftEyePattern(), dna.leftEyePalette());
    layers[4] = LayerPatternPalette(LAYER_INDEX_RIGHT_EYE, dna.rightEyePattern(), dna.rightEyePalette());
    layers[5] = LayerPatternPalette(LAYER_INDEX_BOTTOMWEAR, dna.bottomwearPattern(), dna.bottomwearPalette());
    layers[6] = LayerPatternPalette(LAYER_INDEX_FOOTWEAR, dna.footwearPattern(), dna.footwearPalette());
    layers[7] = LayerPatternPalette(LAYER_INDEX_TOPWEAR, dna.topwearPattern(), dna.topwearPalette());
    layers[8] = LayerPatternPalette(LAYER_INDEX_OUTERWEAR, dna.outerwearPattern(), dna.outerwearPalette());
    layers[9] = LayerPatternPalette(LAYER_INDEX_HANDWEAR, dna.handwearPattern(), dna.handwearPalette());
    layers[10] = LayerPatternPalette(LAYER_INDEX_JEWELRY, dna.jewelryPattern(), dna.jewelryPalette());
    layers[11] = LayerPatternPalette(LAYER_INDEX_FACIAL_HAIR, dna.facialHairPattern(), dna.facialHairPalette());
    layers[12] = LayerPatternPalette(LAYER_INDEX_FACEWEAR, dna.facewearPattern(), dna.facewearPalette());
    layers[13] = LayerPatternPalette(LAYER_INDEX_EYEWEAR, dna.eyewearPattern(), dna.eyewearPalette());
    layers[14] = LayerPatternPalette(LAYER_INDEX_HAIR, dna.hairPattern(), dna.hairPalette());

    return drawLayerComposition(image, canvasId, layers);
  }

  /**
   * @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) public view override returns (string memory) {
    return string(abi.encodePacked('data:image/svg+xml;base64,', renderBase64SVG(dna)));
  }

  /**
   * @notice Render the dna as a byte array.
   * @param dna The DNA to render.
   * @return The byte array of the image.
   */
  function renderHex(bytes32 dna) public view override returns (bytes memory) {
    return _drawOpenAvatar(canvasId, dna);
  }

  /**
   * @notice Render the given DNA as a PNG.
   * @param dna The DNA to render.
   * @return The PNG image.
   */
  function renderPNG(bytes32 dna) public view override returns (bytes memory) {
    return
      encodePNG(
        _drawOpenAvatar(canvasId, dna),
        openAvatarGen0AssetsRead.getCanvasWidth(canvasId),
        openAvatarGen0AssetsRead.getCanvasHeight(canvasId),
        openAvatarGen0AssetsRead.hasAlphaChannel()
      );
  }

  /**
   * @notice Render the given DNA as a base64-encoded PNG.
   * @param dna The DNA to render.
   * @return The PNG image.
   */
  function renderBase64PNG(bytes32 dna) public view override returns (bytes memory) {
    return
      encodeBase64PNG(
        _drawOpenAvatar(canvasId, dna),
        openAvatarGen0AssetsRead.getCanvasWidth(canvasId),
        openAvatarGen0AssetsRead.getCanvasHeight(canvasId),
        openAvatarGen0AssetsRead.hasAlphaChannel()
      );
  }

  function renderSVG(bytes32 dna) public view override returns (bytes memory) {
    uint width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId);
    uint height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId);
    return
      encodeSVG(
        _drawOpenAvatar(canvasId, dna),
        width,
        height,
        openAvatarGen0AssetsRead.hasAlphaChannel(),
        SVG_SCALE * width,
        SVG_SCALE * height
      );
  }

  function renderBase64SVG(bytes32 dna) public view override returns (bytes memory) {
    uint width = openAvatarGen0AssetsRead.getCanvasWidth(canvasId);
    uint height = openAvatarGen0AssetsRead.getCanvasHeight(canvasId);
    return
      encodeBase64SVG(
        _drawOpenAvatar(canvasId, dna),
        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/OpenAvatarGen0Renderer.sol



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

import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol';
import {OpenAvatarGen0CanvasRenderer} from './core/render/OpenAvatarGen0CanvasRenderer.sol';
import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol';

/**
 * @title OpenAvatarGen0Renderer
 * @author Cory Gabrielsen (cory.eth)
 *
 * @dev This contract is responsible for rendering an Avatar based on its DNA.
 *      Avatars are rendered as 32x32 RGBA images with transparency around
 *      the Avatar's body. Avatars are rendered using hand-drawn, original art
 *      created by the contract author (one pixel at a time with a ball mouse).
 *
 * Immutable:
 * Once initialized, the contract is immutable.
 * Art stored in OpenAvatarGen0Assets is immutable (append-only storage).
 * So, rendering is deterministic.
 *
 * Gas:
 * Due to PNG encoding, rendering may cost 10,000,000+ gas.
 * With further base64 encoding (i.e. token URI), combined rendering and
 * encoding may cost 15,000,000+ gas.
 *
 * Terminology:
 * - Canvas Id (uint8): Selects from an array of canvases
 * - Layer Index (uint8): Selects from an array of layers for a given canvas
 * - Pattern Index (uint8): Selects from an array of patterns for a given layer
 * - Palette Index (uint8): Selects from an array of palettes for a given
 *                          pattern
 * - Color Index (uint8): Selects from an array of colors for a given palette
 * - Color (bytes4): RGBA
 *
 * Structure:
 * - Every layer contains an array of selectable patterns.
 * - Each pattern references a specific palette code.
 * - Each palette code corresponds to an array of palettes.
 * - Each pattern (uncompressed) is a 32x32=1024 array of color indices.
 *      - compressed as [height, width, offsetX, offsetY, bytes]
 *  -Each palette is an array of RGBA (bytes4[]) which always starts with
 *   0x00000000 (transparent).
 * - So, a color index of 0x00 is transparent.
 * - In essence, each byte in a pattern is a color index which defines an
 *   RGBA color in the corresponding palette array to draw for that pixel.
 *
 * Rendering:
 * Layers are drawn one over another, from lowest to highest layer index,
 * with alpha-blending. The 32-byte Avatar DNA is interpretted to as
 * defining the pattern and palette for each layer. Pixels are drawn based
 * on color derived from DNA-encoded pattern and palette index. If a DNA
 * defines an invalid index, the layer is drawn as transparent.
 *
 * DNA:
 * It should be noted that while OpenAvatar DNA is interpretted here as
 * implicitly defining asset references for drawing the Avatar, the concept
 * of DNA is designed as a standalone building block. Application-specific
 * re-interpretations of OpenAvatar DNA are entirely possible and encouraged.
 *
 * Encoding:
 * Avatars are rendered as base64-encoded PNGs, embedded within an SVG. The
 * SVG embeds the PNG as a <foreignObject>, chosen due to Safari's lack of
 * support (bug?) for "image-rendering: pixelated" of <image> elements within
 * SVGs.
 */
contract OpenAvatarGen0Renderer is OpenAvatarGenerationZero, OpenAvatarGen0CanvasRenderer, ENSReverseClaimer {
  /// @dev The canvas ID for the default, front-facing Avatar pose.
  uint8 public constant CANVAS_ID = 0;

  // solhint-disable-next-line no-empty-blocks
  constructor(address ownerProxy) OpenAvatarGen0CanvasRenderer(ownerProxy, CANVAS_ID) {}

  /////////////////////////////////////////////////////////////////////////////
  // 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, OpenAvatarGen0CanvasRenderer) 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.
      // IOpenAvatarGen0AssetsCanvasLayerCompositor
      interfaceId == 0xb93e4881 || // ERC165 interface ID for IOpenAvatarGen0Renderer.
      interfaceId == 0x00a663b1 || // ERC165 interface ID for IOpenAvatarGen0RendererDecorator.
      interfaceId == 0x13247985 || // ERC165 interface ID for IOpenAvatarGen0CanvasRenderer.
      interfaceId == 0x2638c94b; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasLayerCompositor.
  }
}

// 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.
  }
}