// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

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



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

pragma solidity ^0.8.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(
        uint256 amount,
        bytes32 salt,
        bytes memory bytecode
    ) internal returns (address addr) {
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(
        bytes32 salt,
        bytes32 bytecodeHash,
        address deployer
    ) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

// File: contracts/Lawless/Lawless.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

import "./lib/LawlessGIF.sol";
import "../lib/TokenizedContract.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

interface IDelegationRegistry {
    function checkDelegateForContract(address delegate, address vault, address contract_) external view returns(bool);
    function checkDelegateForToken(address delegate, address vault, address contract_, uint256 tokenId) external view returns (bool);

}

interface ICustomAttributes {
    function getCustomAttributes () external view returns (bytes memory);
}

interface ILawlessMetadata {
    function generateTokenURI (LawlessData memory) external view returns (string memory);
    function setB64EncodeURI (bool value) external;
    function addMetadataMod (address addr) external;
}

type Ex is uint256;

struct Record {
    address owner;
    uint16 index;
    uint16 version;
    uint16 style;
    uint48 details;
}

struct LawlessData {
    uint id;
    uint modelId;
    uint paletteId;
    address owner;
    uint16 version;
    uint48 details;
}

/*
 * @title Lawless
 * @author Ponderware Ltd
 * @notice chain-complete ERC-721 character contract
 */
contract Lawless is TokenizedContract, LawlessGIF {

    string public name = "lawless";
    string public symbol = unicode"🏴";

    constructor (uint256 tokenId) TokenizedContract(tokenId) {
        addRole(owner(), Role.Uploader);
        addRole(owner(), Role.Curator);
        addRole(owner(), Role.Pauser);
        addRole(0xEBFEFB02CaD474D35CabADEbddF0b32D287BE1bd, Role.CodeLawless);
    }

    /* Events */

    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /* Supply */

    uint256 internal constant maxSupply = 8192;
    uint256 internal constant maxSupplyMod = 8191;
    uint256 public totalSupply = 0;

    /* Bookkeeping Data */

    Record[maxSupply] internal Records;
    mapping (address => uint16[maxSupply+1]) internal TokensByOwner; // index 0 is "length"

    modifier validLawlessId (uint id) {
        require(id < totalSupply, "unrecognized lawless");
        _;
    }

    /* Delegation */

    IDelegationRegistry constant dc = IDelegationRegistry(0x00000000000076A84feF008CDAbe6409d2FE638B);

    bool public delegationEnabled = true;

    function smashFlask () public onlyBy(Role.Ponderware) {
        delegationEnabled = false;
    }

    /* Data */

    function uploadModels (uint48 count, bytes memory data) public onlyBy(Role.Uploader) {
        _uploadModels(count, data);
    }

    function uploadPalettes (uint48 count, bytes memory data) public onlyBy(Role.Uploader) {
        _uploadPalettes(count, data);
    }

    /* Metadata */

    ILawlessMetadata Metadata;

    function setMetadata (bytes calldata metadata) public onlyBy(Role.Curator) {
        removeRole(address(Metadata), Role.Metadata);
        Metadata = ILawlessMetadata(Create2.deploy(0, 0, abi.encodePacked(metadata, abi.encode(address(this), CodexAddress))));
        addRole(address(Metadata), Role.Metadata);
    }

    function setB64EncodeURI (bool value) public onlyBy(Role.Curator) {
        Metadata.setB64EncodeURI(value);
    }

    function addMetadataMod (address addr) public onlyBy(Role.Curator) {
        Metadata.addMetadataMod(addr);
    }

    function getModel (uint id) public view onlyBy(Role.Metadata) returns (Model memory) {
        return _getModel(id);
    }

    function getPalette (uint id) public view onlyBy(Role.Metadata) returns (bytes memory) {
        return _getPalette(id);
    }

    function staticGIF (Model memory model, bytes memory palette) public view onlyBy(Role.Metadata) returns (string memory) {
        return _staticGIF(model, palette);
    }

    function animatedGIF (Model memory model, bytes memory palette) public view onlyBy(Role.Metadata) returns (string memory) {
        return _animatedGIF(model, palette);
    }

    function getModelAndPaletteIds (uint style) internal pure returns (uint, uint) {
        return (style >> 5, style & 31);
    }

    function getData (uint id) public view validLawlessId(id) returns (LawlessData memory) {
        Record storage record = Records[id];
        (uint modelId, uint paletteId) = getModelAndPaletteIds(record.style);
        return LawlessData(id, modelId, paletteId, record.owner, record.version, record.details);
    }

    /* Token URI */

    function tokenURI (Ex tokenId) public view returns (string memory) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        require (Ex.unwrap(tokenId) == (id + Records[id].version * maxSupply), "invalid tokenId");
        return Metadata.generateTokenURI(getData(id));
    }

    /* View Helpers */

    function getTokenId (uint256 id) external view validLawlessId(id) returns (uint) {
        return id + Records[id].version * maxSupply;
    }

    function getLawlessId (Ex tokenId) external view returns (uint id) {
        id = Ex.unwrap(tokenId) & maxSupplyMod;
        require (id < totalSupply && Ex.unwrap(tokenId) == (id + Records[id].version * maxSupply), "invalid tokenId");
    }

    /* Immutable lawless GIF lookups */

    function lawlessGIF (uint256 id, bool base) public view validLawlessId(id) returns (string memory) {
        Record storage record = Records[id];
        (uint modelId, uint paletteId) = getModelAndPaletteIds(record.style);
        uint details = record.details;
        if (!base) {
            uint morph = details >> 24;
            if ((morph & 1024) > 0) {
                modelId = 256 + (morph & 1023);
            }
            uint shift = details >> 12;
            if ((shift & 1024) > 0) {
                paletteId = 32 + (shift & 1023);
            }
        }
        if (details >= 211106232532992 || modelId > 255) {
            return _animatedGIF(_getModel(modelId), _getPalette(paletteId));
        } else {
            return _staticGIF(_getModel(modelId), _getPalette(paletteId));
        }
    }

    function tokenGIF (Ex tokenId, bool base) external view returns (string memory) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        require (Ex.unwrap(tokenId) == (id + Records[id].version * maxSupply), "invalid tokenId");
        return lawlessGIF(id, base);
    }

    /* Details */

    function _updateId (uint id, address owner) internal {
        uint currentVersion = Records[id].version++;
        uint currentExternalId = id + (currentVersion * totalSupply);
        uint nextExternalId = currentExternalId + maxSupply;
        emit Transfer(owner, address(0), currentExternalId);
        emit Transfer(address(0), owner, nextExternalId);
    }

    function _authorized (uint id, address owner, address operator) internal view returns (bool) {
        return (operator == CodexAddress
                || operator == owner
                || TokenApprovals[id] == operator
                || isApprovedForAll(owner, operator)
                || (delegationEnabled
                    && (dc.checkDelegateForContract(operator, owner, address(this))
                        || dc.checkDelegateForToken(operator, owner, address(this), id))));

    }

    function getDetails (Ex tokenId) external view returns (uint, address, uint48) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        Record storage record = Records[id];
        require (id < totalSupply && Ex.unwrap(tokenId) == (id + record.version * maxSupply), "invalid tokenId");
        return (id, record.owner, record.details);
    }

    function authorized (address operator, Ex tokenId) external view returns (bool) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        Record storage record = Records[id];
        require (id < totalSupply && Ex.unwrap(tokenId) == (id + record.version * maxSupply), "invalid tokenId");
        return _authorized(id, record.owner, operator);
    }

    function incrementVersion (address operator, uint id) external validLawlessId(id) onlyBy(Role.Surgeon) {
        Record storage record = Records[id];
        require(_authorized(id, record.owner, operator), "unauthorized");
        _updateId(id, record.owner);
    }

    function updateDetails (address operator, uint id, uint48 details, bool incVersion) external validLawlessId(id) onlyBy(Role.Surgeon) {
        Record storage record = Records[id];
        require(_authorized(id, record.owner, operator), "unauthorized");
        record.details = details;
        if (incVersion) _updateId(id, record.owner);
    }

    /* Minting */

    function handleMint (bytes32 seed, address to, uint48 details) internal {
        uint index = (uint256(seed) % (maxSupply - totalSupply)) + totalSupply;
        Record storage atCursor = Records[totalSupply];
        Record storage atIndex = Records[index];
        uint16 atIndexStyle = atIndex.style;
        if (atCursor.owner == address(0)) {
            atIndex.style = uint16(totalSupply);
        } else {
            atIndex.style = atCursor.style;
        }
        if (atIndex.owner == address(0)) {
            atCursor.style = uint16(index);
        } else {
            atCursor.style = atIndexStyle;
        }

        atIndex.owner = to; // used to check if value has been seen
        atCursor.owner = to;
        atCursor.details = details;

        uint16 setIndex = TokensByOwner[to][0]++;
        atCursor.index = setIndex;
        TokensByOwner[to][setIndex + 1] = uint16(totalSupply);
        emit Transfer(address(0), to, totalSupply);
        totalSupply++;
    }

    function mint (uint256 seed, address to, uint48 details) public onlyBy(Role.Minter) {
        require (totalSupply < maxSupply, "rescue complete");
        handleMint(keccak256(abi.encodePacked(to, seed, totalSupply)), to, details);
    }

    function batchMint (uint256 seed, address[] calldata to, uint48[] calldata details) public onlyBy(Role.Minter) {
        require ((totalSupply + to.length) <= maxSupply, "insufficient supply");
        for (uint i = 0; i < to.length; i++) {
            handleMint(keccak256(abi.encodePacked(to[i], seed, totalSupply, i)), to[i], details[i]);
        }
    }

    /* ERC-165 */

    function supportsInterface(bytes4 interfaceId) public view returns (bool) {

        if (msg.sender == CodexAddress) { // workaround for ERC721 custom metadata
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == type(ICustomAttributes).interfaceId;
        } else {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                interfaceId == 0x780E9D63 || // ERC165 Interface ID for ERC721Enumerable
                interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
                interfaceId == 0x2A55205A || // ERC165 Interface ID for ERC2981
                interfaceId == type(ICustomAttributes).interfaceId;
        }
    }

    /* Custom Attributes */

    function getCustomAttributes () external view returns (bytes memory) {
        string memory mintedPct = toPctString1000x(totalSupply * 1000 / maxSupply);
        return abi.encodePacked(ICodex(CodexAddress).encodeStringAttribute("token type", "ERC-721"),
                                ",",
                                ICodex(CodexAddress).encodeNumericAttribute("total supply", totalSupply),
                                ",",
                                ICodex(CodexAddress).encodeStringAttribute("minted", mintedPct));
    }

    /* ERC-721 Base */

    // Mapping from token ID to approved address
    address[maxSupply] private TokenApprovals;
    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private OperatorApprovals;

    function _transfer (address from, address to, Ex tokenId) private whenNotPaused {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;  // don't need to check if id < totalSupply because Records lookup will fail
        TokenApprovals[id] = address(0); // Clear approvals from the previous owner
        Record storage record = Records[id];
        require(record.owner == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");
        require(Ex.unwrap(tokenId) == (id + (record.version * maxSupply)), "ERC721: Nonexistent token");
        uint16 valueIndex = record.index + 1;
        uint16 lastIndex = TokensByOwner[from][0]--;
        if (lastIndex != valueIndex) {
            uint16 lastId = TokensByOwner[from][lastIndex];
            TokensByOwner[from][valueIndex] = lastId;
            Records[lastId].index = valueIndex - 1;
        }
        TokensByOwner[from][lastIndex] = 0;
        uint16 newOwnerIndex = ++TokensByOwner[to][0];
        record.index = newOwnerIndex - 1;
        TokensByOwner[to][newOwnerIndex] = uint16(id);
        record.owner = to;
        emit Transfer(from, to, Ex.unwrap(tokenId));
    }

    function tokenExists (Ex tokenId) external view returns (bool exists) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        Record storage record = Records[id];
        return (id < totalSupply && Ex.unwrap(tokenId) == (id + record.version * maxSupply));
    }

    function _ownerOf (Ex tokenId) internal view returns (address, uint) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        Record storage record = Records[id];
        require (id < totalSupply && Ex.unwrap(tokenId) == (id + record.version * maxSupply), "invalid tokenId");
        return (record.owner, id);
    }

    function ownerOf (Ex tokenId) external view returns (address owner) {
        (owner,) = _ownerOf(tokenId);
    }

    function balanceOf (address owner) public view returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return TokensByOwner[owner][0];
    }

    function approve (address to, Ex tokenId) external  {
        (address owner, uint id) = _ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");
        require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
                "ERC721: approve caller is not owner nor approved for all");
        TokenApprovals[id] = to;
        emit Approval(owner, to, Ex.unwrap(tokenId));
    }

    function getApproved (Ex tokenId) external view returns (address) {
        uint id = Ex.unwrap(tokenId) & maxSupplyMod;
        require(id < totalSupply
                &&
                (Ex.unwrap(tokenId) == id + maxSupply * Records[id].version),
                "No such token");
        return TokenApprovals[id];
    }

    function isApprovedForAll (address owner, address operator) public view returns (bool) {
        return OperatorApprovals[owner][operator];
    }

    function setApprovalForAll (address operator, bool approved) external {
        require(msg.sender != operator, "ERC721: approve to caller");
        OperatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function _isApprovedOrOwner (address spender, Ex tokenId) internal view returns (bool) {
        (address owner, uint id) = _ownerOf(tokenId);
        return (spender == owner || TokenApprovals[id] == spender || isApprovedForAll(owner, spender));
    }

    function _checkOnERC721Received(address from, address to, Ex tokenId, bytes memory _data) private returns (bool) {
        uint256 size;
        assembly { size := extcodesize(to) }
        if (size > 0) { // checking for contract
            try IERC721Receiver(to).onERC721Received(msg.sender, from, Ex.unwrap(tokenId), _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly { revert(add(32, reason), mload(reason)) }
                }
            }
        } else {
            return true;
        }
    }

    function _safeTransfer (address from, address to, Ex tokenId, bytes memory _data) private {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    function transferFrom (address from, address to, Ex tokenId) external {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
        _transfer(from, to, tokenId);
    }

    function safeTransferFrom (address from, address to, Ex tokenId) external {
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom (address from, address to, Ex tokenId, bytes memory _data) public {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /* ERC-721 Enumerable */

    function tokenByIndex (uint256 index) external view returns (uint256) {
        require (index < totalSupply, "ERC721Enumerable: ?? LOOKUP");
        return(index + Records[index].version * maxSupply);
    }

    function tokenOfOwnerByIndex (address owner, uint256 index) external view returns (uint256) {
        require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        uint id = TokensByOwner[owner][index + 1];
        return(id + Records[id].version * maxSupply);
    }

    /* Royalty Bullshit */

    function royaltyInfo (uint256 /*tokenId*/, uint256 /*salePrice8*/) external view returns (address, uint256) {
        return (owner(), 0);
    }

    /* Util */

    bytes10 private constant _SYMBOLS = "0123456789";
    function toPctString1000x (uint256 value) public pure returns (string memory pct) {
        //Adapted From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol
        uint256 length = 1;
        uint256 v = value;
        bytes memory buffer;
        unchecked {
            if (v >= 100) {
                v /= 100;
                length += 2;
            }
            if (v >= 10) {
                length += 1;
            }
            buffer = new bytes(length);
            uint256 ptr;
            assembly {
            ptr := add(buffer, add(32, length))
                    }
            while (true) {
                ptr--;
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                        }
                value /= 10;
                if (value == 0) break;
            }
        }
        if (length == 1) {
            pct = string(abi.encodePacked("0.", buffer, "%"));
        } else if (length == 2) {
            pct = string(abi.encodePacked(buffer[0], ".", buffer[1], "%"));
        } else if (length == 3) {
            pct = string(abi.encodePacked(buffer[0], buffer[1], ".", buffer[2], "%"));
        } else {
            pct = "100%";
        }
    }
}

// File: contracts/Lawless/lib/LawlessGIF.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

import "../../lib/Base64.sol";
import "../../lib/ItemStorage.sol";
import "../../lib/image/GIF32.sol";
import "solmate/src/utils/SSTORE2.sol";

struct Model {
    uint8 width;
    uint8 height;
    uint8 aniX;
    uint8 aniY;
    uint8 aniWidth;
    uint8 aniHeight;
    uint8 aniDelay1;
    uint8 aniDelay2;
    uint8 aniDelay3;
    uint8 staticWidth;
    uint8 staticHeight;
    uint8 staticOffsetX;
    uint8 staticOffsetY;
    uint8 maxScale;
    bytes f1;
    bytes f2;
    bytes f3;
    bytes f4;
}

contract LawlessGIF {

    /* Data Stores */

    ItemStorage.Store internal ModelData;
    ItemStorage.Store internal PaletteData;

    /* Unpack Models */

    function slice(uint begin, uint len, bytes memory arr) internal pure returns (bytes memory) {
        bytes memory res = new bytes(len);
        for (uint i = 0; i < len; i++) {
            res[i] = arr[i+begin];
        }
        return res;
    }

    function slice2(uint loc, bytes memory arr) internal pure returns (uint) {
        uint res = uint(uint8(arr[loc])) << 8;
        return (res + uint8(arr[loc + 1]));
    }

    function unpackModel (bytes memory input) internal pure returns (Model memory) {

        uint pointer = 14;
        uint len = slice2(pointer, input);
        pointer += 2;
        bytes memory f1 = slice(pointer, len, input);

        pointer += len;
        len = slice2(pointer, input);
        pointer += 2;
        bytes memory f2 = slice(pointer, len, input);

        pointer += len;
        len = slice2(pointer, input);
        pointer += 2;
        bytes memory f3 = slice(pointer, len, input);

        pointer += len;
        len = slice2(pointer, input);
        pointer += 2;
        bytes memory f4 = slice(pointer, len, input);

        return Model(uint8(bytes1(input[0])),
                     uint8(bytes1(input[1])),
                     uint8(bytes1(input[2])),
                     uint8(bytes1(input[3])),
                     uint8(bytes1(input[4])),
                     uint8(bytes1(input[5])),
                     uint8(bytes1(input[6])),
                     uint8(bytes1(input[7])),
                     uint8(bytes1(input[8])),
                     uint8(bytes1(input[9])),
                     uint8(bytes1(input[10])),
                     uint8(bytes1(input[11])),
                     uint8(bytes1(input[12])),
                     uint8(bytes1(input[13])),
                     f1,
                     f2,
                     f3,
                     f4);
    }

    /* Model Data Storage */

    function _uploadModels (uint48 count, bytes memory data) internal {
        ItemStorage.upload(ModelData, count, data);
    }

    function _getModel (uint id) internal view returns (Model memory) {
        return unpackModel(ItemStorage.bget(ModelData, id));
    }

    /* Palettes Data Storage */

    function _uploadPalettes (uint48 count, bytes memory data) internal {
        ItemStorage.upload(PaletteData, count, data);
    }

    function _getPalette (uint id) internal view returns (bytes memory) {
        bytes memory loaded = ItemStorage.bget(PaletteData, id);
        bytes memory palette = new bytes(96);
        for (uint i = 0; i < loaded.length; i++) {
            palette[i] = loaded[i];
        }
        return palette;
    }

    /* Assemble Images */

    uint8 constant MCS = 5;

    function _staticGIF (Model memory m, bytes memory palette) internal pure returns (string memory) {
        bytes memory gif = GIF32.assembleStatic(m.width, m.height, true,
                                                GIF32.frame(0, 0, m.width, m.height, MCS, m.f1),
                                                MCS, palette);

        return string(abi.encodePacked("data:image/gif;base64,",Base64.encode(gif)));

    }

    function _animatedGIF (Model memory m, bytes memory palette) internal pure returns (string memory) {
        bytes memory framedata = abi.encodePacked(
                                                  abi.encodePacked(
                                                                   GIF32.gce10x(false, m.aniDelay1, 0),
                                                                   GIF32.frame(0, 0, m.width, m.height, MCS, m.f1)),

                                                  GIF32.gce10x(true, 0, 0),
                                                  GIF32.frame(m.aniX, m.aniY, m.aniWidth, m.aniHeight, MCS, m.f2),

                                                  GIF32.gce10x(true, m.aniDelay2, 0),
                                                  GIF32.frame(m.aniX, m.aniY, m.aniWidth, m.aniHeight, MCS, m.f3),

                                                  GIF32.gce10x(true, m.aniDelay3, 0),
                                                  GIF32.frame(m.aniX, m.aniY, m.aniWidth, m.aniHeight, MCS, m.f4));


        bytes memory gif = GIF32.assembleAnimated(m.width, m.height, framedata, MCS, palette);

        return string(abi.encodePacked("data:image/gif;base64,",Base64.encode(gif)));

    }

}

// File: contracts/lib/Base64.sol



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

/// [MIT License]
/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <brecht@loopring.org>
library Base64 {
    bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /// @notice Encodes some bytes to the base64 representation
    function encode(bytes memory data) internal pure returns (string memory) {
        uint256 len = data.length;
        if (len == 0) return "";

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

        // Add some extra buffer at the end
        bytes memory result = new bytes(encodedLen + 32);

        bytes memory table = TABLE;

        assembly {
            let tablePtr := add(table, 1)
                let resultPtr := add(result, 32)

                for {
                     let i := 0
                } lt(i, len) {

            } {
            i := add(i, 3)
            let input := and(mload(add(data, i)), 0xffffff)

            let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
            out := shl(8, out)
            out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
            out := shl(224, out)

            mstore(resultPtr, out)

            resultPtr := add(resultPtr, 4)
            }

            switch mod(len, 3)
                          case 1 {
                                  mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
                }
            case 2 {
                    mstore(sub(resultPtr, 1), shl(248, 0x3d))
            }

            mstore(result, encodedLen)
                }

        return string(result);
    }
}

// File: contracts/lib/image/GIF32.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

library GIF32 {

    bytes6 constant private HEADER = 0x474946383961;
    bytes1 constant private FOOTER = 0x3b;
    bytes8 constant private TRANSPARENT = 0x21f9040100000000; // palette index 0 is transparent
    bytes19 constant private ANIMATION = 0x21FF0B4E45545343415045322E300301000000;
    bytes3 constant private GCE_PREFIX = 0x21f904;

    function gce10x (bool disposeClear, uint8 delay, uint8 transparentColorIndex) internal pure returns (bytes memory) {
        uint d = uint256(delay) * 10;
        uint8 delay1 = uint8(d & 255);
        uint8 delay2 = uint8((d >> 8) & 255);
        return abi.encodePacked(GCE_PREFIX,
                                uint8(disposeClear ? 9 : 5),
                                uint8(delay1),
                                uint8(delay2),
                                transparentColorIndex,
                                uint8(0));
    }


    function gce (bool disposeClear, uint8 delay, uint8 transparentColorIndex) internal pure returns (bytes memory) {
        return abi.encodePacked(GCE_PREFIX,
                                uint8(disposeClear ? 9 : 5),
                                uint8(delay),
                                uint8(0),
                                transparentColorIndex,
                                uint8(0));
    }

    function gce (bool disposeClear, uint8 delay) internal pure returns (bytes memory) {
        return abi.encodePacked(GCE_PREFIX,
                                uint8(disposeClear ? 8 : 4),
                                uint8(delay),
                                uint24(0));
    }

    function _frame (uint8 x, uint8 y, uint8 w, uint8 h, uint8 mcs, bytes memory lct_data, bytes memory chunks) private pure returns (bytes memory) {
        return abi.encodePacked(uint8(0x2c),
                                x, uint8(0),
                                y, uint8(0),
                                w, uint8(0),
                                h, uint8(0),
                                lct_data,
                                mcs,
                                chunks,
                                uint8(0));
    }

    function frame (uint8 x, uint8 y, uint8 w, uint8 h, uint8 mcs, bytes memory chunks) internal pure returns (bytes memory) {
        return _frame(x, y, w, h, mcs, abi.encodePacked(uint8(0)), chunks);
    }

    function frame (uint8 x, uint8 y, uint8 w, uint8 h, uint8 mcs, bytes memory chunks, bytes memory lct) internal pure returns (bytes memory) {
        return _frame(x, y, w, h, mcs, abi.encodePacked(uint8(240 + mcs - 1), lct), chunks);
    }

    function _assembleStatic (uint8 width, uint8 height, bool transparency, bytes memory frames, bytes memory gct_data) private pure returns (bytes memory) {
        bytes memory transparent = transparency ? abi.encodePacked(TRANSPARENT) : bytes("");
        return abi.encodePacked(HEADER,
                                width, uint8(0),
                                height, uint8(0),
                                gct_data,
                                transparent,
                                frames,
                                FOOTER);
    }

    function assembleStatic (uint8 width, uint8 height, bool transparency, bytes memory frames) internal pure returns (bytes memory) {
        return _assembleStatic(width, height, transparency, frames, abi.encodePacked(uint24(0)));
    }

    function assembleStatic (uint8 width, uint8 height, bool transparency, bytes memory frames, uint8 mcs, bytes memory gct) internal pure returns (bytes memory) {
        return _assembleStatic(width, height, transparency, frames, abi.encodePacked(uint8(240 + mcs - 1), uint16(0), gct));
    }

    function _assembleAnimated (uint8 width, uint8 height, bytes memory framedata, bytes memory gct_data) private pure returns (bytes memory) {
        return abi.encodePacked(HEADER,
                                width, uint8(0),
                                height, uint8(0),
                                gct_data,
                                ANIMATION,
                                framedata,
                                FOOTER);
    }

    function assembleAnimated (uint8 width, uint8 height, bytes memory framedata) internal pure returns (bytes memory) {
        return _assembleAnimated(width, height, framedata, abi.encodePacked(uint24(0)));
    }

    function assembleAnimated (uint8 width, uint8 height, bytes memory framedata, uint8 mcs, bytes memory gct) internal pure returns (bytes memory) {
        return _assembleAnimated(width, height, framedata, abi.encodePacked(uint8(240 + mcs - 1), uint16(0), gct));
    }
}

// File: contracts/lib/ItemStorage.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

import "solmate/src/utils/SSTORE2.sol";

library ItemStorage {

    struct Block {
        uint48 total;
        uint48 count;
        address pointer;
    }

    struct Store {
        Block[] blocks;
    }

    function total (Store storage store) internal view returns (uint48) {
        uint len = store.blocks.length;
        if (len == 0) {
            return 0;
        } else {
            return store.blocks[len - 1].total;
        }
    }

    function upload (Store storage store, uint48 count, bytes memory data) internal {
        require(data.length < 65536, "Too large");
        address pointer = SSTORE2.write(data);
        store.blocks.push(Block(uint48(total(store) + count), count, pointer));
    }

    function sideload (Store storage store, uint48 count, address pointer) internal {
        store.blocks.push(Block(uint48(total(store)) + count, count, pointer));
    }

    function bget (Store storage store, uint id) internal view returns (bytes memory) {
        require(id < total(store), "Out of bounds");
        uint last = store.blocks.length - 1;
        uint cursor = last / 2;
        Block storage b = store.blocks[cursor];
        while (true) {
            if (id >= b.total) {
                cursor = (last - cursor + 1) / 2 + cursor;
            } else if (id < (b.total - b.count)) {
                uint temp = cursor;
                last = cursor;
                cursor = temp / 2;
            } else {
                cursor = b.count - (b.total - id);
                break;
            }
            b = store.blocks[cursor];
        }
        address pointer = b.pointer;
        uint pos = cursor * 2;
        uint dataStart = uint16(bytes2(SSTORE2.read(pointer, pos, pos + 2)));
        uint dataEnd = uint16(bytes2(SSTORE2.read(pointer, pos + 2, pos + 4)));
        return SSTORE2.read(pointer, dataStart, dataEnd);
    }

    function lget (Store storage store, uint id) internal view returns (bytes memory) {
        require(id < total(store), "Out of bounds");
        uint cursor = 0;
        Block storage b = store.blocks[cursor];
        while (true) {
            if (id < b.total) {
                cursor = b.count - (b.total - id);
                break;
            }
            b = store.blocks[++cursor];
        }
        address pointer = b.pointer;
        uint pos = cursor * 2;
        uint dataStart = uint16(bytes2(SSTORE2.read(pointer, pos, pos + 2)));
        uint dataEnd = uint16(bytes2(SSTORE2.read(pointer, pos + 2, pos + 4)));
        return SSTORE2.read(pointer, dataStart, dataEnd);
    }

}

// File: contracts/lib/Rescuable.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
}

