ETH Price: $3,881.88 (+0.51%)

Contract

0x9F87290F9de941668a8C26BcC04A28e306CDa890

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Unlock Batch1024456602023-05-28 22:56:59566 days ago1685314619IN
0x9F87290F...306CDa890
0 ETH0.0001237097520.001
Unlock Batch915197852023-04-17 11:55:12608 days ago1681732512IN
0x9F87290F...306CDa890
0 ETH0.0001299744170.001
Unlock Batch900295832023-04-13 21:49:44612 days ago1681422584IN
0x9F87290F...306CDa890
0 ETH0.000126440930.001
Unlock Batch900292242023-04-13 21:47:28612 days ago1681422448IN
0x9F87290F...306CDa890
0.05 ETH0.0001287892790.001
Unlock Batch896454832023-04-12 22:48:45613 days ago1681339725IN
0x9F87290F...306CDa890
0 ETH0.0001156447680.001
Unlock Batch895422892023-04-12 15:25:46613 days ago1681313146IN
0x9F87290F...306CDa890
0 ETH0.0001347227060.001
Unlock Batch895262822023-04-12 14:49:30613 days ago1681310970IN
0x9F87290F...306CDa890
0 ETH0.0001089017390.001
Unlock Batch895249612023-04-12 14:45:45613 days ago1681310745IN
0x9F87290F...306CDa890
0 ETH0.0001089017390.001
Unlock Batch895067502023-04-12 14:00:15613 days ago1681308015IN
0x9F87290F...306CDa890
0 ETH0.0000971747550.001
Unlock Batch895064172023-04-12 13:59:30613 days ago1681307970IN
0x9F87290F...306CDa890
0 ETH0.0000971747550.001
Unlock Batch894896422023-04-12 13:16:30613 days ago1681305390IN
0x9F87290F...306CDa890
0 ETH0.0001093591210.001
Unlock Batch818553902023-03-18 8:41:39638 days ago1679128899IN
0x9F87290F...306CDa890
0 ETH0.00008344660.001
Unlock Batch806427282023-03-13 21:26:10643 days ago1678742770IN
0x9F87290F...306CDa890
0 ETH0.0001708363820.001
Unlock Batch806422782023-03-13 21:21:52643 days ago1678742512IN
0x9F87290F...306CDa890
0 ETH0.0001304370640.001
Unlock Batch803899352023-03-12 23:05:40643 days ago1678662340IN
0x9F87290F...306CDa890
0 ETH0.0001990095190.001
Unlock Batch803899032023-03-12 23:05:25643 days ago1678662325IN
0x9F87290F...306CDa890
0 ETH0.0001990095190.001
Unlock Batch803898562023-03-12 23:05:10643 days ago1678662310IN
0x9F87290F...306CDa890
0 ETH0.0001990095190.001
Unlock Batch798151962023-03-10 13:24:26646 days ago1678454666IN
0x9F87290F...306CDa890
0 ETH0.0000955194220.001
Unlock Batch798134612023-03-10 13:14:23646 days ago1678454063IN
0x9F87290F...306CDa890
0 ETH0.0001047660080.001
Unlock Batch798133872023-03-10 13:13:53646 days ago1678454033IN
0x9F87290F...306CDa890
0 ETH0.0001110940690.001
Unlock Batch797915482023-03-10 11:21:30646 days ago1678447290IN
0x9F87290F...306CDa890
0 ETH0.0001981995770.001
Unlock Batch797386892023-03-10 7:28:43646 days ago1678433323IN
0x9F87290F...306CDa890
0 ETH0.0001085536230.001
Unlock Batch796984352023-03-10 3:36:02646 days ago1678419362IN
0x9F87290F...306CDa890
0 ETH0.0001112177650.001
Unlock Batch796141242023-03-09 20:23:49647 days ago1678393429IN
0x9F87290F...306CDa890
0 ETH0.0002757546220.00153703
Unlock Batch792545732023-03-08 12:48:06648 days ago1678279686IN
0x9F87290F...306CDa890
0 ETH0.0001102521120.001
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
1074039472023-07-26 21:24:31508 days ago1690406671
0x9F87290F...306CDa890
0 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PixelStore

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 19 : PixelStore.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { OwnedRoles } from "@turbo-eth/solbase-sol/src/auth/OwnedRoles.sol";
import { PixelPoolyAssets } from "./PixelPoolyAssets.sol";
import { PixelPoolyStorage } from "./PixelPoolyStorage.sol";
import { IAssets } from "./interfaces/IAssets.sol";
import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol";

contract PixelStore is OwnedRoles {
  address public erc721KStorage;
  address public pixelAssets;

  address public birbAddress;
  address public flapAddress;
  uint256 public totalReceived = 0;

  mapping(IAssets.Tier => uint256) internal tierPrices;

  /// -----------------------------------------------------------------------
  /// Storage
  /// -----------------------------------------------------------------------
  address internal erc721K;
  // TODO: possibly refactor this to use PixelPoolyStorage _elements so we dont have redundant storage of unlocked traits
  mapping(bytes32 => bool) internal _isEnabled;

  /// -----------------------------------------------------------------------
  /// Constructor
  /// -----------------------------------------------------------------------

  constructor(
    IAssets.Tier[] memory tiers,
    uint256[] memory prices,
    address _pixelAssets,
    address _birbAddress_,
    address _flapAddress_,
    address _owner
  ) OwnedRoles() {
    _initializeOwner(_owner);
    pixelAssets = _pixelAssets;
    require(tiers.length == prices.length, "PixelStore: invalid input");
    for (uint256 index = 0; index < tiers.length; index++) {
      tierPrices[tiers[index]] = prices[index];
    }
    birbAddress = _birbAddress_;
    flapAddress = _flapAddress_;
  }

  /// -----------------------------------------------------------------------
  /// External Functions
  /// -----------------------------------------------------------------------

  // ========================
  // READS
  // ========================
  function isEnabled(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external view returns (bool unlocked) {
    return _isEnabled[_seid(tokenId, layer, frame)];
  }

  function getTierPrice(IAssets.Tier tier) external view returns (uint256) {
    return tierPrices[tier];
  }

  function getTierPrices(IAssets.Tier[] calldata tiers)
    external
    view
    returns (uint256[] memory prices)
  {
    prices = new uint256[](tiers.length);
    for (uint256 index = 0; index < tiers.length; index++) {
      prices[index] = tierPrices[tiers[index]];
    }
  }

  function getFramePrice(uint8 layer, uint8 frame) external view returns (uint256) {
    return tierPrices[PixelPoolyAssets(pixelAssets).getTier(_eid(layer, frame))];
  }

  function getFramePrices(uint8[] memory layers, uint8[] calldata frames)
    external
    view
    returns (uint256[] memory prices)
  {
    prices = new uint256[](frames.length);
    for (uint256 index = 0; index < frames.length; index++) {
      prices[index] = tierPrices[
        PixelPoolyAssets(pixelAssets).getTier(_eid(layers[index], frames[index]))
      ];
    }
  }

  // ========================
  // WRITES
  // ========================
  function unlock(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external payable {
    uint256 price_ = _handleUnlock(tokenId, layer, frame);
    require(msg.value >= price_, "PixelStore: Insufficient funds");

    totalReceived += msg.value;

    if (msg.value > 0){
      _handleStreamSplit(msg.value);
    }
  }

  function unlockBatch(
    uint256 tokenId,
    IPixelCharacter.Layer[] calldata layer,
    uint8[] calldata frame
  ) external payable {
    require(
      layer.length == frame.length,
      "PixelStore: layer and frame arrays must be the same length"
    );
    uint256 totalCost;
    for (uint256 i = 0; i < layer.length; i++) {
      uint256 price_ = _handleUnlock(tokenId, layer[i], frame[i]);
      totalCost += price_;
    }
    require(msg.value >= totalCost, "PixelStore: Insufficient funds");

    totalReceived += msg.value;

    if (msg.value > 0){
      _handleStreamSplit(msg.value);
    }
  }

  function setERC721KStorage(address _erc721KStorage) external onlyOwner {
    erc721KStorage = _erc721KStorage;
  }

  function release(address to, uint256 value) external onlyOwner {
    (bool _success, ) = payable(to).call{ value: value }("");
    require(_success, "PixelStore:eth-release-failed");
  }

  /// -----------------------------------------------------------------------
  /// Internal Functions
  /// -----------------------------------------------------------------------

  function _handleUnlock(uint256 _tokenId, IPixelCharacter.Layer _layer, uint8 _frame) internal returns (uint256 price) {
    bytes32 seid = _seid(_tokenId, _layer, _frame);
    require(!_isEnabled[seid], "PixelStore:pixel-unlocked");
    bytes32 eid = _eid(_layer, _frame);

    uint256 expiryTimestamp = PixelPoolyAssets(pixelAssets).getExpiryTime(eid);
    if (expiryTimestamp > 0){
      require(block.timestamp < expiryTimestamp, "PixelStore:trait-expired");
    }
    
    IAssets.Tier tier = PixelPoolyAssets(pixelAssets).getTier(eid);

    PixelPoolyStorage(erc721KStorage).setLayerFrameByController(_tokenId, _layer, _frame);
    _isEnabled[seid] = true;

    return tierPrices[tier];
  }

  function _handleStreamSplit(uint256 _amount) internal {
    uint256 birbPercent_ = 80;
    uint256 totalPercent_ = 100;

    uint256 birbShare_ = (_amount * birbPercent_) / totalPercent_;
    uint256 flapShare_ = _amount - birbShare_;

    (bool _success, ) = payable(birbAddress).call{ value: birbShare_ }("");
    require(_success, "PixelStore:birb-eth-release-failed");

    (bool _success2, ) = payable(flapAddress).call{ value: flapShare_ }("");
    require(_success2, "PixelStore:eth-release-failed");
  }

  function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(uint8(layer), frame));
  }

  function _eid(uint8 layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(layer, frame));
  }

  function _seid(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(tokenId, layer, frame));
  }
}

File 2 of 19 : ERC721Storage.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.15;

import { Base64 } from "@turbo-eth/solbase-sol/src/utils/Base64.sol";
import { OwnedRoles } from "@turbo-eth/solbase-sol/src/auth/OwnedRoles.sol";
import { IERC721KImage } from "./interfaces/IERC721KImage.sol";
import { IERC721KTraits } from "./interfaces/IERC721KTraits.sol";

/**
 * @title ERC721Storage
 * @author Kames Geraghty
 */
abstract contract ERC721Storage is OwnedRoles {
  address internal svgRenderInstance;
  address internal traitsFetchInstance;
  ContractURI internal _contractURI;

  struct ContractURI {
    string name;
    string description;
    string image;
    string externalLink;
    string sellerFeeBasisPoints;
    string feeRecipient;
  }

  event SvgRenderUpdated(address svgRender);

  event TraitsFetchUpdated(address traitsFetch);

  event ContractURIUpdated(ContractURI contractURI);

  constructor(
    address svgRender_Instance,
    address traitsFetchInstance_,
    ContractURI memory _contractURI_
  ) OwnedRoles() {
    svgRenderInstance = svgRender_Instance;
    traitsFetchInstance = traitsFetchInstance_;
    _contractURI = _contractURI_;
    _initializeOwner(msg.sender);
  }

  /* ===================================================================================== */
  /* Virtual Functions                                                                     */
  /* ===================================================================================== */

  function _parseName(uint256 _tokenId) internal view virtual returns (string memory);

  function _parseDescription(uint256 _tokenId) internal view virtual returns (string memory);

  /* ===================================================================================== */
  /* External Functions                                                                    */
  /* ===================================================================================== */
  function getERC721KRender() external view returns (address) {
    return svgRenderInstance;
  }

  function getERC72KTraits() external view returns (address) {
    return traitsFetchInstance;
  }

  function getContractDescription() external view returns (ContractURI memory) {
    return _contractURI;
  }

  function render(bytes memory input) external view returns (string memory) {
    return IERC721KImage(svgRenderInstance).render(input);
  }

  function constructTokenURI(
    uint256 tokenId,
    bytes memory input0,
    bytes memory input1
  ) external view virtual returns (string memory uri) {
    string memory image_ = IERC721KImage(svgRenderInstance).render(input0);
    string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1);
    return
      string(
        abi.encodePacked(
          "data:application/json;base64,",
          Base64.encode(
            bytes(
              string.concat(
                '{"name":',
                '"',
                _parseName(tokenId),
                '",',
                '"description":',
                '"',
                _parseDescription(tokenId),
                '",',
                '"image":',
                '"',
                image_,
                '",',
                '"attributes": [',
                traits_,
                "]",
                "}"
              )
            )
          )
        )
      );
  }

  function constructContractURI() external view virtual returns (string memory uri) {
    return
      string(
        abi.encodePacked(
          "data:application/json;base64,",
          Base64.encode(
            bytes(
              string.concat(
                '{"name":',
                '"',
                _contractURI.name,
                '",',
                '"description":',
                '"',
                _contractURI.description,
                '",',
                '"image":',
                '"',
                _contractURI.image,
                '",',
                '"externalLink":',
                '"',
                _contractURI.externalLink,
                '",',
                '"sellerFeeBasisPoints":',
                '"',
                _contractURI.sellerFeeBasisPoints,
                '",',
                '"feeRecipient":',
                '"',
                _contractURI.feeRecipient,
                '"',
                "}"
              )
            )
          )
        )
      );
  }

  function setSvgRender(address svgRender) external onlyOwner {
    svgRenderInstance = svgRender;
    emit SvgRenderUpdated(svgRender);
  }

  function setTraitsFetch(address traitsFetch) external onlyOwner {
    traitsFetchInstance = traitsFetch;
    emit TraitsFetchUpdated(traitsFetch);
  }

  function setContractURI(ContractURI memory contractURI) external onlyOwner {
    _contractURI = contractURI;
    emit ContractURIUpdated(contractURI);
  }
}

