ETH Price: $3,267.73 (+0.76%)
Gas: 2 Gwei

Contract Diff Checker

Contract Name:
goodblocksGen0

Contract Source Code:

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

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

error YoureNotTheOwnerHomie();
error SorryYouCantAbandonOwnershipToTheZeroAddress();

contract goodblocksGen0
{
    constructor()
    {
        _Owner = msg.sender;
    }
    struct GoodBlock
    {
        uint8 pixelSizeIndex;
        uint8 symmetryIndex;
        uint8 colorGroupIndex;
        uint8 paletteIndex;
        bool isDarkBlock; 
        uint16 tokenIndex;
        bytes3 labelColor;
        string blockDNA;
    }
    uint8[5] private PixelSizes = [5,8,10,20,25];
    uint8[5] private PixelSizeHalf = [3,4,5,10,13];
    uint8[5] private PixelSizePadding = [1,2,2,3,5];
    uint8[5] private PixelSizePercents = [20,0,10,5,4];
    uint8[] private PixelSizeWeights = [25,15,20,15,25];
    uint8[] private BlockSymmetryWeights = [40,25,15,15,4,1];
    uint8[] private ColorGroupWeights = [30,10,20,10,25,30,15];
    uint8[] private PixelColorWeights = [65,20,10,5];
    uint8[] private PixelColorWeightsBiggins1 = [20,35,30,15];
    uint8[] private PixelColorWeightsBiggins2 = [40,30,20,10];
    string private GenerationDescription = "goodblocks generation 0 (gen0) is made up of random pixel images. head to goodblocks.io to learn more about the art process!";
    function updateGenerationDescription(string memory _newDescription) external
    {
        if(msg.sender != _Owner) revert YoureNotTheOwnerHomie();
        GenerationDescription = _newDescription;
    }
    string[5] private PixelSizeNames = ["Biggins", "Great Eight", "10 out of 10", "Score", "XXV"];
    string[8] private PixelEightPercents = ["0%", "12.5%", "25%", "37.5%", "50%", "62.5%", "75%", "87.5%"];
    string[6] private BlockSymmetryNames = ["X=0", "Y=0", "Y=X", "Y=-X", "Flipper", "Chaos"];
    string[6] private BlockSymmetryStrings = 
    [
        '(1000,0) scale(-1,1)', // left right
        '(0,1000) scale(1,-1)', // top bottom
        '(1000, 0) scale(-1,1) rotate(-90,500,500)', // diagonal top left to bottom right /
        '(1000, 0) scale(-1,1) rotate(90,500,500)', // diagonal bottom left to top right \
        '(1000,1000) scale(-1,-1)', // flip across x then flip accross y
        '(0,0) scale(1,1)' // do nothing
    ];
    string private constant LabelFlags =
        "01000101"  // palette 0 Joy
        "11111111"  // palette 1 Night
        "11000010"  // palette 2 Cosmos
        "00000110"  // palette 3 Earth
        "10100011"  // palette 4 Arctic
        "01111100"  // palette 5 Serenity
        "10111000"; // palette 6 Twilight

    string[7] private ColorGroupNames = ["Joy", "Night", "Cosmos", "Earth", "Arctic", "Serenity", "Twilight"];
    string[56] private ColorPalettes = 
    [
        "#FDFF8F;#A8ECE7;#F4BEEE;#D47AE8", // 0 palette (Joy)
        "#FD6F96;#FFEBA1;#95DAC1;#6F69AC",
        "#FFDF6B;#FF79CD;#AA2EE6;#23049D",
        "#95E1D3;#EAFFD0;#FCE38A;#FF75A0",
        "#FFCC29;#F58634;#007965;#00AF91",
        "#998CEB;#77E4D4;#B4FE98;#FBF46D",
        "#EEEEEE;#77D970;#172774;#FF0075",
        "#005F99;#FF449F;#FFF5B7;#00EAD3",
        "#0B0B0D;#474A56;#929AAB;#D3D5FD", // 1 palette (Night)
        "#07031A;#4F8A8B;#B1B493;#FFCB74",
        "#2E3A63;#665C84;#71A0A5;#FAB95B",
        "#000000;#226089;#4592AF;#E3C4A8",
        "#1B1F3A;#53354A;#A64942;#FF7844",
        "#1a1a1a;#153B44;#2D6E7E;#C6DE41",
        "#0F0A3C;#07456F;#009F9D;#CDFFEB",
        "#130026;#801336;#C72C41;#EE4540",
        "#111D5E;#C70039;#F37121;#C0E218", // 2 palette (Cosmos)
        "#02383C;#230338;#ED5107;#C70D3A",
        "#03C4A1;#C62A88;#590995;#150485",
        "#00A8CC;#005082;#000839;#FFA41B",
        "#E94560;#0F3460;#16213E;#1A1A2E",
        "#D2FAFB;#FE346E;#512B58;#2C003E",
        "#706C61;#E1F4F3;#FFFFFF;#333333",
        "#FAF7F2;#2BB3C0;#161C2E;#EF6C35",
        "#FFFBE9;#E3CAA5;#CEAB93;#AD8B73", // 3 palette (Earth)
        "#A09F57;#C56824;#CFB784;#EADEB8",
        "#E3D0B9;#E1BC91;#C19277;#62959C",
        "#E9C891;#8A8635;#AE431E;#D06224",
        "#83B582;#D6E4AA;#FFFFC5;#F0DD92",
        "#303E27;#B4BB72;#E7EAA8;#F6FAF7",
        "#A8896C;#F1E8A7;#AED09E;#61B292",
        "#F4DFBA;#EEC373;#CA965C;#876445",
        "#42C2FF;#85F4FF;#B8FFF9;#EFFFFD", // 4 palette (Arctic)
        "#E8F0F2;#A2DBFA;#39A2DB;#053742",
        "#3E64FF;#5EDFFF;#B2FCFF;#ECFCFF",
        "#D1FFFA;#4AA9AF;#3E31AE;#1C226B",
        "#F7F3F3;#C1EAF2;#5CC2F2;#191BA9",
        "#F3F3F3;#303841;#3A4750;#2185D5",
        "#769FCD;#B9D7EA;#D6E6F2;#F7FBFC",
        "#3D6CB9;#00D1FF;#00FFF0;#FAFAF6",
        "#99FEFF;#94DAFF;#94B3FD;#B983FF", // 5 palette (Serenity)
        "#E5707E;#E6B566;#E8E9A1;#A3DDCB",
        "#6892D5;#79D1C3;#C9FDD7;#F8FCFB",
        "#6C5B7B;#C06C84;#F67280;#F8B195",
        "#30475E;#BA6B57;#F1935C;#E7B2A5",
        "#FFEBD3;#264E70;#679186;#FFB4AC",
        "#6DDCCF;#94EBCD;#FFEFA1;#FFCB91",
        "#D8EFF0;#B0E0A8;#F0F69F;#F3C1C6",
        "#35477D;#6C5B7B;#C06C84;#F67280", // 6 palette (Twilight)
        "#F6C065;#55B3B1;#AF0069;#09015F",
        "#470D21;#9C0F48;#D67D3E;#F9E4D4",
        "#001F52;#A10054;#FF8D68;#FFECBA",
        "#FF6C00;#A0204C;#23103A;#282D4F",
        "#FFF9B2;#ECAC5D;#B24080;#3F0713",
        "#FFE98A;#C84771;#61105E;#280B45",
        "#EDE862;#FA9856;#F27370;#22559C"
    ];
    string private constant SvgHeader = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'
        ' preserveAspectRatio="xMinYMin" shape-rendering="crispEdges" viewBox="0 0 1000 1000">';
    string[] private svgBody = 
    [
        // 0 - goodblock token number
        '<g id="goodblock',
        // 1 - goodblock background
        '"><g id="bigBlock"><rect id="background" width="100%" height="100%" fill="',
        // 2 - color group 1 fill
        '" /></g><g id="littleBlocks"><g id="yetAnotherGroupForAnimation"><g id="ogBlocks"><g id="colorOneGroup" fill="',
        // 3 - color group blocks
        '">',
        // 4 - color group 2 fill
        '</g><g id="colorTwoGroup" fill="',
        // 5 - color group 2 blocks
        '">',
        // 6 - color group 3 fill
        '</g><g id="colorThreeGroup" fill="',
        // 7 - color group 3 blocks
        '">',
        // 8 - block symmetry
        '</g></g><g id="poserBlocks"><use xlink:href="#ogBlocks" transform="translate',
        // 9 - label colors
        '"/></g></g></g></g>'
        '<text x="10%" y="96%" text-anchor="middle" fill="#',
        // 10 - goodblock name
        '" font-size="18" font-family="Courier New">',
        // 11 - animation1 background values
        '</text>'
        '<!--lilNudge-->'
        '<animateTransform'
           ' id="lilNudgeA" xlink:href="#littleBlocks"'
           ' attributeName="transform" type="rotate"'
           ' values="0 500 500;15 500 500;0 500 500;"'
           ' begin="colorOneGroup.click" dur=".5"/>'
        '<animate'
           ' xlink:href="#background" attributeName="fill"'
           ' values="',
        // 12 - animation1 background values
        '" begin="colorOneGroup.click" dur=".5"/>'
        '<!--letsGoForASpin-->'
        '<animate xlink:href="#background" attributeName="fill" values="',
        // 13 - animation2 background values
        '" begin="colorTwoGroup.click" dur="2"/>'
        '<animateTransform xlink:href="#yetAnotherGroupForAnimation"'
            ' attributeName="transform" begin="colorTwoGroup.click"'
            ' type="rotate" values="0 500 500;360 500 500" dur="2"/>'
        '<animateTransform xlink:href="#littleBlocks"'
            ' attributeName="transform" begin="colorTwoGroup.click"'
            ' values="0 0;-1000 0" dur=".5"/>'
        '<animateTransform xlink:href="#littleBlocks"'
            ' attributeName="transform" begin="colorTwoGroup.click+.5"'
            ' values="1000 0;-1000 0" dur="1"/>'
        '<animateTransform xlink:href="#littleBlocks"'
            ' attributeName="transform" begin="colorTwoGroup.click+1.5"'
            ' values="1000 0;0 0" dur=".5"/>'
        '<!--whatTheHeckIsHappening?-->'
        '<animate xlink:href="#background"'
            ' attributeName="fill" begin="colorThreeGroup.click"'
            ' values="',
        // 14 - end of svg!
            '" keyTimes="0;.01;.99;1" dur="10"/>'
        '<animateTransform xlink:href="#yetAnotherGroupForAnimation"'
            ' attributeName="transform" begin="colorThreeGroup.click + .02"'
            ' type="translate" values="0 0;0 50;0 250;0 150;0 0;"'
            ' keyTimes="0;.002;.5;.98;1" dur="9.98s"/>'
        '<animateTransform xlink:href="#colorOneGroup"'
            ' attributeName="transform" additive="sum" type="scale"'
            ' begin="colorThreeGroup.click" values="1;.4;.3;.4;1"'
            ' keyTimes="0;.02;.5;.98;1" dur="10s"/>'
        '<animateTransform xlink:href="#colorOneGroup"'
            ' attributeName="transform" additive="sum" type="rotate"'
            ' begin="colorThreeGroup.click + .02" values="0 1000 1000; 360 1000 1000"'
            ' dur="9.98s"/>'
        '<animateTransform xlink:href="#colorTwoGroup"'
            ' attributeName="transform" additive="sum" type="scale"'
            ' begin="colorThreeGroup.click" values="1;.6;.5;.6;1"'
            ' keyTimes="0;.02;.5;.98;1" dur="10s"/>'
        '<animateTransform xlink:href="#colorTwoGroup"'
            ' attributeName="transform" additive="sum" type="rotate"' 
            ' begin="colorThreeGroup.click + .02" values="0 500 600;-360 500 600"'
            ' dur="9.98s"/>'
        '<animateTransform xlink:href="#colorThreeGroup"'
            ' attributeName="transform" additive="sum" type="scale"'
            ' begin="colorThreeGroup.click" values="1;.8;.6;.4;1"'
            ' keyTimes="0;.01;.5;.98;1" dur="10s"/>'
        '<animateTransform xlink:href="#colorThreeGroup"'
            ' attributeName="transform" additive="sum" type="rotate"'
            ' begin="colorThreeGroup.click + .02" values="0 500 500;360 500 500"'
            ' dur="9.98s"/>'
    '</svg>'
    ];
    function updateSvgBody(uint256 _index, string memory _newString) external returns (string[] memory)
    {
        if(msg.sender != _Owner) revert YoureNotTheOwnerHomie();
        svgBody[_index] = _newString;
        return svgBody;
    }
    function substring(string memory str, uint256 startIndex, uint256 endIndex) private pure returns (string memory)
    {
        bytes memory strBytes = bytes(str);
        bytes memory result = new bytes(endIndex - startIndex);
        for (uint256 i = startIndex; i < endIndex; i++)
        {
            result[i - startIndex] = strBytes[i];
        }
        return string(result);
    }
    function random(string memory _input) private pure returns (uint256)
    {
        return uint256(keccak256(abi.encodePacked(_input)));
    }
    function getWeightedItem(uint8[] memory weightArray, uint256 i) private pure returns (uint8)
    {
        uint256 index = 0;
        uint256 j = weightArray[0];
        while (j <= i)
        {
            index++;
            j += weightArray[index];
        }
        return uint8(index);
    }
    function tokenToGoodblock(uint256 _tokenId) public view returns(GoodBlock memory)
    {
        GoodBlock memory goodblock;
        // set index
        goodblock.tokenIndex = uint16(_tokenId);
        // get pixel set
        goodblock.pixelSizeIndex = getWeightedItem(PixelSizeWeights, random(Strings.toString(_tokenId + 20)) % 100);
        // get symmetry
        goodblock.symmetryIndex = getWeightedItem(BlockSymmetryWeights, random(Strings.toString(_tokenId + 22)) % 100) ;
        // get color group
        goodblock.colorGroupIndex = getWeightedItem(ColorGroupWeights, random(Strings.toString(_tokenId + 1)) % 140);
        // get palette index
        goodblock.paletteIndex = uint8(random(string(abi.encodePacked("Wth?", Strings.toString(_tokenId + 4)))) % 8);

        string memory blockDNA =  string(abi.encodePacked(
            Strings.toString(_tokenId),
            "_",
            Strings.toString(goodblock.pixelSizeIndex),
            "_",
            Strings.toString(goodblock.symmetryIndex),
            "_",
            Strings.toString(goodblock.colorGroupIndex),
            "_",
            Strings.toString(goodblock.paletteIndex)
        ));

        // if in list, then label flag
        if (keccak256(abi.encodePacked(substring(LabelFlags, (goodblock.colorGroupIndex * 8 + goodblock.paletteIndex), (goodblock.colorGroupIndex * 8 + goodblock.paletteIndex)+1))) == keccak256("1"))
        {
            goodblock.labelColor = bytes3("fff");
        } else
        {
            goodblock.labelColor = bytes3("000");
        }

        // DARK LOGIC
        if(_tokenId < 2133 || _tokenId > 6120)
        {
            goodblock.isDarkBlock = false;
            goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
            return goodblock;
        }

        //d check if in y bounds
        if(_tokenId > 2132 && _tokenId < 3789)
        {
            // get y and x respectively
            uint tokenDivY = uint(_tokenId/91);
            uint tokenModX = _tokenId%91;
            
            // CHECK EASY ONES
            if(tokenDivY < 28)
            {
                if(tokenModX < 40 || tokenModX > 44)
                {
                    goodblock.isDarkBlock = false;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
                    return goodblock;
                } else
                {
                    goodblock.isDarkBlock = true;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_1"));
                    goodblock.labelColor = bytes3("fff");
                    return goodblock;
                }
            }

            // CHECK REGION
            if(tokenModX > 29 && tokenModX < 62)
            {
                tokenDivY = tokenDivY - 28;
                if(keccak256(abi.encodePacked(substring(d, tokenDivY*32+tokenModX-30, tokenDivY*32+tokenModX-30 + 1))) == keccak256("1"))
                {
                    goodblock.isDarkBlock = true;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_1"));
                    goodblock.labelColor = bytes3("fff");
                    return goodblock;
                }else
                {
                    goodblock.isDarkBlock = false;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
                    return goodblock;
                }
            }
        }
        // g check if in y bounds
        else if(_tokenId > 4077 && _tokenId < 6121)
        {
            // get y and x respectively
            uint tokenDivY = uint(_tokenId/91);
            uint tokenModX = _tokenId%91;
            
            // CHECK EASY ONES
            // d
            if(tokenDivY < 49)
            {
                if(tokenModX < 74 || tokenModX > 78)
                {
                    goodblock.isDarkBlock = false;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
                    return goodblock;
                } else
                {
                    goodblock.isDarkBlock = true;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_1"));
                    goodblock.labelColor = bytes3("fff");
                    return goodblock;
                }
            }
            // g
            else if(tokenDivY > 62)
            {
                if(tokenModX > 27)
                {
                    goodblock.isDarkBlock = false;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
                    return goodblock;
                }
            }

            // CHECK REGION
            if(tokenModX > 12 && tokenModX < 79)
            {
                tokenDivY = tokenDivY - 49;
                if(keccak256(abi.encodePacked(substring(g, tokenDivY*66+tokenModX-13, tokenDivY*66+tokenModX-13 + 1))) == keccak256("1"))
                {
                    goodblock.isDarkBlock = true;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_1"));
                    goodblock.labelColor = bytes3("fff");
                    return goodblock;
                }else
                {
                    goodblock.isDarkBlock = false;
                    goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
                    return goodblock;
                }
            }
        }
        
        goodblock.isDarkBlock = false;
        goodblock.blockDNA = string(abi.encodePacked(blockDNA,"_0"));
        return goodblock;
    }
    function goodblockToSVG(GoodBlock memory _goodblock) private view returns(string memory)
    {
        // set variables for goodblock generation
        string memory rectPercent;
        string[] memory pixelPercents = new string[](PixelSizes[_goodblock.pixelSizeIndex]);
 
        // check if pixel set is 8
        if(PixelSizes[_goodblock.pixelSizeIndex] == 8)
        {
            // set rectangle width/height
            rectPercent = "12.5%";
            // set percents for positions
            for(uint256 i=0; i<PixelSizes[_goodblock.pixelSizeIndex]; i++)
            {
                pixelPercents[i] = PixelEightPercents[i];
            }

        } else
        {
            // set rectangle width/height
            rectPercent = string(abi.encodePacked(Strings.toString(PixelSizePercents[_goodblock.pixelSizeIndex]), "%"));
            // set percents for positions
            for(uint256 i = 0; i<PixelSizes[_goodblock.pixelSizeIndex]; i++) 
            {
                pixelPercents[i] = string(abi.encodePacked(Strings.toString(i*PixelSizePercents[_goodblock.pixelSizeIndex]), "%"));
            }
        }

        // create color groups to house rectangles
        string[3] memory colorGroupRectangles;
        uint256 tempColorGroupIndex;
        string memory blockMap;

        // 0 left right
        // 1 top bottom
        // diag top left
        // diag bottom left
        // diag both
        // chaos

        // create _goodblock
        // loop through y axis top to bottom
        for (uint y=0; y<PixelSizes[_goodblock.pixelSizeIndex]; y++)
        {
            // check for padding first
            if(y < PixelSizePadding[_goodblock.pixelSizeIndex] || 
                PixelSizes[_goodblock.pixelSizeIndex]-y < PixelSizePadding[_goodblock.pixelSizeIndex] + 1)
            {
                continue; 
            }

            // top bottom then skip bottom half
            if(_goodblock.symmetryIndex == 1)
            {
                if((y+1) > PixelSizeHalf[_goodblock.pixelSizeIndex])
                {
                    continue;
                }
            }

            // stop halfway if token%2 == 0
            if(_goodblock.symmetryIndex == 2 && _goodblock.tokenIndex % 2 == 0)
            {
                if((y+1) > PixelSizeHalf[_goodblock.pixelSizeIndex])
                {
                    continue;
                }
            }

            // stop halfway if token%2 == 0
            if(_goodblock.symmetryIndex == 3 && _goodblock.tokenIndex % 2 == 0)
            {
                if((y+1) < PixelSizeHalf[_goodblock.pixelSizeIndex])
                {
                    continue;
                }
            }

            // loop through x axis left to right
            for (uint x=0; x<PixelSizes[_goodblock.pixelSizeIndex]; x++)
            {
                // check for padding first
                if(x < PixelSizePadding[_goodblock.pixelSizeIndex] ||
                    PixelSizes[_goodblock.pixelSizeIndex]-x < PixelSizePadding[_goodblock.pixelSizeIndex] + 1)
                {
                    continue;
                }

                // left right or flipper
                if(_goodblock.symmetryIndex == 0 || _goodblock.symmetryIndex == 4)
                {
                    if((x+1) > PixelSizeHalf[_goodblock.pixelSizeIndex])
                    {
                        continue;
                    }
                }            
                // diagonal top left to bottom right
                else if(_goodblock.symmetryIndex == 2)
                {
                    if(_goodblock.tokenIndex % 2 == 0)
                    {
                        if((x+1) > PixelSizeHalf[_goodblock.pixelSizeIndex])
                        {
                            continue;
                        }

                    } else
                    {
                        if(x > (PixelSizes[_goodblock.pixelSizeIndex] - y - 1))
                        {
                            continue;
                        }
                    }    
                }
                // diagonal bottom left to top right
                else if(_goodblock.symmetryIndex == 3)
                {
                    if(_goodblock.tokenIndex % 2 == 0)
                    {
                        if((x+1) > PixelSizeHalf[_goodblock.pixelSizeIndex]
                        )
                        {
                            continue;
                        }
                    } else
                    {
                        if(x < y + 1)
                        {
                            continue;
                        }
                    }
                }
                
                tempColorGroupIndex = getWeightedItem(PixelColorWeights, random(string(abi.encodePacked(_goodblock.blockDNA, Strings.toString(x*24 + y*22 + _goodblock.tokenIndex)))) % 100);

                // determine color group for next rectangle
                if(_goodblock.pixelSizeIndex == 0)
                {
                    if(_goodblock.tokenIndex % 4 == 0)
                    {
                        tempColorGroupIndex = getWeightedItem(PixelColorWeightsBiggins1, random(string(abi.encodePacked(_goodblock.blockDNA, Strings.toString(x*13 + y*4 + 13 + _goodblock.tokenIndex)))) % 100);
                    } else if(_goodblock.tokenIndex % 2 == 0)
                    {
                        tempColorGroupIndex = getWeightedItem(PixelColorWeightsBiggins2, random(string(abi.encodePacked(_goodblock.blockDNA, Strings.toString(x*13 + y*4 + 13 + _goodblock.tokenIndex)))) % 100);
                    }
                }

                // check if pixel is background color
                if(tempColorGroupIndex == 0)
                {
                    blockMap = string(abi.encodePacked(blockMap, "0"));
                    continue;
                }

                blockMap = string(abi.encodePacked(blockMap, "1"));

                // add rectangle to appropriate color group
                colorGroupRectangles[tempColorGroupIndex-1] = string(abi.encodePacked(
                    colorGroupRectangles[tempColorGroupIndex-1],
                    '<rect x="',
                    pixelPercents[x],
                    '" y="',
                    pixelPercents[y]
                ));
                colorGroupRectangles[tempColorGroupIndex-1] = string(abi.encodePacked(  
                    colorGroupRectangles[tempColorGroupIndex-1],  
                    '" width="',
                    rectPercent,
                    '" height="',
                    rectPercent,
                    '"/>'
                ));

                // next
            }
        }
        
        _goodblock.blockDNA = string(abi.encodePacked(_goodblock.blockDNA, "_", blockMap));

        string memory animatedBG2;

        // determine background color
        string memory bgColor = substring(ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)], 0, 7);
        
        if(_goodblock.isDarkBlock)
        {
            // update animated bg 2
            animatedBG2 = string(abi.encodePacked(
                '#000;',
                bgColor,';',
                bgColor,';',
                '#000'
            ));
            // make bg black
            bgColor = "#000";
        } else
        {
            // update animated bg 2
            animatedBG2 = string(abi.encodePacked(
                bgColor,';',
                '#000;',
                '#000;',
                bgColor
            ));
        }

        // 0 - goodblock token number
        // 1 - goodblock background
        // 2 - color group 1 fill
        // 3 - color group blocks
        // 4 - color group 2 fill
        // 5 - color group 2 blocks
        // 6 - color group 3 fill
        // 7 - color group 3 blocks
        // 8 - block symmetry
        // 9 - animation1 background values
        // 10 - animation1 background values
        // 11 - animation2 background values
        // 12 - end of svg!

        string memory blockSVG = string(abi.encodePacked(
            SvgHeader,
            svgBody[0],
            Strings.toString(uint256(_goodblock.tokenIndex)),
            svgBody[1],
            bgColor,
            svgBody[2]
        ));

        blockSVG = string(abi.encodePacked(
            blockSVG,
            substring(ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)], 8, 15),
            svgBody[3],
            colorGroupRectangles[0],
            svgBody[4]
        ));
        
        blockSVG = string(abi.encodePacked(
            blockSVG,
            substring(ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)], 16, 23),
            svgBody[5],
            colorGroupRectangles[1]
        ));
        
        blockSVG = string(abi.encodePacked(
            blockSVG,
            svgBody[6],
            substring(ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)], 24, 31),
            svgBody[7]
        ));

        blockSVG = string(abi.encodePacked(
            blockSVG,
            colorGroupRectangles[2],
            svgBody[8],
            BlockSymmetryStrings[_goodblock.symmetryIndex]
        ));
        
        blockSVG = string(abi.encodePacked(
            blockSVG,
            svgBody[9],
            _goodblock.labelColor,
            svgBody[10],
            ColorGroupNames[_goodblock.colorGroupIndex],
            ' #',
            Strings.toString(_goodblock.tokenIndex)
        ));
        
        blockSVG = string(abi.encodePacked(
            blockSVG,
            svgBody[11],
            ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)],
            svgBody[12]
        ));

        blockSVG = string(abi.encodePacked(
            blockSVG,
            ColorPalettes[(_goodblock.colorGroupIndex * 8 + _goodblock.paletteIndex)],
            svgBody[13],
            animatedBG2,
            svgBody[14]
        ));

        return blockSVG;        
    }
    function blockToMetadata(GoodBlock memory _goodblock) private view returns(string memory)
    {
        string memory metadata = string(abi.encodePacked(
            '{"trait_type": "Pixel Size", "value":"',
            PixelSizeNames[_goodblock.pixelSizeIndex],
            '"},{"trait_type": "Symmetry", "value":"',
            BlockSymmetryNames[_goodblock.symmetryIndex],
            '"},{"trait_type": "Color Group", "value":"',
            ColorGroupNames[_goodblock.colorGroupIndex],
            '"},{"trait_type": "Palette Index", "value":"',
            Strings.toString(_goodblock.paletteIndex),
            '"},'
        ));

        if(_goodblock.isDarkBlock)
        {
            metadata = string(abi.encodePacked(
                metadata,
                '{"trait_type": "Special Trait", "value":"Do Good"},'
            ));
        } else
        {
            metadata = string(abi.encodePacked(
                metadata,
                '{"trait_type": "Special Trait", "value":"None"},'
            ));
        }

        return metadata;
    }
    function tokenGenURI(uint256 _tokenId, string memory _tokenMetadata, string memory _tokenAttributes) public view returns(string memory)
    {
        // get goodblock
        GoodBlock memory goodblock = tokenToGoodblock(_tokenId);        
        // get svg
        string memory goodblockSVG = string(Base64.encode(bytes(goodblockToSVG(goodblock))));
        // get attributes for metadata
        string memory goodblockMetadata = blockToMetadata(goodblock);
        // set up token uri
        string memory tokenUri = string(abi.encodePacked(
            '{"name":"',
            ColorGroupNames[goodblock.colorGroupIndex],
            ' #',
            Strings.toString(_tokenId),
            '","description":"',
            GenerationDescription,
            '","DNA":"',
            goodblock.blockDNA,
            '",',
            _tokenMetadata,
            ',"attributes":[',
            goodblockMetadata,
            _tokenAttributes,
            '],"image":"data:image/svg+xml;base64,',
            goodblockSVG,
            '"}'
        ));       
        
        return string(abi.encodePacked("data:application/json;base64,", string(Base64.encode(bytes(tokenUri)))));
    }
    string private constant d = "0000111110111110000001111111000000111111111111100001111111111100011111111111111000111111111111101111111111111110011111111111111111111100011111100111111000111111111110000011111001111100000111111111100000111110011111000001111111111000001111100111110000011111111110000011111001111100000111111111110001111110011111100011111111111111111111100111111111111111011111111111111000111111111111100011111111111110000111111111110000011111101111100000011111110000";
    string private constant g = "000011110011111000000111111100000000001111111000000000011111011111001111111111111000011111111111000000111111111110000001111111111111011111111111111000111111111111100001111111111111000011111111111111111111111111111001111111111111110011111111111111100111111111111111111111000111111001111110001111110011111100011111100111111000111111111110000011111001111100000111110011111000001111100111110000011111111110000011111001111100000111110011111000001111100111110000011111111110000011111001111100000111110011111000001111100111110000011111111110000011111001111100000111110011111000001111100111110000011111111111000111111001111110001111110011111100011111100111111000111111011111111111111001111111111111110011111111111111100111111111111111001111111111111000111111111111100001111111111111000011111111111111000111111011111000011111111111000000111111111110000001111111111111000000000011111000000111111100000000001111111000000000111111011111011100000111111000000000000000000000000000000000000000000000000000011111111111111000000000000000000000000000000000000000000000000000011111111111110000000000000000000000000000000000000000000000000000011111111111100000000000000000000000000000000000000000000000000000001111111110000000000000000000000000000000000000000000000000000000";
    address public _Owner;
    function transferOwnership(address _newOwner) external 
    {
        if(msg.sender != _Owner) revert YoureNotTheOwnerHomie();
        if(_newOwner == address(0)) revert SorryYouCantAbandonOwnershipToTheZeroAddress();
        _Owner = _newOwner;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):