Base Sepolia Testnet

Contract

0x025996Bf6139AdD64F7808d6cb935034cd07da9c

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
248032072025-04-23 2:18:2210 hrs ago1745374702  Contract Creation0 ETH

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WarpsMetadata

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 99999999 runs

Other Settings:
shanghai EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 6 : WarpsMetadata.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "lib/openzeppelin-contracts/contracts/utils/Base64.sol";

import "./WarpsArt.sol";
import "../interfaces/IWarps.sol";
import "./Utilities.sol";

/**
 * @title  WarpsMetadata
 * @author Hurls
 * @notice Renders ERC721 compatible metadata for Warps.
 */
library WarpsMetadata {
    /// @dev Render the JSON Metadata for a given Warps token.
    /// @param tokenId The id of the token to render.
    /// @param warps The DB containing all warps.
    function tokenURI(uint256 tokenId, IWarps.Warps storage warps) public view returns (string memory) {
        IWarps.Warp memory warp = WarpsArt.getWarp(tokenId, warps);

        // Generate both static and animated versions
        bytes memory staticSvg = WarpsArt.generateSVG(warp, warps);

        bytes memory metadata = abi.encodePacked(
            "{",
            '"name": "Warps ',
            Utilities.uint2str(tokenId),
            '",',
            '"description": "Up and to the right.",',
            '"image": ',
            '"data:image/svg+xml;base64,',
            Base64.encode(staticSvg),
            '",',
            '"attributes": [',
            attributes(warp),
            "]",
            "}"
        );

        return string(abi.encodePacked("data:application/json;base64,", Base64.encode(metadata)));
    }

    /// @dev Render the JSON atributes for a given Warps token.
    /// @param warp The warp to render.
    function attributes(IWarps.Warp memory warp) public pure returns (bytes memory) {
        bool showVisualAttributes = warp.hasManyWarps;

        return abi.encodePacked(
            showVisualAttributes
                ? trait("Color Band", colorBand(WarpsArt.colorBandIndex(warp, warp.stored.divisorIndex)), ",")
                : "",
            showVisualAttributes
                ? trait("Gradient", gradients(WarpsArt.gradientIndex(warp, warp.stored.divisorIndex)), ",")
                : "",
            trait("Warps", Utilities.uint2str(warp.warpsCount), "")
        );
    }

    /// @dev Get the names for different gradients. Compare WarpsArt.GRADIENTS.
    /// @param gradientIndex The index of the gradient.
    function gradients(uint8 gradientIndex) public pure returns (string memory) {
        return ["None", "Linear", "Double Linear", "Reflected", "Double Angled", "Angled", "Linear Z"][gradientIndex];
    }

    /// @dev Get the percentage values for different color bands. Compare WarpsArt.COLOR_BANDS.
    /// @param bandIndex The index of the color band.
    function colorBand(uint8 bandIndex) public pure returns (string memory) {
        return ["Eighty", "Sixty", "Forty", "Twenty", "Ten", "Five", "One"][bandIndex];
    }

    /// @dev Generate the SVG snipped for a single attribute.
    /// @param traitType The `trait_type` for this trait.
    /// @param traitValue The `value` for this trait.
    /// @param append Helper to append a comma.
    function trait(string memory traitType, string memory traitValue, string memory append)
        public
        pure
        returns (string memory)
    {
        return
            string(abi.encodePacked("{", '"trait_type": "', traitType, '",' '"value": "', traitValue, '"' "}", append));
    }

    /// @dev Generate the HTML for the animation_url in the metadata.
    /// @param tokenId The id of the token to generate the embed for.
    /// @param svg The rendered SVG code to embed in the HTML.
    function generateHTML(uint256 tokenId, bytes memory svg) public pure returns (bytes memory) {
        return abi.encodePacked(
            "<!DOCTYPE html>",
            '<html lang="en">',
            "<head>",
            '<meta charset="UTF-8">',
            '<meta http-equiv="X-UA-Compatible" content="IE=edge">',
            '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
            "<title>Warp #",
            Utilities.uint2str(tokenId),
            "</title>",
            "<style>",
            "html,",
            "body {",
            "margin: 0;",
            "background: #EFEFEF;",
            "overflow: hidden;",
            "}",
            "svg {",
            "max-width: 100vw;",
            "max-height: 100vh;",
            "}",
            "</style>",
            "</head>",
            "<body>",
            svg,
            "</body>",
            "</html>"
        );
    }
}

File 2 of 6 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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));

        /// @solidity memory-safe-assembly
        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;
    }
}

File 3 of 6 : WarpsArt.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "../interfaces/IWarps.sol";
import "./EightyColors.sol";
import "./Utilities.sol";

/**
 * @title  WarpsArt
 * @author Hurls
 * @notice Renders the Warps visuals.
 */