File 3 of 19 : IERC721KImage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface IERC721KImage {
  function render(bytes memory input) external view returns (string memory);
}

File 4 of 19 : IERC721KTraits.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface IERC721KTraits {
  enum DisplayType {
    Base,
    Generic,
    BoostNumber,
    BoostPercent,
    Number,
    Date
  }

  function fetch(bytes memory input) external view returns (string memory);
}

File 5 of 19 : BytesLib.sol
// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;

library BytesLib {
    function concat(bytes memory _preBytes, bytes memory _postBytes)
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(
                0x40,
                and(
                    add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                    not(31) // Round down to the nearest 32 bytes.
                )
            )
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes)
        internal
    {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(
                and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
                2
            )
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(
                    add(tempBytes, lengthmod),
                    mul(0x20, iszero(lengthmod))
                )
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(
                        add(
                            add(_bytes, lengthmod),
                            mul(0x20, iszero(lengthmod))
                        ),
                        _start
                    )
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (address)
    {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(
                mload(add(add(_bytes, 0x20), _start)),
                0x1000000000000000000000000
            )
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint8)
    {
        require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint16)
    {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint32)
    {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint64)
    {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint96)
    {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint128)
    {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (uint256)
    {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start)
        internal
        pure
        returns (bytes32)
    {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes)
        internal
        pure
        returns (bool)
    {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                    // the next line is the loop condition:
                    // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(bytes storage _preBytes, bytes memory _postBytes)
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(
                and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
                2
            )
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {

                        } eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

File 6 of 19 : svg.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import "@openzeppelin/contracts/utils/Strings.sol";
import "./svgUtils.sol";

/**
 * @title svg
 * @author Kames Geraghty
 * @notice SVG construction library using web-like API.
 * @dev Original code from w1nt3r-eth/hot-chain-svg (https://github.com/w1nt3r-eth/hot-chain-svg)
 */
library svg {
  using Strings for uint256;
  using Strings for uint8;

  function g(string memory _props, string memory _children) internal pure returns (string memory) {
    return el("g", _props, _children);
  }

  function path(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("path", _props, _children);
  }

  function text(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("text", _props, _children);
  }

  function line(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("line", _props, _children);
  }

  function circle(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("circle", _props, _children);
  }

  function circle(string memory _props) internal pure returns (string memory) {
    return el("circle", _props);
  }

  function rect(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("rect", _props, _children);
  }

  function rect(string memory _props) internal pure returns (string memory) {
    return el("rect", _props);
  }

  function stop(string memory _props) internal pure returns (string memory) {
    return el("stop", _props);
  }

  function filter(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("filter", _props, _children);
  }

  function defs(string memory _children) internal pure returns (string memory) {
    return el("defs", "", _children);
  }

  function cdata(string memory _content) internal pure returns (string memory) {
    return string.concat("<![CDATA[", _content, "]]>");
  }

  /* GRADIENTS */
  function radialGradient(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("radialGradient", _props, _children);
  }

  function linearGradient(string memory _props, string memory _children)
    internal
    pure
    returns (string memory)
  {
    return el("linearGradient", _props, _children);
  }

  function gradientStop(
    uint256 offset,
    string memory stopColor,
    string memory _props
  ) internal pure returns (string memory) {
    return
      el(
        "stop",
        string.concat(
          prop("stop-color", stopColor),
          " ",
          prop("offset", string.concat(svgUtils.uint2str(offset), "%")),
          " ",
          _props
        )
      );
  }

  function animateTransform(string memory _props) internal pure returns (string memory) {
    return el("animateTransform", _props);
  }

  function image(string memory _href, string memory _props) internal pure returns (string memory) {
    return el("image", string.concat(prop("href", _href), " ", _props));
  }

  /* COMMON */
  // A generic element, can be used to construct any SVG (or HTML) element
  function el(
    string memory _tag,
    string memory _props,
    string memory _children
  ) internal pure returns (string memory) {
    return string.concat("<", _tag, " ", _props, ">", _children, "</", _tag, ">");
  }

  // A generic element, can be used to construct any SVG (or HTML) element without children
  function el(string memory _tag, string memory _props) internal pure returns (string memory) {
    return string.concat("<", _tag, " ", _props, "/>");
  }

  function start(string memory _tag, string memory _props) internal pure returns (string memory) {
    return string.concat("<", _tag, " ", _props, ">");
  }

  function start(string memory _tag) internal pure returns (string memory) {
    return string.concat("<", _tag, ">");
  }

  function end(string memory _tag) internal pure returns (string memory) {
    return string.concat("</", _tag, ">");
  }

  // an SVG attribute
  function prop(string memory _key, string memory _val) internal pure returns (string memory) {
    return string.concat(_key, "=", '"', _val, '" ');
  }

  function stringifyIntSet(
    bytes memory _data,
    uint256 _offset,
    uint256 _len
  ) public pure returns (bytes memory) {
    bytes memory res;
    require(_data.length >= _offset + _len, "Out of range");
    for (uint256 i = _offset; i < _offset + _len; i++) {
      res = abi.encodePacked(res, byte2uint8(_data, i).toString(), " ");
    }
    return res;
  }

  function byte2uint8(bytes memory _data, uint256 _offset) public pure returns (uint8) {
    require(_data.length > _offset, "Out of range");
    return uint8(_data[_offset]);
  }
}

File 7 of 19 : svgUtils.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

// import "@openzeppelin/contracts/utils/LibString.sol";
import { LibString } from "@turbo-eth/solbase-sol/src/utils/LibString.sol";
import { BytesLib } from "../libraries/BytesLib.sol";

/**
 * @title  SVG Utilities
 * @author Kames Geraghty
 * @notice The SVG Utilities Library provides functions for constructing SVG; format CSS and numbers.
 * @dev Original code from w1nt3r-eth/hot-chain-svg (https://github.com/w1nt3r-eth/hot-chain-svg)
 */
library svgUtils {
  using LibString for uint256;
  using LibString for uint8;

  /// @notice Empty SVG element
  string internal constant NULL = "";

  /**
   * @notice Formats a CSS variable line. Includes a semicolon for formatting.
   * @param _key User for which to calculate prize amount.
   * @param _val User for which to calculate prize amount.
   * @return string Generated CSS variable.
   */
  function setCssVar(string memory _key, string memory _val) internal pure returns (string memory) {
    return string.concat("--", _key, ":", _val, ";");
  }

  /**
   * @notice Formats getting a css variable
   * @param _key User for which to calculate prize amount.
   * @return string Generated CSS variable.
   */
  function getCssVar(string memory _key) internal pure returns (string memory) {
    return string.concat("var(--", _key, ")");
  }

  // formats getting a def URL
  function getDefURL(string memory _id) internal pure returns (string memory) {
    return string.concat("url(#", _id, ")");
  }

  // checks if two LibString are equal
  function LibStringEqual(string memory _a, string memory _b) internal pure returns (bool) {
    return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b));
  }

  // returns the length of a string in characters
  function utfStringLength(string memory _str) internal pure returns (uint256 length) {
    uint256 i = 0;
    bytes memory string_rep = bytes(_str);

    while (i < string_rep.length) {
      if (string_rep[i] >> 7 == 0) i += 1;
      else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) i += 2;
      else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) i += 3;
      else if (string_rep[i] >> 3 == bytes1(uint8(0x1E)))
        i += 4;
        //For safety
      else i += 1;

      length++;
    }
  }

  function round2Txt(
    uint256 _value,
    uint8 _decimals,
    uint8 _prec
  ) internal pure returns (bytes memory) {
    return
      abi.encodePacked(
        (_value / 10**_decimals).toString(),
        ".",
        (_value / 10**(_decimals - _prec) - (_value / 10**(_decimals)) * 10**_prec).toString()
      );
  }

  // converts an unsigned integer to a string
  function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) {
    if (_i == 0) {
      return "0";
    }
    uint256 j = _i;
    uint256 len;
    while (j != 0) {
      len++;
      j /= 10;
    }
    bytes memory bstr = new bytes(len);
    uint256 k = len;
    while (_i != 0) {
      k = k - 1;
      uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
      bytes1 b1 = bytes1(temp);
      bstr[k] = b1;
      _i /= 10;
    }
    return string(bstr);
  }

  function splitAddress(address account) internal pure returns (string memory) {
    string memory addy = LibString.toHexString(uint256(uint160(account)), 20);
    bytes memory start = BytesLib.slice(abi.encodePacked(addy), 0, 6);
    bytes memory end = BytesLib.slice(abi.encodePacked(addy), 37, 4);
    return string.concat(string(abi.encodePacked(start)), "...", string(abi.encodePacked(end)));
  }

  function toString(address account) internal pure returns (string memory) {
    return toString(abi.encodePacked(account));
  }

  function toString(uint256 value) internal pure returns (string memory) {
    return toString(abi.encodePacked(value));
  }

  function toString(bytes32 value) internal pure returns (string memory) {
    return toString(abi.encodePacked(value));
  }

  function toString(bytes memory data) internal pure returns (string memory) {
    bytes memory alphabet = "0123456789abcdef";

    bytes memory str = new bytes(2 + data.length * 2);
    str[0] = "0";
    str[1] = "x";
    for (uint256 i = 0; i < data.length; i++) {
      str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
      str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
    }
    return string(str);
  }

  function getColor(bytes memory _colorHex) internal view returns (bytes memory) {
    require(_colorHex.length == 3, "Unknown color");
    return abi.encodePacked(_colorHex, hex"64");
  }

  function getColor(bytes memory _colorHex, uint8 _alpha) internal view returns (bytes memory) {
    require(_colorHex.length == 3, "Unknown color");
    return abi.encodePacked(_colorHex, _alpha);
  }

  function getRgba(bytes memory _colorHex) internal view returns (string memory) {
    return string(toRgba(getColor(_colorHex), 0));
  }

  function toRgba(bytes memory _rgba, uint256 offset) internal pure returns (bytes memory) {
    return
      abi.encodePacked(
        "rgba(",
        byte2uint8(_rgba, offset).toString(),
        ",",
        byte2uint8(_rgba, offset + 1).toString(),
        ",",
        byte2uint8(_rgba, offset + 2).toString(),
        ",",
        byte2uint8(_rgba, offset + 3).toString(),
        "%)"
      );
  }

  function byte2uint8(bytes memory _data, uint256 _offset) internal pure returns (uint8) {
    require(_data.length > _offset, "Out of range");
    return uint8(_data[_offset]);
  }
}

