// 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: contracts/core/assets/OpenAvatarGen0AssetsCanvasStore.sol



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

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {CanvasHeader, IOpenAvatarGen0AssetsCanvasStore} from '../interfaces/assets/IOpenAvatarGen0AssetsCanvasStore.sol';
import {FuseGuard} from '../lib/FuseGuard.sol';
import {KeepAlive} from '../lib/KeepAlive.sol';

/**
 * @title OpenAvatarGen0AssetsCanvasStore
 * @dev This contract stores canvas headers
 * @dev This contract is used for off-chain rendering
 */
contract OpenAvatarGen0AssetsCanvasStore is IOpenAvatarGen0AssetsCanvasStore, FuseGuard, KeepAlive {
  /// @dev Event emitted when a canvas is added.
  event CanvasAdd(uint8 id);

  /// @dev Event emitted when the fuse is burned to disable adding a new canvas.
  event FuseBurnedCanAddCanvas();

  /// @dev Rvert error when canvas already exists.
  error CanvasAlreadyExists(uint8 id);
  /// @dev Revert error when canvas does not exist.
  error CanvasDoesNotExist(uint8 id);
  /// @dev Revert error when canvas size is invalid.
  error InvalidCanvasSize(uint8 width, uint8 height);

  /// @dev The canvas bytes per pixel.
  /// @dev the only reason this is separate and named with CANVAS_ prefix is because
  // there is another one in PaletteStore and they conflict
  uint8 public constant CANVAS_BYTES_PER_PIXEL = 4; // RGBA

  /////////////////////////////////////////////////////////////////////////////
  // Internal Data Structures
  /////////////////////////////////////////////////////////////////////////////

  /// @dev The canvas ids.
  uint8[] public canvasIds;
  /// @dev The canvas headers.
  mapping(uint => CanvasHeader) public canvasHeaders;
  /// @dev Whether a canvas exists.
  mapping(uint => bool) public canvasExists;

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

  /// @dev Flag to indicate if the fuse has been burned for adding a new canvas.
  bool public fuseBurnedCanAddCanvas = false;

  constructor(address ownerProxy) {
    // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner
    // using a proxy allows for using same bytecode in test and prod

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

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

  /**
   * @notice Checks if the contract supports an interface.
   * @param interfaceId The interface identifier, as specified in ERC-165.
   * @return True if the contract supports interfaceID, false otherwise.
   */
  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // canvas
      interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead.
      interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite.
      interfaceId == 0xdc0b08c2; // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore.
  }

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

  /**
   * @notice Burn the fuse to permanently disable adding a new canvas.
   * @dev Only callable by owner.
   * @dev no-op if fuse is already burned
   */
  function burnFuseCanAddCanvas() external onlyOwner {
    if (fuseBurnedCanAddCanvas) return;
    fuseBurnedCanAddCanvas = true;
    emit FuseBurnedCanAddCanvas();
  }

  /**
   * @notice Returns whether the fuse is burned to permanently disable adding a new canvas.
   * @return Whether the fuse is burned to permanently disable adding a new canvas.
   */
  function isFuseBurnedCanAddCanvas() external view returns (bool) {
    return fuseBurnedCanAddCanvas;
  }

  /////////////////////////////////////////////////////////////////////////////
  // Canvas
  /////////////////////////////////////////////////////////////////////////////

  /**
   * @notice Add a canvas to the store.
   * @param header The canvas header.
   */
  function addCanvas(CanvasHeader calldata header) external override onlyOwner {
    if (fuseBurnedCanAddCanvas) revert OperationBlockedByBurnedFuse();
    if (canvasExists[header.id]) revert CanvasAlreadyExists(header.id);
    if (header.width == 0 || header.height == 0) revert InvalidCanvasSize(header.width, header.height);
    canvasHeaders[header.id] = header;
    canvasIds.push(header.id);
    canvasExists[header.id] = true;
    emit CanvasAdd(header.id);
  }

  /**
   * @notice Return the number of canvases stored in the contract.
   * @return The number of canvases stored in the contract.
   */
  function hasCanvas(uint8 id) external view override returns (bool) {
    return canvasExists[id];
  }

  /**
   * @notice Return the canvas header.
   * @param id The canvas to query.
   * @return The canvas header.
   * @dev Returns all zeroes if the canvas does not exist.
   */
  function getCanvasHeader(uint8 id) external view override returns (CanvasHeader memory) {
    return canvasHeaders[id];
  }

  /**
   * @notice Return the number of canvases stored in the contract.
   * @return The number of canvases stored in the contract.
   */
  function getNumCanvasIds() external view override returns (uint) {
    return canvasIds.length;
  }

  /**
   * @notice Return the number of canvases stored in the contract.
   * @return The array of canvas ids stored in the contract.
   */
  function getCanvasIds() external view override returns (uint8[] memory) {
    return canvasIds;
  }

  /**
   * @notice Return the height of the canvas.
   * @param id The canvas to query.
   * @return The height of the canvas.
   */
  function getCanvasHeight(uint8 id) public view override returns (uint8) {
    return canvasHeaders[id].height;
  }

  /**
   * @notice Return the width of the canvas.
   * @param id The canvas to query.
   * @return The width of the canvas.
   */
  function getCanvasWidth(uint8 id) public view override returns (uint8) {
    return canvasHeaders[id].width;
  }

  /**
   * @notice Return the number of bytes in the canvas.
   * @param id The canvas to query.
   * @return The number of bytes in the canvas.
   */
  function getCanvasNumBytes(uint8 id) public view override returns (uint) {
    return CANVAS_BYTES_PER_PIXEL * uint(canvasHeaders[id].width) * uint(canvasHeaders[id].height);
  }

  /**
   * @notice Return the number of pixels in the canvas.
   * @param id The canvas to query.
   * @return The number of pixels in the canvas.
   */
  function getCanvasNumPixels(uint8 id) public view override returns (uint) {
    return uint(canvasHeaders[id].width) * uint(canvasHeaders[id].height);
  }
}

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



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

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IOpenAvatarGen0AssetsPaletteStore, UploadPaletteBatchInput, UploadPaletteInput} from '../interfaces/assets/IOpenAvatarGen0AssetsPaletteStore.sol';
import {FuseGuard} from '../lib/FuseGuard.sol';
import {KeepAlive} from '../lib/KeepAlive.sol';