library WarpsArt {
    /// @dev The semiperfect divisors of the 80 warps.
    function divisors() public pure returns (uint8[8] memory) {
        return [20, 10, 4, 1, 0, 0, 0, 0];
    }

    /// @dev The different color band sizes that we use for the art.
    function colorBands() public pure returns (uint8[7] memory) {
        return [80, 60, 40, 20, 10, 5, 1];
    }

    /// @dev The gradient increment steps.
    function gradients() public pure returns (uint8[7] memory) {
        return [0, 1, 2, 5, 8, 9, 10];
    }

    /// @dev Load a warp from storage and fill its current state settings.
    /// @param tokenId The id of the warp to fetch.
    /// @param warps The DB containing all warps.
    function getWarp(uint256 tokenId, IWarps.Warps storage warps) public view returns (IWarps.Warp memory warp) {
        IWarps.StoredWarp memory stored = warps.all[tokenId];

        return getWarp(tokenId, stored.divisorIndex, warps);
    }

    /// @dev Load a warp from storage and fill its current state settings.
    /// @param tokenId The id of the warp to fetch.
    /// @param divisorIndex The divisorindex to get.
    /// @param warps The DB containing all warps.
    function getWarp(uint256 tokenId, uint8 divisorIndex, IWarps.Warps storage warps)
        public
        view
        returns (IWarps.Warp memory warp)
    {
        IWarps.StoredWarp memory stored = warps.all[tokenId];
        stored.divisorIndex = divisorIndex; // Override in case we're fetching specific state.
        warp.stored = stored;

        // Set up the source of randomness + seed for this Warp.
        warp.seed = stored.seed;

        // Helpers
        warp.isRoot = divisorIndex == 0;
        warp.hasManyWarps = divisorIndex < 6;
        warp.composite = !warp.isRoot && divisorIndex < 7 ? stored.composites[divisorIndex - 1] : 0;

        // Token properties
        warp.colorBand = colorBandIndex(warp, divisorIndex);
        warp.gradient = gradientIndex(warp, divisorIndex);
        warp.warpsCount = divisors()[divisorIndex];
    }

    /// @dev Query the gradient of a given warp at a certain warp count.
    /// @param warp The warp we want to get the gradient for.
    /// @param divisorIndex The warp divisor in question.
    function gradientIndex(IWarps.Warp memory warp, uint8 divisorIndex) public pure returns (uint8) {
        uint256 n = Utilities.random(warp.seed, "gradient", 100);

        return divisorIndex == 0
            ? n < 20 ? uint8(1 + (n % 6)) : 0
            : divisorIndex < 6 ? warp.stored.gradients[divisorIndex - 1] : 0;
    }

    /// @dev Query the color band of a given warp at a certain warp count.
    /// @param warp The warp we want to get the color band for.
    /// @param divisorIndex The warp divisor in question.
    function colorBandIndex(IWarps.Warp memory warp, uint8 divisorIndex) public pure returns (uint8) {
        uint256 n = Utilities.random(warp.seed, "band", 120);

        return divisorIndex == 0
            ? (n > 80 ? 0 : n > 40 ? 1 : n > 20 ? 2 : n > 10 ? 3 : n > 4 ? 4 : n > 1 ? 5 : 6)
            : divisorIndex < 6 ? warp.stored.colorBands[divisorIndex - 1] : 6;
    }

    /// @dev Generate indexes for the color slots of warp parents (up to the EightyColors.COLORS themselves).
    /// @param divisorIndex The current divisorIndex to query.
    /// @param warp The current warp to investigate.
    /// @param warps The DB containing all warps.
    function colorIndexes(uint8 divisorIndex, IWarps.Warp memory warp, IWarps.Warps storage warps)
        public
        view
        returns (uint256[] memory)
    {
        uint8[8] memory divisors_ = divisors();
        uint256 warpsCount = divisors_[divisorIndex];
        uint256 seed = warp.seed;
        uint8 colorBand = colorBands()[colorBandIndex(warp, divisorIndex)];
        uint8 gradient = gradients()[gradientIndex(warp, divisorIndex)];

        // If we're a composited warp, we choose colors only based on
        // the slots available in our parents. Otherwise,
        // we choose based on our available spectrum.
        uint256 possibleColorChoices = divisorIndex > 0 ? divisors_[divisorIndex - 1] * 2 : 80;

        // We initialize our index and select the first color
        uint256[] memory indexes = new uint256[](warpsCount);
        indexes[0] = Utilities.random(seed, possibleColorChoices);

        // If we have more than one warp, continue selecting colors
        if (warp.hasManyWarps) {
            if (gradient > 0) {
                // If we're a gradient warp, we select based on the color band looping around
                // the 80 possible colors
                for (uint256 i = 1; i < warpsCount;) {
                    indexes[i] = (indexes[0] + (i * gradient * colorBand / warpsCount) % colorBand) % 80;
                    unchecked {
                        ++i;
                    }
                }
            } else if (divisorIndex == 0) {
                // If we select initial non gradient colors, we just take random ones
                // available in our color band
                for (uint256 i = 1; i < warpsCount;) {
                    indexes[i] = (indexes[0] + Utilities.random(seed + i, colorBand)) % 80;
                    unchecked {
                        ++i;
                    }
                }
            } else {
                // If we have parent warps, we select our colors from their set
                for (uint256 i = 1; i < warpsCount;) {
                    indexes[i] = Utilities.random(seed + i, possibleColorChoices);
                    unchecked {
                        ++i;
                    }
                }
            }
        }

        // We resolve our color indexes through our parent tree until we reach the root warps
        if (divisorIndex > 0) {
            uint8 previousDivisor = divisorIndex - 1;

            // We already have our current warp, but need the our parent state color indices
            uint256[] memory parentIndexes = colorIndexes(previousDivisor, warp, warps);

            // We also need to fetch the colors of the warp that was composited into us
            IWarps.Warp memory composited = getWarp(warp.composite, previousDivisor, warps);
            uint256[] memory compositedIndexes = colorIndexes(previousDivisor, composited, warps);

            // Replace random indices with parent / root color indices
            uint8 count = divisors_[previousDivisor];

            // We always select the first color from our parent
            uint256 initialBranchIndex = indexes[0] % count;
            indexes[0] = indexes[0] < count ? parentIndexes[initialBranchIndex] : compositedIndexes[initialBranchIndex];

            // If we don't have a gradient, we continue resolving from our parent for the remaining warps
            if (gradient == 0) {
                for (uint256 i; i < warpsCount;) {
                    uint256 branchIndex = indexes[i] % count;
                    indexes[i] = indexes[i] < count ? parentIndexes[branchIndex] : compositedIndexes[branchIndex];

                    unchecked {
                        ++i;
                    }
                }
            } else {
                // If we have a gradient we base the remaining colors off our initial selection
                for (uint256 i = 1; i < warpsCount;) {
                    indexes[i] = (indexes[0] + (i * gradient * colorBand / warpsCount) % colorBand) % 80;

                    unchecked {
                        ++i;
                    }
                }
            }
        }

        return indexes;
    }

    /// @dev Fetch all colors of a given Warp.
    /// @param warp The warp to get colors for.
    /// @param warps The DB containing all warps.
    function colors(IWarps.Warp memory warp, IWarps.Warps storage warps)
        public
        view
        returns (string[] memory, uint256[] memory)
    {
        // A fully composited warp has no color.
        if (warp.stored.divisorIndex == 7) {
            string[] memory zeroColors = new string[](1);
            uint256[] memory zeroIndexes = new uint256[](1);
            zeroColors[0] = "000";
            zeroIndexes[0] = 999;
            return (zeroColors, zeroIndexes);
        }

        // Fetch the indices on the original color mapping.
        uint256[] memory indexes = colorIndexes(warp.stored.divisorIndex, warp, warps);

        // Map over to get the colors.
        string[] memory warpColors = new string[](indexes.length);
        string[80] memory allColors = EightyColors.colors();

        // Always set the first color.
        warpColors[0] = allColors[indexes[0]];

        // Resolve each additional check color via their index in EightyColors.COLORS.
        for (uint256 i = 1; i < indexes.length; i++) {
            warpColors[i] = allColors[indexes[i]];
        }

        return (warpColors, indexes);
    }

    /// @dev Get the number of warps we should display per row.
    /// @param warps The number of warps in the piece.
    function perRow(uint8 warps) public pure returns (uint8) {
        return warps == 80 ? 8 : warps >= 20 ? 4 : warps == 10 || warps == 4 ? 2 : 1;
    }

    /// @dev Get the X-offset for positioning warp horizontally.
    /// @param warps The number of warps in the piece.
    function rowX(uint8 warps) public pure returns (uint16) {
        if (warps == 2) {
            return 310; // Adjusted value to center two warps horizontally
        }
        return warps <= 1 ? 286 : warps == 5 ? 304 : warps == 10 || warps == 4 ? 268 : 196;
    }

    /// @dev Get the Y-offset for positioning warp vertically.
    /// @param warps The number of warps in the piece.
    function rowY(uint8 warps) public pure returns (uint16) {
        if (warps == 2) {
            return 270; // Adjusted value to center two warps vertically
        }
        return warps > 4 ? 160 : warps == 4 ? 268 : warps > 1 ? 304 : 286;
    }

    /// @dev Generate the SVG code for all warps in a given token.
    /// @param data The data object containing rendering settings.
    function generateWarps(WarpRenderData memory data) public pure returns (bytes memory) {
        bytes memory warpsBytes;

        uint8 warpsCount = data.count;
        for (uint8 i; i < warpsCount; i++) {
            // Compute row settings.
            data.indexInRow = i % data.perRow;
            data.isNewRow = data.indexInRow == 0 && i > 0;

            // Compute offsets.
            if (data.isNewRow) data.rowY += data.spaceY;
            if (data.isNewRow && data.indent) {
                if (i == 0) {
                    data.rowX += data.spaceX / 2;
                }

                if (i % (data.perRow * 2) == 0) {
                    data.rowX -= data.spaceX / 2;
                } else {
                    data.rowX += data.spaceX / 2;
                }
            }
            string memory translateX = Utilities.uint2str(data.rowX + data.indexInRow * data.spaceX);
            string memory translateY = Utilities.uint2str(data.rowY);
            string memory color = data.colors[i];

            // Render the current   .
            warpsBytes = abi.encodePacked(
                warpsBytes,
                abi.encodePacked(
                    '<g transform="translate(',
                    translateX,
                    ", ",
                    translateY,
                    ')">',
                    '<g transform="translate(3, 3) scale(',
                    data.scale,
                    ')">',
                    // Outer layer (color)
                    '<path d="M15 26.264c1.271 0 4.012 4.144 5.207 3.703 1.194-.441.668-5.403 1.643-6.233.974-.83 5.698.558 6.334-.56.636-1.117-2.91-4.576-2.69-5.847.221-1.27 4.72-3.29 4.498-4.56-.22-1.271-5.127-1.607-5.763-2.725-.636-1.117 1.53-5.597.556-6.427-.974-.83-4.945 2.114-6.14 1.672C17.45 4.846 16.272 0 15 0c-1.272 0-2.45 4.846-3.645 5.287-1.195.442-5.166-2.502-6.14-1.672-.974.83 1.192 5.31.556 6.427C5.136 11.16.23 11.496.008 12.767c-.22 1.27 4.277 3.29 4.497 4.56.221 1.271-3.325 4.73-2.689 5.847.636 1.118 5.36-.27 6.334.56.974.83.448 5.791 1.643 6.233 1.196.441 3.936-3.703 5.207-3.703Z" fill="#',
                    color,
                    '"/>',
                    // First black layer
                    '<path d="M14.999 24.216c1.04 0 3.283 3.39 4.26 3.03.978-.361.547-4.421 1.345-5.1.797-.679 4.662.456 5.182-.458.52-.915-2.381-3.744-2.2-4.784.18-1.04 3.86-2.691 3.68-3.731-.181-1.04-4.196-1.315-4.716-2.23-.52-.913 1.253-4.58.456-5.258-.797-.679-4.047 1.73-5.025 1.368-.977-.361-1.941-4.326-2.982-4.326-1.04 0-2.004 3.965-2.982 4.326-.977.361-4.227-2.047-5.024-1.368-.797.678.976 4.345.456 5.259-.52.914-4.535 1.19-4.716 2.229-.18 1.04 3.5 2.691 3.68 3.731.18 1.04-2.72 3.87-2.2 4.784.52.914 4.385-.22 5.182.458.797.678.367 4.738 1.344 5.1.978.36 3.22-3.03 4.26-3.03Z" fill="#000"/>',
                    // Middle layer (color)
                    '<path d="M14.998 22.168c.81 0 2.553 2.637 3.314 2.357.76-.281.425-3.44 1.046-3.967.62-.528 3.625.355 4.03-.356.405-.712-1.852-2.912-1.711-3.72.14-.81 3.003-2.094 2.862-2.903-.14-.809-3.263-1.023-3.668-1.734-.404-.711.975-3.562.355-4.09S18.078 9.1 17.318 8.819c-.76-.28-1.51-3.364-2.32-3.364-.809 0-1.558 3.083-2.319 3.364-.76.281-3.288-1.592-3.907-1.064-.62.528.758 3.379.354 4.09-.405.711-3.527.925-3.668 1.734-.14.809 2.722 2.093 2.862 2.902.14.809-2.116 3.01-1.711 3.72.404.712 3.41-.171 4.03.357.62.528.286 3.685 1.046 3.966.76.281 2.505-2.356 3.314-2.356Z" fill="#',
                    color,
                    '"/>',
                    // Inner black layer
                    '<path d="M15.005 20.12c.579 0 1.824 1.884 2.367 1.683.543-.2.304-2.456.747-2.833.443-.377 2.59.254 2.88-.255.288-.508-1.323-2.08-1.223-2.657.1-.578 2.145-1.495 2.044-2.073-.1-.578-2.33-.73-2.62-1.238-.288-.508.696-2.545.254-2.922-.443-.377-2.248.96-2.792.76-.543-.2-1.078-2.403-1.656-2.403-.578 0-1.114 2.202-1.657 2.403-.543.2-2.348-1.137-2.791-.76-.443.377.542 2.414.253 2.922-.29.508-2.52.66-2.62 1.238-.1.578 1.944 1.495 2.044 2.073.1.578-1.511 2.15-1.222 2.657.289.509 2.437-.122 2.88.255.442.377.203 2.632.746 2.833.543.2 1.789-1.683 2.367-1.683Z" fill="#000"/>',
                    // Inner layer (color)
                    '<path d="M15 18.584c.404 0 1.276 1.319 1.656 1.178.38-.14.213-1.719.523-1.983.31-.264 1.813.177 2.015-.178.202-.356-.926-1.456-.855-1.86.07-.405 1.5-1.047 1.43-1.451-.07-.405-1.63-.512-1.833-.867-.203-.356.487-1.782.177-2.046-.31-.264-1.574.673-1.954.532-.38-.14-.755-1.682-1.16-1.682-.404 0-.78 1.542-1.16 1.682-.38.141-1.643-.796-1.953-.532-.31.264.38 1.69.177 2.046-.202.355-1.764.462-1.834.867-.07.404 1.36 1.046 1.431 1.45.07.405-1.058 1.505-.856 1.86.203.356 1.706-.085 2.016.179.31.264.142 1.843.523 1.983.38.14 1.252-1.178 1.656-1.178Z" fill="#',
                    color,
                    '"/>',
                    "</g>",
                    "</g>"
                )
            );
        }

        return warpsBytes;
    }

    /// @dev Collect relevant rendering data for easy access across functions.
    /// @param warp Our current warp loaded from storage.
    /// @param warps The DB containing all warps.
    function collectRenderData(IWarps.Warp memory warp, IWarps.Warps storage warps)
        public
        view
        returns (WarpRenderData memory data)
    {
        // Carry through base settings.
        data.warp = warp;
        data.isBlack = warp.stored.divisorIndex == 7;
        data.count = data.isBlack ? 1 : divisors()[warp.stored.divisorIndex];

        // Compute colors and indexes.
        (string[] memory colors_, uint256[] memory colorIndexes_) = colors(warp, warps);
        data.gridColor = "#000000";
        data.canvasColor = "#000000";
        data.colorIndexes = colorIndexes_;
        data.colors = colors_;

        // Compute positioning data.
        data.scale = data.count > 20 ? "0.528" : data.count > 1 ? "2" : "3";
        data.spaceX = data.count == 80 ? 36 : 72;
        data.spaceY = data.count > 20 ? 36 : 72;
        data.perRow = perRow(data.count);
        data.indent = data.count == 40;
        data.rowX = rowX(data.count);
        data.rowY = rowY(data.count);
    }

    /// @dev Generate the SVG code for rows in the 8x10 Warps grid.
    function generateGridRow() public pure returns (bytes memory) {
        bytes memory row;
        for (uint256 i; i < 8; i++) {
            row = abi.encodePacked(row, '<use href="#square" x="', Utilities.uint2str(196 + i * 36), '" y="160"/>');
        }
        return row;
    }

    /// @dev Generate the SVG code for the entire 8x10 Warps grid.
    function generateGrid() public pure returns (bytes memory) {
        bytes memory grid;
        for (uint256 i; i < 10; i++) {
            grid = abi.encodePacked(grid, '<use href="#row" y="', Utilities.uint2str(i * 36), '"/>');
        }

        return abi.encodePacked('<g id="grid" x="196" y="160">', grid, "</g>");
    }

    /// @dev Generate the complete SVG code for a given Warp.
    /// @param warp The warp to render.
    /// @param warps The DB containing all warps.
    function generateSVG(IWarps.Warp memory warp, IWarps.Warps storage warps) public view returns (bytes memory) {
        WarpRenderData memory data = collectRenderData(warp, warps);

        return abi.encodePacked(
            "<svg ",
            'viewBox="0 0 680 680" ',
            'fill="none" xmlns="http://www.w3.org/2000/svg" ',
            'style="width:100%;background:black;"',
            ">",
            "<defs>",
            '<rect id="square" width="36" height="36" stroke="',
            data.gridColor,
            '"></rect>',
            '<g id="row">',
            generateGridRow(),
            "</g>" "</defs>",
            '<rect width="680" height="680" fill="black"/>',
            '<rect x="188" y="152" width="304" height="376" fill="',
            data.canvasColor,
            '"/>',
            generateGrid(),
            generateWarps(data),
            "</svg>"
        );
    }
}