File 8 of 19 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 9 of 19 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_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) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @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] = _HEX_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 10 of 19 : 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 11 of 19 : OwnedRoles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner and multiroles authorization mixin.
/// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/auth/OwnedRoles.sol)
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol)
/// @dev While the ownable portion follows EIP-173 (https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnedRoles {
    /// -----------------------------------------------------------------------
    /// Events
    /// -----------------------------------------------------------------------

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev The `user`'s roles is updated to `roles`.
    /// Each bit of `roles` represents whether the role is set.
    event RolesUpdated(address indexed user, uint256 indexed roles);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
    uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
        0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;

    /// -----------------------------------------------------------------------
    /// Custom Errors
    /// -----------------------------------------------------------------------

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
    uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;

    /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
    uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;

    /// -----------------------------------------------------------------------
    /// Storage
    /// -----------------------------------------------------------------------

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally choosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    ///
    /// The role slot of `user` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
    ///     let roleSlot := keccak256(0x00, 0x20)
    /// ```
    /// This automatically ignores the upper bits of the `user` in case
    /// they are not clean, as well as keep the `keccak256` under 32-bytes.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /// -----------------------------------------------------------------------
    /// Internal Functions
    /// -----------------------------------------------------------------------

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Grants the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn on.
    function _grantRoles(address user, uint256 roles) internal virtual {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
            let roleSlot := keccak256(0x00, 0x20)
            // Load the current value and `or` it with `roles`.
            let newRoles := or(sload(roleSlot), roles)
            // Store the new value.
            sstore(roleSlot, newRoles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
        }
    }

    /// @dev Removes the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn off.
    function _removeRoles(address user, uint256 roles) internal virtual {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
            let roleSlot := keccak256(0x00, 0x20)
            // Load the current value.
            let currentRoles := sload(roleSlot)
            // Use `and` to compute the intersection of `currentRoles` and `roles`,
            // `xor` it with `currentRoles` to flip the bits in the intersection.
            let newRoles := xor(currentRoles, and(currentRoles, roles))
            // Then, store the new value.
            sstore(roleSlot, newRoles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles)
        }
    }

    /// -----------------------------------------------------------------------
    /// Public Update Functions
    /// -----------------------------------------------------------------------

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Reverts if the `newOwner` is the zero address.
            if iszero(newOwner) {
                mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), newOwner)
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
        }
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        assembly {
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), 0)
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), 0)
        }
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will be automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + ownershipHandoverValidFor();
            assembly {
                // Compute and set the handover slot to 1.
                mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
                sstore(keccak256(0x00, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED))
            sstore(keccak256(0x00, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        assembly {
            // Clean the upper 96 bits.
            pendingOwner := shr(96, shl(96, pendingOwner))
            // Compute and set the handover slot to 0.
            mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
            let handoverSlot := keccak256(0x00, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), pendingOwner)
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), pendingOwner)
        }
    }

    /// @dev Allows the owner to grant `user` `roles`.
    /// If the `user` already has a role, then it will be an no-op for the role.
    function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _grantRoles(user, roles);
    }

    /// @dev Allows the owner to remove `user` `roles`.
    /// If the `user` does not have a role, then it will be an no-op for the role.
    function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _removeRoles(user, roles);
    }

    /// @dev Allow the caller to remove their own roles.
    /// If the caller does not have a role, then it will be an no-op for the role.
    function renounceRoles(uint256 roles) public payable virtual {
        _removeRoles(msg.sender, roles);
    }

    /// -----------------------------------------------------------------------
    /// Public Read Functions
    /// -----------------------------------------------------------------------

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) {
        assembly {
            // Compute the handover slot.
            mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED))
            // Load the handover slot.
            result := sload(keccak256(0x00, 0x20))
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    function ownershipHandoverValidFor() public view virtual returns (uint64) {
        return 48 * 3600;
    }

    /// @dev Returns whether `user` has any of `roles`.
    function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
            // Load the stored value, and set the result to whether the
            // `and` intersection of the value and `roles` is not zero.
            result := iszero(iszero(and(sload(keccak256(0x00, 0x20)), roles)))
        }
    }

    /// @dev Returns whether `user` has all of `roles`.
    function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
            // Whether the stored value is contains all the set bits in `roles`.
            result := eq(and(sload(keccak256(0x00, 0x20)), roles), roles)
        }
    }

    /// @dev Returns the roles of `user`.
    function rolesOf(address user) public view virtual returns (uint256 roles) {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT))
            // Load the stored value.
            roles := sload(keccak256(0x00, 0x20))
        }
    }

    /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
        assembly {
            // Skip the length slot.
            let o := add(ordinals, 0x20)
            // `shl` 5 is equivalent to multiplying by 0x20.
            let end := add(o, shl(5, mload(ordinals)))
            // prettier-ignore
            for {} iszero(eq(o, end)) { o := add(o, 0x20) } {
                roles := or(roles, shl(and(mload(o), 0xff), 1))
            }
        }
    }

    /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
        assembly {
            // Grab the pointer to the free memory.
            let ptr := add(mload(0x40), 0x20)
            // The absence of lookup tables, De Bruijn, etc., here is intentional for
            // smaller bytecode, as this function is not meant to be called on-chain.
            // prettier-ignore
            for { let i := 0 } 1 { i := add(i, 1) } {
                mstore(ptr, i)
                // `shr` 5 is equivalent to multiplying by 0x20.
                // Push back into the ordinals array if the bit is set.
                ptr := add(ptr, shl(5, and(roles, 1)))
                roles := shr(1, roles)
                // prettier-ignore
                if iszero(roles) { break }
            }
            // Set `ordinals` to the start of the free memory.
            ordinals := mload(0x40)
            // Allocate the memory.
            mstore(0x40, ptr)
            // Store the length of `ordinals`.
            mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
        }
    }

    /// -----------------------------------------------------------------------
    /// Modifiers
    /// -----------------------------------------------------------------------

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
        _;
    }

    /// @dev Marks a function as only callable by an account with `roles`.
    modifier onlyRoles(uint256 roles) virtual {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
        _;
    }

    /// @dev Marks a function as only callable by the owner or by an account
    /// with `roles`. Checks for ownership first, then lazily checks for roles.
    modifier onlyOwnerOrRoles(uint256 roles) virtual {
        assembly {
            // If the caller is not the stored owner.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                // Compute the role slot.
                mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
        _;
    }

    /// @dev Marks a function as only callable by an account with `roles`
    /// or the owner. Checks for roles first, then lazily checks for ownership.
    modifier onlyRolesOrOwner(uint256 roles) virtual {
        assembly {
            // Compute the role slot.
            mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT))
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x00, 0x20)), roles)) {
                // If the caller is not the stored owner.
                if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
        _;
    }

    /// -----------------------------------------------------------------------
    /// Role Constants
    /// -----------------------------------------------------------------------

    uint256 internal constant _ROLE_0 = 1 << 0;
    uint256 internal constant _ROLE_1 = 1 << 1;
    uint256 internal constant _ROLE_2 = 1 << 2;
    uint256 internal constant _ROLE_3 = 1 << 3;
    uint256 internal constant _ROLE_4 = 1 << 4;
    uint256 internal constant _ROLE_5 = 1 << 5;
    uint256 internal constant _ROLE_6 = 1 << 6;
    uint256 internal constant _ROLE_7 = 1 << 7;
    uint256 internal constant _ROLE_8 = 1 << 8;
    uint256 internal constant _ROLE_9 = 1 << 9;
    uint256 internal constant _ROLE_10 = 1 << 10;
    uint256 internal constant _ROLE_11 = 1 << 11;
    uint256 internal constant _ROLE_12 = 1 << 12;
    uint256 internal constant _ROLE_13 = 1 << 13;
    uint256 internal constant _ROLE_14 = 1 << 14;
    uint256 internal constant _ROLE_15 = 1 << 15;
    uint256 internal constant _ROLE_16 = 1 << 16;
    uint256 internal constant _ROLE_17 = 1 << 17;
    uint256 internal constant _ROLE_18 = 1 << 18;
    uint256 internal constant _ROLE_19 = 1 << 19;
    uint256 internal constant _ROLE_20 = 1 << 20;
    uint256 internal constant _ROLE_21 = 1 << 21;
    uint256 internal constant _ROLE_22 = 1 << 22;
    uint256 internal constant _ROLE_23 = 1 << 23;
    uint256 internal constant _ROLE_24 = 1 << 24;
    uint256 internal constant _ROLE_25 = 1 << 25;
    uint256 internal constant _ROLE_26 = 1 << 26;
    uint256 internal constant _ROLE_27 = 1 << 27;
    uint256 internal constant _ROLE_28 = 1 << 28;
    uint256 internal constant _ROLE_29 = 1 << 29;
    uint256 internal constant _ROLE_30 = 1 << 30;
    uint256 internal constant _ROLE_31 = 1 << 31;
    uint256 internal constant _ROLE_32 = 1 << 32;
    uint256 internal constant _ROLE_33 = 1 << 33;
    uint256 internal constant _ROLE_34 = 1 << 34;
    uint256 internal constant _ROLE_35 = 1 << 35;
    uint256 internal constant _ROLE_36 = 1 << 36;
    uint256 internal constant _ROLE_37 = 1 << 37;
    uint256 internal constant _ROLE_38 = 1 << 38;
    uint256 internal constant _ROLE_39 = 1 << 39;
    uint256 internal constant _ROLE_40 = 1 << 40;
    uint256 internal constant _ROLE_41 = 1 << 41;
    uint256 internal constant _ROLE_42 = 1 << 42;
    uint256 internal constant _ROLE_43 = 1 << 43;
    uint256 internal constant _ROLE_44 = 1 << 44;
    uint256 internal constant _ROLE_45 = 1 << 45;
    uint256 internal constant _ROLE_46 = 1 << 46;
    uint256 internal constant _ROLE_47 = 1 << 47;
    uint256 internal constant _ROLE_48 = 1 << 48;
    uint256 internal constant _ROLE_49 = 1 << 49;
    uint256 internal constant _ROLE_50 = 1 << 50;
    uint256 internal constant _ROLE_51 = 1 << 51;
    uint256 internal constant _ROLE_52 = 1 << 52;
    uint256 internal constant _ROLE_53 = 1 << 53;
    uint256 internal constant _ROLE_54 = 1 << 54;
    uint256 internal constant _ROLE_55 = 1 << 55;
    uint256 internal constant _ROLE_56 = 1 << 56;
    uint256 internal constant _ROLE_57 = 1 << 57;
    uint256 internal constant _ROLE_58 = 1 << 58;
    uint256 internal constant _ROLE_59 = 1 << 59;
    uint256 internal constant _ROLE_60 = 1 << 60;
    uint256 internal constant _ROLE_61 = 1 << 61;
    uint256 internal constant _ROLE_62 = 1 << 62;
    uint256 internal constant _ROLE_63 = 1 << 63;
    uint256 internal constant _ROLE_64 = 1 << 64;
    uint256 internal constant _ROLE_65 = 1 << 65;
    uint256 internal constant _ROLE_66 = 1 << 66;
    uint256 internal constant _ROLE_67 = 1 << 67;
    uint256 internal constant _ROLE_68 = 1 << 68;
    uint256 internal constant _ROLE_69 = 1 << 69;
    uint256 internal constant _ROLE_70 = 1 << 70;
    uint256 internal constant _ROLE_71 = 1 << 71;
    uint256 internal constant _ROLE_72 = 1 << 72;
    uint256 internal constant _ROLE_73 = 1 << 73;
    uint256 internal constant _ROLE_74 = 1 << 74;
    uint256 internal constant _ROLE_75 = 1 << 75;
    uint256 internal constant _ROLE_76 = 1 << 76;
    uint256 internal constant _ROLE_77 = 1 << 77;
    uint256 internal constant _ROLE_78 = 1 << 78;
    uint256 internal constant _ROLE_79 = 1 << 79;
    uint256 internal constant _ROLE_80 = 1 << 80;
    uint256 internal constant _ROLE_81 = 1 << 81;
    uint256 internal constant _ROLE_82 = 1 << 82;
    uint256 internal constant _ROLE_83 = 1 << 83;
    uint256 internal constant _ROLE_84 = 1 << 84;
    uint256 internal constant _ROLE_85 = 1 << 85;
    uint256 internal constant _ROLE_86 = 1 << 86;
    uint256 internal constant _ROLE_87 = 1 << 87;
    uint256 internal constant _ROLE_88 = 1 << 88;
    uint256 internal constant _ROLE_89 = 1 << 89;
    uint256 internal constant _ROLE_90 = 1 << 90;
    uint256 internal constant _ROLE_91 = 1 << 91;
    uint256 internal constant _ROLE_92 = 1 << 92;
    uint256 internal constant _ROLE_93 = 1 << 93;
    uint256 internal constant _ROLE_94 = 1 << 94;
    uint256 internal constant _ROLE_95 = 1 << 95;
    uint256 internal constant _ROLE_96 = 1 << 96;
    uint256 internal constant _ROLE_97 = 1 << 97;
    uint256 internal constant _ROLE_98 = 1 << 98;
    uint256 internal constant _ROLE_99 = 1 << 99;
    uint256 internal constant _ROLE_100 = 1 << 100;
    uint256 internal constant _ROLE_101 = 1 << 101;
    uint256 internal constant _ROLE_102 = 1 << 102;
    uint256 internal constant _ROLE_103 = 1 << 103;
    uint256 internal constant _ROLE_104 = 1 << 104;
    uint256 internal constant _ROLE_105 = 1 << 105;
    uint256 internal constant _ROLE_106 = 1 << 106;
    uint256 internal constant _ROLE_107 = 1 << 107;
    uint256 internal constant _ROLE_108 = 1 << 108;
    uint256 internal constant _ROLE_109 = 1 << 109;
    uint256 internal constant _ROLE_110 = 1 << 110;
    uint256 internal constant _ROLE_111 = 1 << 111;
    uint256 internal constant _ROLE_112 = 1 << 112;
    uint256 internal constant _ROLE_113 = 1 << 113;
    uint256 internal constant _ROLE_114 = 1 << 114;
    uint256 internal constant _ROLE_115 = 1 << 115;
    uint256 internal constant _ROLE_116 = 1 << 116;
    uint256 internal constant _ROLE_117 = 1 << 117;
    uint256 internal constant _ROLE_118 = 1 << 118;
    uint256 internal constant _ROLE_119 = 1 << 119;
    uint256 internal constant _ROLE_120 = 1 << 120;
    uint256 internal constant _ROLE_121 = 1 << 121;
    uint256 internal constant _ROLE_122 = 1 << 122;
    uint256 internal constant _ROLE_123 = 1 << 123;
    uint256 internal constant _ROLE_124 = 1 << 124;
    uint256 internal constant _ROLE_125 = 1 << 125;
    uint256 internal constant _ROLE_126 = 1 << 126;
    uint256 internal constant _ROLE_127 = 1 << 127;
    uint256 internal constant _ROLE_128 = 1 << 128;
    uint256 internal constant _ROLE_129 = 1 << 129;
    uint256 internal constant _ROLE_130 = 1 << 130;
    uint256 internal constant _ROLE_131 = 1 << 131;
    uint256 internal constant _ROLE_132 = 1 << 132;
    uint256 internal constant _ROLE_133 = 1 << 133;
    uint256 internal constant _ROLE_134 = 1 << 134;
    uint256 internal constant _ROLE_135 = 1 << 135;
    uint256 internal constant _ROLE_136 = 1 << 136;
    uint256 internal constant _ROLE_137 = 1 << 137;
    uint256 internal constant _ROLE_138 = 1 << 138;
    uint256 internal constant _ROLE_139 = 1 << 139;
    uint256 internal constant _ROLE_140 = 1 << 140;
    uint256 internal constant _ROLE_141 = 1 << 141;
    uint256 internal constant _ROLE_142 = 1 << 142;
    uint256 internal constant _ROLE_143 = 1 << 143;
    uint256 internal constant _ROLE_144 = 1 << 144;
    uint256 internal constant _ROLE_145 = 1 << 145;
    uint256 internal constant _ROLE_146 = 1 << 146;
    uint256 internal constant _ROLE_147 = 1 << 147;
    uint256 internal constant _ROLE_148 = 1 << 148;
    uint256 internal constant _ROLE_149 = 1 << 149;
    uint256 internal constant _ROLE_150 = 1 << 150;
    uint256 internal constant _ROLE_151 = 1 << 151;
    uint256 internal constant _ROLE_152 = 1 << 152;
    uint256 internal constant _ROLE_153 = 1 << 153;
    uint256 internal constant _ROLE_154 = 1 << 154;
    uint256 internal constant _ROLE_155 = 1 << 155;
    uint256 internal constant _ROLE_156 = 1 << 156;
    uint256 internal constant _ROLE_157 = 1 << 157;
    uint256 internal constant _ROLE_158 = 1 << 158;
    uint256 internal constant _ROLE_159 = 1 << 159;
    uint256 internal constant _ROLE_160 = 1 << 160;
    uint256 internal constant _ROLE_161 = 1 << 161;
    uint256 internal constant _ROLE_162 = 1 << 162;
    uint256 internal constant _ROLE_163 = 1 << 163;
    uint256 internal constant _ROLE_164 = 1 << 164;
    uint256 internal constant _ROLE_165 = 1 << 165;
    uint256 internal constant _ROLE_166 = 1 << 166;
    uint256 internal constant _ROLE_167 = 1 << 167;
    uint256 internal constant _ROLE_168 = 1 << 168;
    uint256 internal constant _ROLE_169 = 1 << 169;
    uint256 internal constant _ROLE_170 = 1 << 170;
    uint256 internal constant _ROLE_171 = 1 << 171;
    uint256 internal constant _ROLE_172 = 1 << 172;
    uint256 internal constant _ROLE_173 = 1 << 173;
    uint256 internal constant _ROLE_174 = 1 << 174;
    uint256 internal constant _ROLE_175 = 1 << 175;
    uint256 internal constant _ROLE_176 = 1 << 176;
    uint256 internal constant _ROLE_177 = 1 << 177;
    uint256 internal constant _ROLE_178 = 1 << 178;
    uint256 internal constant _ROLE_179 = 1 << 179;
    uint256 internal constant _ROLE_180 = 1 << 180;
    uint256 internal constant _ROLE_181 = 1 << 181;
    uint256 internal constant _ROLE_182 = 1 << 182;
    uint256 internal constant _ROLE_183 = 1 << 183;
    uint256 internal constant _ROLE_184 = 1 << 184;
    uint256 internal constant _ROLE_185 = 1 << 185;
    uint256 internal constant _ROLE_186 = 1 << 186;
    uint256 internal constant _ROLE_187 = 1 << 187;
    uint256 internal constant _ROLE_188 = 1 << 188;
    uint256 internal constant _ROLE_189 = 1 << 189;
    uint256 internal constant _ROLE_190 = 1 << 190;
    uint256 internal constant _ROLE_191 = 1 << 191;
    uint256 internal constant _ROLE_192 = 1 << 192;
    uint256 internal constant _ROLE_193 = 1 << 193;
    uint256 internal constant _ROLE_194 = 1 << 194;
    uint256 internal constant _ROLE_195 = 1 << 195;
    uint256 internal constant _ROLE_196 = 1 << 196;
    uint256 internal constant _ROLE_197 = 1 << 197;
    uint256 internal constant _ROLE_198 = 1 << 198;
    uint256 internal constant _ROLE_199 = 1 << 199;
    uint256 internal constant _ROLE_200 = 1 << 200;
    uint256 internal constant _ROLE_201 = 1 << 201;
    uint256 internal constant _ROLE_202 = 1 << 202;
    uint256 internal constant _ROLE_203 = 1 << 203;
    uint256 internal constant _ROLE_204 = 1 << 204;
    uint256 internal constant _ROLE_205 = 1 << 205;
    uint256 internal constant _ROLE_206 = 1 << 206;
    uint256 internal constant _ROLE_207 = 1 << 207;
    uint256 internal constant _ROLE_208 = 1 << 208;
    uint256 internal constant _ROLE_209 = 1 << 209;
    uint256 internal constant _ROLE_210 = 1 << 210;
    uint256 internal constant _ROLE_211 = 1 << 211;
    uint256 internal constant _ROLE_212 = 1 << 212;
    uint256 internal constant _ROLE_213 = 1 << 213;
    uint256 internal constant _ROLE_214 = 1 << 214;
    uint256 internal constant _ROLE_215 = 1 << 215;
    uint256 internal constant _ROLE_216 = 1 << 216;
    uint256 internal constant _ROLE_217 = 1 << 217;
    uint256 internal constant _ROLE_218 = 1 << 218;
    uint256 internal constant _ROLE_219 = 1 << 219;
    uint256 internal constant _ROLE_220 = 1 << 220;
    uint256 internal constant _ROLE_221 = 1 << 221;
    uint256 internal constant _ROLE_222 = 1 << 222;
    uint256 internal constant _ROLE_223 = 1 << 223;
    uint256 internal constant _ROLE_224 = 1 << 224;
    uint256 internal constant _ROLE_225 = 1 << 225;
    uint256 internal constant _ROLE_226 = 1 << 226;
    uint256 internal constant _ROLE_227 = 1 << 227;
    uint256 internal constant _ROLE_228 = 1 << 228;
    uint256 internal constant _ROLE_229 = 1 << 229;
    uint256 internal constant _ROLE_230 = 1 << 230;
    uint256 internal constant _ROLE_231 = 1 << 231;
    uint256 internal constant _ROLE_232 = 1 << 232;
    uint256 internal constant _ROLE_233 = 1 << 233;
    uint256 internal constant _ROLE_234 = 1 << 234;
    uint256 internal constant _ROLE_235 = 1 << 235;
    uint256 internal constant _ROLE_236 = 1 << 236;
    uint256 internal constant _ROLE_237 = 1 << 237;
    uint256 internal constant _ROLE_238 = 1 << 238;
    uint256 internal constant _ROLE_239 = 1 << 239;
    uint256 internal constant _ROLE_240 = 1 << 240;
    uint256 internal constant _ROLE_241 = 1 << 241;
    uint256 internal constant _ROLE_242 = 1 << 242;
    uint256 internal constant _ROLE_243 = 1 << 243;
    uint256 internal constant _ROLE_244 = 1 << 244;
    uint256 internal constant _ROLE_245 = 1 << 245;
    uint256 internal constant _ROLE_246 = 1 << 246;
    uint256 internal constant _ROLE_247 = 1 << 247;
    uint256 internal constant _ROLE_248 = 1 << 248;
    uint256 internal constant _ROLE_249 = 1 << 249;
    uint256 internal constant _ROLE_250 = 1 << 250;
    uint256 internal constant _ROLE_251 = 1 << 251;
    uint256 internal constant _ROLE_252 = 1 << 252;
    uint256 internal constant _ROLE_253 = 1 << 253;
    uint256 internal constant _ROLE_254 = 1 << 254;
    uint256 internal constant _ROLE_255 = 1 << 255;
}