/**
 * @title OpenAvatarGen0AssetsPaletteStore
 * @dev This contract stores color palettes by (code, index).
 * @dev All palettes of a given code should be the same length.
 */
contract OpenAvatarGen0AssetsPaletteStore is IOpenAvatarGen0AssetsPaletteStore, FuseGuard, KeepAlive {
  /// @dev Error for invalid palette code.
  error InvalidPaletteCode(uint8 code);
  /// @dev Error for empty palette array.
  error EmptyPaletteArray();
  /// @dev Error for invalid palette index.
  error PaletteIndexOutOfBounds(uint8 code, uint8 index);
  /// @dev Error for palette already exists.
  error PaletteAlreadyExists(uint8 code, uint8 index);
  /// @dev Error for invalid palette length.
  error InvalidPaletteLength(uint length);
  /// @dev Error for invalid transparent color code.
  error InvalidTransparentColorCode(bytes4 code);
  /// @dev Event emitted when the fuse is burned to disable uploading a new palette.
  event FuseBurnedCanUploadPalette();
  /// @dev Event emitted when a palette is uploaded.
  event PaletteUpload(uint8 code, uint8 index);

  /// @dev RGBA color depth.
  uint8 public constant DEPTH = 4;
  /// @dev Whether the canvas has an alpha channel.
  bool public constant HAS_ALPHA_CHANNEL = true;

  /////////////////////////////////////////////////////////////////////////////
  // Internal Data Structures
  /////////////////////////////////////////////////////////////////////////////

  /// @dev The color palettes.
  /// @dev 5D array
  ///        bytes4 -----------> (red, green, blue, alpha) color
  ///        bytes4[] ---------> A color palette
  ///        bytes4[][] -------> A set of color palettes (for a palette code)
  ///        bytes4[][][] -----> All the color palette sets for all the palette codes
  bytes4[][][] public palettes;

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

  /// @dev Flag to indicate if the fuse has been burned for uploading a palette.
  bool public fuseBurnedCanUploadPalette = false;

  constructor(address ownerProxy) {
    // will be deployed by ImmutableCreate2Factory and then transferred to the configured owner
    // using a proxy allows for using same bytecode in test and prod

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

  /**
   * @notice Return whether the canvas has an alpha channel.
   * @return Whether the canvas has an alpha channel.
   */
  function hasAlphaChannel() external pure override returns (bool) {
    return HAS_ALPHA_CHANNEL;
  }

  /**
   * @notice Return the number of bytes per pixel.
   * @return The number of bytes per pixel.
   */
  function getBytesPerPixel() public pure override returns (uint8) {
    return DEPTH;
  }

  /////////////////////////////////////////////////////////////////////////////
  // 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 view virtual returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // palette
      interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead.
      interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite.
      interfaceId == 0xc9e0e6b6; // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore.
  }

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

  /**
   * @notice Burn the fuse to permanently disable uploading a new palette.
   * @dev Only callable by owner.
   * @dev no-op if fuse is already burned
   */
  function burnFuseCanUploadPalette() external onlyOwner {
    if (fuseBurnedCanUploadPalette) return;
    fuseBurnedCanUploadPalette = true;
    emit FuseBurnedCanUploadPalette();
  }

  /**
   * @notice Returns whether the fuse is burned to permanently disable uploading a new palette.
   * @return Whether the fuse is burned to permanently disable uploading a new palette.
   */
  function isFuseBurnedCanUploadPalette() external view returns (bool) {
    return fuseBurnedCanUploadPalette;
  }

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

  /**
   * @notice Get the number of palette codes.
   */
  function getNumPaletteCodes() external view override returns (uint) {
    return palettes.length;
  }

  /**
   * @notice Get the number of palettes for the given layer and index.
   * @param code The palette code.
   */
  function getNumPalettes(uint8 code) external view override returns (uint) {
    return (code < palettes.length) ? palettes[code].length : 0;
  }

  /**
   * @notice Get the palette for the given palette code and index.
   * @param code The code for which to get the palette.
   * @param index The index of the palette to get.
   * @return The palette for the given layer, pattern, index.
   * @dev Returns an empty array if the palette does not exist.
   */
  function getPalette(uint8 code, uint8 index) external view override returns (bytes4[] memory) {
    if (code < palettes.length) {
      bytes4[][] storage codePalettes = palettes[code];
      if (index < codePalettes.length) {
        return codePalettes[index];
      }
    }
    // return empty array if not found
    return new bytes4[](0);
  }

  /**
   * @notice Store the given palette in the contract
   * @param input The palette to store.
   */
  function uploadPalette(UploadPaletteInput calldata input) external override onlyOwner {
    // check fuse
    if (fuseBurnedCanUploadPalette) revert OperationBlockedByBurnedFuse();

    // should always include transparent (will revert later if index 0 is not transparent)
    if (input.palette.length == 0) revert InvalidPaletteLength(input.palette.length);

    // Base Case: palettes[code] does not exist yet
    if (input.code == palettes.length) {
      // create new array of palettes
      palettes.push(new bytes4[][](0));
    }
    // Append Case: palettes[code] exists
    if (input.code < palettes.length) {
      bytes4[][] storage codePalettes = palettes[input.code];

      // 3 cases:
      // 1. index == length, append (happy path)
      // 2. index < length, revert
      // 3. index > length, revert
      uint ct = codePalettes.length;
      if (input.index == ct) {
        // append
        // check that the first color is transparent
        if (input.palette[0] != 0x00000000) {
          revert InvalidTransparentColorCode(input.palette[0]);
        }
        // TODO: verify length of each palette matches the same as the first
        // palette uploaded for that code
        codePalettes.push(input.palette);
        emit PaletteUpload(input.code, input.index);
      } else if (input.index < ct) {
        // already exists
        revert PaletteAlreadyExists(input.code, input.index);
      } else {
        // index > ct out of bounds
        revert PaletteIndexOutOfBounds(input.code, input.index);
      }
    } else {
      // out of bounds
      revert InvalidPaletteCode(input.code);
    }
  }

  /**
   * @notice Store the given palettes in the contract
   * @param input The paletted to store.
   */
  function uploadPaletteBatch(UploadPaletteBatchInput calldata input) external override onlyOwner {
    _uploadPaletteBatch(input);
  }

  /**
   * @notice Store the given palettes in the contract
   * @param input The paletted to store.
   */
  function _uploadPaletteBatch(UploadPaletteBatchInput calldata input) internal {
    // check fuse
    if (fuseBurnedCanUploadPalette) revert OperationBlockedByBurnedFuse();

    // should always include transparent (will revert later if index 0 is not transparent)
    if (input.palettes.length == 0) revert EmptyPaletteArray();

    // Base Case: palettes[code] does not exist yet
    if (input.code == palettes.length) {
      // create new array of palettes
      palettes.push(new bytes4[][](0));
    }
    // Append Case: palettes[code] exists
    if (input.code < palettes.length) {
      bytes4[][] storage codePalettes = palettes[input.code];

      // 3 cases:
      // 1. index == length, append (happy path)
      // 2. index < length, revert
      // 3. index > length, revert
      uint ct = codePalettes.length;
      if (input.fromIndex == ct) {
        // append all palettes
        uint len = input.palettes.length;
        for (uint i = 0; i < len; ) {
          bytes4[] calldata palette = input.palettes[i];
          // check that the first color is transparent
          if (palette[0] != 0x00000000) {
            revert InvalidTransparentColorCode(palette[0]);
          }
          // TODO: verify length of each palette matches?
          codePalettes.push(palette);
          emit PaletteUpload(input.code, uint8(i));
          unchecked {
            ++i;
          }
        }
      } else if (input.fromIndex < ct) {
        // already exists
        revert PaletteAlreadyExists(input.code, input.fromIndex);
      } else {
        // index > ct out of bounds
        revert PaletteIndexOutOfBounds(input.code, input.fromIndex);
      }
    } else {
      // out of bounds
      revert InvalidPaletteCode(input.code);
    }
  }

  /**
   * @notice Batch upload multiple palette batches.
   * @param inputs The palette batches to upload.
   */
  function uploadPaletteBatches(UploadPaletteBatchInput[] calldata inputs) external override onlyOwner {
    uint len = inputs.length;
    for (uint i = 0; i < len; ) {
      _uploadPaletteBatch(inputs[i]);
      unchecked {
        ++i;
      }
    }
  }
}

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



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

