creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "packages/contracts/src/ART0x1Program.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.22;\n\nimport {Ownable} from \"openzeppelin/contracts/access/Ownable.sol\";\nimport {SignedMath} from \"openzeppelin/contracts/utils/math/SignedMath.sol\";\nimport {Base64} from \"base64/base64.sol\";\n\nimport {IFileStore, File} from \"./interfaces/IFileStore.sol\";\nimport {IGfxModule} from \"./interfaces/IGfxModule.sol\";\nimport {ART0x1Types} from \"./ART0x1Types.sol\";\n\n/// @author hashrunner.eth\n/// @title  ART0x1Program (v1)\ncontract ART0x1Program is Ownable {\n    //\n    //  ██████╗ ██████╗  ██████╗  ██████╗ ██████╗  █████╗ ███╗   ███╗\n    //  ██╔══██╗██╔══██╗██╔═══██╗██╔════╝ ██╔══██╗██╔══██╗████╗ ████║\n    //  ██████╔╝██████╔╝██║   ██║██║  ███╗██████╔╝███████║██╔████╔██║\n    //  ██╔═══╝ ██╔══██╗██║   ██║██║   ██║██╔══██╗██╔══██║██║╚██╔╝██║\n    //  ██║     ██║  ██║╚██████╔╝╚██████╔╝██║  ██║██║  ██║██║ ╚═╝ ██║\n    //  ╚═╝     ╚═╝  ╚═╝ ╚═════╝  ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝  (v1)\n    //\n\n    // ------------------------------------------------------------------------\n    // LIBRARIES\n    // ------------------------------------------------------------------------\n\n    using SignedMath for int16;\n\n    // ------------------------------------------------------------------------\n    // STORAGE\n    // ------------------------------------------------------------------------\n\n    IFileStore public fileStore =\n        IFileStore(0xFe1411d6864592549AdE050215482e4385dFa0FB);\n\n    string public description;\n\n    // ------------------------------------------------------------------------\n    // CONSTRUCTOR\n    // ------------------------------------------------------------------------\n\n    constructor() Ownable(msg.sender) {\n        description = \"ART0x1 is an Ethereum Runtime Art program and Solidity \"\n        \"re-interpretation of ART1; a computer art program developed by \"\n        \"Richard Williams in 1968 for the IBM 360 mainframe computer.\";\n    }\n\n    // ------------------------------------------------------------------------\n    // PROGRAM\n    // ------------------------------------------------------------------------\n\n    // RUN --------------------------------------------------------------------\n\n    function runProgram(\n        bytes[12] memory _instructions,\n        ART0x1Types.TokenGfx memory _tokenGfx\n    ) public view returns (string memory) {\n        // Define 2x two-dimensional dynamic arrays\n        bytes1[][] memory array1 = new bytes1[][](50);\n        bytes1[][] memory array2 = new bytes1[][](50);\n\n        // workaround two-dimensional array instantiation\n        for (uint i = 0; i < 50; ) {\n            array1[i] = new bytes1[](105);\n            array2[i] = new bytes1[](105);\n            unchecked {\n                ++i;\n            }\n        }\n\n        // Define title string\n        string memory title;\n        // initialize arrays based on first instruction set\n        (array1, array2, title) = init(array1, array2, _instructions[0]);\n\n        // define lookup table for sub programs\n        function(bytes1[][] memory, bytes1[][] memory, bytes memory)\n            view\n            returns (bytes1[][] memory, bytes1[][] memory)[]\n            memory subPrograms = new function(\n                bytes1[][] memory,\n                bytes1[][] memory,\n                bytes memory\n            ) view returns (bytes1[][] memory, bytes1[][] memory)[](6);\n\n        subPrograms[0] = line;\n        subPrograms[1] = solidRect;\n        subPrograms[2] = openRect;\n        subPrograms[3] = triangle;\n        subPrograms[4] = ellipse;\n        subPrograms[5] = quadrant;\n\n        // iterate over remaining instruction strings and call subProgram fxns\n        for (uint i = 1; i < _instructions.length; ) {\n            if (_instructions[i].length == 0) {\n                unchecked {\n                    ++i;\n                }\n                continue;\n            }\n            // convert ASCII digit to uint\n            uint func = uint(uint8(_instructions[i][0])) - 48;\n\n            bytes memory instr = new bytes(_instructions[i].length);\n            for (uint j = 0; j < _instructions[i].length; ) {\n                instr[j] = _instructions[i][j];\n                unchecked {\n                    ++j;\n                }\n            }\n\n            // call figure function based on first digit of instruction string\n            if (func < subPrograms.length) {\n                (array1, array2) = subPrograms[func](array1, array2, instr);\n            } else {\n                revert(\"Invalid sub program: id does not exist.\");\n            }\n            unchecked {\n                ++i;\n            }\n        }\n\n        return render(array1, array2, title, _tokenGfx);\n    }\n\n    // INIT -------------------------------------------------------------------\n\n    function init(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    )\n        internal\n        pure\n        returns (bytes1[][] memory, bytes1[][] memory, string memory)\n    {\n        ART0x1Types.InitInstructions memory initInstr = getInitInstr(\n            _instructions\n        );\n\n        // Fill array1 and array2\n        for (uint i = 0; i < 50; ) {\n            for (uint j = 0; j < 105; ) {\n                if (initInstr.nCol > 0 && j % initInstr.nCol == 0) {\n                    _array1[i][j] = initInstr.sym1;\n                } else {\n                    _array1[i][j] = \" \";\n                }\n                if (initInstr.mCol > 0 && j % initInstr.mCol == 0) {\n                    _array2[i][j] = initInstr.sym2;\n                } else {\n                    _array2[i][j] = \" \";\n                }\n                unchecked {\n                    ++j;\n                }\n            }\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2, initInstr.title);\n    }\n\n    // RENDER -----------------------------------------------------------------\n\n    function render(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        string memory _title,\n        ART0x1Types.TokenGfx memory _tokenGfx\n    ) internal view returns (string memory) {\n        string memory result = string(\n            abi.encodePacked(\n                renderHeaders(_tokenGfx),\n                \"<rect x='0' y='0' width='100%' height='100%' style='fill:\",\n                _tokenGfx.colors[0],\n                \"' />\"\n            )\n        );\n\n        string memory arr1RowChars;\n        string memory arr2RowChars;\n\n        string memory arr1Row;\n        string memory arr2Row;\n\n        uint yPos = 112;\n\n        // Iterate through the arrays to create the SVG text elements\n        for (uint i = 0; i < 50; ) {\n            arr1RowChars = \"\";\n            arr2RowChars = \"\";\n            for (uint j = 0; j < 105; ) {\n                arr1RowChars = string(\n                    abi.encodePacked(arr1RowChars, _array1[i][j])\n                );\n                arr2RowChars = string(\n                    abi.encodePacked(arr2RowChars, _array2[i][j])\n                );\n                unchecked {\n                    ++j;\n                }\n            }\n\n            // Add sym1 row to the SVG content\n            arr1Row = string(\n                abi.encodePacked(\n                    '<text x=\"50%\" y=\"',\n                    uintToString(yPos),\n                    '\" xml:space=\"preserve\"><![CDATA[',\n                    arr1RowChars,\n                    \"]]></text>\"\n                )\n            );\n            result = string(abi.encodePacked(result, arr1Row));\n\n            // Add sym2 row to the SVG content\n            arr2Row = string(\n                abi.encodePacked(\n                    '<text x=\"50%\" y=\"',\n                    uintToString(yPos),\n                    '\" xml:space=\"preserve\"><![CDATA[',\n                    arr2RowChars,\n                    \"]]></text>\"\n                )\n            );\n            result = string(abi.encodePacked(result, arr2Row));\n\n            // Add the title to the last row of the SVG\n            if (i == 49) {\n                result = string(\n                    abi.encodePacked(\n                        result,\n                        '<text x=\"50%\" y=\"',\n                        uintToString(yPos + 36),\n                        '\" xml:space=\"preserve\"><![CDATA[',\n                        _title,\n                        \"]]></text>\"\n                    )\n                );\n            }\n\n            yPos += 12; // Increment yPos by the height of each row\n\n            unchecked {\n                i++;\n            }\n        }\n\n        return string(abi.encodePacked(result, \"</svg>\"));\n    }\n\n    // RENDER UTILS -----------------------------------------------------------\n\n    function renderHeaders(\n        ART0x1Types.TokenGfx memory _tokenGfx\n    ) internal view returns (string memory) {\n        string memory openTags = string(\n            abi.encodePacked(\n                '<svg id=\"svg\" version=\"2.0\" encoding=\"utf-8\" viewBox=\"0 0 109'\n                '6 811\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.or'\n                'g/2000/svg\"><style>'\n            )\n        );\n\n        string memory injectedFont;\n        if (bytes(_tokenGfx.fontFilename).length > 0) {\n            File memory ethfsFile = fileStore.getFile(_tokenGfx.fontFilename);\n            injectedFont = string(\n                abi.encodePacked(\n                    '@font-face{font-family: \"',\n                    _tokenGfx.fontName,\n                    '\";src:url(data:font/woff2;base64,',\n                    ethfsFile.read(),\n                    ')format(\"woff2\");font-weight:normal;font-style:normal;}'\n                )\n            );\n        }\n\n        string memory styles = string(\n            abi.encodePacked(\n                \"text{font:\",\n                _tokenGfx.fontSize,\n                ' \"',\n                _tokenGfx.fontName,\n                '\",monospace;text-anchor:middle;user-select:none;-webkit-user-'\n                \"select:none;-moz-user-select:none;-ms-user-select:none;}text:\"\n                \"nth-child(odd){fill:\",\n                _tokenGfx.colors[1],\n                \";}text:nth-child(even){fill:\",\n                _tokenGfx.colors[2],\n                \";}</style>\"\n            )\n        );\n\n        return string(abi.encodePacked(openTags, injectedFont, styles));\n    }\n\n    // ------------------------------------------------------------------------\n    // SUB PROGRAMS\n    // ------------------------------------------------------------------------\n\n    // LINE -------------------------------------------------------------------\n\n    function line(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        ART0x1Types.ShapeInstructions memory lineInstr = getShapeInstr(\n            _instructions\n        );\n\n        require(\n            lineInstr.nC != 0 || lineInstr.nR != 0,\n            \"Invalid line: nC and nR cannot be zero.\"\n        );\n\n        uint16 maxRows = uint16(_array1.length);\n        uint16 maxCols = uint16(_array1[0].length);\n\n        drawLine(_array1, _array2, lineInstr, maxRows, maxCols);\n\n        uint repeatRowsLength = lineInstr.repeatRows.length;\n        for (uint i = 0; i < repeatRowsLength; ) {\n            ART0x1Types.ShapeInstructions memory repeatedLineInstr = lineInstr;\n            // override row / col vals with repeat vals\n            repeatedLineInstr.row = lineInstr.repeatRows[i];\n            repeatedLineInstr.col = lineInstr.repeatCols[i];\n\n            drawLine(_array1, _array2, repeatedLineInstr, maxRows, maxCols);\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2);\n    }\n\n    function drawLine(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        ART0x1Types.ShapeInstructions memory _instr,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) internal pure {\n        bytes1[][] memory targetArray = _instr.arr == 1 ? _array1 : _array2;\n\n        if (_instr.nR == 1) {\n            drawHorizontalLine(\n                targetArray,\n                _instr.row,\n                _instr.col,\n                _instr.nC,\n                _instr.sym,\n                _maxRows,\n                _maxCols\n            );\n        } else if (_instr.nC == 1) {\n            drawVerticalLine(\n                targetArray,\n                _instr.row,\n                _instr.col,\n                _instr.nR,\n                _instr.sym,\n                _maxRows,\n                _maxCols\n            );\n        } else {\n            drawDiagonalLine(targetArray, _instr, _maxRows, _maxCols);\n        }\n    }\n\n    function drawHorizontalLine(\n        bytes1[][] memory _array,\n        int16 row,\n        int16 col,\n        int16 nC,\n        bytes1 sym,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) public pure {\n        if (nC > 0) {\n            // For positive nC, draw line rightward from the starting column\n            for (int16 i = 0; i < nC; ++i) {\n                int16 newCol = col + i;\n                if (\n                    row >= 0 &&\n                    newCol >= 0 &&\n                    uint16(row) < _maxRows &&\n                    uint16(newCol) < _maxCols\n                ) {\n                    _array[uint16(row)][uint16(newCol)] = sym;\n                }\n            }\n        } else {\n            // For negative nC, draw line leftward, adjusting the loop to\n            // include the start column\n            for (int16 i = 0; i > nC; --i) {\n                int16 newCol = col + i;\n                if (\n                    row >= 0 &&\n                    newCol >= 0 &&\n                    uint16(row) < _maxRows &&\n                    uint16(newCol) < _maxCols\n                ) {\n                    _array[uint16(row)][uint16(newCol)] = sym;\n                }\n            }\n        }\n    }\n\n    function drawVerticalLine(\n        bytes1[][] memory _array,\n        int16 row,\n        int16 col,\n        int16 nR,\n        bytes1 sym,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) internal pure {\n        int16 startRow = row;\n        int16 endRow = row + nR;\n\n        // Adjust start and end for negative nR\n        if (nR < 0) {\n            startRow = row + nR + 1; // Start further up if nR is negative\n            endRow = row + 1; // And end just above the initial row\n        }\n\n        for (int16 i = startRow; i < endRow; ++i) {\n            if (\n                i >= 0 &&\n                col >= 0 &&\n                uint16(i) < _maxRows &&\n                uint16(col) < _maxCols\n            ) {\n                _array[uint16(i)][uint16(col)] = sym;\n            }\n        }\n    }\n\n    function drawDiagonalLine(\n        bytes1[][] memory _array,\n        ART0x1Types.ShapeInstructions memory lineInstr,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) internal pure {\n        // Define vars for Bresenham's line algorithm\n        int16 endRow = int16(lineInstr.row) +\n            (\n                lineInstr.nR > 0\n                    ? int16(lineInstr.nR) - 1\n                    : int16(lineInstr.nR) + 1\n            );\n        int16 endCol = int16(lineInstr.col) +\n            (\n                lineInstr.nC > 0\n                    ? int16(lineInstr.nC) - 1\n                    : int16(lineInstr.nC) + 1\n            );\n\n        int16 currentRow = int16(lineInstr.row);\n        int16 currentCol = int16(lineInstr.col);\n\n        int16 stepRow = lineInstr.nR > 0 ? int16(1) : int16(-1);\n        int16 stepCol = lineInstr.nC > 0 ? int16(1) : int16(-1);\n\n        int16 err = (\n            abs(lineInstr.nC) > abs(lineInstr.nR)\n                ? abs(lineInstr.nC)\n                : -abs(lineInstr.nR)\n        ) / 2;\n        int16 e2;\n\n        // Execute Bresenham's line algorithm\n        while (\n            (stepRow > 0 ? currentRow <= endRow : currentRow >= endRow) ||\n            (stepCol > 0 ? currentCol <= endCol : currentCol >= endCol)\n        ) {\n            if (\n                currentRow >= 0 &&\n                currentCol >= 0 &&\n                uint16(currentRow) < _maxRows &&\n                uint16(currentCol) < _maxCols\n            ) {\n                _array[uint16(currentRow)][uint16(currentCol)] = lineInstr.sym;\n            }\n\n            if (currentCol == endCol && currentRow == endRow) {\n                break;\n            }\n\n            e2 = err;\n            if (e2 > -abs(lineInstr.nC)) {\n                err -= abs(lineInstr.nR);\n                currentCol += stepCol;\n            }\n            if (e2 < abs(lineInstr.nR)) {\n                err += abs(lineInstr.nC);\n                currentRow += stepRow;\n            }\n        }\n    }\n\n    // SOLID RECTANGLE --------------------------------------------------------\n\n    function solidRect(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        ART0x1Types.ShapeInstructions memory rectInstr = getShapeInstr(\n            _instructions\n        );\n\n        require(\n            rectInstr.nC != 0 || rectInstr.nR != 0,\n            \"Invalid solid rectangle: nC and nR cannot be zero.\"\n        );\n        require(\n            int16(rectInstr.row) >= 0,\n            \"Invalid solid rectangle: row must be greater than zero.\"\n        );\n        require(\n            int16(rectInstr.col) >= 0,\n            \"Invalid solid rectangle: col must be greater than zero.\"\n        );\n\n        uint16 maxRows = uint16(_array1.length);\n        uint16 maxCols = uint16(_array1[0].length);\n\n        bytes1[][] memory targetArray = rectInstr.arr == 1 ? _array1 : _array2;\n\n        drawSolidRect(\n            targetArray,\n            rectInstr.row,\n            rectInstr.col,\n            rectInstr,\n            maxRows,\n            maxCols\n        );\n\n        uint repeatRowsLength = rectInstr.repeatRows.length;\n        for (uint i = 0; i < repeatRowsLength; ) {\n            drawSolidRect(\n                targetArray,\n                rectInstr.repeatRows[i],\n                rectInstr.repeatCols[i],\n                rectInstr,\n                maxRows,\n                maxCols\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2);\n    }\n\n    function drawSolidRect(\n        bytes1[][] memory _arr,\n        int16 _row,\n        int16 _col,\n        ART0x1Types.ShapeInstructions memory rectInstr,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) internal pure {\n        for (int16 i = 0; i < rectInstr.nR; ) {\n            int16 newRow = _row + i;\n            drawHorizontalLine(\n                _arr,\n                newRow,\n                _col,\n                rectInstr.nC,\n                rectInstr.sym,\n                _maxRows,\n                _maxCols\n            );\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    // OPEN RECTANGLE ---------------------------------------------------------\n\n    function openRect(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        ART0x1Types.ShapeInstructions memory rectInstr = getShapeInstr(\n            _instructions\n        );\n\n        require(\n            rectInstr.nC != 0 || rectInstr.nR != 0,\n            \"Invalid open rectangle: nC and nR cannot be zero.\"\n        );\n        require(\n            int16(rectInstr.row) >= 0,\n            \"Invalid open rectangle: row must be greater than zero.\"\n        );\n        require(\n            int16(rectInstr.col) >= 0,\n            \"Invalid open rectangle: col must be greater than zero.\"\n        );\n\n        uint16 maxRows = uint16(_array1.length);\n        uint16 maxCols = uint16(_array1[0].length);\n\n        bytes1[][] memory targetArray = rectInstr.arr == 1 ? _array1 : _array2;\n\n        drawOpenRect(\n            targetArray,\n            rectInstr.row,\n            rectInstr.col,\n            rectInstr,\n            maxRows,\n            maxCols\n        );\n\n        uint repeatRowsLength = rectInstr.repeatRows.length;\n        for (uint i = 0; i < repeatRowsLength; ) {\n            drawOpenRect(\n                targetArray,\n                rectInstr.repeatRows[i],\n                rectInstr.repeatCols[i],\n                rectInstr,\n                maxRows,\n                maxCols\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2);\n    }\n\n    function drawOpenRect(\n        bytes1[][] memory _arr,\n        int16 _row,\n        int16 _col,\n        ART0x1Types.ShapeInstructions memory rectInstr,\n        uint16 _maxRows,\n        uint16 _maxCols\n    ) internal pure {\n        // Draw top and bottom horizontal lines\n        for (int16 i = 0; i < rectInstr.nC; ) {\n            int16 newCol = _col + i;\n            if (_row >= 0) {\n                drawHorizontalLine(\n                    _arr,\n                    _row,\n                    newCol,\n                    1,\n                    rectInstr.sym,\n                    _maxRows,\n                    _maxCols\n                );\n            }\n            if (_row + rectInstr.nR - 1 >= 0) {\n                drawHorizontalLine(\n                    _arr,\n                    _row + rectInstr.nR - 1,\n                    newCol,\n                    1,\n                    rectInstr.sym,\n                    _maxRows,\n                    _maxCols\n                );\n            }\n            unchecked {\n                ++i;\n            }\n        }\n\n        // Draw left and right vertical lines\n        for (int16 i = 1; i < rectInstr.nR - 1; ) {\n            int16 newRow = _row + i;\n            if (_col >= 0) {\n                drawVerticalLine(\n                    _arr,\n                    newRow,\n                    _col,\n                    1,\n                    rectInstr.sym,\n                    _maxRows,\n                    _maxCols\n                );\n            }\n            if (_col + rectInstr.nC - 1 >= 0) {\n                drawVerticalLine(\n                    _arr,\n                    newRow,\n                    _col + rectInstr.nC - 1,\n                    1,\n                    rectInstr.sym,\n                    _maxRows,\n                    _maxCols\n                );\n            }\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    // TRIANGLE ---------------------------------------------------------------\n\n    function triangle(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        ART0x1Types.ShapeInstructions memory triInstr = getShapeInstr(\n            _instructions\n        );\n\n        require(\n            (triInstr.nR == 0 && triInstr.nC != 0) ||\n                (triInstr.nR != 0 && triInstr.nC == 0),\n            \"Invalid triangle: either nR or nC must be zero.\"\n        );\n\n        // Determine which array to use\n        bytes1[][] memory targetArray = triInstr.arr == 1 ? _array1 : _array2;\n\n        // Draw the base triangle\n        drawTriangle(\n            targetArray,\n            triInstr.row,\n            triInstr.col,\n            triInstr.nR,\n            triInstr.nC,\n            triInstr.sym\n        );\n\n        // Draw repeated triangles\n        uint repeatRowsLength = triInstr.repeatRows.length;\n        for (uint i = 0; i < repeatRowsLength; ) {\n            drawTriangle(\n                targetArray,\n                triInstr.repeatRows[i],\n                triInstr.repeatCols[i],\n                triInstr.nR,\n                triInstr.nC,\n                triInstr.sym\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2);\n    }\n\n    function drawTriangle(\n        bytes1[][] memory _array,\n        int16 _row,\n        int16 _col,\n        int16 _nR,\n        int16 _nC,\n        bytes1 _sym\n    ) internal pure {\n        int16 absNR = _nR > 0 ? _nR : -_nR;\n        int16 absNC = _nC > 0 ? _nC : -_nC;\n        int16 arrayWidth = int16(uint16(_array[0].length));\n        int16 arrayHeight = int16(uint16(_array.length));\n\n        if (_nR != 0) {\n            for (int16 i = 0; i < absNR; i++) {\n                for (int16 j = -i; j <= i; ) {\n                    int16 x = _col + j;\n                    int16 y = _row + (_nR > 0 ? i : -i);\n                    if (x >= 0 && y >= 0 && x < arrayWidth && y < arrayHeight) {\n                        _array[uint16(y)][uint16(x)] = _sym;\n                    }\n                    unchecked {\n                        ++j;\n                    }\n                }\n            }\n        } else if (_nC != 0) {\n            for (int16 i = 0; i < absNC; i++) {\n                for (int16 j = -i; j <= i; ) {\n                    int16 x = _col + (_nC > 0 ? i : -i);\n                    int16 y = _row + j;\n                    if (x >= 0 && y >= 0 && x < arrayWidth && y < arrayHeight) {\n                        _array[uint16(y)][uint16(x)] = _sym;\n                    }\n                    unchecked {\n                        ++j;\n                    }\n                }\n            }\n        }\n    }\n\n    // ELLIPSE ----------------------------------------------------------------\n\n    function ellipse(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory _instructions\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        // Parse the shape instructions from the input bytes\n        ART0x1Types.ShapeInstructions memory ellInstr = getShapeInstr(\n            _instructions\n        );\n\n        // Determine which array to use based on the 'arr' attribute\n        bytes1[][] memory targetArray = ellInstr.arr == 1 ? _array1 : _array2;\n\n        // Draw the base ellipse\n        drawEllipse(\n            targetArray,\n            ellInstr.row,\n            ellInstr.col,\n            ellInstr.nC,\n            ellInstr.nR,\n            ellInstr.sym\n        );\n\n        // Draw repeated ellipses if needed\n        uint repeatRowsLength = ellInstr.repeatRows.length;\n        for (uint i = 0; i < repeatRowsLength; ) {\n            drawEllipse(\n                targetArray,\n                ellInstr.repeatRows[i],\n                ellInstr.repeatCols[i],\n                ellInstr.nC,\n                ellInstr.nR,\n                ellInstr.sym\n            );\n            unchecked {\n                ++i;\n            }\n        }\n\n        return (_array1, _array2);\n    }\n\n    function drawEllipse(\n        bytes1[][] memory _array,\n        int16 _rowCenter,\n        int16 _colCenter,\n        int16 _horizontalRadius,\n        int16 _verticalRadius,\n        bytes1 _sym\n    ) internal pure {\n        int256[10] memory eData; // Array to hold all necessary variables\n\n        // Initialize variables for the ellipse\n        eData[0] = 0; // x\n        eData[1] = int256(_verticalRadius); // y\n        eData[2] = int256(_horizontalRadius) * int256(_horizontalRadius); // a2\n        eData[3] = int256(_verticalRadius) * int256(_verticalRadius); // b2\n        eData[4] = 2 * eData[2]; // twoA2\n        eData[5] = 2 * eData[3]; // twoB2\n        eData[6] = eData[3] - eData[2] * eData[1] + (eData[2] / 4); // d1\n        eData[8] = 0; // dx\n        eData[9] = eData[4] * eData[1]; // dy\n\n        // Region 1\n        while (eData[8] < eData[9]) {\n            drawEllipsePoints(\n                _array,\n                _rowCenter,\n                _colCenter,\n                int16(eData[0]),\n                int16(eData[1]),\n                _sym\n            );\n            if (eData[6] < 0) {\n                // Next point is to the right\n                eData[6] += eData[3] * ((2 * eData[0]) + 3);\n                eData[8] += eData[5];\n                eData[0] += 1;\n            } else {\n                // Next point is diagonally down and to the right\n                eData[6] +=\n                    eData[3] *\n                    ((2 * eData[0]) + 3) +\n                    eData[2] *\n                    ((-2 * eData[1]) + 2);\n                eData[8] += eData[5];\n                eData[9] -= eData[4];\n                eData[0] += 1;\n                eData[1] -= 1;\n            }\n        }\n\n        // Initialize d2 for Region 2\n        eData[7] =\n            (eData[3] * (eData[0] + 1) * (eData[0] + 1)) +\n            (eData[2] * (eData[1] - 1) * (eData[1] - 1)) -\n            (eData[2] * eData[3]);\n\n        // Region 2\n        while (eData[1] >= 0) {\n            drawEllipsePoints(\n                _array,\n                _rowCenter,\n                _colCenter,\n                int16(eData[0]),\n                int16(eData[1]),\n                _sym\n            );\n            if (eData[7] > (eData[2] * (3 - eData[1]))) {\n                // Next point is directly above\n                eData[7] += eData[2] * ((-2 * eData[1]) + 4);\n                eData[1] -= 1;\n            } else {\n                // Next point is diagonally up and to the right\n                eData[7] +=\n                    eData[3] *\n                    (2 * eData[0] + 2) +\n                    eData[2] *\n                    ((-2 * eData[1]) + 3);\n                eData[8] += eData[5];\n                eData[0] += 1;\n                eData[1] -= 1;\n            }\n        }\n\n        fillEllipse(\n            _array,\n            _rowCenter,\n            _colCenter,\n            _horizontalRadius,\n            _verticalRadius,\n            _sym\n        );\n    }\n\n    function drawEllipsePoints(\n        bytes1[][] memory _array,\n        int16 _rowCenter,\n        int16 _colCenter,\n        int16 _x,\n        int16 _y,\n        bytes1 _sym\n    ) internal pure {\n        int16 width = int16(uint16(_array[0].length));\n        int16 height = int16(uint16(_array.length));\n\n        int16[4] memory dx = [_x, -_x, _x, -_x];\n        int16[4] memory dy = [_y, _y, -_y, -_y];\n\n        for (uint i = 0; i < 4; i++) {\n            int16 newX = _colCenter + dx[i];\n            int16 newY = _rowCenter + dy[i];\n\n            if (newX >= 0 && newX < width && newY >= 0 && newY < height) {\n                _array[uint16(newY)][uint16(newX)] = _sym;\n            }\n        }\n    }\n\n    function fillEllipse(\n        bytes1[][] memory _array,\n        int16 _rowCenter,\n        int16 _colCenter,\n        int16 _horizontalRadius,\n        int16 _verticalRadius,\n        bytes1 _sym\n    ) internal pure {\n        int16 minY = _rowCenter - _verticalRadius;\n        int16 maxY = _rowCenter + _verticalRadius;\n        int16 minX = _colCenter - _horizontalRadius;\n        int16 maxX = _colCenter + _horizontalRadius;\n\n        for (int16 y = minY; y <= maxY; y++) {\n            for (int16 x = minX; x <= maxX; x++) {\n                if (\n                    isPointInEllipse(\n                        x,\n                        y,\n                        _rowCenter,\n                        _colCenter,\n                        _horizontalRadius,\n                        _verticalRadius\n                    )\n                ) {\n                    if (\n                        y >= 0 &&\n                        uint16(y) < _array.length &&\n                        x >= 0 &&\n                        uint16(x) < _array[0].length\n                    ) {\n                        _array[uint16(y)][uint16(x)] = _sym;\n                    }\n                }\n            }\n        }\n    }\n\n    function isPointInEllipse(\n        int16 x,\n        int16 y,\n        int16 _rowCenter,\n        int16 _colCenter,\n        int16 _horizontalRadius,\n        int16 _verticalRadius\n    ) internal pure returns (bool) {\n        int256 dx = int256(x) - int256(_colCenter);\n        int256 dy = int256(y) - int256(_rowCenter);\n        int256 a2 = int256(_horizontalRadius) * int256(_horizontalRadius);\n        int256 b2 = int256(_verticalRadius) * int256(_verticalRadius);\n\n        return (dx * dx) * b2 + (dy * dy) * a2 <= a2 * b2;\n    }\n\n    // QUADRANT ---------------------------------------------------------------\n\n    function quadrant(\n        bytes1[][] memory _array1,\n        bytes1[][] memory _array2,\n        bytes memory\n    ) internal pure returns (bytes1[][] memory, bytes1[][] memory) {\n        uint16 maxRows = uint16(_array1.length);\n        uint16 maxCols = uint16(_array1[0].length);\n\n        // Mirror the top-left quadrant\n        for (uint16 i = 0; i < 25; ++i) {\n            for (uint16 j = 0; j < 52; ++j) {\n                // To top-right quadrant\n                _array1[i][maxCols - j - 1] = _array1[i][j];\n                _array2[i][maxCols - j - 1] = _array2[i][j];\n\n                // To bottom-left quadrant\n                _array1[maxRows - i - 1][j] = _array1[i][j];\n                _array2[maxRows - i - 1][j] = _array2[i][j];\n\n                // To bottom-right quadrant\n                _array1[maxRows - i - 1][maxCols - j - 1] = _array1[i][j];\n                _array2[maxRows - i - 1][maxCols - j - 1] = _array2[i][j];\n            }\n        }\n\n        // Mirror middle column\n        uint16 middleCol = 52;\n        for (uint16 i = 0; i < maxRows / 2; ++i) {\n            // Mirror from top to bottom\n            _array1[maxRows - i - 1][middleCol] = _array1[i][middleCol];\n            _array2[maxRows - i - 1][middleCol] = _array2[i][middleCol];\n        }\n\n        return (_array1, _array2);\n    }\n\n    // ------------------------------------------------------------------------\n    // TOKEN DATA\n    // ------------------------------------------------------------------------\n\n    function tokenHTML(\n        ART0x1Types.TokenCtx memory _tokenCtx\n    ) external view returns (string memory) {\n        string memory script;\n        if (_tokenCtx.moduleAddress != address(0)) {\n            (_tokenCtx.gfx, script) = IGfxModule(_tokenCtx.moduleAddress)\n                .runGfxModule(\n                    _tokenCtx.instructions,\n                    _tokenCtx.prn,\n                    _tokenCtx.moduleUint,\n                    _tokenCtx.moduleString\n                );\n        }\n\n        return\n            string(\n                abi.encodePacked(\n                    '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><style>h'\n                    \"tml,body,svg{margin:0;padding:0;height:100%;width:100%;ov\"\n                    \"erflow:hidden;}@media screen and (max-device-width:480px)\"\n                    \"{body{-webkit-text-size-adjust:100%;}}</style></head>\",\n                    runProgram(_tokenCtx.instructions, _tokenCtx.gfx),\n                    \"<script>\",\n                    script,\n                    \"</script>\"\n                    \"</body></html>\"\n                )\n            );\n    }\n\n    function tokenSVG(\n        ART0x1Types.TokenCtx memory _tokenCtx\n    ) external view returns (string memory) {\n        if (_tokenCtx.moduleAddress != address(0)) {\n            (_tokenCtx.gfx, ) = IGfxModule(_tokenCtx.moduleAddress)\n                .runGfxModule(\n                    _tokenCtx.instructions,\n                    _tokenCtx.prn,\n                    _tokenCtx.moduleUint,\n                    _tokenCtx.moduleString\n                );\n        }\n\n        return runProgram(_tokenCtx.instructions, _tokenCtx.gfx);\n    }\n\n    function tokenURI(\n        ART0x1Types.TokenCtx memory _tokenCtx\n    ) external view returns (string memory) {\n        string memory tokenId = uintToString(_tokenCtx.id);\n\n        string memory script;\n        if (_tokenCtx.moduleAddress != address(0)) {\n            (_tokenCtx.gfx, script) = IGfxModule(_tokenCtx.moduleAddress)\n                .runGfxModule(\n                    _tokenCtx.instructions,\n                    _tokenCtx.prn,\n                    _tokenCtx.moduleUint,\n                    _tokenCtx.moduleString\n                );\n        }\n\n        string memory svg = runProgram(_tokenCtx.instructions, _tokenCtx.gfx);\n\n        string memory json = Base64.encode(\n            bytes(\n                string(\n                    abi.encodePacked(\n                        '{\"id\": \"',\n                        tokenId,\n                        '\", \"name\": \"ART0x1 #',\n                        tokenId,\n                        '\", \"description\": \"',\n                        description,\n                        '\", \"attributes\": ',\n                        getAttributes(_tokenCtx),\n                        ', \"image\": \"data:image/svg+xml;base64,',\n                        Base64.encode(abi.encodePacked(svg)),\n                        '\", \"animation_url\": \"data:text/html;base64,',\n                        Base64.encode(\n                            abi.encodePacked(\n                                '<!DOCTYPE html><html><head><meta charset=\"UTF'\n                                '-8\"><style>html,body,svg{margin:0;padding:0;h'\n                                \"eight:100%;width:100%;overflow:hidden;}@media\"\n                                \" screen and (max-device-width:480px){body{-we\"\n                                \"bkit-text-size-adjust:100%;}}</style></head>\",\n                                svg,\n                                \"<script>\",\n                                script,\n                                \"</script></body></html>\"\n                            )\n                        ),\n                        '\"}'\n                    )\n                )\n            )\n        );\n\n        return string(abi.encodePacked(\"data:application/json;base64,\", json));\n    }\n\n    // TOKEN DATA UTILS -------------------------------------------------------\n\n    function getAttributes(\n        ART0x1Types.TokenCtx memory _tokenCtx\n    ) private pure returns (string memory) {\n        string memory mode = modeToString(_tokenCtx.mode);\n        string memory attributes = string(\n            abi.encodePacked('[{\"trait_type\": \"Mode\", \"value\": \"', mode, '\"}')\n        );\n\n        if (\n            _tokenCtx.mode == ART0x1Types.Mode.ORIGINAL ||\n            _tokenCtx.mode == ART0x1Types.Mode.GALLERY\n        ) {\n            attributes = string(\n                abi.encodePacked(\n                    attributes,\n                    ', {\"trait_type\": \"Artist\", \"value\": \"',\n                    addressToString(_tokenCtx.artistAddress),\n                    '\"}'\n                )\n            );\n        }\n\n        if (_tokenCtx.mode == ART0x1Types.Mode.GALLERY) {\n            attributes = string(\n                abi.encodePacked(\n                    attributes,\n                    ', {\"trait_type\": \"Gallery\", \"value\": \"',\n                    _tokenCtx.galleryName,\n                    '\"}'\n                )\n            );\n            attributes = string(\n                abi.encodePacked(\n                    attributes,\n                    ', {\"trait_type\": \"Curator\", \"value\": \"',\n                    addressToString(_tokenCtx.galleryCurator),\n                    '\"}'\n                )\n            );\n        }\n\n        string memory gfxModuleValue = _tokenCtx.moduleAddress == address(0)\n            ? \"None\"\n            : _tokenCtx.moduleName;\n        attributes = string(\n            abi.encodePacked(\n                attributes,\n                ', {\"trait_type\": \"GFX Module\", \"value\": \"',\n                gfxModuleValue,\n                '\"}'\n            )\n        );\n\n        if (keccak256(bytes(gfxModuleValue)) != keccak256(bytes(\"None\"))) {\n            attributes = string(\n                abi.encodePacked(\n                    attributes,\n                    ', {\"trait_type\": \"Module Author\", \"value\": \"',\n                    addressToString(_tokenCtx.moduleAuthor),\n                    '\"}'\n                )\n            );\n        }\n\n        return string(abi.encodePacked(attributes, \"]\"));\n    }\n\n    function modeToString(\n        ART0x1Types.Mode _mode\n    ) private pure returns (string memory) {\n        if (_mode == ART0x1Types.Mode.PRE_REVEAL) return \"Pre-Reveal\";\n        if (_mode == ART0x1Types.Mode.GALLERY) return \"Gallery\";\n        return \"Original\";\n    }\n\n    // ------------------------------------------------------------------------\n    // PROGRAM UTILS\n    // ------------------------------------------------------------------------\n\n    function getInitInstr(\n        bytes memory _instructions\n    ) internal pure returns (ART0x1Types.InitInstructions memory parsed) {\n        // Skip \"sym1\" and a whitespace\n        uint currentIndex = 5;\n        // Get sym1\n        parsed.sym1 = _instructions[currentIndex++];\n        // Skip a whitespace, \"nCol\" character and a white space\n        currentIndex += 6;\n        // Get nCol\n        uint n = 0;\n        while (\n            currentIndex < _instructions.length &&\n            uint8(_instructions[currentIndex]) >= 48 &&\n            uint8(_instructions[currentIndex]) <= 57\n        ) {\n            n = n * 10 + uint(uint8(_instructions[currentIndex]) - 48);\n            currentIndex++;\n        }\n        parsed.nCol = n;\n        // Skip a whitespace, \"sym2\" and a whitspace\n        currentIndex += 6;\n        // Get sym2\n        parsed.sym2 = _instructions[currentIndex++];\n        // Skip a whitespace, \"mCol\" and a whitespace\n        currentIndex += 6;\n        // Get mCol\n        uint m = 0;\n        while (\n            currentIndex < _instructions.length &&\n            uint8(_instructions[currentIndex]) >= 48 &&\n            uint8(_instructions[currentIndex]) <= 57\n        ) {\n            m = m * 10 + uint(uint8(_instructions[currentIndex]) - 48);\n            currentIndex++;\n        }\n        parsed.mCol = m;\n        // Skip a whitespace, \"title\" and a whitespace\n        currentIndex += 7;\n\n        // Determine maxLength as smaller of 60 chars or remaining _instructions\n        uint maxLength = currentIndex + 60 < _instructions.length\n            ? currentIndex + 60\n            : _instructions.length;\n\n        // Initialize title array with size up to maxLength\n        bytes memory title = new bytes(maxLength - currentIndex);\n\n        // Append characters to title within maxLength limit\n        uint titleIndex = 0;\n        while (currentIndex < maxLength) {\n            title[titleIndex++] = _instructions[currentIndex++];\n        }\n\n        // Set the title in the parsed struct\n        parsed.title = string(title);\n\n        return parsed;\n    }\n\n    function getShapeInstr(\n        bytes memory _instructions\n    ) internal pure returns (ART0x1Types.ShapeInstructions memory shapeInstr) {\n        // Get base shape attrs and store on shapeInstr\n        uint currentIndex = getBaseShape(_instructions, shapeInstr);\n\n        // Count number of repeat rows and cols\n        (uint repeatRowCount, uint repeatColCount) = getRepeatCounts(\n            _instructions,\n            currentIndex\n        );\n\n        // Get repeat coords and store on shapeInstr\n        getRepeatCoords(\n            _instructions,\n            currentIndex,\n            repeatRowCount,\n            repeatColCount,\n            shapeInstr\n        );\n\n        return shapeInstr;\n    }\n\n    function getBaseShape(\n        bytes memory _instructions,\n        ART0x1Types.ShapeInstructions memory shapeInstr\n    ) internal pure returns (uint currentIndex) {\n        // skip shade id, a whitespace, \"sym\" and a whitespace\n        currentIndex = 6;\n\n        // Get sym\n        shapeInstr.sym = _instructions[currentIndex];\n\n        // Skip a whitespace, \"arr\" and  whitespace\n        currentIndex += 6;\n\n        // Get arr\n        shapeInstr.arr = uint8(_instructions[currentIndex++]) - 48;\n\n        // Parsing loop for row, col, nR, and nC values\n        for (uint8 i = 0; i < 4; ) {\n            // Skip index based on key lengths\n            currentIndex += (i == 0 || i == 1) ? 5 : 4;\n\n            // Parse the value using parseInt16 function\n            (int16 parsedValue, uint nextIndex) = parseInt16(\n                _instructions,\n                currentIndex\n            );\n            currentIndex = nextIndex;\n\n            // Assign the parsed value to the corresponding field\n            if (i == 0) {\n                shapeInstr.row = parsedValue;\n            } else if (i == 1) {\n                shapeInstr.col = parsedValue;\n            } else if (i == 2) {\n                shapeInstr.nR = parsedValue;\n            } else {\n                shapeInstr.nC = parsedValue;\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function getRepeatCounts(\n        bytes memory _instructions,\n        uint currentIndex\n    ) internal pure returns (uint repeatRowCount, uint repeatColCount) {\n        // Loop through _instructions and count number of repeat rows and cols\n        for (uint i = currentIndex; i < _instructions.length; ) {\n            if (_instructions[i] == \"r\") {\n                repeatRowCount++;\n            } else if (_instructions[i] == \"c\") {\n                repeatColCount++;\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function getRepeatCoords(\n        bytes memory _instructions,\n        uint currentIndex,\n        uint repeatRowCount,\n        uint repeatColCount,\n        ART0x1Types.ShapeInstructions memory shapeInstr\n    ) internal pure {\n        shapeInstr.repeatRows = new int16[](repeatRowCount);\n        shapeInstr.repeatCols = new int16[](repeatColCount);\n\n        repeatRowCount = 0;\n        repeatColCount = 0;\n\n        uint startIndex;\n\n        while (currentIndex < _instructions.length) {\n            if (repeatRowCount >= 9 && repeatColCount >= 9) {\n                currentIndex += 5; // two-digit repeatKeys\n                startIndex = currentIndex - 4; // whitespace, XX and \"r\" or \"c\"\n            } else {\n                currentIndex += 4; // single-digit repeatKeys\n                startIndex = currentIndex - 3; // whitespace, X and \"r\" or \"c\"\n            }\n\n            if (currentIndex < _instructions.length) {\n                (int16 parsedValue, uint nextIndex) = parseInt16(\n                    _instructions,\n                    currentIndex\n                );\n\n                currentIndex = nextIndex;\n\n                if (uint8(_instructions[startIndex]) == 114) {\n                    shapeInstr.repeatRows[repeatRowCount] = parsedValue;\n                    repeatRowCount += 1;\n                } else {\n                    shapeInstr.repeatCols[repeatColCount] = parsedValue;\n                    repeatColCount += 1;\n                }\n            }\n        }\n    }\n\n    function parseInt16(\n        bytes memory _instructions,\n        uint start\n    ) internal pure returns (int16, uint) {\n        int16 n = 0;\n        bool isNegative = false;\n\n        if (_instructions[start] == bytes1(\"-\")) {\n            isNegative = true;\n            start++;\n        }\n\n        while (\n            start < _instructions.length &&\n            uint8(_instructions[start]) >= 48 &&\n            uint8(_instructions[start]) <= 57\n        ) {\n            uint8 currentChar = uint8(_instructions[start]);\n\n            n = n * 10 + int16(int256(uint(currentChar) - 48));\n            start++;\n        }\n\n        if (isNegative) {\n            n = -n;\n        }\n\n        return (n, start);\n    }\n\n    // ------------------------------------------------------------------------\n    // UTILS\n    // ------------------------------------------------------------------------\n\n    function uintToString(uint _value) internal pure returns (string memory) {\n        if (_value == 0) {\n            return \"0\";\n        }\n        uint temp = _value;\n        uint digits;\n        while (temp != 0) {\n            digits++;\n            temp /= 10;\n        }\n        bytes memory buffer = new bytes(digits);\n        while (_value != 0) {\n            digits -= 1;\n            buffer[digits] = bytes1(uint8(48 + uint(_value % 10)));\n            _value /= 10;\n        }\n        return string(buffer);\n    }\n\n    function abs(int16 a) internal pure returns (int16) {\n        return a >= 0 ? a : -a;\n    }\n\n    function addressToString(\n        address _addr\n    ) internal pure returns (string memory) {\n        bytes memory alphabet = \"0123456789abcdef\";\n        bytes20 value = bytes20(_addr);\n        bytes memory str = new bytes(42);\n        str[0] = \"0\";\n        str[1] = \"x\";\n\n        for (uint i = 0; i < 20; i++) {\n            str[2 + i * 2] = alphabet[uint8(value[i] >> 4)];\n            str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)];\n        }\n\n        return string(str);\n    }\n}\n"
      },
      "packages/contracts/lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "packages/contracts/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // Formula from the \"Bit Twiddling Hacks\" by Sean Eron Anderson.\n            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,\n            // taking advantage of the most significant (or \"sign\" bit) in two's complement representation.\n            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,\n            // the mask will either be `bytes(0)` (if n is positive) or `~bytes32(0)` (if n is negative).\n            int256 mask = n >> 255;\n\n            // A `bytes(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.\n            return uint256((n + mask) ^ mask);\n        }\n    }\n}\n"
      },
      "packages/contracts/lib/base64/base64.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0;\n\n/// @title Base64\n/// @author Brecht Devos - <brecht@loopring.org>\n/// @notice Provides functions for encoding/decoding base64\nlibrary Base64 {\n    string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n    bytes  internal constant TABLE_DECODE = hex\"0000000000000000000000000000000000000000000000000000000000000000\"\n                                            hex\"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000\"\n                                            hex\"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000\"\n                                            hex\"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000\";\n\n    function encode(bytes memory data) internal pure returns (string memory) {\n        if (data.length == 0) return '';\n\n        // load the table into memory\n        string memory table = TABLE_ENCODE;\n\n        // multiply by 4/3 rounded up\n        uint256 encodedLen = 4 * ((data.length + 2) / 3);\n\n        // add some extra buffer at the end required for the writing\n        string memory result = new string(encodedLen + 32);\n\n        assembly {\n            // set the actual output length\n            mstore(result, encodedLen)\n\n            // prepare the lookup table\n            let tablePtr := add(table, 1)\n\n            // input ptr\n            let dataPtr := data\n            let endPtr := add(dataPtr, mload(data))\n\n            // result ptr, jump over length\n            let resultPtr := add(result, 32)\n\n            // run over the input, 3 bytes at a time\n            for {} lt(dataPtr, endPtr) {}\n            {\n                // read 3 bytes\n                dataPtr := add(dataPtr, 3)\n                let input := mload(dataPtr)\n\n                // write 4 characters\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F))))\n                resultPtr := add(resultPtr, 1)\n                mstore8(resultPtr, mload(add(tablePtr, and(        input,  0x3F))))\n                resultPtr := add(resultPtr, 1)\n            }\n\n            // padding with '='\n            switch mod(mload(data), 3)\n            case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }\n            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }\n        }\n\n        return result;\n    }\n\n    function decode(string memory _data) internal pure returns (bytes memory) {\n        bytes memory data = bytes(_data);\n\n        if (data.length == 0) return new bytes(0);\n        require(data.length % 4 == 0, \"invalid base64 decoder input\");\n\n        // load the table into memory\n        bytes memory table = TABLE_DECODE;\n\n        // every 4 characters represent 3 bytes\n        uint256 decodedLen = (data.length / 4) * 3;\n\n        // add some extra buffer at the end required for the writing\n        bytes memory result = new bytes(decodedLen + 32);\n\n        assembly {\n            // padding with '='\n            let lastBytes := mload(add(data, mload(data)))\n            if eq(and(lastBytes, 0xFF), 0x3d) {\n                decodedLen := sub(decodedLen, 1)\n                if eq(and(lastBytes, 0xFFFF), 0x3d3d) {\n                    decodedLen := sub(decodedLen, 1)\n                }\n            }\n\n            // set the actual output length\n            mstore(result, decodedLen)\n\n            // prepare the lookup table\n            let tablePtr := add(table, 1)\n\n            // input ptr\n            let dataPtr := data\n            let endPtr := add(dataPtr, mload(data))\n\n            // result ptr, jump over length\n            let resultPtr := add(result, 32)\n\n            // run over the input, 4 characters at a time\n            for {} lt(dataPtr, endPtr) {}\n            {\n               // read 4 characters\n               dataPtr := add(dataPtr, 4)\n               let input := mload(dataPtr)\n\n               // write 3 bytes\n               let output := add(\n                   add(\n                       shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),\n                       shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))),\n                   add(\n                       shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)),\n                               and(mload(add(tablePtr, and(        input , 0xFF))), 0xFF)\n                    )\n                )\n                mstore(resultPtr, shl(232, output))\n                resultPtr := add(resultPtr, 3)\n            }\n        }\n\n        return result;\n    }\n}\n"
      },
      "packages/contracts/src/interfaces/IFileStore.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.22;\n\n/**\n * @title EthFS File\n * @notice A representation of an onchain file, composed of slices of contract bytecode and utilities to construct the file contents from those slices.\n * @dev For best gas efficiency, it's recommended using `File.read()` as close to the output returned by the contract call as possible. Lots of gas is consumed every time a large data blob is passed between functions.\n */\n\n/**\n * @dev Represents a reference to a slice of bytecode in a contract\n */\nstruct BytecodeSlice {\n    address pointer;\n    uint32 start;\n    uint32 end;\n}\n\n/**\n * @dev Represents a file composed of one or more bytecode slices\n */\nstruct File {\n    // Total length of file contents (sum of all slice sizes). Useful when you want to use DynamicBuffer to build the file contents from the slices.\n    uint256 size;\n    BytecodeSlice[] slices;\n}\n// extend File struct with read functions\nusing {read} for File global;\nusing {readUnchecked} for File global;\n\n/**\n * @dev Error thrown when a slice is out of the bounds of the contract's bytecode\n */\nerror SliceOutOfBounds(\n    address pointer,\n    uint32 codeSize,\n    uint32 sliceStart,\n    uint32 sliceEnd\n);\n\n/**\n * @notice Reads the contents of a file by concatenating its slices\n * @param file The file to read\n * @return contents The concatenated contents of the file\n */\nfunction read(File memory file) view returns (string memory contents) {\n    BytecodeSlice[] memory slices = file.slices;\n    bytes4 sliceOutOfBoundsSelector = SliceOutOfBounds.selector;\n\n    assembly {\n        let len := mload(slices)\n        let size := 0x20\n        contents := mload(0x40)\n        let slice\n        let pointer\n        let start\n        let end\n        let codeSize\n\n        for {\n            let i := 0\n        } lt(i, len) {\n            i := add(i, 1)\n        } {\n            slice := mload(add(slices, add(0x20, mul(i, 0x20))))\n            pointer := mload(slice)\n            start := mload(add(slice, 0x20))\n            end := mload(add(slice, 0x40))\n\n            codeSize := extcodesize(pointer)\n            if gt(end, codeSize) {\n                mstore(0x00, sliceOutOfBoundsSelector)\n                mstore(0x04, pointer)\n                mstore(0x24, codeSize)\n                mstore(0x44, start)\n                mstore(0x64, end)\n                revert(0x00, 0x84)\n            }\n\n            extcodecopy(pointer, add(contents, size), start, sub(end, start))\n            size := add(size, sub(end, start))\n        }\n\n        // update contents size\n        mstore(contents, sub(size, 0x20))\n        // store contents\n        mstore(0x40, add(contents, and(add(size, 0x1f), not(0x1f))))\n    }\n}\n\n/**\n * @notice Reads the contents of a file without reverting on unreadable/invalid slices. Skips any slices that are out of bounds or invalid. Useful if you are composing contract bytecode where a contract can still selfdestruct (which would result in an invalid slice) and want to avoid reverts but still output potentially \"corrupted\" file contents (due to missing data).\n * @param file The file to read\n * @return contents The concatenated contents of the file, skipping invalid slices\n */\nfunction readUnchecked(File memory file) view returns (string memory contents) {\n    BytecodeSlice[] memory slices = file.slices;\n\n    assembly {\n        let len := mload(slices)\n        let size := 0x20\n        contents := mload(0x40)\n        let slice\n        let pointer\n        let start\n        let end\n        let codeSize\n\n        for {\n            let i := 0\n        } lt(i, len) {\n            i := add(i, 1)\n        } {\n            slice := mload(add(slices, add(0x20, mul(i, 0x20))))\n            pointer := mload(slice)\n            start := mload(add(slice, 0x20))\n            end := mload(add(slice, 0x40))\n\n            codeSize := extcodesize(pointer)\n            if lt(end, codeSize) {\n                extcodecopy(\n                    pointer,\n                    add(contents, size),\n                    start,\n                    sub(end, start)\n                )\n                size := add(size, sub(end, start))\n            }\n        }\n\n        // update contents size\n        mstore(contents, sub(size, 0x20))\n        // store contents\n        mstore(0x40, add(contents, and(add(size, 0x1f), not(0x1f))))\n    }\n}\n\n/// @title EthFS FileStore interface\n/// @notice Specifies a content-addressable onchain file store\ninterface IFileStore {\n    event Deployed();\n\n    /**\n     * @dev Emitted when a new file is created\n     * @param indexedFilename The indexed filename for easier finding by filename in logs\n     * @param pointer The pointer address of the file\n     * @param filename The name of the file\n     * @param size The total size of the file\n     * @param metadata Additional metadata of the file, only emitted for use in offchain indexers\n     */\n    event FileCreated(\n        string indexed indexedFilename,\n        address indexed pointer,\n        string filename,\n        uint256 size,\n        bytes metadata\n    );\n\n    /**\n     * @dev Error thrown when a requested file is not found\n     * @param filename The name of the file requested\n     */\n    error FileNotFound(string filename);\n\n    /**\n     * @dev Error thrown when a filename already exists\n     * @param filename The name of the file attempted to be created\n     */\n    error FilenameExists(string filename);\n\n    /**\n     * @dev Error thrown when attempting to create an empty file\n     */\n    error FileEmpty();\n\n    /**\n     * @dev Error thrown when a provided slice for a file is empty\n     * @param pointer The contract address where the bytecode lives\n     * @param start The byte offset to start the slice (inclusive)\n     * @param end The byte offset to end the slice (exclusive)\n     */\n    error SliceEmpty(address pointer, uint32 start, uint32 end);\n\n    /**\n     * @dev Error thrown when the provided pointer's bytecode does not have the expected STOP opcode prefix from SSTORE2\n     * @param pointer The SSTORE2 pointer address\n     */\n    error InvalidPointer(address pointer);\n\n    /**\n     * @notice Returns the address of the CREATE2 deterministic deployer used by this FileStore\n     * @return The address of the CREATE2 deterministic deployer\n     */\n    function deployer() external view returns (address);\n\n    /**\n     * @notice Retrieves the pointer address of a file by its filename\n     * @param filename The name of the file\n     * @return pointer The pointer address of the file\n     */\n    function files(\n        string memory filename\n    ) external view returns (address pointer);\n\n    /**\n     * @notice Checks if a file exists for a given filename\n     * @param filename The name of the file to check\n     * @return True if the file exists, false otherwise\n     */\n    function fileExists(string memory filename) external view returns (bool);\n\n    /**\n     * @notice Retrieves the pointer address for a given filename\n     * @param filename The name of the file\n     * @return pointer The pointer address of the file\n     */\n    function getPointer(\n        string memory filename\n    ) external view returns (address pointer);\n\n    /**\n     * @notice Retrieves a file by its filename\n     * @param filename The name of the file\n     * @return file The file associated with the filename\n     */\n    function getFile(\n        string memory filename\n    ) external view returns (File memory file);\n\n    /**\n     * @notice Creates a new file with the provided file contents\n     * @dev This is a convenience method to simplify small file uploads. It's recommended to use `createFileFromPointers` or `createFileFromSlices` for larger files. This particular method splits `contents` into 24575-byte chunks before storing them via SSTORE2.\n     * @param filename The name of the new file\n     * @param contents The contents of the file\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFile(\n        string memory filename,\n        string memory contents\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file with the provided file contents and file metadata\n     * @dev This is a convenience method to simplify small file uploads. It's recommended to use `createFileFromPointers` or `createFileFromSlices` for larger files. This particular method splits `contents` into 24575-byte chunks before storing them via SSTORE2.\n     * @param filename The name of the new file\n     * @param contents The contents of the file\n     * @param metadata Additional file metadata, usually a JSON-encoded string, for offchain indexers\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFile(\n        string memory filename,\n        string memory contents,\n        bytes memory metadata\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file where its content is composed of the provided string chunks\n     * @dev This is a convenience method to simplify small and nuanced file uploads. It's recommended to use `createFileFromPointers` or `createFileFromSlices` for larger files. This particular will store each chunk separately via SSTORE2. For best gas efficiency, each chunk should be as large as possible (up to the contract size limit) and at least 32 bytes.\n     * @param filename The name of the new file\n     * @param chunks The string chunks composing the file\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromChunks(\n        string memory filename,\n        string[] memory chunks\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file with the provided string chunks and file metadata\n     * @dev This is a convenience method to simplify small and nuanced file uploads. It's recommended to use `createFileFromPointers` or `createFileFromSlices` for larger files. This particular will store each chunk separately via SSTORE2. For best gas efficiency, each chunk should be as large as possible (up to the contract size limit) and at least 32 bytes.\n     * @param filename The name of the new file\n     * @param chunks The string chunks composing the file\n     * @param metadata Additional file metadata, usually a JSON-encoded string, for offchain indexers\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromChunks(\n        string memory filename,\n        string[] memory chunks,\n        bytes memory metadata\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file where its content is composed of the provided SSTORE2 pointers\n     * @param filename The name of the new file\n     * @param pointers The SSTORE2 pointers composing the file\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromPointers(\n        string memory filename,\n        address[] memory pointers\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file with the provided SSTORE2 pointers and file metadata\n     * @param filename The name of the new file\n     * @param pointers The SSTORE2 pointers composing the file\n     * @param metadata Additional file metadata, usually a JSON-encoded string, for offchain indexers\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromPointers(\n        string memory filename,\n        address[] memory pointers,\n        bytes memory metadata\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file where its content is composed of the provided bytecode slices\n     * @param filename The name of the new file\n     * @param slices The bytecode slices composing the file\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromSlices(\n        string memory filename,\n        BytecodeSlice[] memory slices\n    ) external returns (address pointer, File memory file);\n\n    /**\n     * @notice Creates a new file with the provided bytecode slices and file metadata\n     * @param filename The name of the new file\n     * @param slices The bytecode slices composing the file\n     * @param metadata Additional file metadata, usually a JSON-encoded string, for offchain indexers\n     * @return pointer The pointer address of the new file\n     * @return file The newly created file\n     */\n    function createFileFromSlices(\n        string memory filename,\n        BytecodeSlice[] memory slices,\n        bytes memory metadata\n    ) external returns (address pointer, File memory file);\n}\n"
      },
      "packages/contracts/src/interfaces/IGfxModule.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.22;\n\nimport {ART0x1Types} from \"../ART0x1Types.sol\";\n\ninterface IGfxModule {\n    function runGfxModule(\n        bytes[12] memory _instructions,\n        uint _prn,\n        uint _uint,\n        string memory _string\n    )\n        external\n        view\n        returns (ART0x1Types.TokenGfx memory gfx, string memory script);\n}\n"
      },
      "packages/contracts/src/ART0x1Types.sol": {
        "content": "// SPDX-License-Identifier: Unlicense\npragma solidity ^0.8.22;\n\nlibrary ART0x1Types {\n    enum Mode {\n        PRE_REVEAL,\n        ORIGINAL,\n        GALLERY\n    }\n\n    struct InitInstructions {\n        bytes1 sym1;\n        bytes1 sym2;\n        uint nCol;\n        uint mCol;\n        string title;\n    }\n\n    struct ShapeInstructions {\n        bytes1 sym;\n        uint8 arr;\n        int16 row;\n        int16 col;\n        int16 nR;\n        int16 nC;\n        int16[] repeatRows;\n        int16[] repeatCols;\n    }\n\n    struct Gallery {\n        string name;\n        uint price;\n        address curator;\n        address[] instructions;\n        address[] artists;\n    }\n\n    struct GfxModule {\n        string name;\n        address authorAddress;\n        address moduleAddress;\n        uint price;\n    }\n\n    struct TokenGfx {\n        string[3] colors;\n        string fontName;\n        string fontFilename;\n        string fontSize;\n    }\n\n    struct TokenCtx {\n        uint id;\n        uint prn;\n        uint galleryId;\n        uint moduleUint;\n        address artistAddress;\n        address galleryCurator;\n        address moduleAuthor;\n        address moduleAddress;\n        bytes[12] instructions;\n        TokenGfx gfx;\n        Mode mode;\n        string galleryName;\n        string moduleName;\n        string moduleString;\n    }\n}\n"
      },
      "packages/contracts/lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "erc721a/=packages/contracts/lib/ERC721A/",
        "base64/=packages/contracts/lib/base64/",
        "forge-std/=packages/contracts/lib/forge-std/src/",
        "openzeppelin/=packages/contracts/lib/openzeppelin-contracts/",
        "abdk-libraries-solidity/=packages/contracts/lib/abdk-libraries-solidity/",
        "sstore2/=packages/contracts/lib/sstore2/",
        "ds-test/=packages/contracts/lib/ds-test/src/",
        "solady/=packages/contracts/lib/ethfs/packages/contracts/lib/solady/src/",
        "@openzeppelin/contracts/=packages/contracts/lib/openzeppelin-contracts/contracts/",
        "ERC721A/=packages/contracts/lib/ERC721A/contracts/",
        "erc4626-tests/=packages/contracts/lib/openzeppelin-contracts/lib/erc4626-tests/",
        "ethfs/=packages/contracts/lib/ethfs/",
        "openzeppelin-contracts/=packages/contracts/lib/openzeppelin-contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "none",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "viaIR": false,
      "libraries": {}
    }
  }}