/// @dev Bag holding all data relevant for rendering.
struct WarpRenderData {
    IWarps.Warp warp;
    uint256[] colorIndexes;
    string[] colors;
    string canvasColor;
    string gridColor;
    string duration;
    string scale;
    uint32 seed;
    uint16 rowX;
    uint16 rowY;
    uint8 count;
    uint8 spaceX;
    uint8 spaceY;
    uint8 perRow;
    uint8 indexInRow;
    uint8 isIndented;
    bool isNewRow;
    bool isBlack;
    bool indent;
    bool isStatic;
}

File 4 of 6 : IWarps.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IWarps {
    struct StoredWarp {
        uint16[6] composites; // The tokenIds that were composited into this one
        uint8[5] colorBands; // The length of the used color band in percent
        uint8[5] gradients; // Gradient settings for each generation
        uint8 divisorIndex; // Easy access to next / previous divisor
        uint16 seed; // A unique identifier to enable swapping
    }

    struct Warp {
        StoredWarp stored; // We carry over the warp from storage
        uint256 seed; // The instantiated seed for pseudo-randomisation
        uint8 warpsCount; // How many warps this token has
        bool hasManyWarps; // Whether the warp has many warps
        uint16 composite; // The parent tokenId that was composited into this one
        bool isRoot; // Whether it has no parents (80 warps)
        uint8 colorBand; // 100%, 50%, 25%, 12.5%, 6.25%, 5%, 1.25%
        uint8 gradient; // Linearly through the colorBand [1, 2, 3]
    }

    struct Epoch {
        bool committed;
        bool revealed;
        uint64 revealBlock;
        uint128 randomness;
    }

    struct Warps {
        mapping(uint256 => StoredWarp) all; // All warps
        mapping(uint256 => Epoch) epochs; // Epoch data for randomness
        uint32 minted; // The number of warps editions that have been migrated
        uint32 burned; // The number of tokens that have been burned
        uint32 currentEpoch; // Current epoch number
    }

    event Sacrifice(uint256 indexed burnedId, uint256 indexed tokenId);

    event Composite(uint256 indexed tokenId, uint256 indexed burnedId, uint8 indexed warps);

    error NotAllowed();
    error InvalidTokenCount();
    error BlackWarp__InvalidWarp();
}