import {IOpenAvatarGen0AssetsPatternStore, OptionalPatternHeader, PatternHeader, PatternBlob, UploadPatternInput} from '../interfaces/assets/IOpenAvatarGen0AssetsPatternStore.sol';
import {OpenAvatarGen0AssetsCanvasStore} from './OpenAvatarGen0AssetsCanvasStore.sol';
import {OpenAvatarGen0AssetsPaletteStore} from './OpenAvatarGen0AssetsPaletteStore.sol';

/**
 * @title OpenAvatarGen0AssetsPatternStore
 * @dev This contract stores assets via:
 * - canvas
 * - layer
 * - pattern
 * and for palettes, by:
 * - palette code
 * - palette
 */
contract OpenAvatarGen0AssetsPatternStore is
  IOpenAvatarGen0AssetsPatternStore,
  OpenAvatarGen0AssetsCanvasStore,
  OpenAvatarGen0AssetsPaletteStore
{
  /// @dev Event emitted when the fuse is burned to disable adding a new layer.
  event FuseBurnedCanAddLayer();
  /// @dev Event emitted when the fuse is burned to disable uploading a pattern.
  event FuseBurnedCanUploadPattern();
  /// @dev Event emitted when a layer is added to a canvas.
  event LayerAdd(uint8 canvasId, uint8 layer);
  /// @dev Event emitted when a pattern is uploaded.
  event PatternUpload(uint8 canvasId, uint8 layer, uint8 pattern);

  /// @dev Revert error when layer already exists.
  error LayerAlreadyExists(uint8 canvasId, uint8 layer);
  /// @dev Revert error when layer index is out of bounds.
  error LayerIndexOutOfBounds(uint8 canvasId, uint8 layer);
  /// @dev Revert error when pattern has invalid length.
  error InvalidPatternLength(uint width, uint height, uint length);
  /// @dev Revert error when pattern already exists.
  error PatternAlreadyExists(uint8 canvasId, uint8 layer, uint8 index);
  /// @dev Revert error when pattern index is out of bounds.
  error PatternIndexOutOfBounds(uint8 canvasId, uint8 layer, uint8 index);
  /// @dev Revert error when palette code does not exist.
  error UnknownPaletteCode(uint8 code);

  /////////////////////////////////////////////////////////////////////////////
  // Internal Data Structures
  /////////////////////////////////////////////////////////////////////////////

  /// @dev Each pattern has a corresponding header describing the pattern data
  /// @dev Every pattern is an arbitrary blob of bytes
  mapping(uint => PatternBlob[][]) public patterns;

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

  /// @dev Flag to indicate if the fuse has been burned for adding a new layer.
  bool public fuseBurnedCanAddLayer = false;
  /// @dev Flag to indicate if the fuse has been burned for uploading a pattern.
  bool public fuseBurnedCanUploadPattern = false;

  // solhint-disable-next-line no-empty-blocks
  constructor(
    address ownerProxy
  ) OpenAvatarGen0AssetsCanvasStore(ownerProxy) OpenAvatarGen0AssetsPaletteStore(ownerProxy) {}

  /////////////////////////////////////////////////////////////////////////////
  // 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 view virtual override(OpenAvatarGen0AssetsCanvasStore, OpenAvatarGen0AssetsPaletteStore) returns (bool) {
    return
      interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
      // canvas
      interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead.
      interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite.
      interfaceId == 0xdc0b08c2 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore.
      // palette
      interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead.
      interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite.
      interfaceId == 0xc9e0e6b6 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore.
      // pattern
      interfaceId == 0x32c8b38e || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreRead.
      interfaceId == 0xcd89a9e1 || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreWrite.
      interfaceId == 0xff411a6f; // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStore.
  }

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

  /**
   * @notice Burn the fuse to permanently disable adding a new layer.
   * @dev Only callable by owner.
   * @dev no-op if fuse is already burned
   */
  function burnFuseCanAddLayer() external onlyOwner {
    if (fuseBurnedCanAddLayer) return;
    fuseBurnedCanAddLayer = true;
    emit FuseBurnedCanAddLayer();
  }

  /**
   * @notice Returns whether the fuse is burned to permanently disable adding a new layer.
   * @return Whether the fuse is burned to permanently disable adding a new layer.
   */
  function isFuseBurnedCanAddLayer() external view returns (bool) {
    return fuseBurnedCanAddLayer;
  }

  /**
   * @notice Burn the fuse to permanently disable uploading a new pattern.
   * @dev Only callable by owner.
   * @dev no-op if fuse is already burned
   */
  function burnFuseCanUploadPattern() external onlyOwner {
    if (fuseBurnedCanUploadPattern) return;
    fuseBurnedCanUploadPattern = true;
    emit FuseBurnedCanUploadPattern();
  }

  /**
   * @notice Returns whether the fuse is burned to permanently disable uploading a new pattern.
   * @return Whether the fuse is burned to permanently disable uploading a new pattern.
   */
  function isFuseBurnedCanUploadPattern() external view returns (bool) {
    return fuseBurnedCanUploadPattern;
  }

  /**************************************************************************
   * Helpers
   *************************************************************************/

  /**
   * @notice Pack four uint8 values into a single uint32.
   * @param a The first uint8 value.
   * @param b The second uint8 value.
   * @param c The third uint8 value.
   * @param d The fourth uint8 value.
   * @param e The fifth uint8 value.
   * @return The packed uint40 value.
   */
  function packUint40(uint8 a, uint8 b, uint8 c, uint8 d, uint8 e) public pure returns (uint40) {
    return (uint40(a) << 32) | (uint40(b) << 24) | (uint40(c) << 16) | (uint40(d) << 8) | uint40(e);
  }

  /**************************************************************************
   * Layers
   *************************************************************************/

  /**
   * @notice Get the number of layers stored in the contract.
   * @param canvasId The canvas for which to get the pattern data.
   * @return The number of layers stored in the contract.
   */
  function getNumLayers(uint8 canvasId) public view override returns (uint) {
    return patterns[canvasId].length;
  }

  /**
   * @notice Add a new layer to the contract. The layer must not already exist. Layer indices may be skipped.
   * @param canvasId The canvas for which to get the pattern data.
   * @param layer The layer to add.
   * @dev This function is only callable by the contract owner.
   */
  function addLayer(uint8 canvasId, uint8 layer) public override onlyOwner {
    if (fuseBurnedCanAddLayer) revert OperationBlockedByBurnedFuse();
    if (!canvasExists[canvasId]) revert CanvasDoesNotExist(canvasId);
    unchecked {
      // layers can be skipped when adding to reserve for later
      // so we need to fill in the skipped layers with empty arrays
      PatternBlob[][] storage canvasPatterns = patterns[canvasId];
      while (canvasPatterns.length < layer + 1) {
        canvasPatterns.push(new PatternBlob[](0));
      }

      // now we have backfilled the layers, so procede with the main data
      PatternBlob[] storage patternsArrayForNewLayer = canvasPatterns[layer];
      if (patternsArrayForNewLayer.length == 0) {
        // transparent means no height/width/offsets
        // transparent layer has all zeros
        // transparent pattern is empty bytes
        patternsArrayForNewLayer.push(PatternBlob(PatternHeader(0, 0, 0, 0, 0), new bytes(0)));
        emit LayerAdd(canvasId, layer);
      } else {
        revert LayerAlreadyExists(canvasId, layer);
      }
    }
  }

  /**
   * @notice Add multiple layers to the contract. The layers must not already exist. Layer indices may be skipped.
   * @param canvasId The canvas for which to get the pattern data.
   * @param layers_ The layers to add.
   * @dev This function is only callable by the contract owner.
   */
  function addLayers(uint8 canvasId, uint8[] calldata layers_) public override onlyOwner {
    if (!canvasExists[canvasId]) revert CanvasDoesNotExist(canvasId);
    uint len = layers_.length;
    for (uint i = 0; i < len; ) {
      addLayer(canvasId, layers_[i]);
      unchecked {
        ++i;
      }
    }
  }

  /**************************************************************************
   * Patterns
   *************************************************************************/

  /**
   * @notice Get the number of patterns for the given layer and index.
   * @param canvasId The canvas for which to get the pattern data.
   * @param layer The layer for which to get the number of patterns.
   * @return The number of patterns for the given layer and index.
   */
  function getNumPatterns(uint8 canvasId, uint8 layer) public view override returns (uint) {
    PatternBlob[][] storage canvasPatterns = patterns[canvasId];
    if (layer < canvasPatterns.length) {
      return canvasPatterns[layer].length;
    } else {
      // we have a choice to return 0 or revert
      // since any valid layer always has a transparent pattern, we can return 0
      // and callers can use that to determine if the layer exists without handling
      // a revert
      return 0;
    }
  }

  /**
   * @notice Get the pattern header for the given layer and index.
   * @param canvasId The canvas for which to get the pattern data.
   * @param layer The layer for which to get the pattern header.
   * @param index The index of the pattern header to get.
   * @return The pattern header for the given layer and index.
   */
  function getPatternHeader(
    uint8 canvasId,
    uint8 layer,
    uint8 index
  ) external view override returns (OptionalPatternHeader memory) {
    PatternBlob[][] storage canvasPatterns = patterns[canvasId];
    if (layer < canvasPatterns.length) {
      PatternBlob[] storage layerPatterns = canvasPatterns[layer];
      if (index < layerPatterns.length) {
        return OptionalPatternHeader(true, layerPatterns[index].header);
      }
    }
    return OptionalPatternHeader(false, PatternHeader(0, 0, 0, 0, 0));
  }

  /**
   * @notice Get the pattern for the given layer and index.
   * @param canvasId The canvas for which to get the pattern data.
   * @param layer The layer for which to get the pattern data.
   * @param index The index of the pattern to get.
   * @return The pattern for the given layer and index.
   */
  function getPatternData(uint8 canvasId, uint8 layer, uint8 index) public view override returns (bytes memory) {
    PatternBlob[][] storage canvasPatterns = patterns[canvasId];
    if (layer < canvasPatterns.length) {
      PatternBlob[] storage layerPatterns = canvasPatterns[layer];
      if (index < layerPatterns.length) {
        return layerPatterns[index].data;
      }
    }
    return new bytes(0);
  }

  /**
   * @notice Store the given pattern in the contract.
   * @param input The input.
   */
  function uploadPattern(UploadPatternInput calldata input) public override onlyOwner {
    if (fuseBurnedCanUploadPattern) revert OperationBlockedByBurnedFuse();
    if (!canvasExists[input.canvasId]) revert CanvasDoesNotExist(input.canvasId);
    // consistency checks
    if (input.data.length != uint(input.width) * uint(input.height)) {
      revert InvalidPatternLength(input.width, input.height, input.data.length);
    }
    if (input.paletteCode < palettes.length) {
      PatternBlob[][] storage canvasPatterns = patterns[input.canvasId];
      if (input.layer < canvasPatterns.length) {
        PatternBlob[] storage layerPatterns = canvasPatterns[input.layer];
        uint ct = layerPatterns.length;

        // 3 cases:
        // 1. index == length, append (happy path)
        // 2. index < length, revert
        // 3. index > length, revert
        if (input.index == ct) {
          // append
          layerPatterns.push(
            PatternBlob(
              PatternHeader(input.width, input.height, input.offsetX, input.offsetY, input.paletteCode),
              input.data
            )
          );

          emit PatternUpload(input.canvasId, input.layer, input.index);
        } else if (input.index < ct) {
          // already exists
          revert PatternAlreadyExists(input.canvasId, input.layer, input.index);
        } else {
          // out of bounds
          revert PatternIndexOutOfBounds(input.canvasId, input.layer, input.index);
        }
      } else {
        revert LayerIndexOutOfBounds(input.canvasId, input.layer);
      }
    } else {
      revert UnknownPaletteCode(input.paletteCode);
    }
  }

  /**
   * @notice Batch upload multiple patterns.
   * @param inputs The uploadPattern inputs.
   */
  function uploadPatterns(UploadPatternInput[] calldata inputs) public override onlyOwner {
    if (fuseBurnedCanUploadPattern) revert OperationBlockedByBurnedFuse();
    uint len = inputs.length;
    for (uint i = 0; i < len; ) {
      uploadPattern(inputs[i]);
      unchecked {
        ++i;
      }
    }
  }
}

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



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

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