File 12 of 19 : OwnedThreeStep.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Three-step single owner authorization mixin.
/// @author SolBase (https://github.com/Sol-DAO/solbase/blob/main/src/auth/OwnedThreeStep.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract OwnedThreeStep {
    /// -----------------------------------------------------------------------
    /// Events
    /// -----------------------------------------------------------------------

    event OwnerUpdateInitiated(address indexed user, address indexed ownerCandidate);

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

    /// -----------------------------------------------------------------------
    /// Custom Errors
    /// -----------------------------------------------------------------------

    error Unauthorized();

    /// -----------------------------------------------------------------------
    /// Ownership Storage
    /// -----------------------------------------------------------------------

    address public owner;

    address internal _ownerCandidate;

    bool internal _ownerCandidateConfirmed;

    modifier onlyOwner() virtual {
        if (msg.sender != owner) revert Unauthorized();

        _;
    }

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    /// @notice Create contract and set `owner`.
    /// @param _owner The `owner` of contract.
    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /// -----------------------------------------------------------------------
    /// Ownership Logic
    /// -----------------------------------------------------------------------

    /// @notice Initiate ownership transfer.
    /// @param newOwner The `_ownerCandidate` that will `confirmOwner()`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        _ownerCandidate = newOwner;

        emit OwnerUpdateInitiated(msg.sender, newOwner);
    }

    /// @notice Confirm ownership between `owner` and `_ownerCandidate`.
    function confirmOwner() public payable virtual {
        if (_ownerCandidateConfirmed) {
            if (msg.sender != owner) revert Unauthorized();

            delete _ownerCandidateConfirmed;

            address newOwner = _ownerCandidate;

            owner = newOwner;

            emit OwnershipTransferred(msg.sender, newOwner);
        } else {
            if (msg.sender != _ownerCandidate) revert Unauthorized();

            _ownerCandidateConfirmed = true;
        }
    }

    /// @notice Terminate ownership by `owner`.
    function renounceOwner() public payable virtual onlyOwner {
        delete owner;

        emit OwnershipTransferred(msg.sender, address(0));
    }
}

File 13 of 19 : Base64.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library to encode and decode strings in Base64.
/// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/Base64.sol)
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(
        bytes memory data,
        bool fileSafe,
        bool noPadding
    ) internal pure returns (string memory result) {
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0230 will translate "-_" + "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230)))

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                // Run over the input, 3 bytes at a time.
                // prettier-ignore
                for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(    ptr    , mload(and(shr(18, input), 0x3F)))
                    mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
                    mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
                    mstore8(add(ptr, 3), mload(and(        input , 0x3F)))
                    
                    ptr := add(ptr, 4) // Advance 4 bytes.
                    // prettier-ignore
                    if iszero(lt(ptr, end)) { break }
                }

                let r := mod(dataLength, 3)

                switch noPadding
                case 0 {
                    // Offset `ptr` and pad with '='. We can simply write over the end.
                    mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
                    mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
                    // Write the length of the string.
                    mstore(result, encodedLength)
                }
                default {
                    // Write the length of the string.
                    mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1))))
                }

                // Allocate the memory for the string.
                // Add 31 and mask with `not(31)` to round the
                // free memory pointer up the next multiple of 32.
                mstore(0x40, and(add(end, 31), not(31)))
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(bytes memory data) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) {
        result = encode(data, fileSafe, false);
    }

    /// @dev Decodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(string memory data) internal pure returns (bytes memory result) {
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let end := add(data, dataLength)
                let decodedLength := mul(shr(2, dataLength), 3)

                switch and(dataLength, 3)
                case 0 {
                    // If padded.
                    decodedLength := sub(
                        decodedLength,
                        add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d))
                    )
                }
                default {
                    // If non-padded.
                    decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                }

                result := mload(0x40)

                // Write the length of the string.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                // prettier-ignore
                for {} 1 {} {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    mstore(ptr, or(
                        and(m, mload(byte(28, input))),
                        shr(6, or(
                            and(m, mload(byte(29, input))),
                            shr(6, or(
                                and(m, mload(byte(30, input))),
                                shr(6, mload(byte(31, input)))
                            ))
                        ))
                    ))

                    ptr := add(ptr, 3)
                    
                    // prettier-ignore
                    if iszero(lt(data, end)) { break }
                }

                // Allocate the memory for the string.
                // Add 32 + 31 and mask with `not(31)` to round the
                // free memory pointer up the next multiple of 32.
                mstore(0x40, and(add(add(result, decodedLength), 63), not(31)))

                // Restore the zero slot.
                mstore(0x60, 0)
            }
        }
    }
}