interface IERC721_Transfer {
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
}

contract Rescuable {

    function _withdraw(address to) internal {
        payable(to).transfer(address(this).balance);
    }

    /**
    * @dev Rescue ERC20 assets sent directly to this contract.
    */
    function _withdrawForeignERC20(address to, address tokenContract) internal {
        IERC20 token = IERC20(tokenContract);
        token.transfer(to, token.balanceOf(address(this)));
        }

    /**
     * @dev Rescue ERC721 assets sent directly to this contract.
     */
    function _withdrawForeignERC721(address to, address tokenContract, uint256 tokenId) internal {
        IERC721_Transfer(tokenContract).safeTransferFrom(address(this), to, tokenId);
    }


}

// File: contracts/lib/Roles.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2023 Ponderware Ltd

pragma solidity ^0.8.17;

enum Role {
           Super,      // 0
           Admin,      // 1
           Manager,    // 2
           Editor,     // 3
           Minter,     // 4
           Burner,     // 5
           Beneficiary,// 6
           Logger,     // 7
           Uploader,   // 8
           Support,    // 9
           Maintainer, // 10
           Censor,     // 11
           Fixer,      // 12
           Transmitter,// 13
           Shill,      // 14
           LowLevelRedactedDrone, // 15
           CodeLawless,// 16
           Jammer,     // 17
           Ponderware, // 18
           Ranger,     // 19
           Rogue,      // 20
           Pauser,     // 21
           Curator,    // 22
           Chronicler, // 23
           Metadata,   // 24
           Surgeon     // 25
}