/**
 * @title FuseGuard
 * @dev A contract that manages fuses.
 */
abstract contract FuseGuard {
  /// @dev Revert error when operation is blocked by burned fuse.
  error OperationBlockedByBurnedFuse();
}

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



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

import {OpenAvatarGen0AssetsCanvasStore} from './core/assets/OpenAvatarGen0AssetsCanvasStore.sol';
import {OpenAvatarGen0AssetsPaletteStore} from './core/assets/OpenAvatarGen0AssetsPaletteStore.sol';
import {OpenAvatarGen0AssetsPatternStore} from './core/assets/OpenAvatarGen0AssetsPatternStore.sol';
import {ENSReverseClaimer} from './core/lib/ENSReverseClaimer.sol';
import {IOpenAvatarGen0Assets} from './IOpenAvatarGen0Assets.sol';
import {OpenAvatarGenerationZero} from './OpenAvatarGenerationZero.sol';

/**
 * @title OpenAvatarGen0Assets
 * @author Cory Gabrielsen (cory.eth)
 *
 * @dev This contract is responsible for storing OpenAvatar art assets and
 *      metadata. The art was created by hand by the contract author.
 *
 * Immutability:
 * - This contract references no other contracts
 * - All assets are stored in append-only arrays.
 * - Once uploaded, no asset can be modified or deleted.
 * - So, asset references are immutable and reads are invariant.
 * - Fuses can be burned to prevent contract owner uploading further assets.
 * - If all fuses are burned, no further assets can be uploaded and storage is
 *   effectively frozen.
 *
 * Terminology:
 * Colors are stored as color palettes.
 * Patterns are byte arrays that reference colors in a palette.
 *
 * - Palettes:
 *   - A "palette" is an array of RGBA colors (bytes4[]).
 *   - A "palette code" defines an array of palettes (bytes4[][]).
 *
 * - Canvas
 *   - A "canvas" is an image canvas with header (uint8 id, uint8 height,
 *   uint8 width), and an array of layers.
 *
 * - Layer
 *   - A "layer" is a layer of a canvas, which references an array of patterns.
 *
 * - Pattern
 *  - A "pattern" is a byte array that references colors by index in a palette.
 *
 * Fuses:
 * - A fuse can be burned to prevent adding new canvases.
 * - A fuse can be burned to prevent adding new layers (for all canvases).
 * - A fuse can be burned to prevent adding new patterns (for all layers).
 * - A fuse can be burned to prevent adding new palettes (for all patterns).
 *
 * Solidity:
 * Due to C3 Linearization, we cannot "is" IOpenAvatarGen0Assets.
 * However, IOpenAvatarGen0Assets is indeed implemented fully by OpenAvatarGen0AssetsPatternStore.
 */