File 14 of 19 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/LibString.sol)
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
library LibString {
    /// -----------------------------------------------------------------------
    /// Custom Errors
    /// -----------------------------------------------------------------------

    /// @dev The `length` of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /// -----------------------------------------------------------------------
    /// Constants
    /// -----------------------------------------------------------------------

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = uint256(int256(-1));

    /// -----------------------------------------------------------------------
    /// Decimal Operations
    /// -----------------------------------------------------------------------

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 1)
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// -----------------------------------------------------------------------
    /// Hexadecimal Operations
    /// -----------------------------------------------------------------------

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            let m := add(start, and(add(shl(1, length), 0x62), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for {} 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                length := sub(length, 1)
                // prettier-ignore
                if iszero(length) { break }
            }

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := add(sub(end, str), 2)
            // Move the pointer and write the "0x" prefix.
            str := sub(str, 0x20)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            let m := add(start, 0xa0)
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                // prettier-ignore
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := add(sub(end, str), 2)
            // Move the pointer and write the "0x" prefix.
            str := sub(str, 0x20)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the length, 0x02 bytes for the prefix,
            // and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60.
            str := add(start, 0x60)

            // Allocate the memory.
            mstore(0x40, str)
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let length := 20
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                length := sub(length, 1)
                // prettier-ignore
                if iszero(length) { break }
            }

            // Move the pointer and write the "0x" prefix.
            str := sub(str, 32)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, 42)
        }
    }

    /// -----------------------------------------------------------------------
    /// Other String Operations
    /// -----------------------------------------------------------------------

    // For performance and bytecode compactness, all indices of the following operations
    // are byte (ASCII) offsets, not UTF character offsets.

    /// @dev Returns `subject` all occurances of `search` replaced with `replacement`.
    function replace(
        string memory subject,
        string memory search,
        string memory replacement
    ) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        // prettier-ignore
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            // prettier-ignore
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            // prettier-ignore
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            // Zeroize the slot after the string.
            let last := add(add(result, 0x20), k)
            mstore(last, 0)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
            // Store the length of the result.
            mstore(result, k)
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        assembly {
            // prettier-ignore
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    // `result = min(from, subjectLength)`.
                    result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from)))
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)    
                
                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(add(search, 0x20))

                // prettier-ignore
                if iszero(lt(subject, subjectSearchEnd)) { break }

                if iszero(lt(searchLength, 32)) {
                    // prettier-ignore
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        // prettier-ignore
                        if iszero(lt(subject, subjectSearchEnd)) { break }
                    }
                    break
                }
                // prettier-ignore
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        assembly {
            // prettier-ignore
            for {} 1 {} {
                let searchLength := mload(search)
                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) {
                    from := fromMax
                }
                if iszero(mload(search)) {
                    result := from
                    break
                }
                result := not(0) // Initialize to `NOT_FOUND`.

                let subjectSearchEnd := sub(add(subject, 0x20), 1)

                subject := add(add(subject, 0x20), from)
                // prettier-ignore
                if iszero(gt(subject, subjectSearchEnd)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                // prettier-ignore
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(subjectSearchEnd, 1))
                        break
                    }
                    subject := sub(subject, 1)
                    // prettier-ignore
                    if iszero(gt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search) internal pure returns (bool result) {
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength))
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search) internal pure returns (bool result) {
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                // prettier-ignore
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    // prettier-ignore
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        // prettier-ignore
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    // prettier-ignore
                    if iszero(times) { break }
                }
                // Zeroize the slot after the string.
                mstore(output, 0)
                // Store the length.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(
        string memory subject,
        uint256 start,
        uint256 end
    ) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) {
                end := subjectLength
            }
            if iszero(gt(subjectLength, start)) {
                start := subjectLength
            }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                // Copy the `subject` one word at a time, backwards.
                // prettier-ignore
                for { let o := and(add(resultLength, 31), not(31)) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := sub(o, 0x20)
                    // prettier-ignore
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start) internal pure returns (string memory result) {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) {
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) {
        uint256[] memory indices = indicesOf(subject, delimiter);
        assembly {
            if mload(indices) {
                let indexPtr := add(indices, 0x20)
                let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                mstore(sub(indicesEnd, 0x20), mload(subject))
                mstore(indices, add(mload(indices), 1))
                let prevIndex := 0
                // prettier-ignore
                for {} 1 {} {
                    let index := mload(indexPtr)
                    mstore(indexPtr, 0x60)                        
                    if iszero(eq(index, prevIndex)) {
                        let element := mload(0x40)
                        let elementLength := sub(index, prevIndex)
                        mstore(element, elementLength)
                        // Copy the `subject` one word at a time, backwards.
                        // prettier-ignore
                        for { let o := and(add(elementLength, 31), not(31)) } 1 {} {
                            mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                            o := sub(o, 0x20)
                            // prettier-ignore
                            if iszero(o) { break }
                        }
                        // Zeroize the slot after the string.
                        mstore(add(add(element, 0x20), elementLength), 0)
                        // Allocate memory for the length and the bytes,
                        // rounded up to a multiple of 32.
                        mstore(0x40, add(element, and(add(elementLength, 63), not(31))))
                        // Store the `element` into the array.
                        mstore(indexPtr, element)                        
                    }
                    prevIndex := add(index, mload(delimiter))
                    indexPtr := add(indexPtr, 0x20)
                    // prettier-ignore
                    if iszero(lt(indexPtr, indicesEnd)) { break }
                }
                result := indices
                if iszero(mload(delimiter)) {
                    result := add(indices, 0x20)
                    mstore(result, sub(mload(indices), 2))
                }
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b) internal pure returns (string memory result) {
        assembly {
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(mload(a), 32), not(31)) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := sub(o, 0x20)
                // prettier-ignore
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, mload(a))
            // Copy `b` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(bLength, 32), not(31)) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := sub(o, 0x20)
                // prettier-ignore
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes.
                mload(add(a, 0x1f)),
                // `length != 0 && length < 32`. Abuses underflow.
                // Assumes that the length is valid and within the block gas limit.
                lt(sub(mload(a), 1), 0x1f)
            )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behaviour is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes of `a` and `b`.
                or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))),
                // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                // Assumes that the lengths are valid and within the block gas limit.
                lt(sub(add(aLength, mload(b)), 1), 0x1e)
            )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
    function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) {
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(add(a, 0x20), mload(a)), 0)
            // Store the return offset.
            // Assumes that the string does not start from the scratch space.
            mstore(sub(a, 0x20), 0x20)
            // End the transaction, returning the string.
            return(sub(a, 0x20), add(mload(a), 0x40))
        }
    }
}

File 15 of 19 : base64.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @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 (string memory) {
        if (data.length == 0) return '';

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

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

        // add some extra buffer at the end required for the writing
        string memory result = new string(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
        uint256 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 16 of 19 : PixelPoolyAssets.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { OwnedThreeStep } from "@turbo-eth/solbase-sol/src/auth/OwnedThreeStep.sol";
import { svg } from "@erc721k/periphery-sol/contracts/svg/svg.sol";
import { svgUtils } from "@erc721k/periphery-sol/contracts/svg/svgUtils.sol";
import { IAssets } from "./interfaces/IAssets.sol";
import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol";

contract PixelPoolyAssets is IAssets, OwnedThreeStep {
  mapping(bytes32 => Element) private elements;

  // holds the total amount of frames added per layer
  uint16[] private totalFrames; // perhaps rename to something like totalFramesPerLayerArray
  // mapping of layer id to totalFrames index
  mapping(uint8 => IndexInfo) private layerIndex;

  /// -----------------------------------------------------------------------
  /// Constructor
  /// -----------------------------------------------------------------------

  constructor(address owner) OwnedThreeStep(owner) {}

  /// -----------------------------------------------------------------------
  /// External Functions
  /// -----------------------------------------------------------------------

  // ========================
  // READS
  // ========================

  function getElement(bytes32 eid) external view returns (IAssets.Element memory) {
    return elements[eid];
  }

  function getElement(IPixelCharacter.Layer layer, uint8 frame)
    external
    view
    returns (IAssets.Element memory)
  {
    return elements[_eid(layer, frame)];
  }

  function getSVG(bytes32 eid) external view returns (string memory) {
    return elements[eid].svg;
  }

  function getSVG(IPixelCharacter.Layer layer, uint8 frame) external view returns (string memory) {
    return elements[_eid(layer, frame)].svg;
  }

  function getDetails(bytes32 eid) external view returns (string memory, string memory) {
    Element memory e = elements[eid];
    return (e.traitName, e.traitDescription);
  }

  function getDetails(uint256 layer, uint8 frame)
    external
    view
    returns (string memory, string memory)
  {
    Element memory e = elements[_eid(layer, frame)];
    return (e.traitName, e.traitDescription);
  }

  function getDetails(IPixelCharacter.Layer layer, uint8 frame)
    external
    view
    returns (string memory, string memory)
  {
    Element memory e = elements[_eid(layer, frame)];
    return (e.traitName, e.traitDescription);
  }

  function getStats(bytes32 eid) external view returns (IAssets.Stat[] memory stats) {
    return elements[eid].stats;
  }

  function getStats(IPixelCharacter.Layer layer, uint8 frame)
    external
    view
    returns (IAssets.Stat[] memory stats)
  {
    return elements[_eid(layer, frame)].stats;
  }

  function getStats(uint256 layer, uint8 frame)
    external
    view
    returns (IAssets.Stat[] memory stats)
  {
    return elements[_eid(layer, frame)].stats;
  }

  function getTier(bytes32 eid) external view returns (Tier) {
    return elements[eid].tier;
  }

  function getExpiryTime(bytes32 eid) external view returns (uint256) {
    return elements[eid].expiry;
  }

  function getIndexInfoFromLayer(IPixelCharacter.Layer layer) external view returns (IndexInfo memory) {
    return layerIndex[uint8(layer)];
  }

  function getTotalFrames() external view returns (uint16[] memory) {
    return totalFrames;
  }

  function getTotalLayers() external view returns (uint256) {
    return totalFrames.length;
  }

  function getTotalPossibleFrames() external view returns (uint256) {
    uint16 maxFrames_ = 0;
    for (uint8 i = 0; i < totalFrames.length; i++) {
      if (totalFrames[i] > maxFrames_){
        maxFrames_ = totalFrames[i];
      }
    }
    return maxFrames_ * totalFrames.length;
  }

  function getLayerFromIndex(uint256 _index) external view returns (uint8 layerId) {
    bool found_ = false;
    for (uint8 i = 0; i < 12; i++) {
      IndexInfo memory info_ = layerIndex[i];
      if (info_.isSet && info_.index == _index){
        layerId = i;
        found_ = true;
      }
    }
    require(found_, "PixelPoolyAssets:Layer-Index-not-found");
  }

  function getTotalFramesPerLayer(IPixelCharacter.Layer layer) external view returns (uint16) {
    IndexInfo memory info_ = layerIndex[uint8(layer)];
    require(info_.isSet, "PixelPoolyAssets:Layer-not-found");
    return totalFrames[info_.index];
  }

  // ========================
  // WRITES
  // ========================

  function setElement(
    IPixelCharacter.Layer layer,
    uint8 frame,
    Element memory element
  ) external onlyOwner {
    require(element.id == _eid(layer, frame), "PixelPoolyAssets: invalid element id");

    if (elements[element.id].id != element.id){
      // if the element is new, then increment the counter

      IndexInfo memory info_ = layerIndex[uint8(layer)];
      if (info_.isSet){
        // this means the index value of info_ is real
        // totalFrames[info_.index] = totalFrames[info_.index]++;
        totalFrames[info_.index]++;
      } else {
        // this means the layer is not being tracked in the array yet
        info_.index = uint8(totalFrames.length);
        info_.isSet = true;
        layerIndex[uint8(layer)] = info_;
        totalFrames.push(1);
      }

      elements[element.id].id = element.id;
    }

    elements[element.id].tier = element.tier;
    elements[element.id].layer = element.layer;
    elements[element.id].frame = element.frame;
    elements[element.id].expiry = element.expiry;
    elements[element.id].traitName = element.traitName;
    elements[element.id].traitDescription = element.traitDescription;
    elements[element.id].svg = element.svg;
    for (uint256 i = 0; i < element.stats.length; i++) {
      elements[element.id].stats.push(element.stats[i]);
    }
  }

  function setSVG(
    IPixelCharacter.Layer layer,
    uint8 frame,
    string memory svg_
  ) external onlyOwner {
    elements[_eid(layer, frame)].svg = svg_;
  }

  function setTraitData(
    IPixelCharacter.Layer layer,
    uint8 frame,
    string memory traitName,
    string memory traitDescription
  ) external onlyOwner {
    bytes32 __eid = _eid(layer, frame);
    elements[__eid].traitName = traitName;
    elements[__eid].traitDescription = traitDescription;
  }

  /// -----------------------------------------------------------------------
  /// Internal
  /// -----------------------------------------------------------------------

  function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(uint8(layer), frame));
  }

  function _eid(uint256 layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(uint8(layer), frame));
  }
}

File 17 of 19 : PixelPoolyStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { Base64 } from "base64-sol/base64.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ERC721Storage } from "@erc721k/core-sol/contracts/ERC721Storage.sol";
import { IERC721KImage } from "@erc721k/core-sol/contracts/interfaces/IERC721KImage.sol";
import { IERC721KTraits } from "@erc721k/core-sol/contracts/interfaces/IERC721KTraits.sol";
import { IAssets } from "./interfaces/IAssets.sol";
import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol";
import { PixelPoolyAssets } from "./PixelPoolyAssets.sol";
import { PixelStore } from "./PixelStore.sol";

/**
 * @title PixelPoolyStorage
 * @author Kames Geraghty
 */