// File: contracts/lib/TokenizedContract.sol



// SPDX-License-Identifier: AGPL-3.0
// ©2022 Ponderware Ltd

pragma solidity ^0.8.17;

import "./Rescuable.sol";
import "./Roles.sol";

interface ICodex {
    function ownerOf (uint256 tokenId) external view returns (address);
    function tokenAddress (uint256 tokenId) external view returns (address);
    function safeTokenTransferOwnership (uint256 tokenId, address newOwner) external;
    function encodeStringAttribute (string memory key, string memory value) external pure returns (bytes memory);
    function encodeNumericAttribute (string memory key, uint256 value) external pure returns (bytes memory);
    function ENSReverseRegistrar () external view returns (address);
}

interface IReverseRegistrar {
    function claim(address owner) external returns (bytes32);
}

/*
 * @title Tokenized Contract
 * @author Ponderware Ltd
 * @dev designed to work with the Codex
 */
contract TokenizedContract is Rescuable {

    address public CodexAddress;
    uint256 public immutable tokenId;

    constructor (uint256 _tokenId) {
        CodexAddress = msg.sender;
        tokenId = _tokenId;
    }

    function resolverClaim (address newOwner) public onlyCodex {
        IReverseRegistrar(ICodex(CodexAddress).ENSReverseRegistrar()).claim(newOwner);
    }

    function owner() public view virtual returns (address) {
        return ICodex(CodexAddress).ownerOf(tokenId);
    }

    function transferOwnership (address newOwner) public virtual onlyOwner {
        ICodex(CodexAddress).safeTokenTransferOwnership(tokenId, newOwner);
    }

    modifier onlyOwner () {
        require(owner() == msg.sender, "Ownable: caller is not the owner");
        _;
    }

    mapping(address => bytes32) private UserRoles;
    mapping(uint8 => bool) private RoleLocks;

    modifier onlyCodex () {
        require(msg.sender == CodexAddress, "not codex");
        _;
    }

    modifier onlySuper () {
        require(msg.sender == owner() || hasRole(msg.sender, Role.Super) || msg.sender == CodexAddress, "Unauthorized");
        _;
    }

    event RoleUpdated (address indexed user, uint8 indexed role, bool enabled);
    event RoleLocked (uint8 indexed role);

    function _addRole (address user, Role role) private {
        require (role != Role.Ponderware, "you cannot simply become ponderware");
        require (!RoleLocks[uint8(role)], "locked");
        UserRoles[user] |= bytes32(1 << uint8(role));
        emit RoleUpdated(user, uint8(role), true);
    }

    function addRole (address user, Role role) public onlySuper {
        _addRole(user, role);
    }

    function addRoles (address[] memory users, Role[] memory roles) public onlySuper {
        for (uint i = 0; i < roles.length; i++){
            _addRole(users[i], roles[i]);
        }
    }

    function _removeRole (address user, Role role) private {
        require (!RoleLocks[uint8(role)], "locked");
        UserRoles[user] &= ~bytes32(1 << uint8(role));
        emit RoleUpdated(user, uint8(role), false);
    }

    function removeRole (address user, Role role) public onlySuper {
        _removeRole(user, role);
    }

    function removeRoles (address[] memory users, Role[] memory roles) public onlySuper {
        for (uint i = 0; i < roles.length; i++){
            _removeRole(users[i], roles[i]);
        }
    }

    function _lockRole (Role role) private {
        if (!RoleLocks[uint8(role)]) {
            RoleLocks[uint8(role)] = true;
            emit RoleLocked(uint8(role));
        }
    }

    function lockRole (Role role) public onlySuper {
        _lockRole(role);
    }

    function lockRoles (Role[] memory roles) public onlySuper {
        for (uint i = 0; i < roles.length; i++){
            _lockRole(roles[i]);
        }
    }

    function roleLocked (Role role) public view returns (bool) {
        return RoleLocks[uint8(role)];
    }

    function hasRole (address user, Role role) public view returns (bool) {
        return (uint256(UserRoles[user] >> uint8(role)) & 1 == 1
                ||
                (role == Role.Ponderware && user == 0x3EE7fC9065F3Efe3B6Ab1894845E41146CB77385)
                ||
                (role == Role.Super && user == owner()));
    }

    modifier onlyBy (Role role) {
        require (hasRole(msg.sender, role), "user lacks role");
        _;
    }

    /*
    *** Roles Example ***

    function foo () internal onlyBy(Role.Editor) returns (uint256) {
            return (block.number);
    }

    */

    // Pause

    event Paused(address account);
    event Unpaused(address account);

    bool public paused = true;

    function pause () public onlyBy(Role.Pauser) whenNotPaused {
        paused = true;
        emit Paused(msg.sender);
    }

    function unpause () public onlyBy(Role.Pauser) whenPaused {
        paused = false;
        emit Unpaused(msg.sender);
    }

    modifier whenPaused() {
        require(paused == true, "Not Paused");
        _;
    }

    modifier whenNotPaused() {
        require(paused == false, "Paused");
        _;
    }

    // Rescuers

    function withdraw() public virtual onlyOwner {
        _withdraw(owner());
    }

    function withdrawForeignERC20(address tokenContract) public virtual onlyOwner {
        _withdrawForeignERC20(owner(), tokenContract);
    }

    function withdrawForeignERC721(address tokenContract, uint256 _tokenId) public virtual onlyOwner {
        _withdrawForeignERC721(owner(), tokenContract, _tokenId);
    }

}