File 5 of 6 : Utilities.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

library Utilities {
    /// @dev Zero-index based pseudorandom number based on one input and max bound
    function random(uint256 input, uint256 _max) internal pure returns (uint256) {
        return (uint256(keccak256(abi.encodePacked(input))) % _max);
    }

    /// @dev Zero-index based salted pseudorandom number based on two inputs and max bound
    function random(uint256 input, string memory salt, uint256 _max) internal pure returns (uint256) {
        return (uint256(keccak256(abi.encodePacked(input, salt))) % _max);
    }

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

    /// @dev Get the smallest non zero number
    function minGt0(uint8 one, uint8 two) internal pure returns (uint8) {
        return one > two ? two > 0 ? two : one : one;
    }

    /// @dev Get the smaller number
    function min(uint8 one, uint8 two) internal pure returns (uint8) {
        return one < two ? one : two;
    }

    /// @dev Get the larger number
    function max(uint8 one, uint8 two) internal pure returns (uint8) {
        return one > two ? one : two;
    }

    /// @dev Get the average between two numbers
    function avg(uint8 one, uint8 two) internal pure returns (uint8 result) {
        unchecked {
            result = (one >> 1) + (two >> 1) + (one & two & 1);
        }
    }

    /// @dev Get the days since another date (input is seconds)
    function day(uint256 from, uint256 to) internal pure returns (uint24) {
        return uint24((to - from) / 24 hours + 1);
    }
}

File 6 of 6 : EightyColors.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title  EightyColors
 * @author Hurls
 * @notice The eighty colors of Warps.
 */