contract PixelPoolyStorage is IPixelCharacter, ERC721Storage {
  /// @notice Manager role
  uint256 private immutable CONTROLLER_ROLE = 1e18;
  uint256 private immutable SIGNAL_ROLE = 2e18;

  struct SignalDetails {
    string signalDescription;
    uint8 frameId;
    uint256 expiry;
  }

  // @notice Toggle the "Birb Signal" for all active Pixel Pooly NFTs.
  SignalDetails private _signalDetails;

  address public erc721KInstance;
  address public erc721ImageSignalRenderInstance;
  address public erc721ImageSignalTraitsInstance;

  /// @notice PixelStore instance
  address public pixelStoreInstance;

  /// @notice PixelPoolyAssets instance
  address public pixelPoolyAssetsInstance;

  /// @notice The active item frames for a Pixel Pooly character.
  mapping(uint256 => Character) private _characters;

  /// @notice Track unlocked item frames using a bytes32 (hash of item type and frame)
  mapping(uint256 => mapping(bytes32 => bool)) private _elements;

  /// -----------------------------------------------------------------------
  /// Constructor
  /// -----------------------------------------------------------------------

  constructor(
    address pixelPoolyRender,
    address pixelPoolyTraits,
    ContractURI memory _contractURI
  ) ERC721Storage(pixelPoolyRender, pixelPoolyTraits, _contractURI) {}

  /// -----------------------------------------------------------------------
  /// Overrides & EIP Functions
  /// -----------------------------------------------------------------------
  function constructTokenURI(
    uint256 tokenId,
    bytes memory input0,
    bytes memory input1
  ) external view override returns (string memory uri) {
    if (_isSignalActive()) {
      return _signalRender(tokenId, input1);
    } else {
      string memory image_ = IERC721KImage(svgRenderInstance).render(input0);
      string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1);
      return
        string(
          abi.encodePacked(
            "data:application/json;base64,",
            Base64.encode(
              bytes(
                string.concat(
                  '{"name":',
                  '"',
                  _parseName(tokenId),
                  '",',
                  '"description":',
                  '"',
                  _parseDescription(tokenId),
                  '",',
                  '"image":',
                  '"',
                  image_,
                  '",',
                  '"attributes": [',
                  traits_,
                  "]",
                  "}"
                )
              )
            )
          )
        );
    }
  }

  /// -----------------------------------------------------------------------
  /// External Functions
  /// -----------------------------------------------------------------------

  // ========================
  // READS
  // ========================

  function getCharacter(uint256 tokenId) external view returns (Character memory) {
    return _characters[tokenId];
  }

  function getUnlockedTraits(uint256 tokenId) external view returns (IAssets.Element[] memory traits) {
    uint256 maxPossibleElements_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getTotalPossibleFrames();
    IAssets.Element[] memory unlockedTraits_ = new IAssets.Element[](maxPossibleElements_);

    uint16 index_ = 0;
    uint16[] memory totalFramesArray_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getTotalFrames();

    for (uint8 i = 0; i < totalFramesArray_.length; i++) {
      uint8 layer_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getLayerFromIndex(i);
      for (uint8 frame_ = 0; frame_ < totalFramesArray_[i]; frame_++) {
        bytes32 eid_ = _eid(layer_, frame_);
        if (_elements[tokenId][eid_]){
          IAssets.Element memory element_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getElement(eid_);
          unlockedTraits_[index_++] = element_;
        }
      }
    }

    IAssets.Element[] memory unlockedTraitsCleaned = new IAssets.Element[](index_);

    for (uint16 j = 0; j < index_; j++) {
      unlockedTraitsCleaned[j] = unlockedTraits_[j];
    }

    return unlockedTraitsCleaned;
  }

  function getStore() external view returns (address) {
    return pixelStoreInstance;
  }

  /**
   * @notice Get the active item frames for a Pixel Pooly character
   * @param tokenId uint256 - The token ID to query
   * @return bytes - Packed bytes of the active item frames
   */
  function getPreview(uint256 tokenId) external view returns (bytes memory) {
    return _imageBytes(tokenId);
  }

  /**
   * @notice Get the active item frames for a Pixel Pooly character
   * @param tokenId uint256 - The token ID to query
   * @return bytes - Packed bytes of the active item frames
   */
  function getImageBytes(uint256 tokenId) external view returns (bytes memory) {
    return _imageBytes(tokenId);
  }

  function _imageBytes(uint256 tokenId) internal view returns (bytes memory) {
    Character memory character = _characters[tokenId];
    return
      bytes(
        abi.encodePacked(
          character.bg,
          character.right,
          character.left,
          character.body,
          character.bodyAcc,
          character.head,
          character.headAcc,
          character.bodyLower,
          character.leftAcc,
          character.rightAcc,
          character.faceAcc,
          character.wildcard
        )
      );
  }

  /**
   * @notice Get the trait bytes for a Pixel Pooly character
   * @param tokenId uint256 - The token ID to query.
   * @return bytes - Packed instructions for traits PixelPoolyTraits
   */
  function getTraitsBytes(uint256 tokenId) external view returns (bytes memory) {
    Character memory character = _characters[tokenId];

    string[] memory traitTypes = new string[](12);
    traitTypes[0] = "Background";
    traitTypes[1] = "Right";
    traitTypes[2] = "Left";
    traitTypes[3] = "Body";
    traitTypes[4] = "Body Acc";
    traitTypes[5] = "Head";
    traitTypes[6] = "Head Acc";
    traitTypes[7] = "Body Lower";
    traitTypes[8] = "Left Acc";
    traitTypes[9] = "Right Acc";
    traitTypes[10] = "Face Acc";
    traitTypes[11] = "Wildcard";

    uint8[] memory values = new uint8[](12);
    values[0] = character.bg;
    values[1] = character.right;
    values[2] = character.left;
    values[3] = character.body;
    values[4] = character.bodyAcc;
    values[5] = character.head;
    values[6] = character.headAcc;
    values[7] = character.bodyLower;
    values[8] = character.leftAcc;
    values[9] = character.rightAcc;
    values[10] = character.faceAcc;
    values[11] = character.wildcard;

    string[] memory traitValues = new string[](12);
    uint8[] memory statsValues = new uint8[](7);

    for (uint256 index = 0; index < traitTypes.length; index++) {
      (string memory name, string memory description) = PixelPoolyAssets(pixelPoolyAssetsInstance)
        .getDetails(index, values[index]);
      traitValues[index] = string.concat(name, " - ", description);
      IAssets.Stat[] memory stats = PixelPoolyAssets(pixelPoolyAssetsInstance).getStats(
        index,
        values[index]
      );
      statsValues = _calculateTotalStats(statsValues, stats);
    }

    IERC721KTraits.DisplayType[] memory displayTypes = new IERC721KTraits.DisplayType[](12);
    for (uint256 index = 0; index < displayTypes.length; index++) {
      displayTypes[index] = IERC721KTraits.DisplayType.Base;
    }

    return bytes(abi.encode(traitTypes, traitValues, displayTypes, statsValues));
  }

  function formatTraitsBytesFromCharacter(Character memory character)
    external
    view
    returns (bytes memory)
  {
    return _getTraitsBytes(character);
  }

  function formatTraitsBytes(bytes memory data) external view returns (bytes memory) {
    (
      uint8 background,
      uint8 right,
      uint8 left,
      uint8 body,
      uint8 bodyAcc,
      uint8 head,
      uint8 headAcc,
      uint8 bodyLower,
      uint8 leftAcc,
      uint8 rightAcc,
      uint8 faceAcc,
      uint8 wildcard
    ) = abi.decode(
        data,
        (uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8)
      );

    Character memory character = Character(
      background,
      right,
      left,
      body,
      bodyAcc,
      head,
      headAcc,
      bodyLower,
      leftAcc,
      rightAcc,
      faceAcc,
      wildcard
    );
    return _getTraitsBytes(character);
  }

  function _getTraitsBytes(Character memory character) internal view returns (bytes memory) {
    string[] memory traitTypes = new string[](12);
    traitTypes[0] = "Background";
    traitTypes[1] = "Right";
    traitTypes[2] = "Left";
    traitTypes[3] = "Body";
    traitTypes[4] = "Body Acc";
    traitTypes[5] = "Head";
    traitTypes[6] = "Head Acc";
    traitTypes[7] = "Body Lower";
    traitTypes[8] = "Left Acc";
    traitTypes[9] = "Right Acc";
    traitTypes[10] = "Face Acc";
    traitTypes[11] = "Wildcard";

    uint8[] memory values = new uint8[](12);
    values[0] = character.bg;
    values[1] = character.right;
    values[2] = character.left;
    values[3] = character.body;
    values[4] = character.bodyAcc;
    values[5] = character.head;
    values[6] = character.headAcc;
    values[7] = character.bodyLower;
    values[8] = character.leftAcc;
    values[9] = character.rightAcc;
    values[10] = character.faceAcc;
    values[11] = character.wildcard;

    string[] memory traitValues = new string[](12);
    uint8[] memory statsValues = new uint8[](7);

    for (uint256 index = 0; index < traitTypes.length; index++) {
      (string memory name, string memory description) = PixelPoolyAssets(pixelPoolyAssetsInstance)
        .getDetails(index, values[index]);
      traitValues[index] = string.concat(name, " - ", description);
      IAssets.Stat[] memory stats = PixelPoolyAssets(pixelPoolyAssetsInstance).getStats(
        index,
        values[index]
      );
      statsValues = _calculateTotalStats(statsValues, stats);
    }

    IERC721KTraits.DisplayType[] memory displayTypes = new IERC721KTraits.DisplayType[](12);
    for (uint256 index = 0; index < displayTypes.length; index++) {
      displayTypes[index] = IERC721KTraits.DisplayType.Base;
    }

    return bytes(abi.encode(traitTypes, traitValues, displayTypes, statsValues));
  }

  function _calculateTotalStats(uint8[] memory statsCounter, IAssets.Stat[] memory itemStats)
    internal
    pure
    returns (uint8[] memory)
  {
    for (uint256 index = 0; index < itemStats.length; index++) {
      statsCounter[uint256(itemStats[index].category)] += itemStats[index].value;
    }
    return statsCounter;
  }

  function isFrameActive(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external view returns (bool) {
    bytes32 eid = keccak256(abi.encodePacked(layer, frame));
    return _elements[tokenId][eid];
  }

  // ========================
  // WRITES
  // ========================

  function setSignal(
    SignalDetails calldata signal
  ) external {
    require(hasAllRoles(msg.sender, SIGNAL_ROLE), "PixelPooly:signal-unauthorized");
    _signalDetails = signal;
  }

  function disableElement(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external {
    require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:lock-unauthorized");
    _elements[tokenId][_eid(layer, frame)] = false;
  }

  function enableElement(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external {
    require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:unlock-unauthorized");
    _elements[tokenId][_eid(layer, frame)] = true;
  }

  function setLayerFrameByOwner(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external {
    require(
      IERC721(erc721KInstance).ownerOf(tokenId) == msg.sender,
      "PixelPooly:owner-unauthorized"
    );
    bytes32 eid = _eid(layer, frame);
    require(_elements[tokenId][eid], "PixelPooly:layer-frame-not-unlocked");
    _setLayerFrame(tokenId, layer, frame);
  }

  function setLayerFrameByERC721K(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external {
    require(msg.sender == erc721KInstance, "PixelPooly:unauthorized");
    bytes32 eid = _eid(layer, frame);
    require(_elements[tokenId][eid], "PixelPooly:layer-frame-not-unlocked");
    _setLayerFrame(tokenId, layer, frame);
  }

  function setLayerFrameByController(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) external {
    require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:unlock-unauthorized");
    bytes32 eid = _eid(layer, frame);
    _elements[tokenId][eid] = true;
    _setLayerFrame(tokenId, layer, frame);
  }

  function _setLayerFrame(
    uint256 tokenId,
    IPixelCharacter.Layer layer,
    uint8 frame
  ) internal {
    if (layer == Layer.Background) {
      _characters[tokenId].bg = frame;
    } else if (layer == Layer.Body) {
      _characters[tokenId].body = frame;
    } else if (layer == Layer.Head) {
      _characters[tokenId].head = frame;
    } else if (layer == Layer.BodyLower) {
      _characters[tokenId].bodyLower = frame;
    } else if (layer == Layer.Left) {
      _characters[tokenId].left = frame;
    } else if (layer == Layer.Right) {
      _characters[tokenId].right = frame;
    } else if (layer == Layer.HeadAcc) {
      _characters[tokenId].headAcc = frame;
    } else if (layer == Layer.FaceAcc) {
      _characters[tokenId].faceAcc = frame;
    } else if (layer == Layer.BodyAcc) {
      _characters[tokenId].bodyAcc = frame;
    } else if (layer == Layer.Wildcard) {
      _characters[tokenId].wildcard = frame;
    } else if (layer == Layer.LeftAcc) {
      _characters[tokenId].leftAcc = frame;
    } else if (layer == Layer.RightAcc) {
      _characters[tokenId].rightAcc = frame;
    }
  }

  /// ------------------------
  /// Administrative
  /// ------------------------

  function setERC721KInstance(address _erc721KInstance) external onlyOwner {
    erc721KInstance = _erc721KInstance;
  }

  function setPixelStoreInstance(address _pixelStoreInstance) external onlyOwner {
    pixelStoreInstance = _pixelStoreInstance;
  }

  function setPixelPoolyAssetsInstance(address _pixelPoolyAssetsInstance) external onlyOwner {
    pixelPoolyAssetsInstance = _pixelPoolyAssetsInstance;
  }

  /// -----------------------------------------------------------------------
  /// Internal Functions
  /// -----------------------------------------------------------------------

  function _isSignalActive() internal view returns (bool) {
    return _signalDetails.expiry > block.timestamp;
  }

  function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(uint8(layer), frame));
  }

  function _eid(uint8 layer, uint8 frame) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(layer, frame));
  }

  function _parseName(uint256 _tokenId) internal view override returns (string memory) {
    return string.concat("PixelPooly #", Strings.toString(_tokenId));
  }

  function _parseDescription(uint256 _tokenId) internal view override returns (string memory) {
    return string.concat("Member ", Strings.toString(_tokenId), " of the DeFi Defenders Flock");
  }

  function _signalRender(uint256 tokenId, bytes memory input1) internal view returns (string memory uri) {
    string memory image_ = IERC721KImage(svgRenderInstance).render(bytes(
      abi.encodePacked(
        _signalDetails.frameId
      )
    ));
    string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1);
    return
      string(
        abi.encodePacked(
          "data:application/json;base64,",
          Base64.encode(
            bytes(
              string.concat(
                '{"name":',
                '"',
                _parseName(tokenId),
                '",',
                '"description":',
                '"',
                _signalDetails.signalDescription,
                '",',
                '"image":',
                '"',
                image_,
                '",',
                '"attributes": [',
                traits_,
                "]",
                "}"
              )
            )
          )
        )
      );
  }
}

File 18 of 19 : IAssets.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { IPixelCharacter } from "./IPixelCharacter.sol";

interface IAssets {
  enum Tier {
    COMMON,
    RARE,
    EPIC,
    LEGENDARY
  }

  enum StatCategory {
    STRENGTH,
    DEFENSE,
    CONSTITUTION,
    INTELLIGENCE,
    CHARISMA,
    WISDOM,
    LUCK
  }

  struct Stat {
    StatCategory category;
    uint8 value;
  }

  struct Element {
    bytes32 id;
    Tier tier;
    IPixelCharacter.Layer layer;
    uint8 frame;
    uint256 expiry;
    string traitName;
    string traitDescription;
    Stat[] stats;
    string svg;
  }

  struct IndexInfo {
    uint8 index;
    bool isSet;
  }

  function getElement(bytes32 eid) external view returns (Element memory);

  function getElement(IPixelCharacter.Layer layer, uint8 frame)
    external
    view
    returns (Element memory);

  function getDetails(bytes32 eid) external view returns (string memory, string memory);

  function getSVG(bytes32 eid) external view returns (string memory);