// File: solmate/src/utils/SSTORE2.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.

    /*//////////////////////////////////////////////////////////////
                               WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    function write(bytes memory data) internal returns (address pointer) {
        // Prefix the bytecode with a STOP opcode to ensure it cannot be called.
        bytes memory runtimeCode = abi.encodePacked(hex"00", data);

        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x600B             | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xf3    |  0xf3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes.
            runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit.
        );

        /// @solidity memory-safe-assembly
        assembly {
            // Deploy a new contract with the generated creation code.
            // We start 32 bytes into the code to avoid copying the byte length.
            pointer := create(0, add(creationCode, 32), mload(creationCode))
        }

        require(pointer != address(0), "DEPLOYMENT_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    function read(address pointer) internal view returns (bytes memory) {
        return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
    }

    function read(address pointer, uint256 start) internal view returns (bytes memory) {
        start += DATA_OFFSET;

        return readBytecode(pointer, start, pointer.code.length - start);
    }

    function read(
        address pointer,
        uint256 start,
        uint256 end
    ) internal view returns (bytes memory) {
        start += DATA_OFFSET;
        end += DATA_OFFSET;

        require(pointer.code.length >= end, "OUT_OF_BOUNDS");

        return readBytecode(pointer, start, end - start);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function readBytecode(
        address pointer,
        uint256 start,
        uint256 size
    ) private view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            data := mload(0x40)

            // Update the free memory pointer to prevent overriding our data.
            // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
            // Adding 31 to size and running the result through the logic above ensures
            // the memory pointer remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))

            // Store the size of the data in the first 32 byte chunk of free memory.
            mstore(data, size)

            // Copy the code into memory right after the 32 bytes we used to store the size.
            extcodecopy(pointer, add(data, 32), start, size)
        }
    }
}