library EightyColors {
    /// @dev These are sorted in a gradient.
    function colors() public pure returns (string[80] memory) {
        return [
            // Deep purples to violets
            "2D0157",
            "3A0B75",
            "4C1A9E",
            "5B28BC",
            "6B36DB",
            "7B45F9",
            "8860FF",
            "9575FF",
            "A18CFF",
            // Blues
            "0A4BF1",
            "0E5FFF",
            "1E74FF",
            "2E89FF",
            "3E9EFF",
            "4EB3FF",
            "5EC8FF",
            "6EDDFF",
            // Teals and Cyans
            "00B5B5",
            "00C5C5",
            "00D5D5",
            "00E5E5",
            "00F5F5",
            // Greens
            "018A08",
            "029F0E",
            "03B414",
            "04C91A",
            "05DE20",
            "06F326",
            "25FF45",
            "45FF65",
            "65FF85",
            // Yellow-greens
            "85FF65",
            "A5FF45",
            "C5FF25",
            "E5FF05",
            // Yellows
            "FFE600",
            "FFD800",
            "FFCA00",
            "FFBC00",
            "FFAE00",
            // Orange
            "FFA000",
            "FF9200",
            "FF8400",
            "FF7600",
            "FF6800",
            // Coral and Salmon
            "FF5A4F",
            "FF4C41",
            "FF3E33",
            "FF3025",
            "FF2217",
            // Reds
            "FF1409",
            "F01209",
            "E11009",
            "D20E09",
            "C30C09",
            // Deep reds
            "B40A09",
            "A50809",
            "960609",
            "870409",
            "780209",
            // Burgundy to pink
            "690A1E",
            "7A0F33",
            "8B1448",
            "9C195D",
            "AD1E72",
            "BE2387",
            "CF289C",
            "E02DB1",
            "F132C6",
            // Magentas and purples
            "F23ED1",
            "F34ADC",
            "F456E7",
            "F562F2",
            "E77AFF",
            "D886FF",
            "C992FF",
            "BA9EFF",
            "9B89E7",
            "8C68CF",
            "7D47B7"
        ];
    }

    /**
     * @notice Get the color string at a specific index.
     * @param _index The index (0-79) of the color to retrieve.
     * @return The hex string of the color at the specified index.
     * @dev Reverts if the index is out of bounds.
     */
    function getColorByIndex(uint8 _index) public pure returns (string memory) {
        require(_index < 80, "Index out of bounds");
        return colors()[_index];
    }

    /**
     * @notice Get the index of a specific color string.
     * @param _color The hex string of the color to find.
     * @return The index (0-79) of the color.
     * @dev Reverts if the color is not found in the list.
     */
    function getIndexByColor(string memory _color) public pure returns (uint8) {
        string[80] memory _colors = colors();
        bytes32 colorHash = keccak256(abi.encodePacked(_color));

        for (uint8 i = 0; i < 80; i++) {
            if (keccak256(abi.encodePacked(_colors[i])) == colorHash) {
                return i;
            }
        }
        revert("Color not found");
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/",
    "solady-test/=lib/solady/test/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "uniswap-v2-core/=lib/uniswap/v2-core/contracts/",
    "uniswap-v3-core/=lib/uniswap/v3-core/contracts/",
    "uniswap-v3-periphery/=lib/uniswap/v3-periphery/contracts/",
    "uniswap-v4-core/=lib/uniswap/v4-core/src/",
    "uniswap-v4-periphery/=lib/uniswap/v4-periphery/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "uniswap/=lib/uniswap/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 99999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": true,
  "libraries": {
    "src/libraries/EightyColors.sol": {
      "EightyColors": "0xdF9BAa3097206178fdcB9Bc59d9917e5d89c7F79"
    },
    "src/libraries/WarpsArt.sol": {
      "WarpsArt": "0x2e02A531215b73182eF8d8dE8245495673Cd078F"
    },
    "src/libraries/WarpsMetadata.sol": {
      "WarpsMetadata": "0x025996Bf6139AdD64F7808d6cb935034cd07da9c"
    }
  }
}

Contract ABI

API
[{"inputs":[{"components":[{"components":[{"internalType":"uint16[6]","name":"composites","type":"uint16[6]"},{"internalType":"uint8[5]","name":"colorBands","type":"uint8[5]"},{"internalType":"uint8[5]","name":"gradients","type":"uint8[5]"},{"internalType":"uint8","name":"divisorIndex","type":"uint8"},{"internalType":"uint16","name":"seed","type":"uint16"}],"internalType":"struct IWarps.StoredWarp","name":"stored","type":"tuple"},{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint8","name":"warpsCount","type":"uint8"},{"internalType":"bool","name":"hasManyWarps","type":"bool"},{"internalType":"uint16","name":"composite","type":"uint16"},{"internalType":"bool","name":"isRoot","type":"bool"},{"internalType":"uint8","name":"colorBand","type":"uint8"},{"internalType":"uint8","name":"gradient","type":"uint8"}],"internalType":"struct IWarps.Warp","name":"warp","type":"tuple"}],"name":"attributes","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint8","name":"bandIndex","type":"uint8"}],"name":"colorBand","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"svg","type":"bytes"}],"name":"generateHTML","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint8","name":"gradientIndex","type":"uint8"}],"name":"gradients","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"traitType","type":"string"},{"internalType":"string","name":"traitValue","type":"string"},{"internalType":"string","name":"append","type":"string"}],"name":"trait","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]

6080806040523461001a57611ce9908161001f823930815050f35b5f80fdfe6080604090808252600480361015610015575f80fd5b5f91823560e01c9081635f5d5f4214610bdb575080638727240614610b9b578063962f363b14610b0b578063b390ef191461065b578063b7773faa146106085763fa7ea71f14610063575f80fd5b827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451578035602435732e02a531215b73182ef8d8de8245495673cd078f928551937f492544da00000000000000000000000000000000000000000000000000000000855283828601528260248601526103208086604481855af49586156105fe5790879392918497610464575b50506101329161034491895195869485937f113a1f9700000000000000000000000000000000000000000000000000000000855284018a611096565b6103248301525af49384156104595780946103b2575b50506103ae91610349608f61017161016b61016561034e96611397565b97611163565b9361158b565b9287519384916020987f7b000000000000000000000000000000000000000000000000000000000000008a8501527f226e616d65223a2022576172707320000000000000000000000000000000000060218501526101d8815180928c603088019101610f03565b8301907f222c000000000000000000000000000000000000000000000000000000000000918260308201527f226465736372697074696f6e223a2022557020616e6420746f2074686520726960328201527f6768742e222c000000000000000000000000000000000000000000000000000060528201527f22696d616765223a20000000000000000000000000000000000000000000000060588201527f22646174613a696d6167652f7376672b786d6c3b6261736536342c000000000060618201526102ae825180938d607c85019101610f03565b0190607c8201527f2261747472696275746573223a205b0000000000000000000000000000000000607e8201526102ee825180938b608d85019101610f03565b017f5d00000000000000000000000000000000000000000000000000000000000000608d8201527f7d00000000000000000000000000000000000000000000000000000000000000608e82015203606f810184520182610ea0565b611163565b9261039f603d825180967f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008783015261038f81518092898686019101610f03565b810103601d810187520185610ea0565b51928284938452830190610f24565b0390f35b909193503d8082843e6103c58184610ea0565b8201916020818403126104515780519067ffffffffffffffff8211610455570182601f82011215610451578051916103fc83610f67565b9361040988519586610ea0565b8385526020848401011161044e5750608f61017161016b6101658661043f6103ae99976103499760208061034e9c019101610f03565b98965050505050819350610148565b80fd5b5080fd5b8280fd5b8551903d90823e3d90fd5b919650809293503d83116105f7575b61047d8183610ea0565b8101918183039081126105f35788519261049684610dca565b6102408092126105ef578951906104ac82610e14565b80601f850112156105eb578a516104c281610e4c565b8060c08601918383116105e7579186959493918e938e9a9998905b8382106105b85750509361030093610518846105096101329c9a966103449c9a966105a99a8752611012565b60208501526101608601611012565b908201526105296102008401611004565b606082015261053b6102208401610ff5565b6080820152855281015160208501526105576102608201611004565b8d850152610568610280820161105f565b606085015261057a6102a08201610ff5565b608085015261058c6102c0820161105f565b60a085015261059e6102e08201611004565b60c085015201611004565b60e082015296918193506100f6565b60209294969798999a9b50819395506105d18392610ff5565b8152019101918d999897969594928f94926104dd565b8c80fd5b8980fd5b8880fd5b8780fd5b503d610473565b88513d89823e3d90fd5b828460207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451576103ae90610648610643610ee1565b6118a0565b9051918291602083526020830190610f24565b5090827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261044e576024359067ffffffffffffffff821161044e573660238301121561044e576103ae7f3c2f626f64793e0000000000000000000000000000000000000000000000000085610af9610179876106eb6106e4368a84013560248c01610fa1565b9135611397565b9084519586927f3c21444f43545950452068746d6c3e000000000000000000000000000000000060208501527f3c68746d6c206c616e673d22656e223e00000000000000000000000000000000602f8501527f3c686561643e0000000000000000000000000000000000000000000000000000603f8501527f3c6d65746120636861727365743d225554462d38223e0000000000000000000060458501527f3c6d65746120687474702d65717569763d22582d55412d436f6d70617469626c605b8501527f652220636f6e74656e743d2249453d65646765223e0000000000000000000000607b8501527f3c6d657461206e616d653d2276696577706f72742220636f6e74656e743d227760908501527f696474683d6465766963652d77696474682c20696e697469616c2d7363616c6560b08501527f3d312e30223e000000000000000000000000000000000000000000000000000060d08501527f3c7469746c653e5761727020230000000000000000000000000000000000000060d685015261088181518092602060e388019101610f03565b83017f3c2f7469746c653e00000000000000000000000000000000000000000000000060e38201527f3c7374796c653e0000000000000000000000000000000000000000000000000060eb8201527f68746d6c2c00000000000000000000000000000000000000000000000000000060f28201527f626f6479207b000000000000000000000000000000000000000000000000000060f78201527f6d617267696e3a20303b0000000000000000000000000000000000000000000060fd8201527f6261636b67726f756e643a20234546454645463b0000000000000000000000006101078201527f6f766572666c6f773a2068696464656e3b00000000000000000000000000000061011b8201527f7d000000000000000000000000000000000000000000000000000000000000008061012c8301527f737667207b00000000000000000000000000000000000000000000000000000061012d8301527f6d61782d77696474683a2031303076773b0000000000000000000000000000006101328301527f6d61782d6865696768743a2031303076683b00000000000000000000000000006101438301526101558201527f3c2f7374796c653e0000000000000000000000000000000000000000000000006101568201527f3c2f686561643e0000000000000000000000000000000000000000000000000061015e8201527f3c626f64793e0000000000000000000000000000000000000000000000000000610165820152825190610abe8261016b9560208785019101610f03565b01918201527f3c2f68746d6c3e0000000000000000000000000000000000000000000000000061017282015203610159810185520183610ea0565b51918291602083526020830190610f24565b50919060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261044e5767ffffffffffffffff92803584811161045557610b589036908301610fd7565b93602435818111610b9757610b709036908401610fd7565b9260443591821161044e575091610b916103ae959261064894369101610fd7565b91611bf6565b8380fd5b828460207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451576103ae90610648610bd6610ee1565b611a4d565b919290507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360161032081126104515761024090610c1884610dca565b1261044e578351610c2881610e14565b3660231215610451578451610c3c81610e4c565b8060c495368711610dab57905b868210610daf57505081523660e3121561045157845193610c6985610e14565b8461016491368311610dab57905b828210610d9357505060209485830152366101831215610455578551610c9c81610e14565b8061020492368411610d8f578790915b848310610d7757505050868301523560ff8116810361045557606082015261ffff90610224358281168103610b97576080820152835261024435848401526102643560ff8116810361045557858401526102843580151581036104555760608401526102a43590811681036104515760808301526102c43580151581036104515760a08301526102e43560ff811681036104515760c0830152610304359060ff8216820361044e575081610d679160e06103ae94015261158b565b9251928284938452830190610f24565b8190610d8284610ef5565b8152019101908790610cac565b8580fd5b60208091610da084610ef5565b815201910190610c77565b8480fd5b813561ffff81168103610d8f57815260209182019101610c49565b610100810190811067ffffffffffffffff821117610de757604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60a0810190811067ffffffffffffffff821117610de757604052565b60e0810190811067ffffffffffffffff821117610de757604052565b60c0810190811067ffffffffffffffff821117610de757604052565b6020810190811067ffffffffffffffff821117610de757604052565b6040810190811067ffffffffffffffff821117610de757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610de757604052565b6004359060ff82168203610ef157565b5f80fd5b359060ff82168203610ef157565b5f5b838110610f145750505f910152565b8181015183820152602001610f05565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610f6081518092818752878088019101610f03565b0116010190565b67ffffffffffffffff8111610de757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b929192610fad82610f67565b91610fbb6040519384610ea0565b829481845281830111610ef1578281602093845f960137010152565b9080601f83011215610ef157816020610ff293359101610fa1565b90565b519061ffff82168203610ef157565b519060ff82168203610ef157565b9080601f83011215610ef1576040519161102b83610e14565b829060a08101928311610ef157905b8282106110475750505090565b6020809161105484611004565b81520191019061103a565b51908115158203610ef157565b5f915b6005831061107c57505050565b60019060ff8351168152602080910192019201919061106f565b805180515f90845b600683106111485750505060e0610300926080836110c6602060ff96015160c089019061106c565b6110d9604082015161016089019061106c565b8460608201511661020088015261ffff918291015116610220870152602082015161024087015283604083015116610260870152606082015115156102808701526080820151166102a086015260a081015115156102c08601528260c0820151166102e0860152015116910152565b60019061ffff8351168152602080910192019201919061109e565b805115611384576040516060810181811067ffffffffffffffff821117610de757604052604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281516002928382018092116113575760038092049384811b947f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603611357579261123b61122586610f67565b956112336040519788610ea0565b808752610f67565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208701910136823793829183518401925b83811061130657505050505106806001146112b75760021461128e575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff603d91015390565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe81603d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81940153015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c168801015188850153168501015187820153019592919061126e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5060405161139181610e68565b5f815290565b80156114fa5780815f925b6114c45750806113b183610f67565b926113bf6040519485610ea0565b8084527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06113ec82610f67565b01366020860137905b6113fe57505090565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611357578192600a92838204938085029085820414851517156113575782039182116113575760ff80921660300191821161135757855111156114975760f81b7fff00000000000000000000000000000000000000000000000000000000000000165f1a908401601f015390816113f5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611357576001600a91019204806113a2565b5060405161150781610e84565b600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b90929160ff6103209161154c84610340810197611096565b16910152565b6040519061155f82610e84565b600182527f2c000000000000000000000000000000000000000000000000000000000000006020830152565b606081015115155f815f1461187a57506115dd602060ff60608551015116604051809381927fca0e21730000000000000000000000000000000000000000000000000000000083528760048401611534565b0381732e02a531215b73182ef8d8de8245495673cd078f5af490811561180f575f9161183a575b5061161161165091611a4d565b6040519061161e82610e84565b600a82527f436f6c6f722042616e64000000000000000000000000000000000000000000006020830152610b91611552565b905b5f901561181a575061169c602060ff60608551015116604051809381927fe46970e00000000000000000000000000000000000000000000000000000000083528760048401611534565b0381732e02a531215b73182ef8d8de8245495673cd078f5af490811561180f575f916117bf575b509061172360ff60406117196116db610ff2966118a0565b8251906116e782610e84565b600882527f4772616469656e740000000000000000000000000000000000000000000000006020830152610b91611552565b955b015116611397565b9061177460405161173381610e84565b600581526020937f5761727073000000000000000000000000000000000000000000000000000000858301526040519161176c83610e68565b5f8352611bf6565b604051948261178c8794518092878088019101610f03565b83016117a082518093878085019101610f03565b016117b382518093868085019101610f03565b01038084520182610ea0565b906020823d8211611807575b816117d860209383610ea0565b8101031261044e57509061172360ff60406117196116db6117fb610ff297611004565b959650505050506116c3565b3d91506117cb565b6040513d5f823e3d90fd5b9061172360ff6040610ff29481519061183282610e68565b81529561171b565b906020823d8211611872575b8161185360209383610ea0565b8101031261044e575061161161186b61165092611004565b9150611604565b3d9150611846565b6040519061188782610e68565b815290611652565b9060078110156114975760051b0190565b611a499060ff6040918251926118b584610e30565b7f4c696e656172205a0000000000000000000000000000000000000000000000008151916118e283610e84565b600483526020927f4e6f6e6500000000000000000000000000000000000000000000000000000000848201528652805161191b81610e84565b600681527f4c696e65617200000000000000000000000000000000000000000000000000008482015283870152805161195381610e84565b600d81527f446f75626c65204c696e656172000000000000000000000000000000000000008482015281870152805161198b81610e84565b600981527f5265666c6563746564000000000000000000000000000000000000000000000084820152606087015280516119c481610e84565b600d81527f446f75626c6520416e676c65640000000000000000000000000000000000000084820152608087015280516119fd81610e84565b600681527f416e676c656400000000000000000000000000000000000000000000000000008482015260a08701525191611a3683610e84565b6008835282015260c0840152169061188f565b5190565b611a499060ff604091825192611a6284610e30565b7f4f6e650000000000000000000000000000000000000000000000000000000000815191611a8f83610e84565b600683526020927f45696768747900000000000000000000000000000000000000000000000000008482015286528051611ac881610e84565b600581527f536978747900000000000000000000000000000000000000000000000000000084820152838701528051611b0081610e84565b600581527f466f72747900000000000000000000000000000000000000000000000000000084820152818701528051611b3881610e84565b600681527f5477656e747900000000000000000000000000000000000000000000000000008482015260608701528051611b7181610e84565b600381527f54656e00000000000000000000000000000000000000000000000000000000008482015260808701528051611baa81610e84565b600481527f46697665000000000000000000000000000000000000000000000000000000008482015260a08701525191611be383610e84565b6003835282015260c0840152169061188f565b603e90610ff292936040519485927f7b0000000000000000000000000000000000000000000000000000000000000060208501527f2274726169745f74797065223a202200000000000000000000000000000000006021850152611c64815180926020603088019101610f03565b83017f222c2276616c7565223a202200000000000000000000000000000000000000006030820152611ca0825180936020603c85019101610f03565b017f227d000000000000000000000000000000000000000000000000000000000000603c820152611cda8251809360208785019101610f03565b0103601e810184520182610ea056

Deployed Bytecode

0x6080604090808252600480361015610015575f80fd5b5f91823560e01c9081635f5d5f4214610bdb575080638727240614610b9b578063962f363b14610b0b578063b390ef191461065b578063b7773faa146106085763fa7ea71f14610063575f80fd5b827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451578035602435732e02a531215b73182ef8d8de8245495673cd078f928551937f492544da00000000000000000000000000000000000000000000000000000000855283828601528260248601526103208086604481855af49586156105fe5790879392918497610464575b50506101329161034491895195869485937f113a1f9700000000000000000000000000000000000000000000000000000000855284018a611096565b6103248301525af49384156104595780946103b2575b50506103ae91610349608f61017161016b61016561034e96611397565b97611163565b9361158b565b9287519384916020987f7b000000000000000000000000000000000000000000000000000000000000008a8501527f226e616d65223a2022576172707320000000000000000000000000000000000060218501526101d8815180928c603088019101610f03565b8301907f222c000000000000000000000000000000000000000000000000000000000000918260308201527f226465736372697074696f6e223a2022557020616e6420746f2074686520726960328201527f6768742e222c000000000000000000000000000000000000000000000000000060528201527f22696d616765223a20000000000000000000000000000000000000000000000060588201527f22646174613a696d6167652f7376672b786d6c3b6261736536342c000000000060618201526102ae825180938d607c85019101610f03565b0190607c8201527f2261747472696275746573223a205b0000000000000000000000000000000000607e8201526102ee825180938b608d85019101610f03565b017f5d00000000000000000000000000000000000000000000000000000000000000608d8201527f7d00000000000000000000000000000000000000000000000000000000000000608e82015203606f810184520182610ea0565b611163565b9261039f603d825180967f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008783015261038f81518092898686019101610f03565b810103601d810187520185610ea0565b51928284938452830190610f24565b0390f35b909193503d8082843e6103c58184610ea0565b8201916020818403126104515780519067ffffffffffffffff8211610455570182601f82011215610451578051916103fc83610f67565b9361040988519586610ea0565b8385526020848401011161044e5750608f61017161016b6101658661043f6103ae99976103499760208061034e9c019101610f03565b98965050505050819350610148565b80fd5b5080fd5b8280fd5b8551903d90823e3d90fd5b919650809293503d83116105f7575b61047d8183610ea0565b8101918183039081126105f35788519261049684610dca565b6102408092126105ef578951906104ac82610e14565b80601f850112156105eb578a516104c281610e4c565b8060c08601918383116105e7579186959493918e938e9a9998905b8382106105b85750509361030093610518846105096101329c9a966103449c9a966105a99a8752611012565b60208501526101608601611012565b908201526105296102008401611004565b606082015261053b6102208401610ff5565b6080820152855281015160208501526105576102608201611004565b8d850152610568610280820161105f565b606085015261057a6102a08201610ff5565b608085015261058c6102c0820161105f565b60a085015261059e6102e08201611004565b60c085015201611004565b60e082015296918193506100f6565b60209294969798999a9b50819395506105d18392610ff5565b8152019101918d999897969594928f94926104dd565b8c80fd5b8980fd5b8880fd5b8780fd5b503d610473565b88513d89823e3d90fd5b828460207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451576103ae90610648610643610ee1565b6118a0565b9051918291602083526020830190610f24565b5090827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261044e576024359067ffffffffffffffff821161044e573660238301121561044e576103ae7f3c2f626f64793e0000000000000000000000000000000000000000000000000085610af9610179876106eb6106e4368a84013560248c01610fa1565b9135611397565b9084519586927f3c21444f43545950452068746d6c3e000000000000000000000000000000000060208501527f3c68746d6c206c616e673d22656e223e00000000000000000000000000000000602f8501527f3c686561643e0000000000000000000000000000000000000000000000000000603f8501527f3c6d65746120636861727365743d225554462d38223e0000000000000000000060458501527f3c6d65746120687474702d65717569763d22582d55412d436f6d70617469626c605b8501527f652220636f6e74656e743d2249453d65646765223e0000000000000000000000607b8501527f3c6d657461206e616d653d2276696577706f72742220636f6e74656e743d227760908501527f696474683d6465766963652d77696474682c20696e697469616c2d7363616c6560b08501527f3d312e30223e000000000000000000000000000000000000000000000000000060d08501527f3c7469746c653e5761727020230000000000000000000000000000000000000060d685015261088181518092602060e388019101610f03565b83017f3c2f7469746c653e00000000000000000000000000000000000000000000000060e38201527f3c7374796c653e0000000000000000000000000000000000000000000000000060eb8201527f68746d6c2c00000000000000000000000000000000000000000000000000000060f28201527f626f6479207b000000000000000000000000000000000000000000000000000060f78201527f6d617267696e3a20303b0000000000000000000000000000000000000000000060fd8201527f6261636b67726f756e643a20234546454645463b0000000000000000000000006101078201527f6f766572666c6f773a2068696464656e3b00000000000000000000000000000061011b8201527f7d000000000000000000000000000000000000000000000000000000000000008061012c8301527f737667207b00000000000000000000000000000000000000000000000000000061012d8301527f6d61782d77696474683a2031303076773b0000000000000000000000000000006101328301527f6d61782d6865696768743a2031303076683b00000000000000000000000000006101438301526101558201527f3c2f7374796c653e0000000000000000000000000000000000000000000000006101568201527f3c2f686561643e0000000000000000000000000000000000000000000000000061015e8201527f3c626f64793e0000000000000000000000000000000000000000000000000000610165820152825190610abe8261016b9560208785019101610f03565b01918201527f3c2f68746d6c3e0000000000000000000000000000000000000000000000000061017282015203610159810185520183610ea0565b51918291602083526020830190610f24565b50919060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261044e5767ffffffffffffffff92803584811161045557610b589036908301610fd7565b93602435818111610b9757610b709036908401610fd7565b9260443591821161044e575091610b916103ae959261064894369101610fd7565b91611bf6565b8380fd5b828460207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610451576103ae90610648610bd6610ee1565b611a4d565b919290507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360161032081126104515761024090610c1884610dca565b1261044e578351610c2881610e14565b3660231215610451578451610c3c81610e4c565b8060c495368711610dab57905b868210610daf57505081523660e3121561045157845193610c6985610e14565b8461016491368311610dab57905b828210610d9357505060209485830152366101831215610455578551610c9c81610e14565b8061020492368411610d8f578790915b848310610d7757505050868301523560ff8116810361045557606082015261ffff90610224358281168103610b97576080820152835261024435848401526102643560ff8116810361045557858401526102843580151581036104555760608401526102a43590811681036104515760808301526102c43580151581036104515760a08301526102e43560ff811681036104515760c0830152610304359060ff8216820361044e575081610d679160e06103ae94015261158b565b9251928284938452830190610f24565b8190610d8284610ef5565b8152019101908790610cac565b8580fd5b60208091610da084610ef5565b815201910190610c77565b8480fd5b813561ffff81168103610d8f57815260209182019101610c49565b610100810190811067ffffffffffffffff821117610de757604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60a0810190811067ffffffffffffffff821117610de757604052565b60e0810190811067ffffffffffffffff821117610de757604052565b60c0810190811067ffffffffffffffff821117610de757604052565b6020810190811067ffffffffffffffff821117610de757604052565b6040810190811067ffffffffffffffff821117610de757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610de757604052565b6004359060ff82168203610ef157565b5f80fd5b359060ff82168203610ef157565b5f5b838110610f145750505f910152565b8181015183820152602001610f05565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610f6081518092818752878088019101610f03565b0116010190565b67ffffffffffffffff8111610de757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b929192610fad82610f67565b91610fbb6040519384610ea0565b829481845281830111610ef1578281602093845f960137010152565b9080601f83011215610ef157816020610ff293359101610fa1565b90565b519061ffff82168203610ef157565b519060ff82168203610ef157565b9080601f83011215610ef1576040519161102b83610e14565b829060a08101928311610ef157905b8282106110475750505090565b6020809161105484611004565b81520191019061103a565b51908115158203610ef157565b5f915b6005831061107c57505050565b60019060ff8351168152602080910192019201919061106f565b805180515f90845b600683106111485750505060e0610300926080836110c6602060ff96015160c089019061106c565b6110d9604082015161016089019061106c565b8460608201511661020088015261ffff918291015116610220870152602082015161024087015283604083015116610260870152606082015115156102808701526080820151166102a086015260a081015115156102c08601528260c0820151166102e0860152015116910152565b60019061ffff8351168152602080910192019201919061109e565b805115611384576040516060810181811067ffffffffffffffff821117610de757604052604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281516002928382018092116113575760038092049384811b947f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603611357579261123b61122586610f67565b956112336040519788610ea0565b808752610f67565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208701910136823793829183518401925b83811061130657505050505106806001146112b75760021461128e575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff603d91015390565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe81603d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81940153015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c168801015188850153168501015187820153019592919061126e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5060405161139181610e68565b5f815290565b80156114fa5780815f925b6114c45750806113b183610f67565b926113bf6040519485610ea0565b8084527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06113ec82610f67565b01366020860137905b6113fe57505090565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211611357578192600a92838204938085029085820414851517156113575782039182116113575760ff80921660300191821161135757855111156114975760f81b7fff00000000000000000000000000000000000000000000000000000000000000165f1a908401601f015390816113f5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114611357576001600a91019204806113a2565b5060405161150781610e84565b600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b90929160ff6103209161154c84610340810197611096565b16910152565b6040519061155f82610e84565b600182527f2c000000000000000000000000000000000000000000000000000000000000006020830152565b606081015115155f815f1461187a57506115dd602060ff60608551015116604051809381927fca0e21730000000000000000000000000000000000000000000000000000000083528760048401611534565b0381732e02a531215b73182ef8d8de8245495673cd078f5af490811561180f575f9161183a575b5061161161165091611a4d565b6040519061161e82610e84565b600a82527f436f6c6f722042616e64000000000000000000000000000000000000000000006020830152610b91611552565b905b5f901561181a575061169c602060ff60608551015116604051809381927fe46970e00000000000000000000000000000000000000000000000000000000083528760048401611534565b0381732e02a531215b73182ef8d8de8245495673cd078f5af490811561180f575f916117bf575b509061172360ff60406117196116db610ff2966118a0565b8251906116e782610e84565b600882527f4772616469656e740000000000000000000000000000000000000000000000006020830152610b91611552565b955b015116611397565b9061177460405161173381610e84565b600581526020937f5761727073000000000000000000000000000000000000000000000000000000858301526040519161176c83610e68565b5f8352611bf6565b604051948261178c8794518092878088019101610f03565b83016117a082518093878085019101610f03565b016117b382518093868085019101610f03565b01038084520182610ea0565b906020823d8211611807575b816117d860209383610ea0565b8101031261044e57509061172360ff60406117196116db6117fb610ff297611004565b959650505050506116c3565b3d91506117cb565b6040513d5f823e3d90fd5b9061172360ff6040610ff29481519061183282610e68565b81529561171b565b906020823d8211611872575b8161185360209383610ea0565b8101031261044e575061161161186b61165092611004565b9150611604565b3d9150611846565b6040519061188782610e68565b815290611652565b9060078110156114975760051b0190565b611a499060ff6040918251926118b584610e30565b7f4c696e656172205a0000000000000000000000000000000000000000000000008151916118e283610e84565b600483526020927f4e6f6e6500000000000000000000000000000000000000000000000000000000848201528652805161191b81610e84565b600681527f4c696e65617200000000000000000000000000000000000000000000000000008482015283870152805161195381610e84565b600d81527f446f75626c65204c696e656172000000000000000000000000000000000000008482015281870152805161198b81610e84565b600981527f5265666c6563746564000000000000000000000000000000000000000000000084820152606087015280516119c481610e84565b600d81527f446f75626c6520416e676c65640000000000000000000000000000000000000084820152608087015280516119fd81610e84565b600681527f416e676c656400000000000000000000000000000000000000000000000000008482015260a08701525191611a3683610e84565b6008835282015260c0840152169061188f565b5190565b611a499060ff604091825192611a6284610e30565b7f4f6e650000000000000000000000000000000000000000000000000000000000815191611a8f83610e84565b600683526020927f45696768747900000000000000000000000000000000000000000000000000008482015286528051611ac881610e84565b600581527f536978747900000000000000000000000000000000000000000000000000000084820152838701528051611b0081610e84565b600581527f466f72747900000000000000000000000000000000000000000000000000000084820152818701528051611b3881610e84565b600681527f5477656e747900000000000000000000000000000000000000000000000000008482015260608701528051611b7181610e84565b600381527f54656e00000000000000000000000000000000000000000000000000000000008482015260808701528051611baa81610e84565b600481527f46697665000000000000000000000000000000000000000000000000000000008482015260a08701525191611be383610e84565b6003835282015260c0840152169061188f565b603e90610ff292936040519485927f7b0000000000000000000000000000000000000000000000000000000000000060208501527f2274726169745f74797065223a202200000000000000000000000000000000006021850152611c64815180926020603088019101610f03565b83017f222c2276616c7565223a202200000000000000000000000000000000000000006030820152611ca0825180936020603c85019101610f03565b017f227d000000000000000000000000000000000000000000000000000000000000603c820152611cda8251809360208785019101610f03565b0103601e810184520182610ea056

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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