  function getSVG(IPixelCharacter.Layer layer, uint8 frame) external view returns (string memory);

  function getStats(bytes32 eid) external view returns (Stat[] memory);

  function getStats(IPixelCharacter.Layer layer, uint8 frame) external view returns (Stat[] memory);
}

File 19 of 19 : IPixelCharacter.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

interface IPixelCharacter {
  enum Layer {
    Background,
    Right,
    Left,
    Body,
    BodyAcc,
    Head,
    HeadAcc,
    BodyLower,
    LeftAcc,
    RightAcc,
    FaceAcc,
    Wildcard,
    Signal
  }

  struct Character {
    uint8 bg;
    uint8 right;
    uint8 left;
    uint8 body;
    uint8 bodyAcc;
    uint8 head;
    uint8 headAcc;
    uint8 bodyLower;
    uint8 leftAcc;
    uint8 rightAcc;
    uint8 faceAcc;
    uint8 wildcard;
  }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "viaIR": true,
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"enum IAssets.Tier[]","name":"tiers","type":"uint8[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"},{"internalType":"address","name":"_pixelAssets","type":"address"},{"internalType":"address","name":"_birbAddress_","type":"address"},{"internalType":"address","name":"_flapAddress_","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"inputs":[],"name":"birbAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"erc721KStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flapAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"getFramePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"layers","type":"uint8[]"},{"internalType":"uint8[]","name":"frames","type":"uint8[]"}],"name":"getFramePrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IAssets.Tier","name":"tier","type":"uint8"}],"name":"getTierPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IAssets.Tier[]","name":"tiers","type":"uint8[]"}],"name":"getTierPrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"isEnabled","outputs":[{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"ordinalsFromRoles","outputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pixelAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"release","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"name":"rolesFromOrdinals","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc721KStorage","type":"address"}],"name":"setERC721KStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"unlock","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer[]","name":"layer","type":"uint8[]"},{"internalType":"uint8[]","name":"frame","type":"uint8[]"}],"name":"unlockBatch","outputs":[],"stateMutability":"payable","type":"function"}]

608060405234620001015762001a47803803806200001d8162000137565b92833981019060c081830312620001015780516001600160401b039190828111620001015781019183601f8401121562000101578251926200006962000063856200016c565b62000137565b9081948083526020808094019160051b8301019187831162000101578301905b82821062000106575050508201519081116200010157620000f193620000b191830162000196565b620000bf60408301620001f3565b620000cd60608401620001f3565b91620000ea60a0620000e260808701620001f3565b9501620001f3565b9462000297565b6040516116799081620003ce8239f35b600080fd5b815160048110156200010157815290830190830162000089565b50634e487b7160e01b600052604160045260246000fd5b6040519190601f01601f191682016001600160401b038111838210176200015d57604052565b6200016762000120565b604052565b6020906001600160401b03811162000186575b60051b0190565b6200019062000120565b6200017f565b9080601f830112156200010157815190620001b562000063836200016c565b9182938184526020808095019260051b82010192831162000101578301905b828210620001e3575050505090565b81518152908301908301620001d4565b51906001600160a01b03821682036200010157565b6000198114620002185760010190565b634e487b7160e01b600052601160045260246000fd5b8051821015620002435760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b516004811015620002675790565b634e487b7160e01b600052602160045260246000fd5b600481101562000267576000526005602052604060002090565b90929594600060045560018060a01b0380911680638b78c6d8195560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3600180546001600160a01b03191691909316178255805183510362000388576000825b62000338575b5050600280546001600160a01b039687166001600160a01b0319918216179091556003805494909616931692909217909355509050565b8151811015620003825780620003536200037b92866200022e565b51620003746200036e6200036884876200022e565b62000259565b6200027d565b5562000208565b82620002fb565b62000301565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a20696e76616c696420696e707574000000000000006044820152606490fdfe60806040526004361015610013575b600080fd5b60003560e01c80630357371d1461026b57806304a7ec781461026257806313a661ed14610259578063183a4f6e146102505780631c10893f146102475780631cd64df41461023e57806325692962146102355780632b28bb121461022c5780632de94807146102235780634a4ee7b11461021a5780634bc3201d1461021157806350ca771b14610208578063514e62fc146101ff5780635329d742146101f657806354d1f13d146101ed5780635542686d146101e45780635586402d146101db578063642be325146101d2578063715018a6146101c95780637359e41f146101c057806374a0024a146101b75780638da5cb5b146101ae578063a3c2c462146101a5578063ac35ccfa1461019c578063d1f0befb14610193578063d7533f021461018a578063e21de2e814610181578063f04e283e14610178578063f2fde38b1461016f5763fee81cf41461016757600080fd5b61000e610f0f565b5061000e610ea3565b5061000e610e0c565b5061000e610de2565b5061000e610dc3565b5061000e610c84565b5061000e610bdb565b5061000e610b81565b5061000e610b53565b5061000e610b0a565b5061000e610aa9565b5061000e610a14565b5061000e6109ea565b5061000e61099d565b5061000e61084b565b5061000e6107d0565b5061000e6107a6565b5061000e61076b565b5061000e61072e565b5061000e610704565b5061000e6106d5565b5061000e6106a0565b5061000e61066b565b5061000e6105e3565b5061000e61059c565b5061000e61052f565b5061000e610516565b5061000e6104a8565b5061000e6102f8565b5061000e61028a565b600435906001600160a01b038216820361000e57565b503461000e57604036600319011261000e576102a4610274565b638b78c6d8195433036102df576040516102dd916000918291829190602435906001600160a01b03165af16102d76110fb565b50611149565b005b6382b429006000526004601cfd5b60ff81160361000e57565b503461000e57604036600319011261000e5761038061036f60043561031c816102ed565b602061033f60243561032d816102ed565b6001546001600160a01b0316936115a1565b602460405180948193636e4e8d4f60e11b835260048301525afa9081156103b2575b600091610384575b50610f90565b546040519081529081906020820190565b0390f35b6103a5915060203d81116103ab575b61039d81836103f7565b81019061106d565b38610369565b503d610393565b6103ba611082565b610361565b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116103ea57604052565b6103f26103bf565b604052565b90601f8019910116810190811067ffffffffffffffff8211176103ea57604052565b60209067ffffffffffffffff8111610433575b60051b0190565b61043b6103bf565b61042c565b81601f8201121561000e5780359161045783610419565b9261046560405194856103f7565b808452602092838086019260051b82010192831161000e578301905b82821061048f575050505090565b838091833561049d816102ed565b815201910190610481565b503461000e5760208060031936011261000e5760043567ffffffffffffffff811161000e576104db903690600401610440565b906000918180820191805160051b01015b8082036104fe57604051848152602090f35b90918093600160ff8551161b179201909291926104ec565b50602036600319011261000e576102dd60043533610f2e565b50604036600319011261000e57610544610274565b638b78c6d8195433036102df57638b78c6d88160601b1760005260206000209060243582541780925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3005b503461000e57604036600319011261000e5760206105b8610274565b638b78c6d86024359160601b176000528082600020541614604051908152f35b600091031261000e57565b506000806003193601126106325763389a75e13360601b1781526202a30042016020822055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a2604051f35b80fd5b600d111561000e57565b606090600319011261000e576004359060243561065b81610635565b90604435610668816102ed565b90565b503461000e5761068361067d3661063f565b916115d4565b6000526007602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e57638b78c6d86106bf610274565b60601b1760005260208060002054604051908152f35b50604036600319011261000e576106ea610274565b638b78c6d8195433036102df576102dd9060243590610f2e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b5061074c61074461073e3661063f565b9161126e565b341015611099565b610758346004546110e5565b6004553461076257005b6102dd34611461565b503461000e57604036600319011261000e57638b78c6d861078a610274565b60601b1760005260206024358160002054161515604051908152f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b506000806003193601126106325763389a75e13360601b178152806020812055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a2604051f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b50606036600319011261000e5760043567ffffffffffffffff60243581811161000e5761087c90369060040161081a565b92909160443590811161000e5761089790369060040161081a565b8085036109285792919060009384955b8087106108d5576108ba86341015611099565b6108ce6108c9346004546110e5565b600455565b3461076257005b909192939461091761091d916109116108f76108f28b878c611033565b6110f1565b61090a6109058c898b611033565b61108f565b908961126e565b906110e5565b96611000565b9594939291906108a7565b60405162461bcd60e51b815260206004820152603a60248201527f506978656c53746f72653a206c6179657220616e64206672616d65206172726160448201527f7973206d757374206265207468652073616d65206c656e6774680000000000006064820152608490fd5b6004111561000e57565b503461000e57602036600319011261000e576004356109bb81610993565b60048110156109dd575b60005260056020526020604060002054604051908152f35b6109e5610f79565b6109c5565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b5060008060031936011261063257638b78c6d81980543303610a5e57819081337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355604051f35b6382b4290082526004601cfd5b6020908160408183019282815285518094520193019160005b828110610a92575050505090565b835160ff1685529381019392810192600101610a84565b503461000e5760208060031936011261000e576004358160405101906000915b828152838260051b16019060011c918215610ae8576001019190610ac9565b60408051838252808403601f190160051c815290519081906103809082610a6b565b503461000e57602036600319011261000e57610b24610274565b638b78c6d8195433036102df57600080546001600160a01b0319166001600160a01b0392909216919091179055005b503461000e57600036600319011261000e57638b78c6d819546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576020600454604051908152f35b6020908160408183019282815285518094520193019160005b828110610bc7575050505090565b835185529381019392810192600101610bb9565b503461000e5760208060031936011261000e57600490813567ffffffffffffffff811161000e57610c0f903690840161081a565b9091610c1a82610fb7565b93600092835b818110610c3557604051806103808982610ba0565b80610c44610c72928489611033565b35610c4e81610993565b84811015610c77575b8652600585526040862054610c6c828a61104b565b52611000565b610c20565b610c7f610f79565b610c57565b503461000e5760408060031936011261000e57600467ffffffffffffffff813581811161000e57610cb89036908401610440565b9060243590811161000e57610cd0903690840161081a565b610cd981610fb7565b936000928392610d02610cf6610cf660015460018060a01b031690565b6001600160a01b031690565b935b818110610d18578851806103808a82610ba0565b80610d89610d6e610d4d610d39610d32610d94968d61104b565b5160ff1690565b610d4761090586898b611033565b906115a1565b8c5190636e4e8d4f60e11b8252818060209485938c83019190602083019252565b03818c5afa918215610db6575b8a92610d99575b5050610f90565b54610c6c828b61104b565b610d04565b610daf9250803d106103ab5761039d81836103f7565b3880610d82565b610dbe611082565b610d7b565b503461000e57600036600319011261000e5760206040516202a3008152f35b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b50602036600319011261000e57610e21610274565b638b78c6d819805433036102df57606082901b6bffffffffffffffffffffffff191663389a75e117600090815260208120805491936001600160a01b031692914211610e965783905581337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08580a355604051f35b636f5e881884526004601cfd5b50602036600319011261000e57610eb8610274565b638b78c6d81990815433036102df576001600160a01b0316908115610f015781337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b637448fbae6000526004601cfd5b503461000e57602036600319011261000e5763389a75e16106bf610274565b638b78c6d88160601b1760005260206000209182549081161880925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3565b50634e487b7160e01b600052602160045260246000fd5b6004811015610faa575b6000526005602052604060002090565b610fb2610f79565b610f9a565b90610fc182610419565b610fce60405191826103f7565b8281528092610fdf601f1991610419565b0190602036910137565b50634e487b7160e01b600052601160045260246000fd5b6001906000198114611010570190565b611018610fe9565b0190565b50634e487b7160e01b600052603260045260246000fd5b91908110156110435760051b0190565b61043b61101c565b6020918151811015611060575b60051b010190565b61106861101c565b611058565b9081602091031261000e575161066881610993565b506040513d6000823e3d90fd5b35610668816102ed565b156110a057565b60405162461bcd60e51b815260206004820152601e60248201527f506978656c53746f72653a20496e73756666696369656e742066756e647300006044820152606490fd5b81198111611010570190565b3561066881610635565b3d15611144573d9067ffffffffffffffff8211611137575b6040519161112b601f8201601f1916602001846103f7565b82523d6000602084013e565b61113f6103bf565b611113565b606090565b1561115057565b60405162461bcd60e51b815260206004820152601d60248201527f506978656c53746f72653a6574682d72656c656173652d6661696c65640000006044820152606490fd5b1561119c57565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a706978656c2d756e6c6f636b6564000000000000006044820152606490fd5b9081602091031261000e575190565b156111f757565b60405162461bcd60e51b815260206004820152601860248201527f506978656c53746f72653a74726169742d6578706972656400000000000000006044820152606490fd5b91604091949360ff9160608501968552600d811015611261575b602085015216910152565b611269610f79565b611256565b9161127a8183856115d4565b916112a86112a361129f611298866000526007602052604060002090565b5460ff1690565b1590565b611195565b6112b28282611544565b926113286112cd610cf6610cf660015460018060a01b031690565b6040516312288acf60e21b815260048101879052602096879290918381602481855afa908115611454575b600091611427575b5080611416575b506040518080958194636e4e8d4f60e11b8352600483019190602083019252565b03915afa948515611409575b6000956113ea575b505060005461135590610cf6906001600160a01b031681565b91823b1561000e5760006113bb946113ae9461138d93836113c09a60405196879586948593634f604be760e11b85526004850161123c565b03925af180156113dd575b6113c4575b506000526007602052604060002090565b805460ff19166001179055565b610f90565b5490565b806113d16113d7926103d6565b806105d8565b3861139d565b6113e5611082565b611398565b611401929550803d106103ab5761039d81836103f7565b92388061133c565b611411611082565b611334565b6114219042106111f0565b38611307565b6114479150843d861161144d575b61143f81836103f7565b8101906111e1565b38611300565b503d611435565b61145c611082565b6112f8565b806000190460501181151516611537575b606460508202049081811061152a575b60018060a01b03600254169060008080809486604051915af16114a36110fb565b50156114da578180916114d8946114c7610cf6610cf660035460018060a01b031690565b906040519203905af16102d76110fb565b565b60405162461bcd60e51b815260206004820152602260248201527f506978656c53746f72653a626972622d6574682d72656c656173652d6661696c604482015261195960f21b6064820152608490fd5b611532610fe9565b611482565b61153f610fe9565b611472565b9061158e61158091600d841015611594575b6040519283916020830195869060029260ff60f81b809260f81b16835260f81b1660018201520190565b03601f1981018352826103f7565b51902090565b61159c610f79565b611556565b6040516001600160f81b031960f892831b8116602083019081529390921b909116602182015261158e8160228101611580565b916040519160208301938452600d811015611636575b60f890811b60408401521b6001600160f81b0319166041820152602281526060810167ffffffffffffffff811182821017611629575b60405251902090565b6116316103bf565b611620565b61163e610f79565b6115ea56fea2646970667358221220b6c73f779187fa3f5d5f88384ea6227af2d1f28caef0ddb788af288223ec188764736f6c634300080f003300000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b20000