contract OpenAvatarGen0Assets is OpenAvatarGenerationZero, OpenAvatarGen0AssetsPatternStore, ENSReverseClaimer {
  // solhint-disable-next-line no-empty-blocks
  constructor(address ownerProxy) OpenAvatarGen0AssetsPatternStore(ownerProxy) {}

  /////////////////////////////////////////////////////////////////////////////
  // 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, OpenAvatarGen0AssetsPatternStore) 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.
      // assets
      interfaceId == 0x67bf31d1 || // ERC165 interface ID for IOpenAvatarGen0AssetsRead.
      interfaceId == 0x511ecd08 || // ERC165 interface ID for IOpenAvatarGen0AssetsWrite.
      interfaceId == 0x36a1fcd9 || // ERC165 interface ID for IOpenAvatarGen0Assets.
      // canvas
      interfaceId == 0x91411495 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreRead.
      interfaceId == 0x4d4a1c57 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStoreWrite.
      interfaceId == 0xdc0b08c2 || // ERC165 interface ID for IOpenAvatarGen0AssetsCanvasStore.
      // palette
      interfaceId == 0x5577825f || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreRead.
      interfaceId == 0x9c9764e9 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStoreWrite.
      interfaceId == 0xc9e0e6b6 || // ERC165 interface ID for IOpenAvatarGen0AssetsPaletteStore.
      // pattern
      interfaceId == 0x32c8b38e || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreRead.
      interfaceId == 0xcd89a9e1 || // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStoreWrite.
      interfaceId == 0xff411a6f; // ERC165 interface ID for IOpenAvatarGen0AssetsPatternStore.
  }
}

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