Deployed Bytecode

0x60806040526004361015610013575b600080fd5b60003560e01c80630357371d1461026b57806304a7ec781461026257806313a661ed14610259578063183a4f6e146102505780631c10893f146102475780631cd64df41461023e57806325692962146102355780632b28bb121461022c5780632de94807146102235780634a4ee7b11461021a5780634bc3201d1461021157806350ca771b14610208578063514e62fc146101ff5780635329d742146101f657806354d1f13d146101ed5780635542686d146101e45780635586402d146101db578063642be325146101d2578063715018a6146101c95780637359e41f146101c057806374a0024a146101b75780638da5cb5b146101ae578063a3c2c462146101a5578063ac35ccfa1461019c578063d1f0befb14610193578063d7533f021461018a578063e21de2e814610181578063f04e283e14610178578063f2fde38b1461016f5763fee81cf41461016757600080fd5b61000e610f0f565b5061000e610ea3565b5061000e610e0c565b5061000e610de2565b5061000e610dc3565b5061000e610c84565b5061000e610bdb565b5061000e610b81565b5061000e610b53565b5061000e610b0a565b5061000e610aa9565b5061000e610a14565b5061000e6109ea565b5061000e61099d565b5061000e61084b565b5061000e6107d0565b5061000e6107a6565b5061000e61076b565b5061000e61072e565b5061000e610704565b5061000e6106d5565b5061000e6106a0565b5061000e61066b565b5061000e6105e3565b5061000e61059c565b5061000e61052f565b5061000e610516565b5061000e6104a8565b5061000e6102f8565b5061000e61028a565b600435906001600160a01b038216820361000e57565b503461000e57604036600319011261000e576102a4610274565b638b78c6d8195433036102df576040516102dd916000918291829190602435906001600160a01b03165af16102d76110fb565b50611149565b005b6382b429006000526004601cfd5b60ff81160361000e57565b503461000e57604036600319011261000e5761038061036f60043561031c816102ed565b602061033f60243561032d816102ed565b6001546001600160a01b0316936115a1565b602460405180948193636e4e8d4f60e11b835260048301525afa9081156103b2575b600091610384575b50610f90565b546040519081529081906020820190565b0390f35b6103a5915060203d81116103ab575b61039d81836103f7565b81019061106d565b38610369565b503d610393565b6103ba611082565b610361565b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116103ea57604052565b6103f26103bf565b604052565b90601f8019910116810190811067ffffffffffffffff8211176103ea57604052565b60209067ffffffffffffffff8111610433575b60051b0190565b61043b6103bf565b61042c565b81601f8201121561000e5780359161045783610419565b9261046560405194856103f7565b808452602092838086019260051b82010192831161000e578301905b82821061048f575050505090565b838091833561049d816102ed565b815201910190610481565b503461000e5760208060031936011261000e5760043567ffffffffffffffff811161000e576104db903690600401610440565b906000918180820191805160051b01015b8082036104fe57604051848152602090f35b90918093600160ff8551161b179201909291926104ec565b50602036600319011261000e576102dd60043533610f2e565b50604036600319011261000e57610544610274565b638b78c6d8195433036102df57638b78c6d88160601b1760005260206000209060243582541780925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3005b503461000e57604036600319011261000e5760206105b8610274565b638b78c6d86024359160601b176000528082600020541614604051908152f35b600091031261000e57565b506000806003193601126106325763389a75e13360601b1781526202a30042016020822055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a2604051f35b80fd5b600d111561000e57565b606090600319011261000e576004359060243561065b81610635565b90604435610668816102ed565b90565b503461000e5761068361067d3661063f565b916115d4565b6000526007602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e57638b78c6d86106bf610274565b60601b1760005260208060002054604051908152f35b50604036600319011261000e576106ea610274565b638b78c6d8195433036102df576102dd9060243590610f2e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b5061074c61074461073e3661063f565b9161126e565b341015611099565b610758346004546110e5565b6004553461076257005b6102dd34611461565b503461000e57604036600319011261000e57638b78c6d861078a610274565b60601b1760005260206024358160002054161515604051908152f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b506000806003193601126106325763389a75e13360601b178152806020812055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a2604051f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b50606036600319011261000e5760043567ffffffffffffffff60243581811161000e5761087c90369060040161081a565b92909160443590811161000e5761089790369060040161081a565b8085036109285792919060009384955b8087106108d5576108ba86341015611099565b6108ce6108c9346004546110e5565b600455565b3461076257005b909192939461091761091d916109116108f76108f28b878c611033565b6110f1565b61090a6109058c898b611033565b61108f565b908961126e565b906110e5565b96611000565b9594939291906108a7565b60405162461bcd60e51b815260206004820152603a60248201527f506978656c53746f72653a206c6179657220616e64206672616d65206172726160448201527f7973206d757374206265207468652073616d65206c656e6774680000000000006064820152608490fd5b6004111561000e57565b503461000e57602036600319011261000e576004356109bb81610993565b60048110156109dd575b60005260056020526020604060002054604051908152f35b6109e5610f79565b6109c5565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b5060008060031936011261063257638b78c6d81980543303610a5e57819081337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355604051f35b6382b4290082526004601cfd5b6020908160408183019282815285518094520193019160005b828110610a92575050505090565b835160ff1685529381019392810192600101610a84565b503461000e5760208060031936011261000e576004358160405101906000915b828152838260051b16019060011c918215610ae8576001019190610ac9565b60408051838252808403601f190160051c815290519081906103809082610a6b565b503461000e57602036600319011261000e57610b24610274565b638b78c6d8195433036102df57600080546001600160a01b0319166001600160a01b0392909216919091179055005b503461000e57600036600319011261000e57638b78c6d819546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576020600454604051908152f35b6020908160408183019282815285518094520193019160005b828110610bc7575050505090565b835185529381019392810192600101610bb9565b503461000e5760208060031936011261000e57600490813567ffffffffffffffff811161000e57610c0f903690840161081a565b9091610c1a82610fb7565b93600092835b818110610c3557604051806103808982610ba0565b80610c44610c72928489611033565b35610c4e81610993565b84811015610c77575b8652600585526040862054610c6c828a61104b565b52611000565b610c20565b610c7f610f79565b610c57565b503461000e5760408060031936011261000e57600467ffffffffffffffff813581811161000e57610cb89036908401610440565b9060243590811161000e57610cd0903690840161081a565b610cd981610fb7565b936000928392610d02610cf6610cf660015460018060a01b031690565b6001600160a01b031690565b935b818110610d18578851806103808a82610ba0565b80610d89610d6e610d4d610d39610d32610d94968d61104b565b5160ff1690565b610d4761090586898b611033565b906115a1565b8c5190636e4e8d4f60e11b8252818060209485938c83019190602083019252565b03818c5afa918215610db6575b8a92610d99575b5050610f90565b54610c6c828b61104b565b610d04565b610daf9250803d106103ab5761039d81836103f7565b3880610d82565b610dbe611082565b610d7b565b503461000e57600036600319011261000e5760206040516202a3008152f35b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b50602036600319011261000e57610e21610274565b638b78c6d819805433036102df57606082901b6bffffffffffffffffffffffff191663389a75e117600090815260208120805491936001600160a01b031692914211610e965783905581337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08580a355604051f35b636f5e881884526004601cfd5b50602036600319011261000e57610eb8610274565b638b78c6d81990815433036102df576001600160a01b0316908115610f015781337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b637448fbae6000526004601cfd5b503461000e57602036600319011261000e5763389a75e16106bf610274565b638b78c6d88160601b1760005260206000209182549081161880925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3565b50634e487b7160e01b600052602160045260246000fd5b6004811015610faa575b6000526005602052604060002090565b610fb2610f79565b610f9a565b90610fc182610419565b610fce60405191826103f7565b8281528092610fdf601f1991610419565b0190602036910137565b50634e487b7160e01b600052601160045260246000fd5b6001906000198114611010570190565b611018610fe9565b0190565b50634e487b7160e01b600052603260045260246000fd5b91908110156110435760051b0190565b61043b61101c565b6020918151811015611060575b60051b010190565b61106861101c565b611058565b9081602091031261000e575161066881610993565b506040513d6000823e3d90fd5b35610668816102ed565b156110a057565b60405162461bcd60e51b815260206004820152601e60248201527f506978656c53746f72653a20496e73756666696369656e742066756e647300006044820152606490fd5b81198111611010570190565b3561066881610635565b3d15611144573d9067ffffffffffffffff8211611137575b6040519161112b601f8201601f1916602001846103f7565b82523d6000602084013e565b61113f6103bf565b611113565b606090565b1561115057565b60405162461bcd60e51b815260206004820152601d60248201527f506978656c53746f72653a6574682d72656c656173652d6661696c65640000006044820152606490fd5b1561119c57565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a706978656c2d756e6c6f636b6564000000000000006044820152606490fd5b9081602091031261000e575190565b156111f757565b60405162461bcd60e51b815260206004820152601860248201527f506978656c53746f72653a74726169742d6578706972656400000000000000006044820152606490fd5b91604091949360ff9160608501968552600d811015611261575b602085015216910152565b611269610f79565b611256565b9161127a8183856115d4565b916112a86112a361129f611298866000526007602052604060002090565b5460ff1690565b1590565b611195565b6112b28282611544565b926113286112cd610cf6610cf660015460018060a01b031690565b6040516312288acf60e21b815260048101879052602096879290918381602481855afa908115611454575b600091611427575b5080611416575b506040518080958194636e4e8d4f60e11b8352600483019190602083019252565b03915afa948515611409575b6000956113ea575b505060005461135590610cf6906001600160a01b031681565b91823b1561000e5760006113bb946113ae9461138d93836113c09a60405196879586948593634f604be760e11b85526004850161123c565b03925af180156113dd575b6113c4575b506000526007602052604060002090565b805460ff19166001179055565b610f90565b5490565b806113d16113d7926103d6565b806105d8565b3861139d565b6113e5611082565b611398565b611401929550803d106103ab5761039d81836103f7565b92388061133c565b611411611082565b611334565b6114219042106111f0565b38611307565b6114479150843d861161144d575b61143f81836103f7565b8101906111e1565b38611300565b503d611435565b61145c611082565b6112f8565b806000190460501181151516611537575b606460508202049081811061152a575b60018060a01b03600254169060008080809486604051915af16114a36110fb565b50156114da578180916114d8946114c7610cf6610cf660035460018060a01b031690565b906040519203905af16102d76110fb565b565b60405162461bcd60e51b815260206004820152602260248201527f506978656c53746f72653a626972622d6574682d72656c656173652d6661696c604482015261195960f21b6064820152608490fd5b611532610fe9565b611482565b61153f610fe9565b611472565b9061158e61158091600d841015611594575b6040519283916020830195869060029260ff60f81b809260f81b16835260f81b1660018201520190565b03601f1981018352826103f7565b51902090565b61159c610f79565b611556565b6040516001600160f81b031960f892831b8116602083019081529390921b909116602182015261158e8160228101611580565b916040519160208301938452600d811015611636575b60f890811b60408401521b6001600160f81b0319166041820152602281526060810167ffffffffffffffff811182821017611629575b60405251902090565b6116316103bf565b611620565b61163e610f79565b6115ea56fea2646970667358221220b6c73f779187fa3f5d5f88384ea6227af2d1f28caef0ddb788af288223ec188764736f6c634300080f0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b20000

-----Decoded View---------------
Arg [0] : tiers (uint8[]): 0,1,2,3
Arg [1] : prices (uint256[]): 0,50000000000000000,200000000000000000,500000000000000000
Arg [2] : _pixelAssets (address): 0xb22742948c4AA488B991E5047e553DfDa8Ec2cdC
Arg [3] : _birbAddress_ (address): 0x2F050994E566ce4c7BdD6227bF90D0134ecDa81f
Arg [4] : _flapAddress_ (address): 0x34Bc2128e1Dca41E26824390D2e907e6272bFB42
Arg [5] : _owner (address): 0xd61FA937b8f237901D354f48f6b14995fE468bF2

-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc
Arg [3] : 0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f
Arg [4] : 00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42
Arg [5] : 000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [14] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [15] : 00000000000000000000000000000000000000000000000006f05b59d3b20000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.