ETH Price: $3,368.25 (+0.39%)
Gas: 7 Gwei

Token

Authorship Token (AUTH)
 

Overview

Max Total Supply

31 AUTH

Holders

27

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
Sybil Delegate: transmissions11
Balance
1 AUTH
0x7ed52863829ab99354f3a0503a622e82acd5f7d3
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
AuthorshipToken

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 875 runs

Other Settings:
default evmVersion
File 1 of 18 : IEmblemWeaver.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

import "./IShields.sol";
import "./IFrameGenerator.sol";
import "./IFieldGenerator.sol";
import "./IHardwareGenerator.sol";
import "./IShieldBadgeSVGs.sol";

interface IEmblemWeaver {
    function fieldGenerator() external returns (IFieldGenerator);

    function hardwareGenerator() external returns (IHardwareGenerator);

    function frameGenerator() external returns (IFrameGenerator);

    function shieldBadgeSVGGenerator() external returns (IShieldBadgeSVGs);

    function generateShieldURI(IShields.Shield memory shield)
        external
        view
        returns (string memory);

    function generateShieldBadgeURI(IShields.ShieldBadge shieldBadge)
        external
        view
        returns (string memory);
}

File 2 of 18 : IFieldGenerator.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

interface IFieldGenerator {
    enum FieldCategories {
        MYTHIC,
        HERALDIC
    }
    struct FieldData {
        string title;
        FieldCategories fieldType;
        string svgString;
    }

    function generateField(uint16 field, uint24[4] memory colors)
        external
        view
        returns (FieldData memory);
}

File 3 of 18 : IFrameGenerator.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

interface IFrameGenerator {
    struct FrameData {
        string title;
        uint256 fee;
        string svgString;
    }

    function generateFrame(uint16 Frame)
        external
        view
        returns (FrameData memory);
}

File 4 of 18 : IHardwareGenerator.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

interface IHardwareGenerator {
    enum HardwareCategories {
        STANDARD,
        SPECIAL
    }
    struct HardwareData {
        string title;
        HardwareCategories hardwareType;
        string svgString;
    }

    function generateHardware(uint16 hardware)
        external
        view
        returns (HardwareData memory);
}

File 5 of 18 : IShieldBadgeSVGs.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

import "./IShields.sol";

interface IShieldBadgeSVGs {
    function generateShieldBadgeSVG(IShields.ShieldBadge shieldBadge)
        external
        view
        returns (string memory);
}

File 6 of 18 : IShields.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

import "./IEmblemWeaver.sol";

interface IShields {
    enum ShieldBadge {
        MAKER,
        STANDARD
    }

    struct Shield {
        bool built;
        uint16 field;
        uint16 hardware;
        uint16 frame;
        ShieldBadge shieldBadge;
        uint24[4] colors;
    }

    function emblemWeaver() external view returns (IEmblemWeaver);

    function shields(uint256 tokenId)
        external
        view
        returns (
            uint16 field,
            uint16 hardware,
            uint16 frame,
            uint24 color1,
            uint24 color2,
            uint24 color3,
            uint24 color4,
            ShieldBadge shieldBadge
        );
}

File 7 of 18 : IShieldsAPI.sol
// SPDX-License-Identifier: The Unlicense
pragma solidity ^0.8.9;

import "./IShields.sol";
import "./IFieldGenerator.sol";
import "./IHardwareGenerator.sol";
import "./IFrameGenerator.sol";

interface IShieldsAPI {
    function getShield(uint256 shieldId)
        external
        view
        returns (IShields.Shield memory);

    function getShieldSVG(uint256 shieldId)
        external
        view
        returns (string memory);

    function getShieldSVG(
        uint16 field,
        uint24[4] memory colors,
        uint16 hardware,
        uint16 frame
    ) external view returns (string memory);

    function isShieldBuilt(uint256 shieldId) external view returns (bool);

    function getField(uint16 field, uint24[4] memory colors)
        external
        view
        returns (IFieldGenerator.FieldData memory);

    function getFieldTitle(uint16 field, uint24[4] memory colors)
        external
        view
        returns (string memory);

    function getFieldSVG(uint16 field, uint24[4] memory colors)
        external
        view
        returns (string memory);

    function getHardware(uint16 hardware)
        external
        view
        returns (IHardwareGenerator.HardwareData memory);

    function getHardwareTitle(uint16 hardware)
        external
        view
        returns (string memory);

    function getHardwareSVG(uint16 hardware)
        external
        view
        returns (string memory);

    function getFrame(uint16 frame)
        external
        view
        returns (IFrameGenerator.FrameData memory);

    function getFrameTitle(uint16 frame) external view returns (string memory);

    function getFrameSVG(uint16 frame) external view returns (string memory);
}

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

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

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

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(uint256(-value));
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

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

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

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

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

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

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

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

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

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

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

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksumed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, and(add(o, 31), not(31))) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

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

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

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

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

                result := not(0) // Initialize to `NOT_FOUND`.

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

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

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

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

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

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

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 67108863)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                // Restore the result.
                result := mload(0x40)
                // Stores the string length.
                mstore(result, length)
                // Zeroize the slot after the string.
                let last := add(add(result, 0x20), length)
                mstore(last, 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, and(add(last, 31), not(31)))
            }
        }
    }

    /// @dev Returns a lowercased copy of the string.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store the bytes of the packed offsets and strides into the scratch space.
                // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                mstore(0x1f, 0x900094)
                mstore(0x08, 0xc0000000a6ab)
                // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 31)))
                result := add(result, shr(5, t))
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let end := add(s, mload(s))
                result := add(mload(0x40), 0x20)
                // Store "\\u0000" in scratch space.
                // Store "0123456789abcdef" in scratch space.
                // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                // into the scratch space.
                mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                // Bitmask for detecting `["\"","\\"]`.
                let e := or(shl(0x22, 1), shl(0x5c, 1))
            } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            let last := result
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Restore the result to the start of the free memory.
            result := mload(0x40)
            // Store the length of the result.
            mstore(result, sub(last, add(result, 0x20)))
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

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

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

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

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

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

File 9 of 18 : Owned.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

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

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

File 10 of 18 : ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(address from, address to, uint256 id) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender]
                || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(address from, address to, uint256 id) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0
                || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "")
                    == ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        virtual
    {
        transferFrom(from, to, id);

        require(
            to.code.length == 0
                || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data)
                    == ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
            || interfaceId == 0x80ac58cd // ERC165 Interface ID for ERC721
            || interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0
                || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "")
                    == ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0
                || ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data)
                    == ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(address, address, uint256, bytes calldata)
        external
        virtual
        returns (bytes4)
    {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

File 11 of 18 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Efficient library for creating string representations of integers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol)
library LibString {
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
            // to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
            // trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
            let newFreeMemoryPointer := add(mload(0x40), 160)

            // Update the free memory pointer to avoid overriding our string.
            mstore(0x40, newFreeMemoryPointer)

            // Assign str to the end of the zone of newly allocated memory.
            str := sub(newFreeMemoryPointer, 32)

            // Clean the last word of memory it may not be overwritten.
            mstore(str, 0)

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

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 { } {
                // Move the pointer 1 byte to the left.
                str := sub(str, 1)

                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))

                // Keep dividing temp until zero.
                temp := div(temp, 10)

                // prettier-ignore
                if iszero(temp) { break }
            }

            // Compute and cache the final total length of the string.
            let length := sub(end, str)

            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 32)

            // Store the string's length at the start of memory allocated for our string.
            mstore(str, length)
        }
    }
}

File 12 of 18 : AuthorshipToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IShieldsAPI } from "shields-api/interfaces/IShieldsAPI.sol";
import { Owned } from "solmate/auth/Owned.sol";
import { ERC721 } from "solmate/tokens/ERC721.sol";
import { LibString } from "solmate/utils/LibString.sol";

import { ICurta } from "@/contracts/interfaces/ICurta.sol";
import { Base64 } from "@/contracts/utils/Base64.sol";

/// @title The Authorship Token ERC-721 token contract
/// @author fiveoutofnine
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @notice ``Authorship Tokens'' are ERC-721 tokens that are required to add
/// puzzles to Curta. Each Authorship Token may be used like a ticket once.
/// After an Authorship Token has been used to add a puzzle, it can never be
/// used again to add another puzzle. As soon as a puzzle has been deployed and
/// added to Curta, anyone may attempt to solve it.
/// @dev Other than the initial distribution, the only way to obtain an
/// Authorship Token will be to be the first solver to any puzzle on Curta.
contract AuthorshipToken is ERC721, Owned {
    using LibString for uint256;

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

    /// @notice The shields API contract.
    /// @dev This is the mainnet address.
    IShieldsAPI constant shieldsAPI = IShieldsAPI(0x740CBbF0116a82F64e83E1AE68c92544870B0C0F);

    /// @notice Salt used to compute the seed in {AuthorshipToken.tokenURI}.
    bytes32 constant SALT = bytes32("Curta.AuthorshipToken");

    // -------------------------------------------------------------------------
    // Errors
    // -------------------------------------------------------------------------

    /// @notice Emitted when there are no tokens available to claim.
    error NoTokensAvailable();

    /// @notice Emitted when `msg.sender` is not authorized.
    error Unauthorized();

    // -------------------------------------------------------------------------
    // Immutable Storage
    // -------------------------------------------------------------------------

    /// @notice The Curta / Flags contract.
    address public immutable curta;

    /// @notice The number of seconds until an additional token is made
    /// available for minting by the author.
    uint256 public immutable issueLength;

    /// @notice The timestamp of when the contract was deployed.
    uint256 public immutable deployTimestamp;

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

    /// @notice The number of tokens that have been claimed by the owner.
    uint256 public numClaimedByOwner;

    /// @notice The total supply of tokens.
    uint256 public totalSupply;

    /// @notice Mapping to keep track of which addresses have claimed from
    // the mint list.
    mapping(address => bool) public hasClaimed;

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

    /// @param _curta The Curta / Flags contract.
    /// @param _issueLength The number of seconds until an additional token is
    /// made available for minting by the author.
    /// @param _authors The list of authors in the initial batch.
    constructor(address _curta, uint256 _issueLength, address[] memory _authors)
        ERC721("Authorship Token", "AUTH")
        Owned(msg.sender)
    {
        curta = _curta;
        issueLength = _issueLength;
        deployTimestamp = block.timestamp;

        // Mint tokens to the initial batch of authors.
        uint256 length = _authors.length;
        for (uint256 i; i < length;) {
            _mint(_authors[i], i + 1);
            unchecked {
                ++i;
            }
        }
        totalSupply = length;
    }

    // -------------------------------------------------------------------------
    // Functions
    // -------------------------------------------------------------------------

    /// @notice Mints a token to `_to`.
    /// @dev Only the Curta contract can call this function.
    /// @param _to The address to mint the token to.
    function curtaMint(address _to) external {
        // Revert if the sender is not the Curta contract.
        if (msg.sender != curta) revert Unauthorized();

        unchecked {
            uint256 tokenId = ++totalSupply;

            _mint(_to, tokenId);
        }
    }

    /// @notice Mints a token to `_to`.
    /// @dev Only the owner can call this function. The owner may claim a token
    /// every `issueLength` seconds.
    /// @param _to The address to mint the token to.
    function ownerMint(address _to) external onlyOwner {
        unchecked {
            uint256 numIssued = (block.timestamp - deployTimestamp) / issueLength;
            uint256 numMintable = numIssued - numClaimedByOwner++;

            // Revert if no tokens are available to mint.
            if (numMintable == 0) revert NoTokensAvailable();

            // Mint token
            uint256 tokenId = ++totalSupply;

            _mint(_to, tokenId);
        }
    }

    // -------------------------------------------------------------------------
    // ERC721Metadata
    // -------------------------------------------------------------------------

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @param _tokenId The token ID.
    /// @return URI for the token.
    function tokenURI(uint256 _tokenId) public view override returns (string memory) {
        require(_ownerOf[_tokenId] != address(0), "NOT_MINTED");

        // Generate seed.
        uint256 seed = uint256(keccak256(abi.encodePacked(_tokenId, SALT)));

        // Bitpacked colors.
        uint256 colors = 0x6351CEFF00FFB300FF6B00B5000A007FFF78503C323232FE7FFF6C28A2FF007A;

        // Shuffle `colors` by performing 4 iterations of Fisher-Yates shuffle.
        // We do this to pick 4 unique colors from `colors`.
        unchecked {
            uint256 shift = 24 * (seed % 11);
            colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ 0xFFFFFF))
                | ((colors & 0xFFFFFF) << shift) | ((colors >> shift) & 0xFFFFFF);
            seed >>= 4;

            shift = 24 * (seed % 10);
            colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 24)))
                | (((colors >> 24) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 24);
            seed >>= 4;

            shift = 24 * (seed % 9);
            colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 48)))
                | (((colors >> 48) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 48);
            seed >>= 4;

            shift = 24 * (seed & 7);
            colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 72)))
                | (((colors >> 72) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 72);
            seed >>= 3;
        }

        return string.concat(
            "data:application/json;base64,",
            Base64.encode(
                abi.encodePacked(
                    '{"name":"Authorship Token #',
                    _tokenId.toString(),
                    '","description":"This token allows 1 puzzle to be added to Curta. Once it has '
                    'been used, it can never be used again.","image_data":"data:image/svg+xml;base6'
                    "4,",
                    Base64.encode(
                        abi.encodePacked(
                            '<svg width="750" height="750" xmlns="http://www.w3.org/2000/svg" fill='
                            '"none" viewBox="0 0 750 750"><style>.a{filter:url(#c)drop-shadow(0 0 2'
                            "px #007fff);fill:#fff;width:4px}.b{filter:drop-shadow(0 0 .5px #007fff"
                            ");fill:#000;width:3px}.c{height:13px}.d{height:6px}.e{height:4px}.f{he"
                            "ight:12px}.g{height:5px}.h{height:3px}.i{width:320px;height:620px}.j{c"
                            "x:375px;r:20px}.k{stroke:#27303d}.l{fill:#000}.n{fill:#0d1017}.o{strok"
                            'e-width:2px}@font-face{font-family:"A";src:url(data:font/woff2;charset'
                            "=utf-8;base64,d09GMgABAAAAABFIAA8AAAAAIkwAABDvAAEAAAAAAAAAAAAAAAAAAAAA"
                            "AAAAAAAAHIFYBmAAPAiBCgmXYhEICp9Am3cLQgABNgIkA0IEIAWDTAcgDIExGzcfE24MPW"
                            "wcDIypV8n+6wPbWHpYvwUfDDXpJJFBslTuUGiwURSfehX/e+bvN6mwulNEycH87rF0PSHI"
                            "FQ6frhshySw8/9Scf9/MJISZENRCTVFJ1cErnlKV3fCVijl22lBTW3X3ffk7sF1tDOFpz3"
                            "ulSlfAabKmnF0P4oO2fq29SR7sfaASK3SVZcuSrsSqs2ba0noIACigkR+0RR3auT4I9sP0"
                            "SVMG/h9t36wowdPc8MB/BrGVwfJtxfAEzawVNUU9XXUyuP84V72fJk1TzgBQZhYxC9gekU"
                            "Nhz5h1atzZDSj//9es7H0/lQGuGT4e6XiUalRBoP6vADYkPQw1aeL0ALMknWQQFKMiVrss"
                            "zLp1cq3f5XA2MbxTH7ZlmG9qAh5RGLTi3/buX4sAOtmYKD17DyzGNX3c/JkkoAFYFiqgoL"
                            "lcoKwN+8SZs2bQJpy/039f0IT07mYumDGX3OkeQKhtalzAJiHFqmDHRepg85j2HtMhYXoI"
                            "Qja+acMcHkFiWRYc64dhBHE74RiyoF9YUybKGmygLFPKgQE3mWU0qdIeFGz+mufSyI0eTo"
                            "/ebjdXaEmONvbHdNDGSrUbWQ8gfyoXADcUpJDKwxZTQlmjHdljgkI92rIAkHysWd+tiwiI"
                            "D5Xor0lTmjPIn2Bl2xlLdc/6xALygxzlIHIGSp5aRIVlzTcyxsJaE5QLskMtpMy7JpvuPj"
                            "Uo2MWFiwACT2mape1/WBm2jfvwKbF3yOytnKr/kmDe/ffSHOMjO3TegzdAmwwQWGKAQK+c"
                            "Bhh0LF7h+dMwkwVOj6a4TfI4nt98Vtdg3vXfxfuD5LHZiSN72tFbVsUc3R0zeztLSohtiS"
                            "0svM6iU/Uv3Qmfl4/otQv/jh3g4A0oOWcHRc4GbtNJzgEHmgru3bEQPEgIi7gWnfcZKgEO"
                            "8+Z1XMUoKtO09Tjp2lUOvJjROEPveThU3/tfL0bd6jo8v07lwHVvcrLss7BvExTOLIdsVZ"
                            "QXQPLOgBZIcA2J124CFjvY6BQaQwUxDEhIzTQBj/7xNBKgmC25O1wmzuk8DHIxcRpve9ih"
                            "ai6Dx+eQS2Guk8G9aNoPq46hCuEQzpn0DrEA5cNx6ybsFycgDTIqICT1EGrGPKQhWGg3yT"
                            "vz+bYPRKis1fC3mwRiQEUi5Jar7lMsZqIS3VOUGEgg0ul47PrH07gfVmIW9T9FbNECKbbf"
                            "pconk3yVaGo/Ahrbr9P224ag88ZW7LKAitUe741kKQXVSBLZmCnMYw0TiopBOyYcr0WduE"
                            "S4x/5FIgsNvUH1weP6wNRJz2bNhJwTgeqoZZ7qMnvnrUhnbAIRXAwkkj7vIhYqrV7vEolF"
                            "TQks4hu8RBWo/k7XFtMKN7H/WQszwb877koOlFCxeSdgNLsxYo4J88ywwByLLLDEEsusYI"
                            "U1WGWdETtghEGeEbdsv0umBvraHesHFh31K9LvwKb+RPo7pjtYICDGRl1S6pFSn5QGpDQk"
                            "pREpjUlpQkpTUpqRjl1C7RrFylpIi8Z2N8fzOIYKy236t9kAq2A1DGxCmmsBx1i3Ys+Gde"
                            "8VbuTYckbHpx02h1XI9jTdy8b5t0FJ3jV2B3qsK99NWYU0AuJQCE7aNgY7v4D+PpPntlJ3"
                            "ZPt2YA9qNdc1B5B+QYF9NBfawMGwAORUY4sforK0c02NigE7EJM2+5e5DbfaKR06nyGLw4"
                            "HI9tnbgSOAHbjDvTs1indO+g2T2v7IN9BxRA39GLhFjJj4+TxR3L5OP9np8qlbpuTlgxyf"
                            "O6d6VD/EzLFZoYVyD4ry/OV4qUjBWI4Nh90xqbdg54Qoz1214l9VZxI826xbcYbHGcxXXc"
                            "IqizxhNjenKLAfXHiH4BiD59GYroC6eA6eHavJMXIrTsbfkyREzrF25c922geeRUkzTZC2"
                            "UOKsTVfeCebp8JgateYdrGE9IaY76Ppjgc0XcccsJd28zkURtobF+UkH2pkp9UZ75dR3ze"
                            "OUC5gawFLO3iS1uakbsnXnr/7lIAcLsZjJ+w99NeIoGM56PeI4+t5HWymr6o9cC5wNG3H+"
                            "m82F7F5/o0ytyLvC6ti+HVWv43Ed/ifmG09TXXtu2b+1FeeXOuqzfuJuIlxjZ/dRb/5KbM"
                            "VcevBs78zQgCNonHE12BsOzeOkp1GF3ILsi9HPIw5XZKfCztTUaNYPK0ShfLQWg2Sn5c1o"
                            "yAroqWgwEwxpJNBQJhgmQy8azgQjGglUzAQlMnSjUiYoayRQORNUyNCDKpmgqpFAI5lglA"
                            "y9aTQTjGkk0FgmGCdDdxrPBBMaCTSRycRJnXy5UywmPwr6CrXNS0WYEiLCJg+q/XmYmCrl"
                            "YZqUZ053yIMZ/vwCM6V8zJLyMVvKx5z6BZgrFWCeVID5UkGhBZ2O+2vKmy6je13YMcmnth"
                            "q6+PUkQ2cEsSwCQY3cNPAwuRWoToMjDRmK4jL2YEBFAq9ic5UWqbQsykJncsHOgA6dDFtq"
                            "+4iQ5CAXnilOc7mczhZuw5YU5wxKOBW47qqa18byl2C+0Je9kpLlr0VqXl27py+7c+UFmC"
                            "9DuGxcFLhwtksTbHq5LlWGZuCUWZNSNub56z6vhzc8dklVXP+K19zMp2Gw+AWepkrKxCkB"
                            "FNMmnkhS+mIiBmbzxbsYu3/5uNB70qcZioGOLk4pwVD1KjUAJ8b2o+fvE7IX0jP+UvmvC/"
                            "9zTtTVhXlrXunL7lzwgXH0NT5eVVdNlAt0UumDdTUSxgQwsa7JiHLbdx+N9YpznKAMNccv"
                            "n28rh84dOcYAj1WJhyfD9DWdacXBsxebjfKUwmfoQg8iRlXZ6fEy4xAa0pv0GejZgi9HNl"
                            "+VPZ+h5aQJoqNPjcVobNAYnVcai9d8oi97lmY8z5j8lOLljWQ0fIrVgVlfWtNoRtVj1tWJ"
                            "DV7Ri/UyugbiLSQ1l7NObx9m6qcSJ+t3Yl5J34KxaVImS1+88EXY68CjXjQW5jQmulLBDC"
                            "g8RvQxjvrMq5i5pxp2kde0xy/bEDZNyghjc2O/U2E8VH9dXPONMSjesGUhiseWie5ddqqh"
                            "QfPLSl3F46EWs64VBy/C8LoTXXhrtPOXPXi8rEaJvqHu5b0Giy5whUeCM2teY23Nxvnzp8"
                            "m1O1bUrN2vYO2K/54OGzrbuyY5dHDt43aqy7UiJ3mN9zPjCpMFbBf7/IwolBQHNYAMMEi2"
                            "eOax5Af+SYKk5Ftty4uNXpQhSvBSk+NwwKH6VqiSnNnJWieKCDxBEZkKR5BPuicuUQmnFt"
                            "ZVM3IDfJHhfP2bzKbHE6ExyVcGyDdWqYxjDWwRi8A6SdIPtzB7tiQsWOxEcCciH3XnqlQ0"
                            "dFMTH4SkRDvCZLv3v6pKrhriHBLZFMOC/hyD9ElxDduSi35Bk7+sFiGpgpnuNBRGBd6gSL"
                            "mfVrbkv9wY10nPyEpPUJ4ZQA2VAEFUTLSAppnkp+575H1YSnKMePiKalkZ1NtrNCNp/NVu"
                            "Jw4FSl2hQV5RYKBXFOqQOrH/T2W+sGC3CEVdvBviVPTZZumyrT4JJOk5c5lYPL2+VUgVec"
                            "/vOTZm95Ve8r1Dspn8dClc+6DoO3+tc4xQSTC5uJeEkSpI6rG+HgaV9H5K5kFKQfj6VDYK"
                            "jGns9YnhSohHfjraDQ17bfVIIZUw8pKLa+qQEOmMsoBgd0QUiHJHBAOLMyqyc0ZIUhgKsM"
                            "Tg0NDEYAygSWEx804b0aBKnqbSZbQa03i73BmsmMQdGuwVm0xT8dAgiQv7Y5F6X6M9IgxD"
                            "m6j98u1+cbdtM6ZKxNMOn803a3lsaXGvql6xvWpia6pQPWqUcmJ0Vf2N9J35T2/D0Aq9X9"
                            "/LiNn0JZMYcyJK6HMiGXvW697jX0wUEYjticR2LoIQlZ6+1I2WZRL0qvgQc+804puiH+Rh"
                            "XP3n178mUvxMxuoYGIfVlRgzue0ARYr9NLcGAD2Vl5jGAROR3cVwvUD6Q6Ep0iFtCjJOZI"
                            "yKZAwPYx6mbEjYPWGXElHi/Vs3qPtvkD4H6C+zF15Ahg0FHXTyC1qDA+f1H7kBBjiGM+fF"
                            "M4pJhNeMcH8p7KHTfHQ5SBjIUlxTGYZ01BVqq3QFGm2KOieINiqSMSr86jn1ur37gDd/Si"
                            "+6sw1nXmDQnebqnfZNiSkbw3IOuCISSLdrnxIkfdu379sy4ZNmTychwnWgX9ewkKsoE/pz"
                            "6N6QkL2hztbp5fhDpt8flVD9cML/Ozd7gN1lD7QH2YPtIfZQexgLv0pQLe5ZGLcxhqeWlf"
                            "uo4AGTtzAGmPqke7wB+CPw7rP5phD/4Jp/h+u0/7/3F55NOEIenxjDI5A4wKXER8gNypPh"
                            "7mw0qFXibtIdD6ScM3YFwUCrAKVFNJ+5ITNvUV/gOcCJ/kQXtJZy0VgDo1NeIe0RY+uZoD"
                            "taIIocaM+8dXFDu3LB2AuHFF3gBZqRHhUpTFe+A6SFnM+wwSW04ZCm7MiidXk+1EVPLlEL"
                            "Y/hGQkhQYo5RJS0u0QDUeCwoUVz3d51KuWyyNeO73UI02URWpMMm1LxDOuEvYoylo8WlV6"
                            "lv8Lzp2XAOb1Bk5ZOsenamH1YDUthuPyzPR7q/Nl+uGHvnMSCEKixZKLwsClU+AW6h9Uwx"
                            "c5zXtGgPZKTNLxYXmDFrPx+UBz64p2oHMlG8QFT8ZHKB8lpE6sh5Y29HLcPAzXnh0WFy+8"
                            "QGHyHXM0kJj3YCbvJudjuZcNbbvrl6c5y5dbl9Ua9gUzaUnDH2DmZJluoxBAUqD0XRp4LT"
                            "1zOAwXVS9SFma9Z3Ow4MZH0JNLw7mOOIMfkcd+UerSqKWJpkvnQbocRNTTvtzMLfyfKSbV"
                            "zo+oftgP9JojCaNGPqtd8m9mu0oWybrQ62kidABygizeao16mU4mbSZFD5szXxu+2seN+f"
                            "N/C+ONDR3z0QSXu1xac4z0BPi0WqXTHQiS23iyR6LYtzKOO/CNG8c2Tr7t/tAoYM/Z0syB"
                            "80Rt//4bDt0xNOjWYyQczsMZTVX4lnibna/u9fw48fNgFIpNOyOCrpgaeOILkARCV5DiXe"
                            "8EXPWQ4OdEcIGO720kah2SLjqQvJDI/8QxCp+ISkGFe3R3nHBbjptZqOhlrLslxLBpmZyk"
                            "E4K2xIxAgAq+1G6J+KTlnejgNaJrWm7SmaNFuPJgOlFoxtKbSLWA4OaExq19dlBwsGPkHR"
                            "rXTs0O0Q9rou9s3+eNiZd+dpjFVpKzWjZGOGhaCjqkKgBz9VUzEKPhhjXewSzahn/t/pS9"
                            "uHUpZUqS1MbX6NCCB0X3Go/OuYwPz/mlj1O8Dz6R9eAbxMJbdG+3Ffma11B/xRABD44auE"
                            "MhHN+jezsRbExZke8snDQxEpdF/kvhRZe3OPpzwY5Hs8Eh/s5PWHI4CbKdiZgw2FLhSyCG"
                            "gwvqEigB+Vd+U1f2A0EHYUwhjdUcHF3I4q2ZgdNVpxpqON+XxIb6eFDKUHs5jNkqLJ1XiZ"
                            "EkrvJpVkUsjETR/FZ7nk6Uzquh8zmUAX3u1D0/3DKcx5JT5pmzyJuSxUfGJS4ghmM44JlL"
                            "mDmEViFoaazhcgH5fEU7iZLKtkHiUMoIzBh5vGHSyks52c68LcoVmqR3we1X0LuuWsP5QR"
                            "NUrjU+r4fCbg+ReG8S5kh4kzGMc0JvXzyQ7rdKoZTypQxuRM0khPXNJMrcMqaFitk6QCwo"
                            "kHnOHO8PJmkVUVPvmKMvPsZvy6nwRaaHR4OKlH7ymZ9va2cIfmqPTXi0I1WUm0n83ofjHY"
                            "E3BFN20mGv4SgQMAJiae1Co9m1tJ7bByn6e2vMVE1maWcw4T0arYhOLybl7RX6FH70WOrZ"
                            'MW6dCcHc6I9atPW9msuGc/bptop2dPAAA=)}</style><defs><radialGradient id="'
                            'b"><stop stop-color="#007FFF"/><stop offset="100%" stop-opacity="0"/><'
                            '/radialGradient><filter id="c"><feGaussianBlur stdDeviation="8" in="So'
                            'urceGraphic" result="offset-blur"/><feComposite operator="out" in="Sou'
                            'rceGraphic" in2="offset-blur" result="inverse"/><feFlood flood-color="'
                            '#007FFF" flood-opacity=".95" result="color"/><feComposite operator="in'
                            '" in="color" in2="inverse" result="shadow"/><feComposite in="shadow" i'
                            'n2="SourceGraphic"/><feComposite operator="atop" in="shadow" in2="Sour'
                            'ceGraphic"/></filter><mask id="a"><path fill="#000" d="M0 0h750v750H0z'
                            '"/><rect class="i" x="215" y="65" rx="20" fill="#FFF"/><circle class="'
                            'j l" cy="65"/><circle class="j l" cy="685"/></mask></defs><path fill="'
                            '#10131C" d="M0 0h750v750H0z"/><rect class="i n" x="215" y="65" mask="u'
                            'rl(#a)" rx="20"/><circle mask="url(#a)" fill="url(#b)" cx="375" cy="38'
                            '1" r="180"/><circle class="j k n" cy="125"/><g transform="translate(35'
                            '9 110)"><circle class="n" cy="16" cx="16" r="16"/><rect class="a c" x='
                            '"8" y="7" rx="2"/><rect class="b f" x="8.5" y="7.5" rx="1.5"/><rect cl'
                            'ass="a e" x="8" y="21" rx="2"/><rect class="b h" x="8.5" y="21.5" rx="'
                            '1.5"/><rect class="a d" x="14" y="7" rx="2"/><rect class="b g" x="14.5'
                            '" y="7.5" rx="1.5"/><rect class="a e" x="14" y="14" rx="2"/><rect clas'
                            's="b h" x="14.5" y="14.5" rx="1.5"/><rect class="a d" x="14" y="19" rx'
                            '="2"/><rect class="b g" x="14.5" y="19.5" rx="1.5"/><rect class="a c" '
                            'x="20" y="12" rx="2"/><rect class="b f" x="20.5" y="12.5" rx="1.5"/><r'
                            'ect class="a e" x="20" y="7" rx="2"/><rect class="b h" x="20.5" y="7.5'
                            '" rx="1.5"/></g><path d="M338.814 168.856c-.373 0-.718-.063-1.037-.191'
                            "a2.829 2.829 0 0 1-.878-.606 2.828 2.828 0 0 1-.606-.878 2.767 2.767 0"
                            " 0 1-.193-1.037v-.336c0-.372.064-.723.192-1.053.138-.319.34-.611.606-."
                            "877a2.59 2.59 0 0 1 .878-.59 2.58 2.58 0 0 1 1.038-.208h4.26c.245 0 .4"
                            "8.032.703.096.212.053.425.143.638.27.223.118.415.256.574.416.16.16.304"
                            ".345.431.558.043.064.07.133.08.208a.301.301 0 0 1-.016.095.346.346 0 0"
                            " 1-.175.256.42.42 0 0 1-.32.032.333.333 0 0 1-.239-.192 3.016 3.016 0 "
                            "0 0-.303-.399 2.614 2.614 0 0 0-.415-.303 1.935 1.935 0 0 0-.463-.191 "
                            "1.536 1.536 0 0 0-.495-.048c-.712 0-1.42-.006-2.122-.016-.713 0-1.425."
                            "005-2.138.016-.266 0-.51.042-.734.127-.234.096-.442.24-.623.431a1.988 "
                            "1.988 0 0 0-.43.623 1.961 1.961 0 0 0-.144.75v.335a1.844 1.844 0 0 0 ."
                            "574 1.356 1.844 1.844 0 0 0 1.356.574h4.261c.17 0 .33-.015.48-.047a2.0"
                            "2 2.02 0 0 0 .446-.192c.149-.074.282-.165.399-.271.106-.107.207-.229.3"
                            "03-.367a.438.438 0 0 1 .255-.144c.096-.01.187.01.272.064a.35.35 0 0 1 "
                            ".16.24.306.306 0 0 1-.033.27 2.653 2.653 0 0 1-.43.527c-.16.139-.346.2"
                            "66-.559.383-.213.117-.42.197-.622.24-.213.053-.436.08-.67.08h-4.262Zm1"
                            "7.553 0c-.713 0-1.324-.266-1.835-.797a2.69 2.69 0 0 1-.766-1.931v-2.66"
                            "5c0-.117.037-.213.112-.287a.37.37 0 0 1 .27-.112c.118 0 .214.037.288.1"
                            "12a.39.39 0 0 1 .112.287v2.664c0 .533.18.99.542 1.373a1.71 1.71 0 0 0 "
                            "1.293.559h3.878c.51 0 .941-.187 1.292-.559a1.93 1.93 0 0 0 .543-1.372v"
                            "-2.665a.39.39 0 0 1 .111-.287.389.389 0 0 1 .288-.112.37.37 0 0 1 .271"
                            ".112.39.39 0 0 1 .112.287v2.664c0 .756-.256 1.4-.766 1.932-.51.531-1.1"
                            "28.797-1.851.797h-3.894Zm23.824-.718a.456.456 0 0 1 .16.192c.01.042.01"
                            "6.09.016.143a.47.47 0 0 1-.016.112.355.355 0 0 1-.143.208.423.423 0 0 "
                            "1-.24.063h-.048a.141.141 0 0 1-.064-.016c-.02 0-.037-.005-.047-.016a10"
                            "4.86 104.86 0 0 1-1.18-.83c-.374-.265-.746-.531-1.118-.797-.011 0-.016"
                            "-.006-.016-.016-.01 0-.016-.005-.016-.016-.01 0-.016-.005-.016-.016h-5"
                            ".553v1.324a.39.39 0 0 1-.112.288.425.425 0 0 1-.287.111.37.37 0 0 1-.2"
                            "72-.111.389.389 0 0 1-.111-.288v-4.946c0-.054.005-.107.016-.16a.502.50"
                            "2 0 0 1 .095-.128.374.374 0 0 1 .128-.08.316.316 0 0 1 .144-.031h6.893"
                            "c.256 0 .49.048.702.143.224.085.42.218.59.4.182.18.32.377.416.59.085.2"
                            "23.127.457.127.702v.335c0 .223-.032.43-.095.622a2.107 2.107 0 0 1-.32."
                            "527c-.138.18-.292.319-.462.415-.17.106-.362.186-.575.24l.702.51c.234.1"
                            "7.469.345.703.526Zm-8.281-4.228v2.425h6.494a.954.954 0 0 0 .4-.08.776."
                            "776 0 0 0 .334-.223c.107-.106.186-.218.24-.335.053-.128.08-.266.08-.41"
                            "5v-.32a.954.954 0 0 0-.08-.398 1.232 1.232 0 0 0-.224-.351 1.228 1.228"
                            " 0 0 0-.35-.224.954.954 0 0 0-.4-.08h-6.494Zm24.67-.782c.106 0 .202.03"
                            "7.287.111a.37.37 0 0 1 .112.272.39.39 0 0 1-.112.287.425.425 0 0 1-.28"
                            "7.112h-3.64v4.579a.37.37 0 0 1-.111.272.348.348 0 0 1-.271.127.397.397"
                            " 0 0 1-.288-.127.37.37 0 0 1-.111-.272v-4.579h-3.639a.37.37 0 0 1-.271"
                            "-.111.39.39 0 0 1-.112-.287.37.37 0 0 1 .112-.272.37.37 0 0 1 .271-.11"
                            "1h8.058Zm15.782-.048c.723 0 1.34.266 1.85.798.511.532.767 1.17.767 1.9"
                            "15v2.68a.37.37 0 0 1-.112.272.397.397 0 0 1-.287.127.348.348 0 0 1-.27"
                            "2-.127.348.348 0 0 1-.127-.272v-1.196h-7.532v1.196a.348.348 0 0 1-.128"
                            ".272.348.348 0 0 1-.271.127.348.348 0 0 1-.271-.127.348.348 0 0 1-.128"
                            "-.272v-2.68c0-.745.255-1.383.766-1.915.51-.532 1.128-.798 1.851-.798h3"
                            ".894Zm-5.697 3.415h7.548v-.702c0-.532-.176-.984-.527-1.357-.362-.383-."
                            "792-.574-1.292-.574H408.5c-.51 0-.942.191-1.293.574a1.875 1.875 0 0 0-"
                            ".542 1.357v.702ZM297.898 204.5h4.16l1.792-5.152h9.408l1.824 5.152h4.44"
                            "8l-8.704-23.2h-4.288l-8.64 23.2Zm10.624-18.496 3.52 9.952h-7.008l3.488"
                            "-9.952Zm22.81 18.496h3.807v-17.216h-3.808v9.184c0 3.104-1.024 5.344-3."
                            "872 5.344s-3.168-2.272-3.168-4.608v-9.92h-3.808v10.848c0 4.096 1.664 6"
                            ".784 5.76 6.784 2.336 0 4.096-.992 5.088-2.784v2.368Zm7.678-17.216h-2."
                            "56v2.752h2.56v9.952c0 3.52.736 4.512 4.416 4.512h2.816v-2.912h-1.376c-"
                            "1.632 0-2.048-.416-2.048-2.176v-9.376h3.456v-2.752h-3.456v-4.544h-3.80"
                            "8v4.544Zm13.179-5.984h-3.809v23.2h3.808v-9.152c0-3.104 1.088-5.344 4-5"
                            ".344s3.264 2.272 3.264 4.608v9.888h3.808v-10.816c0-4.096-1.696-6.784-5"
                            ".856-6.784-2.4 0-4.224.992-5.216 2.784V181.3Zm16.86 14.624c0-3.968 2.1"
                            "44-5.92 4.544-5.92 2.4 0 4.544 1.952 4.544 5.92s-2.144 5.888-4.544 5.8"
                            "88c-2.4 0-4.544-1.92-4.544-5.888Zm4.544-9.024c-4.192 0-8.48 2.816-8.48"
                            " 9.024 0 6.208 4.288 8.992 8.48 8.992s8.48-2.784 8.48-8.992c0-6.208-4."
                            "288-9.024-8.48-9.024Zm20.057.416a10.32 10.32 0 0 0-.992-.064c-2.08.032"
                            "-3.744 1.184-4.672 3.104v-3.072h-3.744V204.5h3.808v-9.024c0-3.456 1.37"
                            "6-4.416 3.776-4.416.576 0 1.184.032 1.824.096v-3.84Zm14.665 4.672c-.70"
                            "4-3.456-3.776-5.088-7.136-5.088-3.744 0-7.008 1.952-7.008 4.992 0 3.13"
                            "6 2.272 4.448 5.184 5.024l2.592.512c1.696.32 2.976.96 2.976 2.368s-1.4"
                            "72 2.24-3.456 2.24c-2.24 0-3.52-1.024-3.872-2.784h-3.712c.416 3.264 3."
                            "232 5.664 7.456 5.664 3.904 0 7.296-1.984 7.296-5.568 0-3.36-2.656-4.4"
                            "48-6.144-5.12l-2.432-.48c-1.472-.288-2.304-.896-2.304-2.048 0-1.152 1."
                            "536-1.888 3.2-1.888 1.92 0 3.36.608 3.776 2.176h3.584Zm6.284-10.688h-3"
                            ".808v23.2h3.808v-9.152c0-3.104 1.088-5.344 4-5.344s3.264 2.272 3.264 4"
                            ".608v9.888h3.808v-10.816c0-4.096-1.696-6.784-5.856-6.784-2.4 0-4.224.9"
                            "92-5.216 2.784V181.3Zm14.076 0v3.84h3.808v-3.84h-3.808Zm0 5.984V204.5h"
                            "3.808v-17.216h-3.808Zm10.781 8.608c0-3.968 1.952-5.888 4.448-5.888 2.6"
                            "56 0 4.256 2.272 4.256 5.888 0 3.648-1.6 5.92-4.256 5.92-2.496 0-4.448"
                            "-1.952-4.448-5.92Zm-3.648-8.608V210.1h3.808v-7.872c1.024 1.696 2.816 2"
                            ".688 5.12 2.688 4.192 0 7.392-3.488 7.392-9.024 0-5.504-3.2-8.992-7.39"
                            '2-8.992-2.304 0-4.096.992-5.12 2.688v-2.304h-3.808Z" fill="#F0F6FC"/><'
                            'path class="k" stroke-dashoffset="5" stroke-dasharray="10" d="M215 545'
                            'h320"/><g transform="translate(231 237) scale(0.384)">',
                            shieldsAPI.getShieldSVG({
                                field: uint16(seed % 300),
                                colors: [
                                    uint24(colors & 0xFFFFFF),
                                    uint24((colors >> 24) & 0xFFFFFF),
                                    uint24((colors >> 48) & 0xFFFFFF),
                                    uint24((colors >> 72) & 0xFFFFFF)
                                ],
                                hardware: uint16((seed >> 9) % 120),
                                frame: uint16((seed >> 17) % 5)
                            }),
                            '</g><text font-family="A" x="50%" y="605" fill="#F0F6FC" font-size="40'
                            '" dominant-baseline="central" text-anchor="middle">#',
                            _zfill(_tokenId),
                            '</text><rect class="i k o" x="215" y="65" mask="url(#a)" rx="20"/><cir'
                            'cle class="j k o" cy="65" mask="url(#a)"/><circle class="j k o" cy="68'
                            '5" mask="url(#a)"/></svg>'
                        )
                    ),
                    '","attributes":[{"trait_type":"Used","value":',
                    ICurta(curta).hasUsedAuthorshipToken(_tokenId) ? "true" : "false",
                    "}]}"
                )
            )
        );
    }

    // -------------------------------------------------------------------------
    // Helper Functions
    // -------------------------------------------------------------------------

    /// @notice Converts `_value` to a string with leading zeros to reach a
    /// minimum of 7 characters.
    /// @param _value Number to convert.
    /// @return string memory The string representation of `_value` with leading
    /// zeros.
    function _zfill(uint256 _value) internal pure returns (string memory) {
        string memory result = _value.toString();

        if (_value < 10) return string.concat("000000", result);
        else if (_value < 100) return string.concat("00000", result);
        else if (_value < 1000) return string.concat("0000", result);
        else if (_value < 10_000) return string.concat("000", result);
        else if (_value < 100_000) return string.concat("00", result);
        else if (_value < 1_000_000) return string.concat("0", result);

        return result;
    }
}

File 13 of 18 : FlagRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { LibString } from "solady/utils/LibString.sol";

import { ICurta } from "@/contracts/interfaces/ICurta.sol";
import { IColormapRegistry } from "@/contracts/interfaces/IColormapRegistry.sol";
import { Base64 } from "@/contracts/utils/Base64.sol";

/// @title Curta Flag Renderer
/// @author fiveoutofnine
/// @notice A contract that renders the JSON and SVG for a Flag token.
contract FlagRenderer {
    using LibString for uint256;
    using LibString for address;
    using LibString for string;

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

    /// @notice The colormap registry.
    IColormapRegistry constant colormapRegistry =
        IColormapRegistry(0x0000000012883D1da628e31c0FE52e35DcF95D50);

    /// @notice Render the JSON and SVG for a Flag token.
    /// @param _puzzleData The puzzle data.
    /// @param _tokenId The token ID.
    /// @param _author The author of the puzzle.
    /// @param _solveTime The time it took to solve the puzzle.
    /// @param _solveMetadata The metadata associated with the solve.
    /// @param _phase The phase of the puzzle.
    /// @param _solves The number of solves the puzzle has.
    /// @param _colors The colors of the Flag.
    /// @return string memory The JSON and SVG for the Flag token.
    function render(
        ICurta.PuzzleData memory _puzzleData,
        uint256 _tokenId,
        address _author,
        uint40 _solveTime,
        uint56 _solveMetadata,
        uint8 _phase,
        uint32 _solves,
        uint120 _colors
    ) external view returns (string memory) {
        // Generate the puzzle's attributes.
        string memory attributes;
        {
            attributes = string.concat(
                '[{"trait_type":"Puzzle","value":"',
                _puzzleData.puzzle.name(),
                '"},{"trait_type":"Puzzle ID","value":',
                uint256(_tokenId >> 128).toString(),
                '},{"trait_type":"Author","value":"',
                _author.toHexStringChecksumed(),
                '"},{"trait_type":"Phase","value":"',
                uint256(_phase).toString(),
                '"},{"trait_type":"Solver","value":"',
                _formatValueAsAddress(uint256(_solveMetadata & 0xFFFFFFF)),
                '"},{"trait_type":"Solve time","value":',
                uint256(_solveTime).toString(),
                '},{"trait_type":"Rank","value":',
                uint256(uint128(_tokenId)).toString(),
                "}]"
            );
        }

        // Generate the puzzle's SVG.
        string memory image;
        {
            image = string.concat(
                '<svg xmlns="http://www.w3.org/2000/svg" width="550" height="550" viewBox="0 0 550 '
                '550"><style>@font-face{font-family:A;src:url(data:font/woff2;charset-utf-8;base64,'
                "d09GMgABAAAAABIoABAAAAAAKLAAABHJAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjobkE4ci2AGYD9TVE"
                "FURACBHBEICqccoBELgRYAATYCJAOCKAQgBYNuByAMBxt3I6OimvUVZH+VwJOd1yhF0zgE5TExCEqYGJii"
                "xbbnwP4dZ3ojLIOo9nusZ/fRJ8AQgAICFZmKIpSpqJRPjvUpssca0NE7gG12KCpiscLIQJvXhQltNGlMwA"
                "rs/BmdK5fVh2tlJjmcJIfyqegIjKaFAkL2xmzu3G5f+LKucK0RgScgqNbInt05eA7IdyHLQlJ5ILKkAI2L"
                "smRUYiTh5D8sgBvNUikgepThvv9/7Vt9l9274oNYWrFooc1GREOjlHln5tu8NRvMZ1WEJN6I4hZFQyGaVu"
                "tEQvmh0iqJSMiE1ggNX4fm60G4VBK+hVS6yPZZNHETOvYf/6wI8AMAxSaiREKCqKkRPT1iaEjMNZdYzh2C"
                "AK+6KbX/oC8NZO9cTOaDLAPg/gNAbl9N5AMKCBAGxaF4IxHCbAZIOiZOprfyvA2svxHYCN8xFXIgBO2JgJ"
                "BkCIqIycqraOuabY655plvgYUWWWyJFXbwhFCsukQ59cgGY8Uaah88kjL5fx5QlZdkxUNbkDHIMQ++qS7D"
                "1nnyFUqhHlNMvSuZK1fXqRhUhap69aRhnUyKyxTMwBwswBKswBpswPbkEbJ1HEqlntKpR3SrK716R994N1"
                "DVqeeMDo7QrrukpSqSHZgDmAvzvM+v2ZzAQlgEi2EJWZsN0avrRGpFo5UcJiIGx7eiGrZDwnw0YhSkHLvi"
                "Hr+vWx0joCfCKSOyA4H7P0c+r0GXbANfz4GrrMyyqmP3wDWW4+y7l4S762X1ZcwjQBoOINXvM01OAf7nvs"
                "RQg0/rE+A09OW8FQJ4+UuJqyqznjiGunWqiav0+BQcegIjR2e5j2Vobwwvcie5j95yFcPCdxXG3bniECmQ"
                "lY+G0onLqnE5DS6v7b2gZ4mitQ7WhOJHTYxPgMEWFybAIUDx8NO8gqIS0iKSADIiogBiACALcgDyAFIs+B"
                "XiIyikKF4YBJxNgexM0bwcHj5yCokJx0MQ4KIC6SEOEmq18Mvyy89HgP8PidnpugQNSdmjaFy+GJcMSlf4"
                "Ah5oXEtksEDvXgovEdwBIgtEBviBYrA4vPyCwqLikkCkVaxstBbdr7aCAF2wB26Tv5ZdCzwhHtqe5nGikN"
                "jUSech2B6UOBAO1bDSwsivQJsoFjJWicmn36MiJaFvvWhFeqy57HUgEUphEVsjMGJDGFXREWOxEofjPCli"
                "XRRB3WS0H+AVlcX3GTyC0n0fwWGX5vsENrNk3wewWCX5XsFkFPe9gDFa1PcIekTnewLVK/nqaBW9bsWjLI"
                "Gg4ttD1jEkDgYSuSage1OPEAnn2F7zmWoV0dA4MSPis2P5kCECJk18nngCMT+BvSY+X6IJy2dgJ4kvsDYK"
                "bK/AhogntE0/QbkGlkd8kfV9QNJmsEDii63/HrQ62JBY4kVwmW0dqNorftedTLNI/9Jwq8E8SEnWrpgFdt"
                "eqYDJ9XG2eJUNcTB6su+JX1LfQZqHilGkzYd1Vb4kSByBCFgA0FcB2iABI0X4/xbtD+J1UEN+ZoPFO+YJI"
                "CNIy8z4j+rLM2wgNzLklWiwtez2+AHI+9gGEcy3MK7eJBBZKWFDOEMW8OJ6ExdjdkHZ7ZkVYil/MtBgsS0"
                "5lip5noyTbtpWVRQrGxJ5JiTGZtUBisHeMbYCvo5P+ZL0/k2J2AMUAAhxw+2E/KqzTdmtx5F8ggD9MGwHy"
                "BoDFoYHzk9QOBeIg9R3dvx6djJADLt/h1ykwhAHpAVlvgYXzrkIe4H5liJiWz9Mk3l7DHUBRmBZUMzLPfK"
                "uRMHBEiyOQJEVm/cPGYGyyDNmQSDI+F/Xbu3pUT2q9lg0b1K9Hlw7tWkI4CnCR+qdvsh4enfkaYG6Gcxjw"
                "BeAbAIBal+6LTXcrxeKEiunCi8zS+1uXOjYHoSy4EXNuXkTrLUlJoxV2bJDH6MsjMRs2G8tvlsasojT2oc"
                "V1t2yIQlPwMtICCfpWK9WZO4Gtw0u6s9hTS71G58ThjlakCzVBM+YjScM6npVwCfM87HwHH+taYCugEo46"
                "imBSKa2zVdcKK2dXdqkxv+fATPp118TM+6zLjl8d9qi/nFU+7Z33ur7XwZ49VJKIDxrDoLtnrVnJqjCtyB"
                "zUORSvqlCwuWmQ4ZFzVkjuR+3LenE5OgrwP9z1ydHwC0fDuRDNSl4Su2swM0M9LZu6qPGvbFWLJtQXztSm"
                "XWVsBMMwICeOrkmssuxQS5FuyYRyhEp4ENa2fggVisSS8hKz6+VFpnzeUf7nKpa16O6PwSSpY3rPB6lkYd"
                "mD2eiFl5fdD5UH7cvlUFsXSqADVrUGbd0qFemfJZd24YZXzPOJLiRp4+AkEPTEmBlDDhuxbsWnSyzsqWL6"
                "qhuvebv4UDyR4ktUGqSewgqsJA8zzM+034SQLE6JQnFH2WMSFWX/Vjm3ScreuSFIcjRszN+KnHQh11XZAy"
                "7epzEf3CyIdsPSF2HD2o3ZYuelKqikg6KzSHyV649Jnd4fc2iCgSBucqfLg2IO5RLfutpKusgmDxapq5HS"
                "pD5+la82KwhVyt5s5uwS6/1rNoBtgOCIzT8kbSY36KqJKcgoulfNLFTbYPoNbvKgbg96Pb2qPjH70758+m"
                "S13tbHYiV1G+/CA9mR8CT2hKIViO9KwYVq9oFYkOnvb5WViCClYQewYAHcAHH6YXP4Ijk+ZH12N/Km7AiJ"
                "1FHBY9mdPusl8p9IdsP6MMCsMwx/zB8l45BuqXt1846SoAdDZt6noTlcl7wAOl3rhrKSN9YaTvXLRN/eui"
                "WJb3YpB0vZ4tbnW4qlSBranIajAtSTVhxAq/hdmN+FKlMFOGlqAFVadqEYTaqDvZE9nAQUYO7qVTS8a8mO"
                "mOnlseQ3x4kdT8+91dHd+tu2/tUJ29qTc22ZIUENg7SkVilBZOv2wILgtKHWgTTItPVLlQuh1+Sy+qzChx"
                "FD9fhnrxrw0DcU2mORFxFYiQlKQWyur/h376Bx3LLs8I9P17x9vlyNd1J6XDfiycouNOQtAQoOWy59fnXC"
                "Df/61J4vmXjLq6odtfavXtXaKbSdd4aTgAIqHnAHTIQ4ofkJXMOCAqkmTag4Knf3pw2v8dMDVoeODFp6TL"
                "35Y3mqYjqCNszj0QZnQitpIH4SktQankeXnGkuiBgd5Jy3EUiVyAxjfS1sxBOKs3KK25KUCGRz/crlPZLh"
                "vYJb1oup/FOtyNBL0nKp/Dl3RUlet79/LX1RhiTP2sCQsSGhcy29Arez6znwSWlRdXWQ8QdGItUXZksngz"
                "k5y3J/dwIK2B+xyzHCyQA1g6ReIfVeYyPlVk9GSkZvNvlObR3lfl8e2GmoPSOszzM/vFngbjt+u+Z38MG9"
                "Ap2Tglef6yAqoK8jLFowHBs6AdwrJYx90+xUYq5JWIA8nqyrXjx3dEscaHkUFcUCCphiG23zHzbN9LwpLk"
                "CsXYxta++aqI1sotDaQcsTAAWxgAKwzVN74HVkgfX27QLHyA/ugUNYB76rmdlfQnpUVU163F+amT1QiuDq"
                "qvRvrYESWANxiACMYdHpuiI1b2SRXxRT2ozVQQtqZJZCQFGsl/aFrQFJtrSEFgb4gTubmqwkhrthdvkUDE"
                "Dd0G39rHWlgNcalyI15aPaY7LzO2II7mx6DFbM7rbppZswDNUd5xUSV2TtnoDnxZLMWBCHiA0Y67zX/ZPN"
                "T0vyRVYuxrY1dI8XkBr9Itugzc1biCVQFQnYLES4t4a/GkXrqkYeVqcMg7Upy9dLXTPKqJ46HQpDTy0LH8"
                "j8NVn9Zq59XkbaBBxn9L/d/bnnJhEIe65Gfvv6lfFtzzUCac/NyB9f7dfvZiZ85visjuTuv5MJPPjM9lsd"
                "Abd0ABQkgXiz9F1L2u/tsCR9dIXDLe32aKQzAipafZicZgqlOYZxaFbdvvPUWVzrhNREUsCd2zJH90dyZY"
                "V24EBSuU74/4CE2EQ6ksLKHPSI4o/6RpRDEIQyqleIBYtqizt7vEJKIBD8CXk1OyLDuz0YQhE6R3R8H9Kt"
                "4dgZs5ZBRAdMnH4xqI25pvpUBPZPHJSltUJdlr4dPezrQJPO/92YvZZRWVdFC0FsKBxQALj7KsedmlYkxE"
                "ajJbOPdFXyRlmMWUiAJNb8JDchBGa32RPsxBc0BuwSHNWk7d3PBQrXtKeGLY+cWnP2XgBHvEoDyLPvPS4U"
                "gqKEoP2CikHjygGj5mXxLQM85ZSJqqIrjxPAXdYioD378Fb+gtr4sAkcgBOreJ/luya88RjWPAiAFzk7Gs"
                "UjV7pExTnWAAliGfPjOwFhl7pH9pNdgAW42HEPgf19lXuBjQeRMAh5CdcO1qVtku3UOvgbBqFz9pttzbEj"
                "NjVz37qGDv9u05JN27Rel3gtf+gQ0r/xjmrSKCGsFsKAwWwao/P443RmEzAgLKxmjCjhMRhe50MthUAIIB"
                "fVe0YwGjxJRRAAgZSSBl/AXVdpACz73sPCLAWQiO+6oKrfuHrQqHlZYvMoTzljoqboypMEr5WNzDnscSJD"
                "xrB+fMXVZ+WuadxENHOOD3E7k5EdRPMdq4EQw5wdq9R9W1CgELL4WGHM5Jl54BbQL2LaE7GHr5sKyAZuzy"
                "MNvXa52B1Ay5aQ/FwIgaD6ACGBBbkQJO3MYjqKLnh2cXkQDHkkD6T0Tkz2BSVi0vPfdvSf/DNxIxk0SvfJ"
                "07AjtBzK4MmFncKZxcnwP9DO2Jyp6fnsOYj2REmMvlFa+IiHpJXFUIJbENNlK9+XTI2l+Hwa/HzwwOHnR+"
                "v8XAuK86K95grRX8+DsVzFef+URghHYhPdacGpXo5OGV4xKXtSAuJ3OOAiPSl+CVux3yA8tf4oJTtnLyW2"
                "MiAkN50rgy6WqSphR2Q0XKIAafEk7J/ayPXok7hxtnbxk2jwx3d40JHLP0JPaBSJ+OGTuMMnOJXhR0jnhN"
                "tEGcw9NnLa9vq8gvqflnKBnR9/OoTTGy1ImjzDAAe50ClcpZB8tnmPEHm/tHn+zd6N5X8/MXEe6fnjDWiD"
                "HSNEBm88mNXNjU+aOMuqOBaUxfX1TmcHOYabOvipI3ak8aBJ/RIXTT4fekn9EQ9M5SqukjKrgjjNjQI5FA"
                "eTVOZPExQd8BVmrfvyi2j+KWVsDCpxw65GVlBG1WVS2W63EG0rvOF/qfQAx3BdnIepazwx4rYf28vRgeVD"
                "IEX6ODgyvOIGraSdncJVZpPPNu0WIu+/bl55sW9j2d9PDNy2jNyJekmjQ/1ENn88mNnNTUiYPMsoP+YYYY"
                "4jWnsmc0PwEda2RAnE9gd82HsCBf8EooDkc3jWBlnrw/vnY4C1Dr8vfDfJK8qWTXyCCv6fjFjFGtaxgU1s"
                "YRs72CUVSwPWsIEt7GCXci3124AqsIZ1bGATW9jGzohuTgCE/o9XfeCd/xvTsv2BwP+TjdPeYCCk30jV4P"
                "+dKQI9GSFp3qePQUh/awUI1lFcFAHLLXuXTAHlGOgG0Dvf2Z+n5UzCSNAfIyGPN3bOeF/9PVIcRUPhSOh6"
                "tKVcGMmKsbevN84MEsu+Lhh1f59JkajfBk9pPgMnx/e3M9A+R3HUZsstexfbDgVjFwB99wTovLN9/Cs5Qs"
                "75W0d5CQC++bx7HYBvj5Kufrv/W6gSKw2ARQEQ/C8luPB4xOLz5x4A4Xiy65cCY8v0FbZNTj6fzTyZvsPJ"
                "cTqQhJBpczx5isTxnayxWPq4z1q7a22ckZ5h0QkN18ZZL/7wo5k3086aBm+t41Vjk3F6MlT14xfYPWl6PK"
                "ZImZQ9vN/1i2mTWhoZdCsfhZPOYrSub29aOZjBgjMdOd3FwDu5v+WeQSibhd5F/nrwjIrzgiDAertIu0Ci"
                "hGdoEuBzlyhJSYB7AOsm8u65KUFv3bSpD6oa426m2X52teRDKx1CDvzhCjcMb3hXkc93mQhl4aDxIBAvQ6"
                "IYUaIl0zA1DkrZzhZwsq/Dl4wj0e5uq0QCsThYTGWZlMii/6MVm0RDO4Ngdx8viaORkeNiRnefgsnMYQI8"
                "5lB4khB0MnN1w1mUHTjm247hTiAOGwVHlBRxGBJZGxqbmGeB7aj8UDlJ+U2JvzjjtmMI8GmQpO9TbmrsIF"
                "sTY7MdZeFQNhKgK5lM1STySmSEiTrO6X8Ln0n5Lv4Vj6wGAA==)}@font-face{font-family:B;src:u"
                "rl(data:font/woff2;charset-utf-8;base64,d09GMgABAAAAAAhQABAAAAAAD4gAAAf2AAEAAAAAAA"
                "AAAAAAAAAAAAAAAAAAAAAAGhYbhCocUAZgP1NUQVRIAHQRCAqPQIx0Cy4AATYCJANYBCAFg1YHIAwHG/YM"
                "UZRR0hhkPxNMlbEboUgq+fBI0pEkVMwtRsc8kTqPvob/GX6NoH7st/e+aHLMEyXSaHjI5tU7oVEJRaw0mk"
                "2nBHj/P27694UIJFCoHdq50ok6paJURELVmdGOUTWmxtTDXPn9zPxom4glMvErMJW4alpRu79ZXw6UtFRH"
                "/wP4wv6/tVZ3013cAY9nCTwkHhFCIlPKnxP7J3PoDKpJxLtGOqGIyGnDsiZIBUIhtMhTmqVhJtyMGedU9E"
                "0+RQBNABIEsYYgoG6AMbCFBeszo257Y86ocMBvxbAtP8gZBbBEOEyDoQ1DoYwEq3ZEjMdbW2SsoQwLL9Uh"
                "NpGWBcwcl5DPdS1HrnGR9VrcRDbPruGaAV4JWAbmEqeVSq7ZMHXB1nex0hbQ7QIMGoIBRSRh5+0YDIXAw8"
                "cnIpISHHITgyjoEDy8B/JkgkRVMjPzT0ne3bgMTGT5pDwUY8MWguETwiujkBoBKnOkFvhQmsKnmteaEAWY"
                "WD5Z4+HCpeXNialh5VNv+btjzhGmFhB3gDI+YIIQClkOOkqaOegsIERWDHC0nRNKUiu0EeCAkCI1SO+AE4"
                "ipIPIVYOpAkCGiaJwXSj7pfs51YvNESuDpkwVAc2RxpYRAv9NtV69OtSqVdLQhmCmgnBPDnjp9HpgAaRGY"
                "wgN4i0LEjm6MCA/yIm5l/dGcHT5ukKVUUrUMxoUUjvZBN7psscGtpPxTq/D4bFn6FhxEFz65g2XefcoU3U"
                "ZFwXAXP6GLn93jZd1/Lip6ajQyrPgy8C6k33lCWyDxCB/K0OgsTGKThMmsLuw4pBuRzJcbjRKoJz675Shy"
                "M8IaR8dbz4jdf85bnhrlYYVT2BnD0GMZQhnGvnWOjwKmaDOIpBG9neMc9Tiq4LBwHGe9E8MyOLNNYIlkcO"
                "GtW6No8Ey/60O677y3abuTVPjsnh9qqYNxJEMwSXO5Y3y/A8ZPGNPzLoAVsszIg05NKXOFnMNODMskJRfG"
                "SkdB50AuxIVJEMXSiYsB4nhFEptEs/2FwESO8kv7S4xPCzIqBnJLp3Z2bpOwu7B2Ua3WGKwZTRqj011j8o"
                "xzIpPHha8aCJpgXUCCXJmkQtrOSuUNnBlHLLWvo3k0x9g9NCkk0zPtzybSyaIkhg2a8EEKIy9UKCZclMjh"
                "OlbA8MtvYnFa4MsODySK8Cs+J/27bXBorTb80ET/gyMObQ+j6SMQO+jPoelfaJ1t0Pfuuo3f7ndInLeS5f"
                "/e3p43DwAYuRUA/r8bv0JDgd3msXUbu+/Zs1HqUNUlezKk4Zyny3aWdd133rdpN1ADRpys6J1V0ac3rwLk"
                "uWBZ/q/5YP4FReQW5ZoVseyyPFnp1YkrbU4t3p0ad1rd1MEsqTQ9mFWy9w/TDIKYfM2NRcmlT92ymiL9o9"
                "IOrAzp+Wvh//Ok/652HIRppafOjC3e/QnXwtb+G/4LL7qhyQ5o6HS/MSpYVGSpt1o1cjJ/U019feuxjYPW"
                "Pl45teicrmtG7BJDv90hCXf3fTZdE0ZFRB1Y6X2z2BCm7PS689S3R6ZNssU6V6ZqnFegC7tXBd8pYPpsU+"
                "6m0is1JwYfb/T5+LnRd+AJw6YdSs/ULT1MvH96P/7JTaWbzuh86YY6JX6mQqM+qfbBwMHQxzAKRrLPHzVA"
                "6+ITGr3STVVu89kEfHhh6rHFsGWHMuZm6QLWTN8xFsYztQYA+tU1+NJndDB+9s0ezTrVoTSZOgInXLiW+H"
                "blsZ1RA2/71lzanul/8XBcesmlwrCHafn6RPNtVZm7ZlxYcM+Vz2f2j2nV//CCO0CCCUigFsJ8pfucOUr3"
                "+RQC7BrN6y+rbh9w8aZNr5MjgA+pnKb41lw/8qQOeiyCusn8nV6YbUYnG/M1FRfyJyVLpsS/L268QoAYN+"
                "FtB9an1u04sfmNuUdWa02uW5W3V0eUAXrM1elMWi8yPvZ+qV73NjUDTl0zGfYcutAQtd3b7xCMS+9zbdJp"
                "vU+XWe9HzmsP7mfu4kAXXb1ZdbHWm9JqvQUX66uqjkD0YaeaOwW3ZwJ/1ovPPd6CDdmMgPmzpqYsXvmNZG"
                "jU/ZLCbsuRg3nnOuHoSL2bb13gJZi4AM71OPc6wgTEft8pSrc+4wbbPRt9M1FuNcf3qFy1LReWYLHF+Sgm"
                "XJL0eZ1e/Tg17fKWw2c3Ou11Vh0ANxL654L5qIFu2eck/kDEzGnuVnZTr8ptsxj/wGGxwxMGVHy/h5x5Mj"
                "w+Q9e2FPjZMIubRWZfGrtooUym+FjZQvrqYxfOA9vclhZu1qXt/P6O9b6hupPn9Ry3PTSR9fFJTycGEECh"
                "+PB3s7bZUzztizLMAOD746FhAH7kq37/J79VI7EQgDIMIMA/MRs9B/Xnz5P/q/nfq5t9V8A9xYfvchFqyH"
                "GgJjnNkZI9UA3AdFqI3+6QckegVpOREqmoyd0+3E61AmyUfZtlfcBL/bB44l2MhC8goGcTCEWSQ2KJ31aX"
                "ioeKAgC3AA8DQlMbMBa0Bh6ZFgOumzsGwgQf9aRuMSQFEoMw5hgkQMcKlLoLda5C6QNEAiFEjCE7ij7WNW"
                "Rv4GI55AEQq4C8PeZxhGK2B7FQUwAKOMSBQCUTC6OrsgDRIJW02jp85uWZrlBkKHiDHM/Xg/zFI0/gIWUB"
                "Q6PIOJhsqjVONk6WsEh7Pr4zO4JXtRkVKeSIU0uSok0mEmMOtIMT+OlRClJ9471uzlPTJCrkYX7/PQbo/I"
                "z+3axYAg==)}text,tspan{dominant-baseline:central}.a{font-family:A;letter-spacing:-"
                ".05em}.b{font-family:B}.c{font-size:16px}.d{font-size:12px}.f{fill:#",
                uint256((_colors >> 72) & 0xFFFFFF).toHexStringNoPrefix(3), // Fill
                "}.h{fill:#",
                uint256((_colors >> 24) & 0xFFFFFF).toHexStringNoPrefix(3), // Primary text
                "}.i{fill:#",
                uint256(_colors & 0xFFFFFF).toHexStringNoPrefix(3), // Secondary text
                "}.j{fill:none;stroke-linejoin:round;stroke-linecap:round;stroke:#",
                uint256(_colors & 0xFFFFFF).toHexStringNoPrefix(3) // Secondary text
            );
        }
        {
            image = string.concat(
                image,
                '}.x{width:1px;height:1px}</style><mask id="m"><rect width="20" height="20" rx="0.3'
                '70370" fill="#FFF"/></mask><path d="M0 0h550v550H0z" style="fill:#',
                uint256((_colors >> 96) & 0xFFFFFF).toHexStringNoPrefix(3), // Background
                '"/><rect x="143" y="69" width="264" height="412" rx="8" fill="#',
                uint256((_colors >> 48) & 0xFFFFFF).toHexStringNoPrefix(3), // Border
                '"/><rect class="f" x="147" y="73" width="256" height="404" rx="4"/>',
                _drawStars(_phase)
            );
        }
        {
            image = string.concat(
                image,
                '<text class="a h" x="163" y="101" font-size="20">Puzzle #',
                (_tokenId >> 128).toString(),
                '</text><text x="163" y="121"><tspan class="b d i">Created by </tspan><tspan class='
                '"a d h">'
            );
        }
        {
            uint256 luma =
                ((_colors >> 88) & 0xFF) + ((_colors >> 80) & 0xFF) + ((_colors >> 72) & 0xFF);
            image = string.concat(
                image,
                _formatValueAsAddress(uint160(_author) >> 132), // Authors
                '</tspan></text><rect x="163" y="137" width="224" height="224" fill="rgba(',
                luma < ((255 * 3) >> 1) ? "255,255,255" : "0,0,0", // Background behind the heatmap
                ',0.2)" rx="8"/>',
                _drawDrunkenBishop(_solveMetadata, _tokenId),
                '<path class="j" d="M176.988 387.483A4.992 4.992 0 0 0 173 385.5a4.992 4.992 0 0 0-'
                "3.988 1.983m7.975 0a6 6 0 1 0-7.975 0m7.975 0A5.977 5.977 0 0 1 173 389a5.977 5.97"
                '7 0 0 1-3.988-1.517M175 381.5a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"/><text class="a c h" x'
                '="187" y="383">',
                _formatValueAsAddress(_solveMetadata >> 28), // Captured by
                '</text><text class="b d i" x="187" y="403">Captured by</text><path class="j" d="m2'
                "85.5 380 2 1.5-2 1.5m3 0h2m-6 5.5h9a1.5 1.5 0 0 0 1.5-1.5v-8a1.5 1.5 0 0 0-1.5-1.5"
                'h-9a1.5 1.5 0 0 0-1.5 1.5v8a1.5 1.5 0 0 0 1.5 1.5z"/><text class="a c h" x="303" y'
                '="383">',
                _formatValueAsAddress(_solveMetadata & 0xFFFFFFF), // Solution
                '</text><text class="b d i" x="303" y="403">Solution</text><path class="j" d="M176 '
                "437.5h-6m6 0a2 2 0 0 1 2 2h-10a2 2 0 0 1 2-2m6 0v-2.25a.75.75 0 0 0-.75-.75h-.58m-"
                "4.67 3v-2.25a.75.75 0 0 1 .75-.75h.581m3.338 0h-3.338m3.338 0a4.97 4.97 0 0 1-.654"
                "-2.115m-2.684 2.115a4.97 4.97 0 0 0 .654-2.115m-3.485-4.561c-.655.095-1.303.211-1."
                "944.347a4.002 4.002 0 0 0 3.597 3.314m-1.653-3.661V428a4.49 4.49 0 0 0 1.653 3.485"
                "m-1.653-3.661v-1.01a32.226 32.226 0 0 1 4.5-.314c1.527 0 3.03.107 4.5.313v1.011m-7"
                ".347 3.661a4.484 4.484 0 0 0 1.832.9m5.515-4.561V428a4.49 4.49 0 0 1-1.653 3.485m1"
                ".653-3.661a30.88 30.88 0 0 1 1.944.347 4.002 4.002 0 0 1-3.597 3.314m0 0a4.484 4.4"
                '84 0 0 1-1.832.9m0 0a4.515 4.515 0 0 1-2.03 0"/><text><tspan class="a c h" x="187"'
                ' y="433">'
            );
        }
        {
            image = string.concat(
                image,
                uint256(uint128(_tokenId)).toString(), // Rank
                ' </tspan><tspan class="a d i" y="435">/ ',
                uint256(_solves).toString(), // Solvers
                '</tspan></text><text class="b d i" x="187" y="453">Rank</text><path class="j" d="M'
                '289 429v4h3m3 0a6 6 0 1 1-12 0 6 6 0 0 1 12 0z"/><text class="a c h" x="303" y="43'
                '3">',
                _formatTime(_solveTime), // Solve time
                '</text><text class="b d i" x="303" y="453">Solve time</text></svg>'
            );
        }

        return string.concat(
            "data:application/json;base64,",
            Base64.encode(
                abi.encodePacked(
                    '{"name":"',
                    _puzzleData.puzzle.name(),
                    ": Flag #",
                    uint256(uint128(_tokenId)).toString(),
                    '","description":"This token represents solve #',
                    uint256(uint128(_tokenId)).toString(),
                    " in puzzle #",
                    uint256(_tokenId >> 128).toString(),
                    '.","image_data": "data:image/svg+xml;base64,',
                    Base64.encode(abi.encodePacked(image)),
                    '","attributes":',
                    attributes,
                    "}"
                )
            )
        );
    }

    /// @notice Returns the SVG component for the ``heatmap'' generated by
    /// applying the Drunken Bishop algorithm.
    /// @param _solveMetadata A bitpacked `uint56` containing metadata of the
    /// solver and solution.
    /// @param _tokenId The token ID of the Flag.
    /// @return string memory The SVG for the heatmap.
    function _drawDrunkenBishop(uint56 _solveMetadata, uint256 _tokenId)
        internal
        view
        returns (string memory)
    {
        uint256 seed = uint256(keccak256(abi.encodePacked(_tokenId, _solveMetadata)));
        // Select the colormap.
        bytes32 colormapHash = [
            bytes32(0xfd29b65966772202ffdb08f653439b30c849f91409915665d99dbfa5e5dab938),
            bytes32(0x850ce48e7291439b1e41d21fc3f75dddd97580a4ff94aa9ebdd2bcbd423ea1e8),
            bytes32(0x4f5e8ea8862eff315c110b682ee070b459ba8983a7575c9a9c4c25007039109d),
            bytes32(0xf2e92189cb6903b98d854cd74ece6c3fafdb2d3472828a950633fdaa52e05032),
            bytes32(0xa33e6c7c5627ecabfd54c4d85f9bf04815fe89a91379fcf56ccd8177e086db21),
            bytes32(0xaa84b30df806b46f859a413cb036bc91466307aec5903fc4635c00a421f25d5c),
            bytes32(0x864a6ee98b9b21ac0291523750d637250405c24a6575e1f75cfbd7209a810ce6),
            bytes32(0xfd60cd3811f002814944a7d36167b7c9436187a389f2ee476dc883e37dc76bd2),
            bytes32(0xa8309447f8bd3b5e5e88a0abc05080b7682e4456c388b8636d45f5abb2ad2587),
            bytes32(0x3be719b0c342797212c4cb33fde865ed9cbe486eb67176265bc0869b54dee925),
            bytes32(0xca0da6b6309ed2117508207d68a59a18ccaf54ba9aa329f4f60a77481fcf2027),
            bytes32(0x5ccb29670bb9de0e3911d8e47bde627b0e3640e49c3d6a88d51ff699160dfbe1),
            bytes32(0x3de8f27f386dab3dbab473f3cc16870a717fe5692b4f6a45003d175c559dfcba),
            bytes32(0x026736ef8439ebcf8e7b8006bf8cb7482ced84d71b900407a9ed63e1b7bfe234),
            bytes32(0xc1806ea961848ac00c1f20aa0611529da522a7bd125a3036fe4641b07ee5c61c),
            bytes32(0x87970b686eb726750ec792d49da173387a567764d691294d764e53439359c436),
            bytes32(0xaa6277ab923279cf59d78b9b5b7fb5089c90802c353489571fca3c138056fb1b),
            bytes32(0xdc1cecffc00e2f3196daaf53c27e53e6052a86dc875adb91607824d62469b2bf)
        ][seed % 18];

        // We start at the middle of the board.
        uint256 index = 210;
        uint256 max = 1;
        uint8[] memory counts = new uint8[](400);
        counts[index] = 1;

        // Apply Drunken Bishop algorithm.
        unchecked {
            while (seed != 0) {
                (uint256 x, uint256 y) = (index % 20, index / 20);

                assembly {
                    // Read down/up
                    switch and(shr(1, seed), 1)
                    // Up case
                    case 0 { index := add(index, mul(20, iszero(eq(y, 19)))) }
                    // Down case
                    default { index := sub(index, mul(20, iszero(eq(y, 0)))) }

                    // Read left/right
                    switch and(seed, 1)
                    // Left case
                    case 0 { index := add(index, iszero(eq(x, 19))) }
                    // Right case
                    default { index := sub(index, iszero(eq(y, 0))) }
                }
                if (++counts[index] > max) max = counts[index];
                seed >>= 2;
            }
        }

        // Draw heatmap from counts.
        string memory image = '<g transform="translate(167 141) scale(10.8)" mask="url(#m)">';
        unchecked {
            for (uint256 i; i < 400; ++i) {
                image = string.concat(
                    image,
                    '<rect class="x" x="',
                    (i % 20).toString(),
                    '" y="',
                    (i / 20).toString(),
                    '" fill="#',
                    colormapRegistry.getValueAsHexString(
                        colormapHash, uint8((uint256(counts[i]) * 255) / max)
                    ),
                    '"/>'
                );
            }
        }

        return string.concat(image, "</g>");
    }

    /// @notice Returns the SVG component for the stars corresponding to the
    /// phase, including the background pill.
    /// @dev Phase 0 = 3 stars; Phase 1 = 2 stars; Phase 2 = 1 star. Also, note
    /// that the SVGs are returned positioned relative to the whole SVG for the
    /// Flag.
    /// @param _phase The phase of the solve.
    /// @return string memory The SVG for the stars.
    function _drawStars(uint8 _phase) internal pure returns (string memory) {
        // This will never underflow because `_phase` is always in the range
        // [0, 4].
        unchecked {
            uint256 width = ((4 - _phase) << 4);

            return string.concat(
                '<rect class="h" x="',
                (383 - width).toString(),
                '" y="97" width="',
                width.toString(),
                '" height="24" rx="12"/><path id="s" d="M366.192 103.14c.299-.718 1.317-.718 1.616 '
                "0l1.388 3.338 3.603.289c.776.062 1.09 1.03.499 1.536l-2.745 2.352.838 3.515c.181.7"
                "57-.642 1.355-1.306.95L367 113.236l-3.085 1.884c-.664.405-1.487-.193-1.306-.95l.83"
                '8-3.515-2.745-2.352c-.591-.506-.277-1.474.5-1.536l3.602-.289 1.388-3.337z"/>',
                _phase < 2 ? '<use href="#s" x="-16" />' : "",
                _phase < 1 ? '<use href="#s" x="-32" />' : ""
            );
        }
    }

    /// @notice Helper function to format the last 28 bits of a value as a
    /// hexstring of length 7. If the value is less than 24 bits, it is padded
    /// with leading zeros.
    /// @param _value The value to format.
    /// @return string memory The formatted string.
    function _formatValueAsAddress(uint256 _value) internal pure returns (string memory) {
        return string.concat(
            string(abi.encodePacked(bytes32("0123456789ABCDEF")[(_value >> 24) & 0xF])),
            (_value & 0xFFFFFF).toHexStringNoPrefix(3).toCase(true)
        );
    }

    /// @notice Helper function to format seconds into a length string. In order
    /// to fit the solve time in the card, we format it as follows:
    ///     * 0:00:00 to 95:59:59
    ///     * 96 hours to 983 hours
    ///     * 41 days to 729 days
    ///     * 2 years onwards
    /// @param _solveTime The solve time in seconds.
    /// @return string memory The formatted string.
    function _formatTime(uint40 _solveTime) internal pure returns (string memory) {
        if (_solveTime < 96 hours) {
            uint256 numHours = _solveTime / (1 hours);
            uint256 numMinutes = (_solveTime % (1 hours)) / (1 minutes);
            uint256 numSeconds = _solveTime % (1 minutes);

            return string.concat(
                _zeroPadOne(numHours), ":", _zeroPadOne(numMinutes), ":", _zeroPadOne(numSeconds)
            );
        } else if (_solveTime < 41 days) {
            return string.concat(uint256(_solveTime / (1 hours)).toString(), " hours");
        } else if (_solveTime < 730 days) {
            return string.concat(uint256(_solveTime / (1 days)).toString(), " days");
        }
        return string.concat(uint256(_solveTime / (365 days)).toString(), " years");
    }

    /// @notice Helper function to zero pad a number by 1 if it is less than 10.
    /// @param _value The number to zero pad.
    /// @return string memory The zero padded string.
    function _zeroPadOne(uint256 _value) internal pure returns (string memory) {
        if (_value < 10) {
            return string.concat("0", _value.toString());
        }
        return _value.toString();
    }
}

File 14 of 18 : IColormapRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IPaletteGenerator } from "@/contracts/interfaces/IPaletteGenerator.sol";

/// @title The interface for the colormap registry.
/// @author fiveoutofnine
/// @dev A colormap may be defined in 2 ways: (1) via segment data and (2) via a
/// ``palette generator.''
///     1. via segment data
///     2. or via a palette generator ({IPaletteGenerator}).
/// Segment data contains 1 `uint256` each for red, green, and blue describing
/// their intensity values along the colormap. Each `uint256` contains 24-bit
/// words bitpacked together with the following structure (bits are
/// right-indexed):
///     | Bits      | Meaning                                              |
///     | --------- | ---------------------------------------------------- |
///     | `23 - 16` | Position in the colormap the segment begins from     |
///     | `15 - 08` | Intensity of R, G, or B the previous segment ends at |
///     | `07 - 00` | Intensity of R, G, or B the next segment starts at   |
/// Given some position, the output will be computed via linear interpolations
/// on the segment data for R, G, and B. A maximum of 10 of these segments fit
/// within 256 bits, so up to 9 segments can be defined. If you need more
/// granularity or a nonlinear palette function, you may implement
/// {IPaletteGenerator} and define a colormap with that.
interface IColormapRegistry {
    // -------------------------------------------------------------------------
    // Errors
    // -------------------------------------------------------------------------

    /// @notice Emitted when a colormap already exists.
    /// @param _colormapHash Hash of the colormap's definition.
    error ColormapAlreadyExists(bytes32 _colormapHash);

    /// @notice Emitted when a colormap does not exist.
    /// @param _colormapHash Hash of the colormap's definition.
    error ColormapDoesNotExist(bytes32 _colormapHash);

    /// @notice Emitted when a segment data used to define a colormap does not
    /// follow the representation outlined in {IColormapRegistry}.
    /// @param _segmentData Segment data for 1 of R, G, or B. See
    /// {IColormapRegistry} for its representation.
    error SegmentDataInvalid(uint256 _segmentData);

    // -------------------------------------------------------------------------
    // Structs
    // -------------------------------------------------------------------------

    /// @notice Segment data that defines a colormap when read via piece-wise
    /// linear interpolation.
    /// @dev Each param contains 24-bit words, so each one may contain at most
    /// 9 (24*10 - 1) segments. See {IColormapRegistry} for how the segment data
    /// should be structured.
    /// @param r Segment data for red's color value along the colormap.
    /// @param g Segment data for green's color value along the colormap.
    /// @param b Segment data for blue's color value along the colormap.
    struct SegmentData {
        uint256 r;
        uint256 g;
        uint256 b;
    }

    // -------------------------------------------------------------------------
    // Events
    // -------------------------------------------------------------------------

    /// @notice Emitted when a colormap is registered via a palette generator
    /// function.
    /// @param _hash Hash of `_paletteGenerator`.
    /// @param _paletteGenerator Instance of {IPaletteGenerator} for the
    /// colormap.
    event RegisterColormap(bytes32 _hash, IPaletteGenerator _paletteGenerator);

    /// @notice Emitted when a colormap is registered via segment data.
    /// @param _hash Hash of `_segmentData`.
    /// @param _segmentData Segment data defining the colormap.
    event RegisterColormap(bytes32 _hash, SegmentData _segmentData);

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

    /// @param _colormapHash Hash of the colormap's definition (segment data).
    /// @return uint256 Segment data for red's color value along the colormap.
    /// @return uint256 Segment data for green's color value along the colormap.
    /// @return uint256 Segment data for blue's color value along the colormap.
    function segments(bytes32 _colormapHash) external view returns (uint256, uint256, uint256);

    /// @param _colormapHash Hash of the colormap's definition (palette
    /// generator).
    /// @return IPaletteGenerator Instance of {IPaletteGenerator} for the
    /// colormap.
    function paletteGenerators(bytes32 _colormapHash) external view returns (IPaletteGenerator);

    // -------------------------------------------------------------------------
    // Actions
    // -------------------------------------------------------------------------

    /// @notice Register a colormap with a palette generator.
    /// @param _paletteGenerator Instance of {IPaletteGenerator} for the
    /// colormap.
    function register(IPaletteGenerator _paletteGenerator) external;

    /// @notice Register a colormap with segment data that will be read via
    /// piece-wise linear interpolation.
    /// @dev See {IColormapRegistry} for how the segment data should be
    /// structured.
    /// @param _segmentData Segment data defining the colormap.
    function register(SegmentData memory _segmentData) external;

    // -------------------------------------------------------------------------
    // View
    // -------------------------------------------------------------------------

    /// @notice Get the red, green, and blue color values of a color in a
    /// colormap at some position.
    /// @dev Each color value will be returned as a 18 decimal fixed-point
    /// number in [0, 1]. Note that the function *will not* revert if
    /// `_position` is an invalid input (i.e. greater than 1e18). This
    /// responsibility is left to the implementation of {IPaletteGenerator}s.
    /// @param _colormapHash Hash of the colormap's definition.
    /// @param _position 18 decimal fixed-point number in [0, 1] representing
    /// the position in the colormap (i.e. 0 being min, and 1 being max).
    /// @return uint256 Intensity of red in that color at the position
    /// `_position`.
    /// @return uint256 Intensity of green in that color at the position
    /// `_position`.
    /// @return uint256 Intensity of blue in that color at the position
    /// `_position`.
    function getValue(bytes32 _colormapHash, uint256 _position)
        external
        view
        returns (uint256, uint256, uint256);

    /// @notice Get the red, green, and blue color values of a color in a
    /// colormap at some position.
    /// @dev Each color value will be returned as a `uint8` number in [0, 255].
    /// @param _colormapHash Hash of the colormap's definition.
    /// @param _position Position in the colormap (i.e. 0 being min, and 255
    /// being max).
    /// @return uint8 Intensity of red in that color at the position
    /// `_position`.
    /// @return uint8 Intensity of green in that color at the position
    /// `_position`.
    /// @return uint8 Intensity of blue in that color at the position
    /// `_position`.
    function getValueAsUint8(bytes32 _colormapHash, uint8 _position)
        external
        view
        returns (uint8, uint8, uint8);

    /// @notice Get the hexstring for a color in a colormap at some position.
    /// @param _colormapHash Hash of the colormap's definition.
    /// @param _position Position in the colormap (i.e. 0 being min, and 255
    /// being max).
    /// @return string Hexstring excluding ``#'' (e.g. `007CFF`) of the color
    /// at the position `_position`.
    function getValueAsHexString(bytes32 _colormapHash, uint8 _position)
        external
        view
        returns (string memory);
}

File 15 of 18 : ICurta.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IPuzzle } from "./IPuzzle.sol";
import { AuthorshipToken } from "@/contracts/AuthorshipToken.sol";
import { FlagRenderer } from "@/contracts/FlagRenderer.sol";

/// @title The interface for Curta
/// @notice A CTF protocol, where players create and solve EVM puzzles to earn
/// NFTs.
/// @dev Each solve is represented by an NFT. However, the NFT with token ID 0
/// is reserved to denote ``Fermat''—the author's whose puzzle went the longest
/// unsolved.
interface ICurta {
    // -------------------------------------------------------------------------
    // Errors
    // -------------------------------------------------------------------------

    /// @notice Emitted when an Authorship Token has already been used to add a
    /// puzzle to Curta.
    /// @param _tokenId The ID of an Authorship Token.
    error AuthorshipTokenAlreadyUsed(uint256 _tokenId);

    /// @notice Emitted when a puzzle's solution is incorrect.
    error IncorrectSolution();

    /// @notice Emitted when insufficient funds are sent during "Phase 2"
    /// submissions.
    error InsufficientFunds();

    /// @notice Emitted when a puzzle is already marked as Fermat.
    /// @param _puzzleId The ID of a puzzle.
    error PuzzleAlreadyFermat(uint32 _puzzleId);

    /// @notice Emitted when a solver has already solved a puzzle.
    /// @param _puzzleId The ID of a puzzle.
    error PuzzleAlreadySolved(uint32 _puzzleId);

    /// @notice Emitted when a puzzle does not exist.
    /// @param _puzzleId The ID of a puzzle.
    error PuzzleDoesNotExist(uint32 _puzzleId);

    /// @notice Emitted when the puzzle was not the one that went longest
    /// unsolved.
    /// @param _puzzleId The ID of a puzzle.
    error PuzzleNotFermat(uint32 _puzzleId);

    /// @notice Emitted when a puzzle has not been solved yet.
    /// @param _puzzleId The ID of a puzzle.
    error PuzzleNotSolved(uint32 _puzzleId);

    /// @notice Emitted when submissions for a puzzle is closed.
    /// @param _puzzleId The ID of a puzzle.
    error SubmissionClosed(uint32 _puzzleId);

    /// @notice Emitted when `msg.sender` is not authorized.
    error Unauthorized();

    // -------------------------------------------------------------------------
    // Structs
    // -------------------------------------------------------------------------

    /// @notice A struct containing data about the puzzle corresponding to
    /// Fermat (i.e. the puzzle that went the longest unsolved).
    /// @param puzzleId The ID of the puzzle.
    /// @param timeTaken The number of seconds it took to first solve the
    /// puzzle.
    struct Fermat {
        uint32 puzzleId;
        uint40 timeTaken;
    }

    /// @notice A struct containing data about a puzzle.
    /// @param puzzle The address of the puzzle.
    /// @param addedTimestamp The timestamp at which the puzzle was added.
    /// @param firstSolveTimestamp The timestamp at which the first valid
    /// solution was submitted.
    struct PuzzleData {
        IPuzzle puzzle;
        uint40 addedTimestamp;
        uint40 firstSolveTimestamp;
    }

    /// @notice A struct containing the number of solves a puzzle has.
    /// @param colors A bitpacked `uint120` of 5 24-bit colors for the puzzle's
    /// Flags in the following order (left-to-right):
    ///     * Background color
    ///     * Fill color
    ///     * Border color
    ///     * Primary text color
    ///     * Secondary text color
    /// @param phase0Solves The total number of Phase 0 solves a puzzle has.
    /// @param phase1Solves The total number of Phase 1 solves a puzzle has.
    /// @param phase2Solves The total number of Phase 2 solves a puzzle has.
    /// @param solves The total number of solves a puzzle has.
    struct PuzzleColorsAndSolves {
        uint120 colors;
        uint32 phase0Solves;
        uint32 phase1Solves;
        uint32 phase2Solves;
        uint32 solves;
    }

    // -------------------------------------------------------------------------
    // Events
    // -------------------------------------------------------------------------

    /// @notice Emitted when a puzzle is added.
    /// @param id The ID of the puzzle.
    /// @param author The address of the puzzle author.
    /// @param puzzle The address of the puzzle.
    event AddPuzzle(uint32 indexed id, address indexed author, IPuzzle puzzle);

    /// @notice Emitted when a puzzle is solved.
    /// @param id The ID of the puzzle.
    /// @param solver The address of the solver.
    /// @param solution The solution.
    /// @param phase The phase in which the puzzle was solved.
    event SolvePuzzle(uint32 indexed id, address indexed solver, uint256 solution, uint8 phase);

    /// @notice Emitted when a puzzle's colors are updated.
    /// @param id The ID of the puzzle.
    /// @param colors A bitpacked `uint120` of 5 24-bit colors for the puzzle's
    /// Flags.
    event UpdatePuzzleColors(uint32 indexed id, uint256 colors);

    // -------------------------------------------------------------------------
    // Immutable Storage
    // -------------------------------------------------------------------------

    /// @notice The Flag metadata and art renderer contract.
    function flagRenderer() external view returns (FlagRenderer);

    /// @return The Authorship Token contract.
    function authorshipToken() external view returns (AuthorshipToken);

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

    /// @return The total number of puzzles.
    function puzzleId() external view returns (uint32);

    /// @return puzzleId The ID of the puzzle corresponding to Fermat.
    /// @return timeTaken The number of seconds it took to solve the puzzle.
    function fermat() external view returns (uint32 puzzleId, uint40 timeTaken);

    /// @param _puzzleId The ID of a puzzle.
    /// @return colors A bitpacked `uint120` of 5 24-bit colors for the puzzle's
    /// Flags.
    /// @return phase0Solves The total number of Phase 0 solves a puzzle has.
    /// @return phase1Solves The total number of Phase 1 solves a puzzle has.
    /// @return phase2Solves The total number of Phase 2 solves a puzzle has.
    /// @return solves The total number of solves a puzzle has.
    function getPuzzleColorsAndSolves(uint32 _puzzleId)
        external
        view
        returns (
            uint120 colors,
            uint32 phase0Solves,
            uint32 phase1Solves,
            uint32 phase2Solves,
            uint32 solves
        );

    /// @param _puzzleId The ID of a puzzle.
    /// @return puzzle The address of the puzzle.
    /// @return addedTimestamp The timestamp at which the puzzle was added.
    /// @return firstSolveTimestamp The timestamp at which the first solution
    /// was submitted.
    function getPuzzle(uint32 _puzzleId)
        external
        view
        returns (IPuzzle puzzle, uint40 addedTimestamp, uint40 firstSolveTimestamp);

    /// @param _puzzleId The ID of a puzzle.
    /// @return The address of the puzzle author.
    function getPuzzleAuthor(uint32 _puzzleId) external view returns (address);

    /// @param _solver The address of a solver.
    /// @param _puzzleId The ID of a puzzle.
    /// @return Whether `_solver` has solved the puzzle of ID `_puzzleId`.
    function hasSolvedPuzzle(address _solver, uint32 _puzzleId) external view returns (bool);

    /// @param _tokenId The ID of an Authorship Token.
    /// @return Whether the Authorship Token of ID `_tokenId` has been used to
    /// add a puzzle.
    function hasUsedAuthorshipToken(uint256 _tokenId) external view returns (bool);

    // -------------------------------------------------------------------------
    // Functions
    // -------------------------------------------------------------------------

    /// @notice Mints a Flag NFT if the provided solution solves the puzzle.
    /// @param _puzzleId The ID of the puzzle.
    /// @param _solution The solution.
    function solve(uint32 _puzzleId, uint256 _solution) external payable;

    /// @notice Adds a puzzle to the contract. Note that an unused Authorship
    /// Token is required to add a puzzle (see {AuthorshipToken}).
    /// @param _puzzle The address of the puzzle.
    /// @param _id The ID of the Authorship Token to burn.
    function addPuzzle(IPuzzle _puzzle, uint256 _id) external;

    /// @notice Set the colors for a puzzle's Flags.
    /// @dev Only the author of the puzzle of ID `_puzzleId` may set its token
    /// renderer.
    /// @param _puzzleId The ID of the puzzle.
    /// @param _colors A bitpacked `uint120` of 5 24-bit colors for the puzzle's
    /// Flags.
    function setPuzzleColors(uint32 _puzzleId, uint120 _colors) external;

    /// @notice Burns and mints NFT #0 to the author of the puzzle of ID
    /// `_puzzleId` if it is the puzzle that went longest unsolved.
    /// @dev The puzzle of ID `_puzzleId` must have been solved at least once.
    /// @param _puzzleId The ID of the puzzle.
    function setFermat(uint32 _puzzleId) external;
}

File 16 of 18 : IPaletteGenerator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @title The interface for a palette generator.
/// @author fiveoutofnine
/// @dev `IPaletteGenerator` contains generator functions for a color's red,
/// green, and blue color values. Each of these functions is intended to take in
/// a 18 decimal fixed-point number in [0, 1] representing the position in the
/// colormap and return the corresponding 18 decimal fixed-point number in
/// [0, 1] representing the value of each respective color.
interface IPaletteGenerator {
    // -------------------------------------------------------------------------
    // Errors
    // -------------------------------------------------------------------------

    /// @notice Reverts if the position is not a valid input.
    /// @dev The position is not a valid input if it is greater than 1e18.
    /// @param _position Position in the colormap.
    error InvalidPosition(uint256 _position);

    // -------------------------------------------------------------------------
    // Generators
    // -------------------------------------------------------------------------

    /// @notice Computes the intensity of red of the palette at some position.
    /// @dev The function should revert if `_position` is not a valid input
    /// (i.e. greater than 1e18). Also, the return value for all inputs must be
    /// a 18 decimal.
    /// @param _position Position in the colormap.
    /// @return uint256 Intensity of red in that color at the position
    /// `_position`.
    function r(uint256 _position) external pure returns (uint256);

    /// @notice Computes the intensity of green of the palette at some position.
    /// @dev The function should revert if `_position` is not a valid input
    /// (i.e. greater than 1e18). Also, the return value for all inputs must be
    /// a 18 decimal.
    /// @param _position Position in the colormap.
    /// @return uint256 Intensity of green in that color at the position
    /// `_position`.
    function g(uint256 _position) external pure returns (uint256);

    /// @notice Computes the intensity of blue of the palette at some position.
    /// @dev The function should revert if `_position` is not a valid input
    /// (i.e. greater than 1e18). Also, the return value for all inputs must be
    /// a 18 decimal.
    /// @param _position Position in the colormap.
    /// @return uint256 Intensity of blue in that color at the position
    /// `_position`.
    function b(uint256 _position) external pure returns (uint256);
}

File 17 of 18 : IPuzzle.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @title The interface for a puzzle on Curta
/// @notice The goal of players is to view the source code of the puzzle (may
/// range from just the bytecode to Solidity—whatever the author wishes to
/// provide), interpret the code, solve it as if it was a regular puzzle, then
/// verify the solution on-chain.
/// @dev Since puzzles are on-chain, everyone can view everyone else's
/// submissions. The generative aspect prevents front-running and allows for
/// multiple winners: even if players view someone else's solution, they still
/// have to figure out what the rules/constraints of the puzzle are and apply
/// the solution to their respective starting position.
interface IPuzzle {
    /// @notice Returns the puzzle's name.
    /// @return The puzzle's name.
    function name() external pure returns (string memory);

    /// @notice Generates the puzzle's starting position based on a seed.
    /// @dev The seed is intended to be `msg.sender` of some wrapper function or
    /// call.
    /// @param _seed The seed to use to generate the puzzle.
    /// @return The puzzle's starting position.
    function generate(address _seed) external returns (uint256);

    /// @notice Verifies that a solution is valid for the puzzle.
    /// @dev `_start` is intended to be an output from {IPuzzle-generate}.
    /// @param _start The puzzle's starting position.
    /// @param _solution The solution to the puzzle.
    /// @return Whether the solution is valid.
    function verify(uint256 _start, uint256 _solution) external returns (bool);
}

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

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
    string internal constant TABLE =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" "9+/";

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return "";
        string memory table = TABLE;
        uint256 encodedLength = ((data.length + 2) / 3) << 2;
        string memory result = new string(encodedLength + 0x20);

        assembly {
            mstore(result, encodedLength)
            let tablePtr := add(table, 1)
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))
            let resultPtr := add(result, 0x20)

            for { } lt(dataPtr, endPtr) { } {
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)
                mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0x12, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0xC, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(input, 0x3F)))))
                resultPtr := add(resultPtr, 1)
            }
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(0xF0, 0x3D3D)) }
            case 2 { mstore(sub(resultPtr, 1), shl(0xF8, 0x3D)) }
        }

        return result;
    }
}

Settings
{
  "remappings": [
    "@/contracts/=src/",
    "@/script/=script/",
    "@/test/=test/",
    "colormap-registry/=lib/colormap-registry/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "shields-api-contract/=lib/shields-api-contract/contracts/",
    "shields-api/=lib/shields-api-contract/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 875
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_curta","type":"address"},{"internalType":"uint256","name":"_issueLength","type":"uint256"},{"internalType":"address[]","name":"_authors","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NoTokensAvailable","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curta","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"curtaMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hasClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"issueLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numClaimedByOwner","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"ownerMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e06040523480156200001157600080fd5b5060405162006557380380620065578339810160408190526200003491620002a1565b336040518060400160405280601081526020016f20baba3437b939b434b8102a37b5b2b760811b81525060405180604001604052806004815260200163082aaa8960e31b81525081600090816200008c919062000422565b5060016200009b828262000422565b5050600680546001600160a01b0319166001600160a01b0384169081179091556040519091506000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b03831660805260a08290524260c052805160005b818110156200014d5762000144838281518110620001275762000127620004ee565b60200260200101518260016200013e919062000504565b6200015b565b60010162000105565b50600855506200052c915050565b6001600160a01b038216620001ab5760405162461bcd60e51b81526020600482015260116024820152701253959053125117d49150d25412515395607a1b60448201526064015b60405180910390fd5b6000818152600260205260409020546001600160a01b031615620002035760405162461bcd60e51b815260206004820152600e60248201526d1053149150511657d3525395115160921b6044820152606401620001a2565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b80516001600160a01b03811681146200028657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600060608486031215620002b757600080fd5b620002c2846200026e565b60208581015160408701519295509350906001600160401b0380821115620002e957600080fd5b818701915087601f830112620002fe57600080fd5b8151818111156200031357620003136200028b565b8060051b604051601f19603f830116810181811085821117156200033b576200033b6200028b565b60405291825284820192508381018501918a8311156200035a57600080fd5b938501935b82851015620003835762000373856200026e565b845293850193928501926200035f565b8096505050505050509250925092565b600181811c90821680620003a857607f821691505b602082108103620003c957634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200041d57600081815260208120601f850160051c81016020861015620003f85750805b601f850160051c820191505b81811015620004195782815560010162000404565b5050505b505050565b81516001600160401b038111156200043e576200043e6200028b565b62000456816200044f845462000393565b84620003cf565b602080601f8311600181146200048e5760008415620004755750858301515b600019600386901b1c1916600185901b17855562000419565b600085815260208120601f198616915b82811015620004bf578886015182559484019460019091019084016200049e565b5085821015620004de5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b808201808211156200052657634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c051615fdf620005786000396000818161028d015261067101526000818161035b01526106500152600081816102fd01528181610a0a0152610ff50152615fdf6000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806370a08231116100e3578063a22cb4651161008c578063c87b56dd11610066578063c87b56dd14610390578063e985e9c5146103a3578063f2fde38b146103d157600080fd5b8063a22cb46514610343578063a7f98a3a14610356578063b88d4fde1461037d57600080fd5b80638da5cb5b116100bd5780638da5cb5b1461031f57806395d89b41146103325780639762d8131461033a57600080fd5b806370a08231146102c257806373b2e80e146102d55780637cc6bf98146102f857600080fd5b80631e3bcc8e1161014557806349edb4251161011f57806349edb42514610275578063607ad0d7146102885780636352211e146102af57600080fd5b80631e3bcc8e1461023c57806323b872dd1461024f57806342842e0e1461026257600080fd5b8063081812fc11610176578063081812fc146101cf578063095ea7b31461021057806318160ddd1461022557600080fd5b806301ffc9a71461019257806306fdde03146101ba575b600080fd5b6101a56101a0366004611572565b6103e4565b60405190151581526020015b60405180910390f35b6101c2610481565b6040516101b191906115ba565b6101f86101dd3660046115ed565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101b1565b61022361021e36600461161d565b61050f565b005b61022e60085481565b6040519081526020016101b1565b61022361024a366004611647565b610603565b61022361025d366004611662565b610707565b610223610270366004611662565b6108fa565b610223610283366004611647565b6109ff565b61022e7f000000000000000000000000000000000000000000000000000000000000000081565b6101f86102bd3660046115ed565b610a7a565b61022e6102d0366004611647565b610ad1565b6101a56102e3366004611647565b60096020526000908152604090205460ff1681565b6101f87f000000000000000000000000000000000000000000000000000000000000000081565b6006546101f8906001600160a01b031681565b6101c2610b45565b61022e60075481565b6102236103513660046116ac565b610b52565b61022e7f000000000000000000000000000000000000000000000000000000000000000081565b61022361038b3660046116e3565b610bbe565b6101c261039e3660046115ed565b610cb3565b6101a56103b136600461177e565b600560209081526000928352604080842090915290825290205460ff1681565b6102236103df366004611647565b611118565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316148061044757507f80ac58cd000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b8061047b57507f5b5e139f000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b6000805461048e906117b1565b80601f01602080910402602001604051908101604052809291908181526020018280546104ba906117b1565b80156105075780601f106104dc57610100808354040283529160200191610507565b820191906000526020600020905b8154815290600101906020018083116104ea57829003601f168201915b505050505081565b6000818152600260205260409020546001600160a01b03163381148061055857506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b61059a5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064015b60405180910390fd5b600082815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b6006546001600160a01b0316331461064c5760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610591565b60007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042038161069f5761069f6117eb565b60078054600181019091559190049150810360008190036106ec576040517fc4dc60e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880546001019081905561070184826111ba565b50505050565b6000818152600260205260409020546001600160a01b038481169116146107705760405162461bcd60e51b815260206004820152600a60248201527f57524f4e475f46524f4d000000000000000000000000000000000000000000006044820152606401610591565b6001600160a01b0382166107c65760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610591565b336001600160a01b038416148061080057506001600160a01b038316600090815260056020908152604080832033845290915290205460ff165b8061082157506000818152600460205260409020546001600160a01b031633145b61085e5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b6044820152606401610591565b6001600160a01b03808416600081815260036020908152604080832080546000190190559386168083528483208054600101905585835260028252848320805473ffffffffffffffffffffffffffffffffffffffff199081168317909155600490925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b610905838383610707565b6001600160a01b0382163b15806109ae5750604051630a85bd0160e11b8082523360048301526001600160a01b03858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af115801561097e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a29190611801565b6001600160e01b031916145b6109fa5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610591565b505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a61576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6008805460010190819055610a7682826111ba565b5050565b6000818152600260205260409020546001600160a01b031680610acc5760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b6044820152606401610591565b919050565b60006001600160a01b038216610b295760405162461bcd60e51b815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152606401610591565b506001600160a01b031660009081526003602052604090205490565b6001805461048e906117b1565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b610bc9858585610707565b6001600160a01b0384163b1580610c605750604051630a85bd0160e11b808252906001600160a01b0386169063150b7a0290610c119033908a9089908990899060040161181e565b6020604051808303816000875af1158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611801565b6001600160e01b031916145b610cac5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610591565b5050505050565b6000818152600260205260409020546060906001600160a01b0316610d075760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b6044820152606401610591565b6000827f43757274612e417574686f7273686970546f6b656e0000000000000000000000604051602001610d45929190918252602082015260400190565b60408051601f19818403018152919052805160209091012090507f6351ceff00ffb300ff6b00b5000a007fff78503c323232fe7fff6c28a2ff007a6000600b830660180290508082901c62ffffff16818362ffffff16901b8262ffffff901b6000191862ffffff18841617179150600483901c9250600a8381610dca57610dca6117eb565b06601802905060188183901c62ffffff16901b81601884901c62ffffff16901b8262ffffff901b6000191865ffffff00000018841617179150600483901c925060098381610e1a57610e1a6117eb565b06601802905060308183901c62ffffff16901b81603084901c62ffffff16901b8262ffffff901b6000191868ffffff00000000000018841617179150600483901c925082600716601802905060488183901c62ffffff16901b81604884901c62ffffff16901b8262ffffff901b600019186bffffff00000000000000000018841617179150600383901c9250506110f0610eb3856112ed565b610fc673740cbbf0116a82f64e83e1ae68c92544870b0c0f63b63e7046610edc61012c88611872565b6040805160808101825262ffffff808a16825260188a901c8116602083015260308a901c811692820192909252604889901c9091166060820152610f25607860098b901c611872565b610f34600560118c901c611872565b6040518563ffffffff1660e01b8152600401610f539493929190611886565b600060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f9891908101906118f2565b610fa188611331565b604051602001610fb29291906119bb565b6040516020818303038152906040526113f9565b6040517fe4ff676d000000000000000000000000000000000000000000000000000000008152600481018890527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e4ff676d90602401602060405180830381865afa158015611044573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110689190615bf1565b6110a7576040518060400160405280600581526020017f66616c73650000000000000000000000000000000000000000000000000000008152506110de565b6040518060400160405280600481526020017f74727565000000000000000000000000000000000000000000000000000000008152505b604051602001610fb293929190615c0e565b6040516020016111009190615db1565b60405160208183030381529060405292505050919050565b6006546001600160a01b031633146111615760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610591565b6006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831690811790915560405133907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6001600160a01b0382166112105760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610591565b6000818152600260205260409020546001600160a01b0316156112755760405162461bcd60e51b815260206004820152600e60248201527f414c52454144595f4d494e5445440000000000000000000000000000000000006044820152606401610591565b6001600160a01b0382166000818152600360209081526040808320805460010190558483526002909152808220805473ffffffffffffffffffffffffffffffffffffffff19168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a9004806113075750819003601f19909101908152919050565b6060600061133e836112ed565b9050600a831015611371578060405160200161135a9190615df6565b604051602081830303815290604052915050919050565b606483101561138b578060405160200161135a9190615e3b565b6103e88310156113a6578060405160200161135a9190615e80565b6127108310156113c1578060405160200161135a9190615ec5565b620186a08310156113dd578060405160200161135a9190615f0a565b620f424083101561047b578060405160200161135a9190615f34565b6060815160000361141857505060408051602081019091526000815290565b6000604051806060016040528060408152602001615f93604091399050600060026003855160026114499190615f5d565b6114539190615f7e565b901b90506000611464826020615f5d565b67ffffffffffffffff81111561147c5761147c6118dc565b6040519080825280601f01601f1916602001820160405280156114a6576020820181803683370190505b509050818152600183018586518101602084015b818310156115145760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b938201939093526004016114ba565b60038951066001811461152e576002811461153f5761154b565b613d3d60f01b60011983015261154b565b603d60f81b6000198301525b509398975050505050505050565b6001600160e01b03198116811461156f57600080fd5b50565b60006020828403121561158457600080fd5b813561158f81611559565b9392505050565b60005b838110156115b1578181015183820152602001611599565b50506000910152565b60208152600082518060208401526115d9816040850160208701611596565b601f01601f19169190910160400192915050565b6000602082840312156115ff57600080fd5b5035919050565b80356001600160a01b0381168114610acc57600080fd5b6000806040838503121561163057600080fd5b61163983611606565b946020939093013593505050565b60006020828403121561165957600080fd5b61158f82611606565b60008060006060848603121561167757600080fd5b61168084611606565b925061168e60208501611606565b9150604084013590509250925092565b801515811461156f57600080fd5b600080604083850312156116bf57600080fd5b6116c883611606565b915060208301356116d88161169e565b809150509250929050565b6000806000806000608086880312156116fb57600080fd5b61170486611606565b945061171260208701611606565b935060408601359250606086013567ffffffffffffffff8082111561173657600080fd5b818801915088601f83011261174a57600080fd5b81358181111561175957600080fd5b89602082850101111561176b57600080fd5b9699959850939650602001949392505050565b6000806040838503121561179157600080fd5b61179a83611606565b91506117a860208401611606565b90509250929050565b600181811c908216806117c557607f821691505b6020821081036117e557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561181357600080fd5b815161158f81611559565b60006001600160a01b03808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a0601f19601f85011683010190509695505050505050565b600082611881576118816117eb565b500690565b61ffff858116825260e082019060208084018760005b60048110156118be57815162ffffff168352918301919083019060010161189c565b5050505080851660a084015280841660c08401525095945050505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561190457600080fd5b815167ffffffffffffffff8082111561191c57600080fd5b818401915084601f83011261193057600080fd5b815181811115611942576119426118dc565b604051601f8201601f19908116603f0116810190838211818310171561196a5761196a6118dc565b8160405282815287602084870101111561198357600080fd5b611994836020830160208801611596565b979650505050505050565b600081516119b1818560208601611596565b9290920192915050565b7f3c7376672077696474683d2237353022206865696768743d223735302220786d81527f6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f7376672260208201527f2066696c6c3d226e6f6e65222076696577426f783d223020302037353020373560408201527f30223e3c7374796c653e2e617b66696c7465723a75726c2823632964726f702d60608201527f736861646f7728302030203270782023303037666666293b66696c6c3a23666660808201527f663b77696474683a3470787d2e627b66696c7465723a64726f702d736861646f60a08201527f7728302030202e3570782023303037666666293b66696c6c3a233030303b776960c08201527f6474683a3370787d2e637b6865696768743a313370787d2e647b68656967687460e08201527f3a3670787d2e657b6865696768743a3470787d2e667b6865696768743a3132706101008201527f787d2e677b6865696768743a3570787d2e687b6865696768743a3370787d2e696101208201527f7b77696474683a33323070783b6865696768743a36323070787d2e6a7b63783a6101408201527f33373570783b723a323070787d2e6b7b7374726f6b653a233237333033647d2e6101608201527f6c7b66696c6c3a233030307d2e6e7b66696c6c3a233064313031377d2e6f7b736101808201527f74726f6b652d77696474683a3270787d40666f6e742d666163657b666f6e742d6101a08201527f66616d696c793a2241223b7372633a75726c28646174613a666f6e742f776f666101c08201527f66323b636861727365743d7574662d383b6261736536342c643039474d6741426101e08201527f41414141414246494141384141414141496b77414142447641414541414141416102008201527f41414141414141414141414141414141414141414141414148494659426d41416102208201527f5041694243676d5859684549437039416d33634c516741424e67496b413049456102408201527f494157445441636744494578477a63664532344d505777634449797056386e2b6102608201527f367750625748705976775566444458704a4a4642736c547555476977555253666102808201527f6568582f652b62764e366d77756c4e45796348383772463050534849465136666102a08201527f72687368795377382f39536366392f4d4a49535a454e52435456464a316345726102c08201527f6e6c4b5633664356696a6c32326c42545733583366666b3773463174444f46706102e08201527f7a33756c536c664161624b6d6e463050346f4f326671323953523773666141536103008201527f4b3353565a6375537273537173326261306e6f49414369676b522b30525233616103208201527f755434493973503053564d472f6839743336776f77645063384d422f427247566103408201527f77664a74786641457a6177564e5555395858557975503834563732664a6b31546103608201527f7a6742515a685978433967656b554e687a35683161747a5a44536a2f2f3965736103808201527f3748302f6c5147754754346536586955616c52426f5036764144596b505177316103a08201527f61654c30414c4d6b6e575151464b4d69567273737a4c703163713366355841326103c08201527f4d62785448375a6c6d47397141683552474c5469332f6275583473414f746d596103e08201527f4b44313744797a474e5833632f4a6b6b6f414659466971676f4c6c636f4b774e6104008201527f2b38535a733262514a70792f3033396630495430376d59756d444758334f6b656104208201527f514b6874616c7a414a694846716d44485265706738356a3248744d6859586f496104408201527f516a612b61634d63486b4669575259633634646842484537345269796f4639596104608201527f5579624b476d79674c46504b675145336d5755307164496546477a2b6d7566536104808201527f79493065546f2f65626a645861456d4f4e766248644e444753725562575138676104a08201527f66796f5841446355704a444b77785a54516c6d6a48646c6a676b4939327249416104c08201527f6b48797357642b74697769494435586f72306c546d6a50496e32426c32786c4c6104e08201527f64632f3678414c7967787a6c49484947537035615249566c7a54637978734a616105008201527f4535514c736b4d74704d79374a707675506a556f324d57466977414354326d616105208201527f7065312f57426d326a6676774b624633794f79746e4b722f6b6d44652f6666536105408201527f484f4d6a4f335465677a64416d77775157474b41514b2b63426868304c4637686105608201527f2b644d776b77564f6a366134546649346e7439385674646733765866786675446105808201527f354c485a69534e3732744662567355633352307a657a744c536f6874695330736105a08201527f764d3669552f557633516d666c342f6f7451762f6a6833673441306f4f5763486105c08201527f5263344762744e4a7a6745486d6772753362455150456749693767576e66635a6105e08201527f4b67454f382b5a31584d556f4b744f3039546a70326c554f764a6a524f4550766106008201527f65546855332f74664c306264366a6f387630376c7748567663724c73733742766106208201527f4578544f4c496473565a515851504c4f67425a496341324a31323443466a76596106408201527f3642516151775578444568497a5451426a2f37784e424b676d4332354f31776d6106608201527f7a756b3844484978635270766539696861693644782b6551533247756b3847396106808201527f614e6f5071343668437545517a706e304472454135634e7836796273467963676106a08201527f445449714943543145477247504b5168574767337954767a2b625950524b69736106c08201527f316643336d77526951455569354a6172376c4d735a71495333564f55474567676106e08201527f30756c343750724830376766566d495739543946624e45434b62626670636f6e6107008201527f6b33795661476f2f41687262723950323234616738385a57374c4b41697455656107208201527f3734316b4b51585653424c5a6d436e4d59773054696f70424f795963723057646107408201527f75455334782f35464967734e7655483177655036774e524a7a32624e684a77546107608201527f6765716f5a5a37714d6e766e7255686e624149525841776b6b6a3776496859716107808201527f72563776456f6c4654516b73346875385242576f2f6b375846744d4b4e37482f6107a08201527f5751737a77623837376b6f4f6c464378655364674e4c7378596f344a383879776107c08201527f7742794c4c4c44454573757359495531574757644554746768454765456264736107e08201527f7630756d4276726148657348466833314b394c76774b622b52506f37706a74596108008201527f49434447526c3153367046536e3551477044516b705245706a556c70516b70546108208201527f557071526a6c314337527246796c704969385a324e38667a4f49594b793233366108408201527f74396b4171324131444778436d6d73427831693359732b4764653856627554596108608201527f636b62487078303268315849396a5464793862357430464a336a5632423371736108808201527f4b39394e5759553041754a51434537614e6759377634442b5070506e746c4a336108a08201527f5a507432594139714e6463314235422b515946394e426661774d4777414f52556108c08201527f593473666f724b306330324e69674537454a4d322b356535446266614b5230366108e08201527f6e79474c7734484939746e6267534f4148626a4476547331696e644f2b6732546109008201527f327637494e39427852413339474c68466a4a6a342b547852334c354f50396e706109208201527f38716c627075546c677879664f36643656442f457a4c465a6f595679443472796109408201527f2f4f563471556a425749344e68393078716264673534516f7a313231346c39566109608201527f5a78493832367862635962484763785858634971697a78684e6a656e4b4c41666109808201527f584869483442694435394759726f4336654136654861764a4d584972547362666109a08201527f6b7952457a724632356339323267656552556b7a545a4332554f4b73545666656109c08201527f43656270384a67617465596472474539496159373650706a67633058636363736109e08201527f4a6432387a6b5552746f62462b556b4832706b7039555a37356452337a654f55610a008201527f4335676177464c4f3369533175616b62736e586e722f376c4941634c735a6a4a610a208201527f2b7739394e65496f474d3536506549342b74354857796d72366f39634335774e610a408201527f4733482b6d3832463746352f6f307974794c76433674692b4856577634334564610a608201527f2f69666d473039545858747532622b31466565584f75717a66754a75496c786a610a808201527f5a2f6452622f354b624d56636576427337387a5167434e6f6e4845313242734f610aa08201527f7a654f6b7031474633494c7369394850497735585a4b66437a745455614e5950610ac08201527f4b305368664c51576732536e3563316f7941726f71576777457778704a4e4251610ae08201527f4a68676d517938617a67516a47676c557a41516c4d6e536a5569596f61795251610b008201527f4f524e55794e43444b706d677170464149356c676c417939615451546a476b6b610b208201527f3046676d474364446478725042424d61435453527963524a6e5879355579776d610b408201527f507772364372584e5330575945694c434a672b712f586d596d43726c595a7155610b608201527f5a30353379494d5a2f7677434d3656387a4a4c794d56764b78357a36425a6772610b808201527f4657436556494435556b4768425a324f2b32764b6d79366a653133594d636d6e610ba08201527f746871362b50556b5132634573537743515933634e5041777552576f546f4d6a610bc08201527f44526d4b346a4c3259454246417139696335555771625173796b4a6e6373484f610be08201527f67413664444674712b346951354341586e696c4f63376d637a685a7577355955610c008201527f3577784b4f42573437717161313862796c32432b304a65396b704c6c72305671610c208201527f586c323770792b37632b55466d43394475477863464c6877746b735462487135610c408201527f4c6c57475a754355575a4e534e756235367a3676687a6338646b6c5658502b4b610c608201527f31397a4d703247772b415765706b724b78436b42464e4d6d6e6b68532b6d4969610c808201527f426d627a7862735975332f35754e42373071635a696f474f4c6b347077564431610ca08201527f4b6a55414a3862326f2b667645374958306a502b55766d76432f397a54745456610cc08201527f68586c7258756e4c376c7a77675848304e5435655656644e6c41743055756d44610ce08201527f64545553786751777361374a69484c6264782b4e3959707a6e4b414d4e636376610d008201527f6e323872683834644f6359416a31574a68796644394457646163584273786562610d208201527f6a664b55776d666f51673869526c585a36664579347841613070763047656a5a610d408201527f676939484e6c2b56505a2b683561514a6f714e506a63566f624e41596e566361610d608201527f693964386f6939376c6d59387a356a386c4f4c6c6a5751306649725667566c66610d808201527f57744e6f5274566a3174574a44563752692f5579756762694c5351316c374e4f610da08201527f6278396d367163534a2b7433596c354a33344b786156496d53312b3838455859610dc08201527f3638436a586a5157356a516d756c4c4244436738527651786a76724d71356935610de08201527f707870326b64653078792f6245445a4e7967686a63324f2f5532453856483964610e008201527f58504f4e4d536a65734755686973655769653564647171685166504c536c3346610e208201527f343645577336345642792f43384c6f545858687274504f58505869387245614a610e408201527f767148753562304769793577685565434d3274655932334e78766e7a70386d31610e608201527f4f316255724e3276594f324b2f35344f477a7262757959356448447434336171610e808201527f793755694a336d4e397a506a43704d46624266372f49776f6c4251484e59414d610ea08201527f4d456932654f61783541662b53594b6b354674747934754e5870516853764253610ec08201527f6b2b4e77774b4836567169536e4e6e4a5769654b43447842455a6b4b52354250610ee08201527f7569637555516d6e46745a564d334944664a4868665032627a4b624845364578610f008201527f79566347794464577159786a445777526938413653644950747a423774695173610f208201527f574f7845634363694833586e716c513064464d544834536b524476435a4c7633610f408201527f7636704b7268726948424c5a464d4f432f68794439456c784464755369333542610f608201527f6b372b734669477067706e754e42524742643667534c6d665672626b76397759610f808201527f31306e5079457050554a345a5141325641454655544c534170706e6b702b3537610fa08201527f35483159536e4b4d6550694b616c6b5a314e74724e434e702f4e56754a773446610fc08201527f536c326851563552594b4258464f71514f72482f5432572b7347433343455664610fe08201527f764276695650545a5a756d797254344a4a4f6b3563356c59504c322b565567566110008201527f65632f764f545a6d393556653872314473706e3864436c632b36446f4f332b746110208201527f6334785153544335754a65456b5370493672472b486761563948354b356b464b6110408201527f51666a365644594b6a476e7339596e68536f6848666a726144513137626656496110608201527f495a557738704b4c612b7151454f6d4d736f42676430515569484a4842414f4c6110808201527f4d79717963305a495568674b734d5467304e44455941796753574578383034626110a08201527f3061424b6e7162535a6251613033693733426d736d4d5164477577566d3078546110c08201527f38644167695176375935463658364d39496778446d366a393875312b636264746110e08201527f4d365a4b784e4d4f6e38303361336c7361584776716c367876577069613670516111008201527f50577155636d4a305666324e394a333554322f443041713958392f4c694e6e306111208201527f4a5a4d5963794a4b36484d69475876573639376a5830775545596a74696352326111408201527f4c6f49516c5a362b314932575a524c3071766751632b383034707569482b52686111608201527f5850336e3137386d5576784d78756f59474966566c52677a75653041525972396111808201527f4e4c6347414432566c356a4741524f52336356777655443651364570306946746111a08201527f436a4a4f5a49794b5a4177505978366d62456a5950574758456c48692f5673336111c08201527f715074766b44344836432b7a46313541686730464858547943317144412b66316111e08201527f48376b42426a69474d2b66464d34704a684e654d63483870374b4854664851356112008201527f53426a49556c785447595a303142567171335146476d324b4f6965494e6971536112208201527f4d537238366a6e31757233376744642f53692b367377316e586d44516e6562716112408201527f6e665a4e69536b627733494f75434953534c64726e78496b66647533373973796112608201527f345a4e6d54796368776e5767583965776b4b736f452f707a364e36516b4c32686112808201527f7a74627035666844707438666c564439634d4c2f4f7a6437674e316c443751486112a08201527f3259507449665a516578674c763070514c65355a474c6378687165576c66756f6112c08201527f34414754747a41476d50716b653777422b435077377250357068442f344a702f6112e08201527f682b75302f372f334635354e4f4549656e786a4449354134774b58455238674e6113008201527f79705068376d77307146586962744964443653634d33594677554372414b56466113208201527f4e4a2b3549544e7655562f674f63434a2f6b5158744a5a79305667446f314e656113408201527f49653052592b755a6f44746149496f63614d2b386458464475334c42324175486113608201527f46463367425a7152486855705446652b413653466e4d2b7777535730345a436d6113808201527f374d696964586b2b314556504c6c454c592f6847516b6851596f35524a5330756113a08201527f305144556543776f55567a336435314b755779794e654f3733554930325552576113c08201527f704d4d6d314c78444f754576596f796c6f38576c56366c76384c7a703258414f6113e08201527f6231426b355a4f73656e616d483159445574687550797a505237712f4e6c2b756114008201527f4748766e4d5343454b69785a4b4c7773436c552b415736683955777863357a586114208201527f744767505a4b544e4c7859586d44467250782b55427a363470326f484d6c47386114408201527f514654385a484b42386c704536736835593239484c6350417a586e68305746796114608201527f2b385147487948584d306b4a6a33594362764a75646a755a634e626276726c366114808201527f6335793564626c3955613967557a61556e444832446d5a4a6c756f78424155716114a08201527f4430585270344c54317a4f41775856533953466d61395a334f77344d5a48304a6114c08201527f4e4c77376d4f4f494d666b63642b55657253714b574a706b766e51626f63524e6114e08201527f545476747a4d4c6679664b5362567a6f2b6f66746750394a6f6a43614e4750716115008201527f7464386d396d75306f577962725136326b696441427967697a65616f31366d556115208201527f346d62535a464435737a5878752b3273654e2b664e2f432b4f4e4452337a30516115408201527f53587531786163347a304250693057715854485169533233697952364c59747a6115608201527f4b4f4f2f434e47386332547237742f74416f594d2f5a30737942383052742f2f6115808201527f3462447430784e4f6a57597951637a734d5a545658346c6e69626e612f7539666115a08201527f773438664e674649704e4f794f4372706761654f494c6b4152435635446958656115c08201527f384558505751344f64456349474f3732306b616832534c6a7151764a44492f386115e08201527f517843702b49536b4746653352336e4842626a70745a714f686c724c736c784c6116008201527f42706d5a796b45344b32784978416741712b3147364a2b4b546c6e656a674e616116208201527f4a72576d37536d614e4675504a674f6c466f78744b62534c5741344f614578716116408201527f3139646c42777347506b485272585473304f305139726f753973332b654e695a6116608201527f642b64706a4656704b7a576a5a474f476861436a716b4b67427a3956557a454b6116808201527f5068686a586577537a61686e2f742f705339754855705a557153314d6258364e6116a08201527f434342305833476f2f4f755977507a2f6d6c6a314f38447a365239654162784d6116c08201527f4a6264472b3346666d613131422f785241424434346175454d68484e2b6a657a6116e08201527f73526245785a6b6538736e445178457064462f6b7668525a65334f50707a77596117008201527f3548733845682f7335505748493443624b64695a677732464c685379434767776117208201527f7671456967422b56642b5531663241304548595577686a6455634846334934716117408201527f325a67644e56707870714f4e2b58784962366546444b554873356a4e6b714c4a6117608201527f3158695a456b72764a70566b55736a4554522f465a376e6b36557a717568387a6117808201527f6d55415833753144302f33444b6378354a5435706d7a794a7553785566474a536117a08201527f3467686d4d34344a6c4c6d446d455669466f61617a686367483566455537695a6117c08201527f4c4b746b4869554d6f497a42683576474853796b7335326336384c636f566d716117e08201527f523377653158304c75755773503551524e55726a552b7234664362672b5265476118008201527f3853356b68346b7a474d63304a76587a79513772644b6f5a547970517875524d6118208201527f306b6850584e4a4d72634d71614669746b365143776f6b486e4f484f38504a6d6118408201527f6b56555650766d4b4d7650735a7679366e775261614852344f4b6c4837796d5a6118608201527f397661326349666d715054586930493157556d306e38336f666a4859453342466118808201527f4e32306d4776345367514d414a69616531436f396d31744a376242796e3665326118a08201527f764d5645316d61576377345430617259684f4c79626c3752583646483730574f6118c08201527f725a4d5736644363486336493961745057396d737547632f6270746f703264506118e08201527f4141413d297d3c2f7374796c653e3c646566733e3c72616469616c47726164696119008201527f656e742069643d2262223e3c73746f702073746f702d636f6c6f723d222330306119208201527f37464646222f3e3c73746f70206f66667365743d2231303025222073746f702d6119408201527f6f7061636974793d2230222f3e3c2f72616469616c4772616469656e743e3c666119608201527f696c7465722069643d2263223e3c6665476175737369616e426c7572207374646119808201527f446576696174696f6e3d22382220696e3d22536f7572636547726170686963226119a08201527f20726573756c743d226f66667365742d626c7572222f3e3c6665436f6d706f736119c08201527f697465206f70657261746f723d226f75742220696e3d22536f757263654772616119e08201527f706869632220696e323d226f66667365742d626c75722220726573756c743d22611a008201527f696e7665727365222f3e3c6665466c6f6f6420666c6f6f642d636f6c6f723d22611a208201527f233030374646462220666c6f6f642d6f7061636974793d222e39352220726573611a408201527f756c743d22636f6c6f72222f3e3c6665436f6d706f73697465206f7065726174611a608201527f6f723d22696e2220696e3d22636f6c6f722220696e323d22696e766572736522611a808201527f20726573756c743d22736861646f77222f3e3c6665436f6d706f736974652069611aa08201527f6e3d22736861646f772220696e323d22536f7572636547726170686963222f3e611ac08201527f3c6665436f6d706f73697465206f70657261746f723d2261746f702220696e3d611ae08201527f22736861646f772220696e323d22536f7572636547726170686963222f3e3c2f611b008201527f66696c7465723e3c6d61736b2069643d2261223e3c706174682066696c6c3d22611b208201527f233030302220643d224d302030683735307637353048307a222f3e3c72656374611b408201527f20636c6173733d22692220783d223231352220793d223635222072783d223230611b608201527f222066696c6c3d2223464646222f3e3c636972636c6520636c6173733d226a20611b808201527f6c222063793d223635222f3e3c636972636c6520636c6173733d226a206c2220611ba08201527f63793d22363835222f3e3c2f6d61736b3e3c2f646566733e3c70617468206669611bc08201527f6c6c3d22233130313331432220643d224d302030683735307637353048307a22611be08201527f2f3e3c7265637420636c6173733d2269206e2220783d223231352220793d2236611c008201527f3522206d61736b3d2275726c28236129222072783d223230222f3e3c63697263611c208201527f6c65206d61736b3d2275726c28236129222066696c6c3d2275726c2823622922611c408201527f2063783d22333735222063793d223338312220723d22313830222f3e3c636972611c608201527f636c6520636c6173733d226a206b206e222063793d22313235222f3e3c672074611c808201527f72616e73666f726d3d227472616e736c617465283335392031313029223e3c63611ca08201527f6972636c6520636c6173733d226e222063793d223136222063783d2231362220611cc08201527f723d223136222f3e3c7265637420636c6173733d226120632220783d22382220611ce08201527f793d2237222072783d2232222f3e3c7265637420636c6173733d226220662220611d008201527f783d22382e352220793d22372e35222072783d22312e35222f3e3c7265637420611d208201527f636c6173733d226120652220783d22382220793d223231222072783d2232222f611d408201527f3e3c7265637420636c6173733d226220682220783d22382e352220793d223231611d608201527f2e35222072783d22312e35222f3e3c7265637420636c6173733d226120642220611d808201527f783d2231342220793d2237222072783d2232222f3e3c7265637420636c617373611da08201527f3d226220672220783d2231342e352220793d22372e35222072783d22312e3522611dc08201527f2f3e3c7265637420636c6173733d226120652220783d2231342220793d223134611de08201527f222072783d2232222f3e3c7265637420636c6173733d226220682220783d2231611e008201527f342e352220793d2231342e35222072783d22312e35222f3e3c7265637420636c611e208201527f6173733d226120642220783d2231342220793d223139222072783d2232222f3e611e408201527f3c7265637420636c6173733d226220672220783d2231342e352220793d223139611e608201527f2e35222072783d22312e35222f3e3c7265637420636c6173733d226120632220611e808201527f783d2232302220793d223132222072783d2232222f3e3c7265637420636c6173611ea08201527f733d226220662220783d2232302e352220793d2231322e35222072783d22312e611ec08201527f35222f3e3c7265637420636c6173733d226120652220783d2232302220793d22611ee08201527f37222072783d2232222f3e3c7265637420636c6173733d226220682220783d22611f008201527f32302e352220793d22372e35222072783d22312e35222f3e3c2f673e3c706174611f208201527f6820643d224d3333382e383134203136382e383536632d2e33373320302d2e37611f408201527f31382d2e3036332d312e3033372d2e31393161322e38323920322e3832392030611f608201527f203020312d2e3837382d2e36303620322e38323820322e383238203020302031611f808201527f2d2e3630362d2e38373820322e37363720322e3736372030203020312d2e3139611fa08201527f332d312e303337762d2e33333663302d2e3337322e3036342d2e3732332e3139611fc08201527f322d312e3035332e3133382d2e3331392e33342d2e3631312e3630362d2e3837611fe08201527f3761322e353920322e3539203020302031202e3837382d2e353920322e3538206120008201527f322e353820302030203120312e3033382d2e32303868342e3236632e323435206120208201527f30202e34382e3033322e3730332e3039362e3231322e3035332e3432352e31346120408201527f332e3633382e32372e3232332e3131382e3431352e3235362e3537342e3431366120608201527f2e31362e31362e3330342e3334352e3433312e3535382e3034332e3036342e306120808201527f372e3133332e30382e323038612e3330312e3330312030203020312d2e3031366120a08201527f2e3039352e3334362e3334362030203020312d2e3137352e3235362e34322e346120c08201527f322030203020312d2e33322e3033322e3333332e3333332030203020312d2e326120e08201527f33392d2e31393220332e30313620332e3031362030203020302d2e3330332d2e6121008201527f33393920322e36313420322e3631342030203020302d2e3431352d2e333033206121208201527f312e39333520312e3933352030203020302d2e3436332d2e31393120312e35336121408201527f3620312e3533362030203020302d2e3439352d2e303438632d2e37313220302d6121608201527f312e34322d2e3030362d322e3132322d2e3031362d2e37313320302d312e34326121808201527f352e3030352d322e3133382e3031362d2e32363620302d2e35312e3034322d2e6121a08201527f3733342e3132372d2e3233342e3039362d2e3434322e32342d2e3632332e34336121c08201527f3161312e39383820312e3938382030203020302d2e34332e36323320312e39366121e08201527f3120312e3936312030203020302d2e3134342e3735762e33333561312e3834346122008201527f20312e383434203020302030202e35373420312e33353620312e38343420312e6122208201527f38343420302030203020312e3335362e35373468342e323631632e31372030206122408201527f2e33332d2e3031352e34382d2e30343761322e303220322e30322030203020306122608201527f202e3434362d2e313932632e3134392d2e3037342e3238322d2e3136352e33396122808201527f392d2e3237312e3130362d2e3130372e3230372d2e3232392e3330332d2e33366122a08201527f37612e3433382e343338203020302031202e3235352d2e313434632e3039362d6122c08201527f2e30312e3138372e30312e3237322e303634612e33352e3335203020302031206122e08201527f2e31362e32342e3330362e3330362030203020312d2e3033332e323720322e366123008201527f353320322e3635332030203020312d2e34332e353237632d2e31362e3133392d6123208201527f2e3334362e3236362d2e3535392e3338332d2e3231332e3131372d2e34322e316123408201527f39372d2e3632322e32342d2e3231332e3035332d2e3433362e30382d2e36372e6123608201527f3038682d342e3236325a6d31372e3535332030632d2e37313320302d312e33326123808201527f342d2e3236362d312e3833352d2e37393761322e363920322e363920302030206123a08201527f312d2e3736362d312e393331762d322e36363563302d2e3131372e3033372d2e6123c08201527f3231332e3131322d2e323837612e33372e3337203020302031202e32372d2e316123e08201527f3132632e3131382030202e3231342e3033372e3238382e313132612e33392e336124008201527f39203020302031202e3131322e32383776322e3636346330202e3533332e31386124208201527f2e39392e35343220312e33373361312e373120312e373120302030203020312e6124408201527f3239332e35353968332e383738632e35312030202e3934312d2e31383720312e6124608201527f3239322d2e35353961312e393320312e3933203020302030202e3534332d312e6124808201527f333732762d322e363635612e33392e3339203020302031202e3131312d2e32386124a08201527f372e3338392e333839203020302031202e3238382d2e3131322e33372e3337206124c08201527f3020302031202e3237312e3131322e33392e3339203020302031202e3131322e6124e08201527f32383776322e3636346330202e3735362d2e32353620312e342d2e37363620316125008201527f2e3933322d2e35312e3533312d312e3132382e3739372d312e3835312e3739376125208201527f682d332e3839345a6d32332e3832342d2e373138612e3435362e3435362030206125408201527f302031202e31362e313932632e30312e3034322e3031362e30392e3031362e316125608201527f3433612e34372e34372030203020312d2e3031362e3131322e3335352e3335356125808201527f2030203020312d2e3134332e3230382e3432332e3432332030203020312d2e326125a08201527f342e303633682d2e303438612e3134312e3134312030203020312d2e3036342d6125c08201527f2e303136632d2e303220302d2e3033372d2e3030352d2e3034372d2e303136616125e08201527f3130342e3836203130342e38362030203020312d312e31382d2e3833632d2e336126008201527f37342d2e3236352d2e3734362d2e3533312d312e3131382d2e3739372d2e30316126208201527f3120302d2e3031362d2e3030362d2e3031362d2e3031362d2e303120302d2e306126408201527f31362d2e3030352d2e3031362d2e3031362d2e303120302d2e3031362d2e30306126608201527f352d2e3031362d2e303136682d352e35353376312e333234612e33392e3339206126808201527f30203020312d2e3131322e3238382e3432352e3432352030203020312d2e32386126a08201527f372e3131312e33372e33372030203020312d2e3237322d2e3131312e3338392e6126c08201527f3338392030203020312d2e3131312d2e323838762d342e39343663302d2e30356126e08201527f342e3030352d2e3130372e3031362d2e3136612e3530322e35303220302030206127008201527f31202e3039352d2e3132382e3337342e333734203020302031202e3132382d2e6127208201527f30382e3331362e333136203020302031202e3134342d2e30333168362e3839336127408201527f632e3235362030202e34392e3034382e3730322e3134332e3232342e3038352e6127608201527f34322e3231382e35392e342e3138322e31382e33322e3337372e3431362e35396127808201527f2e3038352e3232332e3132372e3435372e3132372e373032762e3333356330206127a08201527f2e3232332d2e3033322e34332d2e3039352e36323261322e31303720322e31306127c08201527f372030203020312d2e33322e353237632d2e3133382e31382d2e3239322e33316127e08201527f392d2e3436322e3431352d2e31372e3130362d2e3336322e3138362d2e3537356128008201527f2e32346c2e3730322e3531632e3233342e31372e3436392e3334352e3730332e6128208201527f3532365a6d2d382e3238312d342e32323876322e34323568362e343934612e396128408201527f35342e393534203020302030202e342d2e30382e3737362e37373620302030206128608201527f30202e3333342d2e323233632e3130372d2e3130362e3138362d2e3231382e326128808201527f342d2e3333352e3035332d2e3132382e30382d2e3236362e30382d2e343135766128a08201527f2d2e3332612e3935342e3935342030203020302d2e30382d2e33393820312e326128c08201527f333220312e3233322030203020302d2e3232342d2e33353120312e32323820316128e08201527f2e3232382030203020302d2e33352d2e3232342e3935342e39353420302030206129008201527f302d2e342d2e3038682d362e3439345a6d32342e36372d2e373832632e3130366129208201527f2030202e3230322e3033372e3238372e313131612e33372e33372030203020316129408201527f202e3131322e3237322e33392e33392030203020312d2e3131322e3238372e346129608201527f32352e3432352030203020312d2e3238372e313132682d332e363476342e35376129808201527f39612e33372e33372030203020312d2e3131312e3237322e3334382e333438206129a08201527f30203020312d2e3237312e3132372e3339372e3339372030203020312d2e32386129c08201527f382d2e3132372e33372e33372030203020312d2e3131312d2e323732762d342e6129e08201527f353739682d332e363339612e33372e33372030203020312d2e3237312d2e3131612a008201527f312e33392e33392030203020312d2e3131322d2e3238372e33372e3337203020612a208201527f302031202e3131322d2e3237322e33372e3337203020302031202e3237312d2e612a408201527f31313168382e3035385a6d31352e3738322d2e303438632e373233203020312e612a608201527f33342e32363620312e38352e3739382e3531312e3533322e37363720312e3137612a808201527f2e37363720312e39313576322e3638612e33372e33372030203020312d2e3131612aa08201527f322e3237322e3339372e3339372030203020312d2e3238372e3132372e333438612ac08201527f2e3334382030203020312d2e3237322d2e3132372e3334382e33343820302030612ae08201527f20312d2e3132372d2e323732762d312e313936682d372e35333276312e313936612b008201527f612e3334382e3334382030203020312d2e3132382e3237322e3334382e333438612b208201527f2030203020312d2e3237312e3132372e3334382e3334382030203020312d2e32612b408201527f37312d2e3132372e3334382e3334382030203020312d2e3132382d2e32373276612b608201527f2d322e363863302d2e3734352e3235352d312e3338332e3736362d312e393135612b808201527f2e35312d2e35333220312e3132382d2e37393820312e3835312d2e3739386833612ba08201527f2e3839345a6d2d352e36393720332e34313568372e353438762d2e3730326330612bc08201527f2d2e3533322d2e3137362d2e3938342d2e3532372d312e3335372d2e3336322d612be08201527f2e3338332d2e3739322d2e3537342d312e3239322d2e353734483430382e3563612c008201527f2d2e353120302d2e3934322e3139312d312e3239332e35373461312e38373520612c208201527f312e3837352030203020302d2e35343220312e333537762e3730325a4d323937612c408201527f2e383938203230342e3568342e31366c312e3739322d352e31353268392e3430612c608201527f386c312e38323420352e31353268342e3434386c2d382e3730342d32332e3268612c808201527f2d342e3238386c2d382e36342032332e325a6d31302e3632342d31382e343936612ca08201527f20332e353220392e393532682d372e3030386c332e3438382d392e3935325a6d612cc08201527f32322e38312031382e34393668332e383037762d31372e323136682d332e3830612ce08201527f3876392e313834633020332e3130342d312e30323420352e3334342d332e3837612d008201527f3220352e333434732d332e3136382d322e3237322d332e3136382d342e363038612d208201527f762d392e3932682d332e3830387631302e383438633020342e30393620312e36612d408201527f363420362e37383420352e373620362e37383420322e333336203020342e3039612d608201527f362d2e39393220352e3038382d322e37383476322e3336385a6d372e3637382d612d808201527f31372e323136682d322e353676322e37353268322e353676392e393532633020612da08201527f332e35322e37333620342e35313220342e34313620342e35313268322e383136612dc08201527f762d322e393132682d312e333736632d312e36333220302d322e3034382d2e34612de08201527f31362d322e3034382d322e313736762d392e33373668332e343536762d322e37612e008201527f3532682d332e343536762d342e353434682d332e38303876342e3534345a6d31612e208201527f332e3137392d352e393834682d332e3830397632332e3268332e383038762d39612e408201527f2e31353263302d332e31303420312e3038382d352e33343420342d352e333434612e608201527f73332e32363420322e32373220332e32363420342e36303876392e3838386833612e808201527f2e383038762d31302e38313663302d342e3039362d312e3639362d362e373834612ea08201527f2d352e3835362d362e3738342d322e3420302d342e3232342e3939322d352e32612ec08201527f313620322e373834563138312e335a6d31362e38362031342e36323463302d33612ee08201527f2e39363820322e3134342d352e393220342e3534342d352e393220322e342030612f008201527f20342e35343420312e39353220342e35343420352e3932732d322e3134342035612f208201527f2e3838382d342e35343420352e383838632d322e3420302d342e3534342d312e612f408201527f39322d342e3534342d352e3838385a6d342e3534342d392e303234632d342e31612f608201527f393220302d382e343820322e3831362d382e343820392e303234203020362e32612f808201527f303820342e32383820382e39393220382e343820382e39393273382e34382d32612fa08201527f2e37383420382e34382d382e39393263302d362e3230382d342e3238382d392e612fc08201527f3032342d382e34382d392e3032345a6d32302e3035372e3431366131302e3332612fe08201527f2031302e33322030203020302d2e3939322d2e303634632d322e30382e3033326130008201527f2d332e37343420312e3138342d342e36373220332e313034762d332e303732686130208201527f2d332e373434563230342e3568332e383038762d392e30323463302d332e34356130408201527f3620312e3337362d342e34313620332e3737362d342e3431362e3537362030206130608201527f312e3138342e30333220312e3832342e303936762d332e38345a6d31342e36366130808201527f3520342e363732632d2e3730342d332e3435362d332e3737362d352e3038382d6130a08201527f372e3133362d352e3038382d332e37343420302d372e30303820312e3935322d6130c08201527f372e30303820342e393932203020332e31333620322e32373220342e343438206130e08201527f352e31383420352e3032346c322e3539322e35313263312e3639362e333220326131008201527f2e3937362e393620322e39373620322e333638732d312e34373220322e32342d6131208201527f332e34353620322e3234632d322e323420302d332e35322d312e3032342d332e6131408201527f3837322d322e373834682d332e373132632e34313620332e32363420332e32336131608201527f3220352e36363420372e34353620352e36363420332e393034203020372e32396131808201527f362d312e39383420372e3239362d352e35363820302d332e33362d322e3635366131a08201527f2d342e3434382d362e3134342d352e31326c2d322e3433322d2e3438632d312e6131c08201527f3437322d2e3238382d322e3330342d2e3839362d322e3330342d322e303438206131e08201527f302d312e31353220312e3533362d312e38383820332e322d312e38383820312e6132008201527f3932203020332e33362e36303820332e37373620322e31373668332e3538345a6132208201527f6d362e3238342d31302e363838682d332e3830387632332e3268332e383038766132408201527f2d392e31353263302d332e31303420312e3038382d352e33343420342d352e336132608201527f343473332e32363420322e32373220332e32363420342e36303876392e3838386132808201527f68332e383038762d31302e38313663302d342e3039362d312e3639362d362e376132a08201527f38342d352e3835362d362e3738342d322e3420302d342e3232342e3939322d356132c08201527f2e32313620322e373834563138312e335a6d31342e303736203076332e3834686132e08201527f332e383038762d332e3834682d332e3830385a6d3020352e393834563230342e6133008201527f3568332e383038762d31372e323136682d332e3830385a6d31302e37383120386133208201527f2e36303863302d332e39363820312e3935322d352e38383820342e3434382d356133408201527f2e38383820322e363536203020342e32353620322e32373220342e32353620356133608201527f2e383838203020332e3634382d312e3620352e39322d342e32353620352e39326133808201527f2d322e34393620302d342e3434382d312e3935322d342e3434382d352e39325a6133a08201527f6d2d332e3634382d382e363038563231302e3168332e383038762d372e3837326133c08201527f63312e30323420312e36393620322e38313620322e36383820352e313220322e6133e08201527f36383820342e313932203020372e3339322d332e34383820372e3339322d392e6134008201527f30323420302d352e3530342d332e322d382e3939322d372e3339322d382e39396134208201527f322d322e33303420302d342e3039362e3939322d352e313220322e363838762d6134408201527f322e333034682d332e3830385a222066696c6c3d2223463046364643222f3e3c6134608201527f7061746820636c6173733d226b22207374726f6b652d646173686f66667365746134808201527f3d223522207374726f6b652d6461736861727261793d2231302220643d224d326134a08201527f31352035343568333230222f3e3c67207472616e73666f726d3d227472616e736134c08201527f6c617465283233312032333729207363616c6528302e33383429223e000000006134e08201526000615be9615b02615afc615a616134fc86018861199f565b7f3c2f673e3c7465787420666f6e742d66616d696c793d22412220783d2235302581527f2220793d22363035222066696c6c3d22234630463646432220666f6e742d736960208201527f7a653d2234302220646f6d696e616e742d626173656c696e653d2263656e747260408201527f616c2220746578742d616e63686f723d226d6964646c65223e230000000000006060820152607a0190565b8561199f565b7f3c2f746578743e3c7265637420636c6173733d2269206b206f2220783d22323181527f352220793d22363522206d61736b3d2275726c28236129222072783d2232302260208201527f2f3e3c636972636c6520636c6173733d226a206b206f222063793d223635222060408201527f6d61736b3d2275726c28236129222f3e3c636972636c6520636c6173733d226a60608201527f206b206f222063793d2236383522206d61736b3d2275726c28236129222f3e3c60808201527f2f7376673e00000000000000000000000000000000000000000000000000000060a082015260a50190565b949350505050565b600060208284031215615c0357600080fd5b815161158f8161169e565b7f7b226e616d65223a22417574686f727368697020546f6b656e20230000000000815260008451615c4681601b850160208901611596565b7f222c226465736372697074696f6e223a225468697320746f6b656e20616c6c6f601b918401918201527f777320312070757a7a6c6520746f20626520616464656420746f204375727461603b8201527f2e204f6e636520697420686173206265656e20757365642c2069742063616e20605b8201527f6e65766572206265207573656420616761696e2e222c22696d6167655f646174607b8201527f61223a22646174613a696d6167652f7376672b786d6c3b6261736536342c0000609b8201528451615d1b8160b9840160208901611596565b7f222c2261747472696275746573223a5b7b2274726169745f74797065223a225560b992909101918201527f736564222c2276616c7565223a0000000000000000000000000000000000000060d98201528351615d7f8160e6840160208801611596565b61199460e6828401017f7d5d7d0000000000000000000000000000000000000000000000000000000000815260030190565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251615de981601d850160208701611596565b91909101601d0192915050565b7f3030303030300000000000000000000000000000000000000000000000000000815260008251615e2e816006850160208701611596565b9190910160060192915050565b7f3030303030000000000000000000000000000000000000000000000000000000815260008251615e73816005850160208701611596565b9190910160050192915050565b7f3030303000000000000000000000000000000000000000000000000000000000815260008251615eb8816004850160208701611596565b9190910160040192915050565b7f3030300000000000000000000000000000000000000000000000000000000000815260008251615efd816003850160208701611596565b9190910160030192915050565b61030360f41b815260008251615f27816002850160208701611596565b9190910160020192915050565b600360fc1b815260008251615f50816001850160208701611596565b9190910160010192915050565b8082018082111561047b57634e487b7160e01b600052601160045260246000fd5b600082615f8d57615f8d6117eb565b50049056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa164736f6c6343000811000a00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce000000000000000000000000000000000000000000000000000000000003f4800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000afdc1a3ef3992f53c10fc798d242e15e2f0df51a0000000000000000000000008fc68a56f9682312953a1730ae62afd1a99fdc4f0000000000000000000000007ed52863829ab99354f3a0503a622e82acd5f7d3000000000000000000000000b8c2c29ee19d8307cb7255e1cd9cbde883a267d5000000000000000000000000f296178d553c8ec21a2fbd2c5dda8ca9ac905a000000000000000000000000000734d56da60852a03e2aafae8a36ffd8c12b32f10000000000000000000000001e79b045dc29eae9fdc69673c9dcd7c53e5e159d000000000000000000000000230d31eec85f4063a405b0f95bde509c0d0a8b5d000000000000000000000000ea23c259b637f72d80697d4a4d1302df9f64530b000000000000000000000000b0623c91c65621df716ab8afe5f66656b21a9108000000000000000000000000068484f7bd2b7d7c5a698d89e75ddcaf3a92b87900000000000000000000000012518c3c808ef33e496fd42033bd312919cd3fe1000000000000000000000000b578405df1f9d4dfdd46a0bd152d518d4c5fe0ac000000000000000000000000b3fd340cb00f7d1b27556e7231a93ca6ffa0bd57000000000000000000000000174787a207bf4ed4d8db0945602e49f42c146474000000000000000000000000fee555e9367b83fb0952a945539faae54f0560a4000000000000000000000000e7aa7af667016837733f3ca3809bde04697730ef000000000000000000000000d84365dad6e6db6fa2d431992acb1e050789be6900000000000000000000000046622e91f95f274f4f76460b38d1f5e00905f767000000000000000000000000152ac2bc1821c5c9eca56d1f35d8b0d8b61187f5000000000000000000000000b958d9fa0417e116f446d0a06d0326805ad73bd5000000000000000000000000bad58e133138549936d2576ebc33251be841d3e90000000000000000000000006dacb7352b4ec1e2b979a05e3cf1f126ad641110000000000000000000000000040dbc0811377dcfa98ffabbe7f6b2f411986931000000000000000000000000d6da3f2b9ec0e51ba0c6aee4080a8179246962ed

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061018d5760003560e01c806370a08231116100e3578063a22cb4651161008c578063c87b56dd11610066578063c87b56dd14610390578063e985e9c5146103a3578063f2fde38b146103d157600080fd5b8063a22cb46514610343578063a7f98a3a14610356578063b88d4fde1461037d57600080fd5b80638da5cb5b116100bd5780638da5cb5b1461031f57806395d89b41146103325780639762d8131461033a57600080fd5b806370a08231146102c257806373b2e80e146102d55780637cc6bf98146102f857600080fd5b80631e3bcc8e1161014557806349edb4251161011f57806349edb42514610275578063607ad0d7146102885780636352211e146102af57600080fd5b80631e3bcc8e1461023c57806323b872dd1461024f57806342842e0e1461026257600080fd5b8063081812fc11610176578063081812fc146101cf578063095ea7b31461021057806318160ddd1461022557600080fd5b806301ffc9a71461019257806306fdde03146101ba575b600080fd5b6101a56101a0366004611572565b6103e4565b60405190151581526020015b60405180910390f35b6101c2610481565b6040516101b191906115ba565b6101f86101dd3660046115ed565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101b1565b61022361021e36600461161d565b61050f565b005b61022e60085481565b6040519081526020016101b1565b61022361024a366004611647565b610603565b61022361025d366004611662565b610707565b610223610270366004611662565b6108fa565b610223610283366004611647565b6109ff565b61022e7f0000000000000000000000000000000000000000000000000000000063ffd43f81565b6101f86102bd3660046115ed565b610a7a565b61022e6102d0366004611647565b610ad1565b6101a56102e3366004611647565b60096020526000908152604090205460ff1681565b6101f87f00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce81565b6006546101f8906001600160a01b031681565b6101c2610b45565b61022e60075481565b6102236103513660046116ac565b610b52565b61022e7f000000000000000000000000000000000000000000000000000000000003f48081565b61022361038b3660046116e3565b610bbe565b6101c261039e3660046115ed565b610cb3565b6101a56103b136600461177e565b600560209081526000928352604080842090915290825290205460ff1681565b6102236103df366004611647565b611118565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000006001600160e01b03198316148061044757507f80ac58cd000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b8061047b57507f5b5e139f000000000000000000000000000000000000000000000000000000006001600160e01b03198316145b92915050565b6000805461048e906117b1565b80601f01602080910402602001604051908101604052809291908181526020018280546104ba906117b1565b80156105075780601f106104dc57610100808354040283529160200191610507565b820191906000526020600020905b8154815290600101906020018083116104ea57829003601f168201915b505050505081565b6000818152600260205260409020546001600160a01b03163381148061055857506001600160a01b038116600090815260056020908152604080832033845290915290205460ff165b61059a5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b60448201526064015b60405180910390fd5b600082815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0387811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b6006546001600160a01b0316331461064c5760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610591565b60007f000000000000000000000000000000000000000000000000000000000003f4807f0000000000000000000000000000000000000000000000000000000063ffd43f42038161069f5761069f6117eb565b60078054600181019091559190049150810360008190036106ec576040517fc4dc60e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600880546001019081905561070184826111ba565b50505050565b6000818152600260205260409020546001600160a01b038481169116146107705760405162461bcd60e51b815260206004820152600a60248201527f57524f4e475f46524f4d000000000000000000000000000000000000000000006044820152606401610591565b6001600160a01b0382166107c65760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610591565b336001600160a01b038416148061080057506001600160a01b038316600090815260056020908152604080832033845290915290205460ff165b8061082157506000818152600460205260409020546001600160a01b031633145b61085e5760405162461bcd60e51b815260206004820152600e60248201526d1393d517d055551213d49256915160921b6044820152606401610591565b6001600160a01b03808416600081815260036020908152604080832080546000190190559386168083528483208054600101905585835260028252848320805473ffffffffffffffffffffffffffffffffffffffff199081168317909155600490925284832080549092169091559251849392917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b610905838383610707565b6001600160a01b0382163b15806109ae5750604051630a85bd0160e11b8082523360048301526001600160a01b03858116602484015260448301849052608060648401526000608484015290919084169063150b7a029060a4016020604051808303816000875af115801561097e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a29190611801565b6001600160e01b031916145b6109fa5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610591565b505050565b336001600160a01b037f00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce1614610a61576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6008805460010190819055610a7682826111ba565b5050565b6000818152600260205260409020546001600160a01b031680610acc5760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b6044820152606401610591565b919050565b60006001600160a01b038216610b295760405162461bcd60e51b815260206004820152600c60248201527f5a45524f5f4144445245535300000000000000000000000000000000000000006044820152606401610591565b506001600160a01b031660009081526003602052604090205490565b6001805461048e906117b1565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b610bc9858585610707565b6001600160a01b0384163b1580610c605750604051630a85bd0160e11b808252906001600160a01b0386169063150b7a0290610c119033908a9089908990899060040161181e565b6020604051808303816000875af1158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190611801565b6001600160e01b031916145b610cac5760405162461bcd60e51b815260206004820152601060248201527f554e534146455f524543495049454e54000000000000000000000000000000006044820152606401610591565b5050505050565b6000818152600260205260409020546060906001600160a01b0316610d075760405162461bcd60e51b815260206004820152600a6024820152691393d517d3525395115160b21b6044820152606401610591565b6000827f43757274612e417574686f7273686970546f6b656e0000000000000000000000604051602001610d45929190918252602082015260400190565b60408051601f19818403018152919052805160209091012090507f6351ceff00ffb300ff6b00b5000a007fff78503c323232fe7fff6c28a2ff007a6000600b830660180290508082901c62ffffff16818362ffffff16901b8262ffffff901b6000191862ffffff18841617179150600483901c9250600a8381610dca57610dca6117eb565b06601802905060188183901c62ffffff16901b81601884901c62ffffff16901b8262ffffff901b6000191865ffffff00000018841617179150600483901c925060098381610e1a57610e1a6117eb565b06601802905060308183901c62ffffff16901b81603084901c62ffffff16901b8262ffffff901b6000191868ffffff00000000000018841617179150600483901c925082600716601802905060488183901c62ffffff16901b81604884901c62ffffff16901b8262ffffff901b600019186bffffff00000000000000000018841617179150600383901c9250506110f0610eb3856112ed565b610fc673740cbbf0116a82f64e83e1ae68c92544870b0c0f63b63e7046610edc61012c88611872565b6040805160808101825262ffffff808a16825260188a901c8116602083015260308a901c811692820192909252604889901c9091166060820152610f25607860098b901c611872565b610f34600560118c901c611872565b6040518563ffffffff1660e01b8152600401610f539493929190611886565b600060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f9891908101906118f2565b610fa188611331565b604051602001610fb29291906119bb565b6040516020818303038152906040526113f9565b6040517fe4ff676d000000000000000000000000000000000000000000000000000000008152600481018890527f00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce6001600160a01b03169063e4ff676d90602401602060405180830381865afa158015611044573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110689190615bf1565b6110a7576040518060400160405280600581526020017f66616c73650000000000000000000000000000000000000000000000000000008152506110de565b6040518060400160405280600481526020017f74727565000000000000000000000000000000000000000000000000000000008152505b604051602001610fb293929190615c0e565b6040516020016111009190615db1565b60405160208183030381529060405292505050919050565b6006546001600160a01b031633146111615760405162461bcd60e51b815260206004820152600c60248201526b15539055551213d49256915160a21b6044820152606401610591565b6006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831690811790915560405133907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6001600160a01b0382166112105760405162461bcd60e51b815260206004820152601160248201527f494e56414c49445f524543495049454e540000000000000000000000000000006044820152606401610591565b6000818152600260205260409020546001600160a01b0316156112755760405162461bcd60e51b815260206004820152600e60248201527f414c52454144595f4d494e5445440000000000000000000000000000000000006044820152606401610591565b6001600160a01b0382166000818152600360209081526040808320805460010190558483526002909152808220805473ffffffffffffffffffffffffffffffffffffffff19168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a9004806113075750819003601f19909101908152919050565b6060600061133e836112ed565b9050600a831015611371578060405160200161135a9190615df6565b604051602081830303815290604052915050919050565b606483101561138b578060405160200161135a9190615e3b565b6103e88310156113a6578060405160200161135a9190615e80565b6127108310156113c1578060405160200161135a9190615ec5565b620186a08310156113dd578060405160200161135a9190615f0a565b620f424083101561047b578060405160200161135a9190615f34565b6060815160000361141857505060408051602081019091526000815290565b6000604051806060016040528060408152602001615f93604091399050600060026003855160026114499190615f5d565b6114539190615f7e565b901b90506000611464826020615f5d565b67ffffffffffffffff81111561147c5761147c6118dc565b6040519080825280601f01601f1916602001820160405280156114a6576020820181803683370190505b509050818152600183018586518101602084015b818310156115145760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b938201939093526004016114ba565b60038951066001811461152e576002811461153f5761154b565b613d3d60f01b60011983015261154b565b603d60f81b6000198301525b509398975050505050505050565b6001600160e01b03198116811461156f57600080fd5b50565b60006020828403121561158457600080fd5b813561158f81611559565b9392505050565b60005b838110156115b1578181015183820152602001611599565b50506000910152565b60208152600082518060208401526115d9816040850160208701611596565b601f01601f19169190910160400192915050565b6000602082840312156115ff57600080fd5b5035919050565b80356001600160a01b0381168114610acc57600080fd5b6000806040838503121561163057600080fd5b61163983611606565b946020939093013593505050565b60006020828403121561165957600080fd5b61158f82611606565b60008060006060848603121561167757600080fd5b61168084611606565b925061168e60208501611606565b9150604084013590509250925092565b801515811461156f57600080fd5b600080604083850312156116bf57600080fd5b6116c883611606565b915060208301356116d88161169e565b809150509250929050565b6000806000806000608086880312156116fb57600080fd5b61170486611606565b945061171260208701611606565b935060408601359250606086013567ffffffffffffffff8082111561173657600080fd5b818801915088601f83011261174a57600080fd5b81358181111561175957600080fd5b89602082850101111561176b57600080fd5b9699959850939650602001949392505050565b6000806040838503121561179157600080fd5b61179a83611606565b91506117a860208401611606565b90509250929050565b600181811c908216806117c557607f821691505b6020821081036117e557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601260045260246000fd5b60006020828403121561181357600080fd5b815161158f81611559565b60006001600160a01b03808816835280871660208401525084604083015260806060830152826080830152828460a0840137600060a0848401015260a0601f19601f85011683010190509695505050505050565b600082611881576118816117eb565b500690565b61ffff858116825260e082019060208084018760005b60048110156118be57815162ffffff168352918301919083019060010161189c565b5050505080851660a084015280841660c08401525095945050505050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561190457600080fd5b815167ffffffffffffffff8082111561191c57600080fd5b818401915084601f83011261193057600080fd5b815181811115611942576119426118dc565b604051601f8201601f19908116603f0116810190838211818310171561196a5761196a6118dc565b8160405282815287602084870101111561198357600080fd5b611994836020830160208801611596565b979650505050505050565b600081516119b1818560208601611596565b9290920192915050565b7f3c7376672077696474683d2237353022206865696768743d223735302220786d81527f6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f7376672260208201527f2066696c6c3d226e6f6e65222076696577426f783d223020302037353020373560408201527f30223e3c7374796c653e2e617b66696c7465723a75726c2823632964726f702d60608201527f736861646f7728302030203270782023303037666666293b66696c6c3a23666660808201527f663b77696474683a3470787d2e627b66696c7465723a64726f702d736861646f60a08201527f7728302030202e3570782023303037666666293b66696c6c3a233030303b776960c08201527f6474683a3370787d2e637b6865696768743a313370787d2e647b68656967687460e08201527f3a3670787d2e657b6865696768743a3470787d2e667b6865696768743a3132706101008201527f787d2e677b6865696768743a3570787d2e687b6865696768743a3370787d2e696101208201527f7b77696474683a33323070783b6865696768743a36323070787d2e6a7b63783a6101408201527f33373570783b723a323070787d2e6b7b7374726f6b653a233237333033647d2e6101608201527f6c7b66696c6c3a233030307d2e6e7b66696c6c3a233064313031377d2e6f7b736101808201527f74726f6b652d77696474683a3270787d40666f6e742d666163657b666f6e742d6101a08201527f66616d696c793a2241223b7372633a75726c28646174613a666f6e742f776f666101c08201527f66323b636861727365743d7574662d383b6261736536342c643039474d6741426101e08201527f41414141414246494141384141414141496b77414142447641414541414141416102008201527f41414141414141414141414141414141414141414141414148494659426d41416102208201527f5041694243676d5859684549437039416d33634c516741424e67496b413049456102408201527f494157445441636744494578477a63664532344d505777634449797056386e2b6102608201527f367750625748705976775566444458704a4a4642736c547555476977555253666102808201527f6568582f652b62764e366d77756c4e45796348383772463050534849465136666102a08201527f72687368795377382f39536366392f4d4a49535a454e52435456464a316345726102c08201527f6e6c4b5633664356696a6c32326c42545733583366666b3773463174444f46706102e08201527f7a33756c536c664161624b6d6e463050346f4f326671323953523773666141536103008201527f4b3353565a6375537273537173326261306e6f49414369676b522b30525233616103208201527f755434493973503053564d472f6839743336776f77645063384d422f427247566103408201527f77664a74786641457a6177564e5555395858557975503834563732664a6b31546103608201527f7a6742515a685978433967656b554e687a35683161747a5a44536a2f2f3965736103808201527f3748302f6c5147754754346536586955616c52426f5036764144596b505177316103a08201527f61654c30414c4d6b6e575151464b4d69567273737a4c703163713366355841326103c08201527f4d62785448375a6c6d47397141683552474c5469332f6275583473414f746d596103e08201527f4b44313744797a474e5833632f4a6b6b6f414659466971676f4c6c636f4b774e6104008201527f2b38535a733262514a70792f3033396630495430376d59756d444758334f6b656104208201527f514b6874616c7a414a694846716d44485265706738356a3248744d6859586f496104408201527f516a612b61634d63486b4669575259633634646842484537345269796f4639596104608201527f5579624b476d79674c46504b675145336d5755307164496546477a2b6d7566536104808201527f79493065546f2f65626a645861456d4f4e766248644e444753725562575138676104a08201527f66796f5841446355704a444b77785a54516c6d6a48646c6a676b4939327249416104c08201527f6b48797357642b74697769494435586f72306c546d6a50496e32426c32786c4c6104e08201527f64632f3678414c7967787a6c49484947537035615249566c7a54637978734a616105008201527f4535514c736b4d74704d79374a707675506a556f324d57466977414354326d616105208201527f7065312f57426d326a6676774b624633794f79746e4b722f6b6d44652f6666536105408201527f484f4d6a4f335465677a64416d77775157474b41514b2b63426868304c4637686105608201527f2b644d776b77564f6a366134546649346e7439385674646733765866786675446105808201527f354c485a69534e3732744662567355633352307a657a744c536f6874695330736105a08201527f764d3669552f557633516d666c342f6f7451762f6a6833673441306f4f5763486105c08201527f5263344762744e4a7a6745486d6772753362455150456749693767576e66635a6105e08201527f4b67454f382b5a31584d556f4b744f3039546a70326c554f764a6a524f4550766106008201527f65546855332f74664c306264366a6f387630376c7748567663724c73733742766106208201527f4578544f4c496473565a515851504c4f67425a496341324a31323443466a76596106408201527f3642516151775578444568497a5451426a2f37784e424b676d4332354f31776d6106608201527f7a756b3844484978635270766539696861693644782b6551533247756b3847396106808201527f614e6f5071343668437545517a706e304472454135634e7836796273467963676106a08201527f445449714943543145477247504b5168574767337954767a2b625950524b69736106c08201527f316643336d77526951455569354a6172376c4d735a71495333564f55474567676106e08201527f30756c343750724830376766566d495739543946624e45434b62626670636f6e6107008201527f6b33795661476f2f41687262723950323234616738385a57374c4b41697455656107208201527f3734316b4b51585653424c5a6d436e4d59773054696f70424f795963723057646107408201527f75455334782f35464967734e7655483177655036774e524a7a32624e684a77546107608201527f6765716f5a5a37714d6e766e7255686e624149525841776b6b6a3776496859716107808201527f72563776456f6c4654516b73346875385242576f2f6b375846744d4b4e37482f6107a08201527f5751737a77623837376b6f4f6c464378655364674e4c7378596f344a383879776107c08201527f7742794c4c4c44454573757359495531574757644554746768454765456264736107e08201527f7630756d4276726148657348466833314b394c76774b622b52506f37706a74596108008201527f49434447526c3153367046536e3551477044516b705245706a556c70516b70546108208201527f557071526a6c314337527246796c704969385a324e38667a4f49594b793233366108408201527f74396b4171324131444778436d6d73427831693359732b4764653856627554596108608201527f636b62487078303268315849396a5464793862357430464a336a5632423371736108808201527f4b39394e5759553041754a51434537614e6759377634442b5070506e746c4a336108a08201527f5a507432594139714e6463314235422b515946394e426661774d4777414f52556108c08201527f593473666f724b306330324e69674537454a4d322b356535446266614b5230366108e08201527f6e79474c7734484939746e6267534f4148626a4476547331696e644f2b6732546109008201527f327637494e39427852413339474c68466a4a6a342b547852334c354f50396e706109208201527f38716c627075546c677879664f36643656442f457a4c465a6f595679443472796109408201527f2f4f563471556a425749344e68393078716264673534516f7a313231346c39566109608201527f5a78493832367862635962484763785858634971697a78684e6a656e4b4c41666109808201527f584869483442694435394759726f4336654136654861764a4d584972547362666109a08201527f6b7952457a724632356339323267656552556b7a545a4332554f4b73545666656109c08201527f43656270384a67617465596472474539496159373650706a67633058636363736109e08201527f4a6432387a6b5552746f62462b556b4832706b7039555a37356452337a654f55610a008201527f4335676177464c4f3369533175616b62736e586e722f376c4941634c735a6a4a610a208201527f2b7739394e65496f474d3536506549342b74354857796d72366f39634335774e610a408201527f4733482b6d3832463746352f6f307974794c76433674692b4856577634334564610a608201527f2f69666d473039545858747532622b31466565584f75717a66754a75496c786a610a808201527f5a2f6452622f354b624d56636576427337387a5167434e6f6e4845313242734f610aa08201527f7a654f6b7031474633494c7369394850497735585a4b66437a745455614e5950610ac08201527f4b305368664c51576732536e3563316f7941726f71576777457778704a4e4251610ae08201527f4a68676d517938617a67516a47676c557a41516c4d6e536a5569596f61795251610b008201527f4f524e55794e43444b706d677170464149356c676c417939615451546a476b6b610b208201527f3046676d474364446478725042424d61435453527963524a6e5879355579776d610b408201527f507772364372584e5330575945694c434a672b712f586d596d43726c595a7155610b608201527f5a30353379494d5a2f7677434d3656387a4a4c794d56764b78357a36425a6772610b808201527f4657436556494435556b4768425a324f2b32764b6d79366a653133594d636d6e610ba08201527f746871362b50556b5132634573537743515933634e5041777552576f546f4d6a610bc08201527f44526d4b346a4c3259454246417139696335555771625173796b4a6e6373484f610be08201527f67413664444674712b346951354341586e696c4f63376d637a685a7577355955610c008201527f3577784b4f42573437717161313862796c32432b304a65396b704c6c72305671610c208201527f586c323770792b37632b55466d43394475477863464c6877746b735462487135610c408201527f4c6c57475a754355575a4e534e756235367a3676687a6338646b6c5658502b4b610c608201527f31397a4d703247772b415765706b724b78436b42464e4d6d6e6b68532b6d4969610c808201527f426d627a7862735975332f35754e42373071635a696f474f4c6b347077564431610ca08201527f4b6a55414a3862326f2b667645374958306a502b55766d76432f397a54745456610cc08201527f68586c7258756e4c376c7a77675848304e5435655656644e6c41743055756d44610ce08201527f64545553786751777361374a69484c6264782b4e3959707a6e4b414d4e636376610d008201527f6e323872683834644f6359416a31574a68796644394457646163584273786562610d208201527f6a664b55776d666f51673869526c585a36664579347841613070763047656a5a610d408201527f676939484e6c2b56505a2b683561514a6f714e506a63566f624e41596e566361610d608201527f693964386f6939376c6d59387a356a386c4f4c6c6a5751306649725667566c66610d808201527f57744e6f5274566a3174574a44563752692f5579756762694c5351316c374e4f610da08201527f6278396d367163534a2b7433596c354a33344b786156496d53312b3838455859610dc08201527f3638436a586a5157356a516d756c4c4244436738527651786a76724d71356935610de08201527f707870326b64653078792f6245445a4e7967686a63324f2f5532453856483964610e008201527f58504f4e4d536a65734755686973655769653564647171685166504c536c3346610e208201527f343645577336345642792f43384c6f545858687274504f58505869387245614a610e408201527f767148753562304769793577685565434d3274655932334e78766e7a70386d31610e608201527f4f316255724e3276594f324b2f35344f477a7262757959356448447434336171610e808201527f793755694a336d4e397a506a43704d46624266372f49776f6c4251484e59414d610ea08201527f4d456932654f61783541662b53594b6b354674747934754e5870516853764253610ec08201527f6b2b4e77774b4836567169536e4e6e4a5769654b43447842455a6b4b52354250610ee08201527f7569637555516d6e46745a564d334944664a4868665032627a4b624845364578610f008201527f79566347794464577159786a445777526938413653644950747a423774695173610f208201527f574f7845634363694833586e716c513064464d544834536b524476435a4c7633610f408201527f7636704b7268726948424c5a464d4f432f68794439456c784464755369333542610f608201527f6b372b734669477067706e754e42524742643667534c6d665672626b76397759610f808201527f31306e5079457050554a345a5141325641454655544c534170706e6b702b3537610fa08201527f35483159536e4b4d6550694b616c6b5a314e74724e434e702f4e56754a773446610fc08201527f536c326851563552594b4258464f71514f72482f5432572b7347433343455664610fe08201527f764276695650545a5a756d797254344a4a4f6b3563356c59504c322b565567566110008201527f65632f764f545a6d393556653872314473706e3864436c632b36446f4f332b746110208201527f6334785153544335754a65456b5370493672472b486761563948354b356b464b6110408201527f51666a365644594b6a476e7339596e68536f6848666a726144513137626656496110608201527f495a557738704b4c612b7151454f6d4d736f42676430515569484a4842414f4c6110808201527f4d79717963305a495568674b734d5467304e44455941796753574578383034626110a08201527f3061424b6e7162535a6251613033693733426d736d4d5164477577566d3078546110c08201527f38644167695176375935463658364d39496778446d366a393875312b636264746110e08201527f4d365a4b784e4d4f6e38303361336c7361584776716c367876577069613670516111008201527f50577155636d4a305666324e394a333554322f443041713958392f4c694e6e306111208201527f4a5a4d5963794a4b36484d69475876573639376a5830775545596a74696352326111408201527f4c6f49516c5a362b314932575a524c3071766751632b383034707569482b52686111608201527f5850336e3137386d5576784d78756f59474966566c52677a75653041525972396111808201527f4e4c6347414432566c356a4741524f52336356777655443651364570306946746111a08201527f436a4a4f5a49794b5a4177505978366d62456a5950574758456c48692f5673336111c08201527f715074766b44344836432b7a46313541686730464858547943317144412b66316111e08201527f48376b42426a69474d2b66464d34704a684e654d63483870374b4854664851356112008201527f53426a49556c785447595a303142567171335146476d324b4f6965494e6971536112208201527f4d537238366a6e31757233376744642f53692b367377316e586d44516e6562716112408201527f6e665a4e69536b627733494f75434953534c64726e78496b66647533373973796112608201527f345a4e6d54796368776e5767583965776b4b736f452f707a364e36516b4c32686112808201527f7a74627035666844707438666c564439634d4c2f4f7a6437674e316c443751486112a08201527f3259507449665a516578674c763070514c65355a474c6378687165576c66756f6112c08201527f34414754747a41476d50716b653777422b435077377250357068442f344a702f6112e08201527f682b75302f372f334635354e4f4549656e786a4449354134774b58455238674e6113008201527f79705068376d77307146586962744964443653634d33594677554372414b56466113208201527f4e4a2b3549544e7655562f674f63434a2f6b5158744a5a79305667446f314e656113408201527f49653052592b755a6f44746149496f63614d2b386458464475334c42324175486113608201527f46463367425a7152486855705446652b413653466e4d2b7777535730345a436d6113808201527f374d696964586b2b314556504c6c454c592f6847516b6851596f35524a5330756113a08201527f305144556543776f55567a336435314b755779794e654f3733554930325552576113c08201527f704d4d6d314c78444f754576596f796c6f38576c56366c76384c7a703258414f6113e08201527f6231426b355a4f73656e616d483159445574687550797a505237712f4e6c2b756114008201527f4748766e4d5343454b69785a4b4c7773436c552b415736683955777863357a586114208201527f744767505a4b544e4c7859586d44467250782b55427a363470326f484d6c47386114408201527f514654385a484b42386c704536736835593239484c6350417a586e68305746796114608201527f2b385147487948584d306b4a6a33594362764a75646a755a634e626276726c366114808201527f6335793564626c3955613967557a61556e444832446d5a4a6c756f78424155716114a08201527f4430585270344c54317a4f41775856533953466d61395a334f77344d5a48304a6114c08201527f4e4c77376d4f4f494d666b63642b55657253714b574a706b766e51626f63524e6114e08201527f545476747a4d4c6679664b5362567a6f2b6f66746750394a6f6a43614e4750716115008201527f7464386d396d75306f577962725136326b696441427967697a65616f31366d556115208201527f346d62535a464435737a5878752b3273654e2b664e2f432b4f4e4452337a30516115408201527f53587531786163347a304250693057715854485169533233697952364c59747a6115608201527f4b4f4f2f434e47386332547237742f74416f594d2f5a30737942383052742f2f6115808201527f3462447430784e4f6a57597951637a734d5a545658346c6e69626e612f7539666115a08201527f773438664e674649704e4f794f4372706761654f494c6b4152435635446958656115c08201527f384558505751344f64456349474f3732306b616832534c6a7151764a44492f386115e08201527f517843702b49536b4746653352336e4842626a70745a714f686c724c736c784c6116008201527f42706d5a796b45344b32784978416741712b3147364a2b4b546c6e656a674e616116208201527f4a72576d37536d614e4675504a674f6c466f78744b62534c5741344f614578716116408201527f3139646c42777347506b485272585473304f305139726f753973332b654e695a6116608201527f642b64706a4656704b7a576a5a474f476861436a716b4b67427a3956557a454b6116808201527f5068686a586577537a61686e2f742f705339754855705a557153314d6258364e6116a08201527f434342305833476f2f4f755977507a2f6d6c6a314f38447a365239654162784d6116c08201527f4a6264472b3346666d613131422f785241424434346175454d68484e2b6a657a6116e08201527f73526245785a6b6538736e445178457064462f6b7668525a65334f50707a77596117008201527f3548733845682f7335505748493443624b64695a677732464c685379434767776117208201527f7671456967422b56642b5531663241304548595577686a6455634846334934716117408201527f325a67644e56707870714f4e2b58784962366546444b554873356a4e6b714c4a6117608201527f3158695a456b72764a70566b55736a4554522f465a376e6b36557a717568387a6117808201527f6d55415833753144302f33444b6378354a5435706d7a794a7553785566474a536117a08201527f3467686d4d34344a6c4c6d446d455669466f61617a686367483566455537695a6117c08201527f4c4b746b4869554d6f497a42683576474853796b7335326336384c636f566d716117e08201527f523377653158304c75755773503551524e55726a552b7234664362672b5265476118008201527f3853356b68346b7a474d63304a76587a79513772644b6f5a547970517875524d6118208201527f306b6850584e4a4d72634d71614669746b365143776f6b486e4f484f38504a6d6118408201527f6b56555650766d4b4d7650735a7679366e775261614852344f4b6c4837796d5a6118608201527f397661326349666d715054586930493157556d306e38336f666a4859453342466118808201527f4e32306d4776345367514d414a69616531436f396d31744a376242796e3665326118a08201527f764d5645316d61576377345430617259684f4c79626c3752583646483730574f6118c08201527f725a4d5736644363486336493961745057396d737547632f6270746f703264506118e08201527f4141413d297d3c2f7374796c653e3c646566733e3c72616469616c47726164696119008201527f656e742069643d2262223e3c73746f702073746f702d636f6c6f723d222330306119208201527f37464646222f3e3c73746f70206f66667365743d2231303025222073746f702d6119408201527f6f7061636974793d2230222f3e3c2f72616469616c4772616469656e743e3c666119608201527f696c7465722069643d2263223e3c6665476175737369616e426c7572207374646119808201527f446576696174696f6e3d22382220696e3d22536f7572636547726170686963226119a08201527f20726573756c743d226f66667365742d626c7572222f3e3c6665436f6d706f736119c08201527f697465206f70657261746f723d226f75742220696e3d22536f757263654772616119e08201527f706869632220696e323d226f66667365742d626c75722220726573756c743d22611a008201527f696e7665727365222f3e3c6665466c6f6f6420666c6f6f642d636f6c6f723d22611a208201527f233030374646462220666c6f6f642d6f7061636974793d222e39352220726573611a408201527f756c743d22636f6c6f72222f3e3c6665436f6d706f73697465206f7065726174611a608201527f6f723d22696e2220696e3d22636f6c6f722220696e323d22696e766572736522611a808201527f20726573756c743d22736861646f77222f3e3c6665436f6d706f736974652069611aa08201527f6e3d22736861646f772220696e323d22536f7572636547726170686963222f3e611ac08201527f3c6665436f6d706f73697465206f70657261746f723d2261746f702220696e3d611ae08201527f22736861646f772220696e323d22536f7572636547726170686963222f3e3c2f611b008201527f66696c7465723e3c6d61736b2069643d2261223e3c706174682066696c6c3d22611b208201527f233030302220643d224d302030683735307637353048307a222f3e3c72656374611b408201527f20636c6173733d22692220783d223231352220793d223635222072783d223230611b608201527f222066696c6c3d2223464646222f3e3c636972636c6520636c6173733d226a20611b808201527f6c222063793d223635222f3e3c636972636c6520636c6173733d226a206c2220611ba08201527f63793d22363835222f3e3c2f6d61736b3e3c2f646566733e3c70617468206669611bc08201527f6c6c3d22233130313331432220643d224d302030683735307637353048307a22611be08201527f2f3e3c7265637420636c6173733d2269206e2220783d223231352220793d2236611c008201527f3522206d61736b3d2275726c28236129222072783d223230222f3e3c63697263611c208201527f6c65206d61736b3d2275726c28236129222066696c6c3d2275726c2823622922611c408201527f2063783d22333735222063793d223338312220723d22313830222f3e3c636972611c608201527f636c6520636c6173733d226a206b206e222063793d22313235222f3e3c672074611c808201527f72616e73666f726d3d227472616e736c617465283335392031313029223e3c63611ca08201527f6972636c6520636c6173733d226e222063793d223136222063783d2231362220611cc08201527f723d223136222f3e3c7265637420636c6173733d226120632220783d22382220611ce08201527f793d2237222072783d2232222f3e3c7265637420636c6173733d226220662220611d008201527f783d22382e352220793d22372e35222072783d22312e35222f3e3c7265637420611d208201527f636c6173733d226120652220783d22382220793d223231222072783d2232222f611d408201527f3e3c7265637420636c6173733d226220682220783d22382e352220793d223231611d608201527f2e35222072783d22312e35222f3e3c7265637420636c6173733d226120642220611d808201527f783d2231342220793d2237222072783d2232222f3e3c7265637420636c617373611da08201527f3d226220672220783d2231342e352220793d22372e35222072783d22312e3522611dc08201527f2f3e3c7265637420636c6173733d226120652220783d2231342220793d223134611de08201527f222072783d2232222f3e3c7265637420636c6173733d226220682220783d2231611e008201527f342e352220793d2231342e35222072783d22312e35222f3e3c7265637420636c611e208201527f6173733d226120642220783d2231342220793d223139222072783d2232222f3e611e408201527f3c7265637420636c6173733d226220672220783d2231342e352220793d223139611e608201527f2e35222072783d22312e35222f3e3c7265637420636c6173733d226120632220611e808201527f783d2232302220793d223132222072783d2232222f3e3c7265637420636c6173611ea08201527f733d226220662220783d2232302e352220793d2231322e35222072783d22312e611ec08201527f35222f3e3c7265637420636c6173733d226120652220783d2232302220793d22611ee08201527f37222072783d2232222f3e3c7265637420636c6173733d226220682220783d22611f008201527f32302e352220793d22372e35222072783d22312e35222f3e3c2f673e3c706174611f208201527f6820643d224d3333382e383134203136382e383536632d2e33373320302d2e37611f408201527f31382d2e3036332d312e3033372d2e31393161322e38323920322e3832392030611f608201527f203020312d2e3837382d2e36303620322e38323820322e383238203020302031611f808201527f2d2e3630362d2e38373820322e37363720322e3736372030203020312d2e3139611fa08201527f332d312e303337762d2e33333663302d2e3337322e3036342d2e3732332e3139611fc08201527f322d312e3035332e3133382d2e3331392e33342d2e3631312e3630362d2e3837611fe08201527f3761322e353920322e3539203020302031202e3837382d2e353920322e3538206120008201527f322e353820302030203120312e3033382d2e32303868342e3236632e323435206120208201527f30202e34382e3033322e3730332e3039362e3231322e3035332e3432352e31346120408201527f332e3633382e32372e3232332e3131382e3431352e3235362e3537342e3431366120608201527f2e31362e31362e3330342e3334352e3433312e3535382e3034332e3036342e306120808201527f372e3133332e30382e323038612e3330312e3330312030203020312d2e3031366120a08201527f2e3039352e3334362e3334362030203020312d2e3137352e3235362e34322e346120c08201527f322030203020312d2e33322e3033322e3333332e3333332030203020312d2e326120e08201527f33392d2e31393220332e30313620332e3031362030203020302d2e3330332d2e6121008201527f33393920322e36313420322e3631342030203020302d2e3431352d2e333033206121208201527f312e39333520312e3933352030203020302d2e3436332d2e31393120312e35336121408201527f3620312e3533362030203020302d2e3439352d2e303438632d2e37313220302d6121608201527f312e34322d2e3030362d322e3132322d2e3031362d2e37313320302d312e34326121808201527f352e3030352d322e3133382e3031362d2e32363620302d2e35312e3034322d2e6121a08201527f3733342e3132372d2e3233342e3039362d2e3434322e32342d2e3632332e34336121c08201527f3161312e39383820312e3938382030203020302d2e34332e36323320312e39366121e08201527f3120312e3936312030203020302d2e3134342e3735762e33333561312e3834346122008201527f20312e383434203020302030202e35373420312e33353620312e38343420312e6122208201527f38343420302030203020312e3335362e35373468342e323631632e31372030206122408201527f2e33332d2e3031352e34382d2e30343761322e303220322e30322030203020306122608201527f202e3434362d2e313932632e3134392d2e3037342e3238322d2e3136352e33396122808201527f392d2e3237312e3130362d2e3130372e3230372d2e3232392e3330332d2e33366122a08201527f37612e3433382e343338203020302031202e3235352d2e313434632e3039362d6122c08201527f2e30312e3138372e30312e3237322e303634612e33352e3335203020302031206122e08201527f2e31362e32342e3330362e3330362030203020312d2e3033332e323720322e366123008201527f353320322e3635332030203020312d2e34332e353237632d2e31362e3133392d6123208201527f2e3334362e3236362d2e3535392e3338332d2e3231332e3131372d2e34322e316123408201527f39372d2e3632322e32342d2e3231332e3035332d2e3433362e30382d2e36372e6123608201527f3038682d342e3236325a6d31372e3535332030632d2e37313320302d312e33326123808201527f342d2e3236362d312e3833352d2e37393761322e363920322e363920302030206123a08201527f312d2e3736362d312e393331762d322e36363563302d2e3131372e3033372d2e6123c08201527f3231332e3131322d2e323837612e33372e3337203020302031202e32372d2e316123e08201527f3132632e3131382030202e3231342e3033372e3238382e313132612e33392e336124008201527f39203020302031202e3131322e32383776322e3636346330202e3533332e31386124208201527f2e39392e35343220312e33373361312e373120312e373120302030203020312e6124408201527f3239332e35353968332e383738632e35312030202e3934312d2e31383720312e6124608201527f3239322d2e35353961312e393320312e3933203020302030202e3534332d312e6124808201527f333732762d322e363635612e33392e3339203020302031202e3131312d2e32386124a08201527f372e3338392e333839203020302031202e3238382d2e3131322e33372e3337206124c08201527f3020302031202e3237312e3131322e33392e3339203020302031202e3131322e6124e08201527f32383776322e3636346330202e3735362d2e32353620312e342d2e37363620316125008201527f2e3933322d2e35312e3533312d312e3132382e3739372d312e3835312e3739376125208201527f682d332e3839345a6d32332e3832342d2e373138612e3435362e3435362030206125408201527f302031202e31362e313932632e30312e3034322e3031362e30392e3031362e316125608201527f3433612e34372e34372030203020312d2e3031362e3131322e3335352e3335356125808201527f2030203020312d2e3134332e3230382e3432332e3432332030203020312d2e326125a08201527f342e303633682d2e303438612e3134312e3134312030203020312d2e3036342d6125c08201527f2e303136632d2e303220302d2e3033372d2e3030352d2e3034372d2e303136616125e08201527f3130342e3836203130342e38362030203020312d312e31382d2e3833632d2e336126008201527f37342d2e3236352d2e3734362d2e3533312d312e3131382d2e3739372d2e30316126208201527f3120302d2e3031362d2e3030362d2e3031362d2e3031362d2e303120302d2e306126408201527f31362d2e3030352d2e3031362d2e3031362d2e303120302d2e3031362d2e30306126608201527f352d2e3031362d2e303136682d352e35353376312e333234612e33392e3339206126808201527f30203020312d2e3131322e3238382e3432352e3432352030203020312d2e32386126a08201527f372e3131312e33372e33372030203020312d2e3237322d2e3131312e3338392e6126c08201527f3338392030203020312d2e3131312d2e323838762d342e39343663302d2e30356126e08201527f342e3030352d2e3130372e3031362d2e3136612e3530322e35303220302030206127008201527f31202e3039352d2e3132382e3337342e333734203020302031202e3132382d2e6127208201527f30382e3331362e333136203020302031202e3134342d2e30333168362e3839336127408201527f632e3235362030202e34392e3034382e3730322e3134332e3232342e3038352e6127608201527f34322e3231382e35392e342e3138322e31382e33322e3337372e3431362e35396127808201527f2e3038352e3232332e3132372e3435372e3132372e373032762e3333356330206127a08201527f2e3232332d2e3033322e34332d2e3039352e36323261322e31303720322e31306127c08201527f372030203020312d2e33322e353237632d2e3133382e31382d2e3239322e33316127e08201527f392d2e3436322e3431352d2e31372e3130362d2e3336322e3138362d2e3537356128008201527f2e32346c2e3730322e3531632e3233342e31372e3436392e3334352e3730332e6128208201527f3532365a6d2d382e3238312d342e32323876322e34323568362e343934612e396128408201527f35342e393534203020302030202e342d2e30382e3737362e37373620302030206128608201527f30202e3333342d2e323233632e3130372d2e3130362e3138362d2e3231382e326128808201527f342d2e3333352e3035332d2e3132382e30382d2e3236362e30382d2e343135766128a08201527f2d2e3332612e3935342e3935342030203020302d2e30382d2e33393820312e326128c08201527f333220312e3233322030203020302d2e3232342d2e33353120312e32323820316128e08201527f2e3232382030203020302d2e33352d2e3232342e3935342e39353420302030206129008201527f302d2e342d2e3038682d362e3439345a6d32342e36372d2e373832632e3130366129208201527f2030202e3230322e3033372e3238372e313131612e33372e33372030203020316129408201527f202e3131322e3237322e33392e33392030203020312d2e3131322e3238372e346129608201527f32352e3432352030203020312d2e3238372e313132682d332e363476342e35376129808201527f39612e33372e33372030203020312d2e3131312e3237322e3334382e333438206129a08201527f30203020312d2e3237312e3132372e3339372e3339372030203020312d2e32386129c08201527f382d2e3132372e33372e33372030203020312d2e3131312d2e323732762d342e6129e08201527f353739682d332e363339612e33372e33372030203020312d2e3237312d2e3131612a008201527f312e33392e33392030203020312d2e3131322d2e3238372e33372e3337203020612a208201527f302031202e3131322d2e3237322e33372e3337203020302031202e3237312d2e612a408201527f31313168382e3035385a6d31352e3738322d2e303438632e373233203020312e612a608201527f33342e32363620312e38352e3739382e3531312e3533322e37363720312e3137612a808201527f2e37363720312e39313576322e3638612e33372e33372030203020312d2e3131612aa08201527f322e3237322e3339372e3339372030203020312d2e3238372e3132372e333438612ac08201527f2e3334382030203020312d2e3237322d2e3132372e3334382e33343820302030612ae08201527f20312d2e3132372d2e323732762d312e313936682d372e35333276312e313936612b008201527f612e3334382e3334382030203020312d2e3132382e3237322e3334382e333438612b208201527f2030203020312d2e3237312e3132372e3334382e3334382030203020312d2e32612b408201527f37312d2e3132372e3334382e3334382030203020312d2e3132382d2e32373276612b608201527f2d322e363863302d2e3734352e3235352d312e3338332e3736362d312e393135612b808201527f2e35312d2e35333220312e3132382d2e37393820312e3835312d2e3739386833612ba08201527f2e3839345a6d2d352e36393720332e34313568372e353438762d2e3730326330612bc08201527f2d2e3533322d2e3137362d2e3938342d2e3532372d312e3335372d2e3336322d612be08201527f2e3338332d2e3739322d2e3537342d312e3239322d2e353734483430382e3563612c008201527f2d2e353120302d2e3934322e3139312d312e3239332e35373461312e38373520612c208201527f312e3837352030203020302d2e35343220312e333537762e3730325a4d323937612c408201527f2e383938203230342e3568342e31366c312e3739322d352e31353268392e3430612c608201527f386c312e38323420352e31353268342e3434386c2d382e3730342d32332e3268612c808201527f2d342e3238386c2d382e36342032332e325a6d31302e3632342d31382e343936612ca08201527f20332e353220392e393532682d372e3030386c332e3438382d392e3935325a6d612cc08201527f32322e38312031382e34393668332e383037762d31372e323136682d332e3830612ce08201527f3876392e313834633020332e3130342d312e30323420352e3334342d332e3837612d008201527f3220352e333434732d332e3136382d322e3237322d332e3136382d342e363038612d208201527f762d392e3932682d332e3830387631302e383438633020342e30393620312e36612d408201527f363420362e37383420352e373620362e37383420322e333336203020342e3039612d608201527f362d2e39393220352e3038382d322e37383476322e3336385a6d372e3637382d612d808201527f31372e323136682d322e353676322e37353268322e353676392e393532633020612da08201527f332e35322e37333620342e35313220342e34313620342e35313268322e383136612dc08201527f762d322e393132682d312e333736632d312e36333220302d322e3034382d2e34612de08201527f31362d322e3034382d322e313736762d392e33373668332e343536762d322e37612e008201527f3532682d332e343536762d342e353434682d332e38303876342e3534345a6d31612e208201527f332e3137392d352e393834682d332e3830397632332e3268332e383038762d39612e408201527f2e31353263302d332e31303420312e3038382d352e33343420342d352e333434612e608201527f73332e32363420322e32373220332e32363420342e36303876392e3838386833612e808201527f2e383038762d31302e38313663302d342e3039362d312e3639362d362e373834612ea08201527f2d352e3835362d362e3738342d322e3420302d342e3232342e3939322d352e32612ec08201527f313620322e373834563138312e335a6d31362e38362031342e36323463302d33612ee08201527f2e39363820322e3134342d352e393220342e3534342d352e393220322e342030612f008201527f20342e35343420312e39353220342e35343420352e3932732d322e3134342035612f208201527f2e3838382d342e35343420352e383838632d322e3420302d342e3534342d312e612f408201527f39322d342e3534342d352e3838385a6d342e3534342d392e303234632d342e31612f608201527f393220302d382e343820322e3831362d382e343820392e303234203020362e32612f808201527f303820342e32383820382e39393220382e343820382e39393273382e34382d32612fa08201527f2e37383420382e34382d382e39393263302d362e3230382d342e3238382d392e612fc08201527f3032342d382e34382d392e3032345a6d32302e3035372e3431366131302e3332612fe08201527f2031302e33322030203020302d2e3939322d2e303634632d322e30382e3033326130008201527f2d332e37343420312e3138342d342e36373220332e313034762d332e303732686130208201527f2d332e373434563230342e3568332e383038762d392e30323463302d332e34356130408201527f3620312e3337362d342e34313620332e3737362d342e3431362e3537362030206130608201527f312e3138342e30333220312e3832342e303936762d332e38345a6d31342e36366130808201527f3520342e363732632d2e3730342d332e3435362d332e3737362d352e3038382d6130a08201527f372e3133362d352e3038382d332e37343420302d372e30303820312e3935322d6130c08201527f372e30303820342e393932203020332e31333620322e32373220342e343438206130e08201527f352e31383420352e3032346c322e3539322e35313263312e3639362e333220326131008201527f2e3937362e393620322e39373620322e333638732d312e34373220322e32342d6131208201527f332e34353620322e3234632d322e323420302d332e35322d312e3032342d332e6131408201527f3837322d322e373834682d332e373132632e34313620332e32363420332e32336131608201527f3220352e36363420372e34353620352e36363420332e393034203020372e32396131808201527f362d312e39383420372e3239362d352e35363820302d332e33362d322e3635366131a08201527f2d342e3434382d362e3134342d352e31326c2d322e3433322d2e3438632d312e6131c08201527f3437322d2e3238382d322e3330342d2e3839362d322e3330342d322e303438206131e08201527f302d312e31353220312e3533362d312e38383820332e322d312e38383820312e6132008201527f3932203020332e33362e36303820332e37373620322e31373668332e3538345a6132208201527f6d362e3238342d31302e363838682d332e3830387632332e3268332e383038766132408201527f2d392e31353263302d332e31303420312e3038382d352e33343420342d352e336132608201527f343473332e32363420322e32373220332e32363420342e36303876392e3838386132808201527f68332e383038762d31302e38313663302d342e3039362d312e3639362d362e376132a08201527f38342d352e3835362d362e3738342d322e3420302d342e3232342e3939322d356132c08201527f2e32313620322e373834563138312e335a6d31342e303736203076332e3834686132e08201527f332e383038762d332e3834682d332e3830385a6d3020352e393834563230342e6133008201527f3568332e383038762d31372e323136682d332e3830385a6d31302e37383120386133208201527f2e36303863302d332e39363820312e3935322d352e38383820342e3434382d356133408201527f2e38383820322e363536203020342e32353620322e32373220342e32353620356133608201527f2e383838203020332e3634382d312e3620352e39322d342e32353620352e39326133808201527f2d322e34393620302d342e3434382d312e3935322d342e3434382d352e39325a6133a08201527f6d2d332e3634382d382e363038563231302e3168332e383038762d372e3837326133c08201527f63312e30323420312e36393620322e38313620322e36383820352e313220322e6133e08201527f36383820342e313932203020372e3339322d332e34383820372e3339322d392e6134008201527f30323420302d352e3530342d332e322d382e3939322d372e3339322d382e39396134208201527f322d322e33303420302d342e3039362e3939322d352e313220322e363838762d6134408201527f322e333034682d332e3830385a222066696c6c3d2223463046364643222f3e3c6134608201527f7061746820636c6173733d226b22207374726f6b652d646173686f66667365746134808201527f3d223522207374726f6b652d6461736861727261793d2231302220643d224d326134a08201527f31352035343568333230222f3e3c67207472616e73666f726d3d227472616e736134c08201527f6c617465283233312032333729207363616c6528302e33383429223e000000006134e08201526000615be9615b02615afc615a616134fc86018861199f565b7f3c2f673e3c7465787420666f6e742d66616d696c793d22412220783d2235302581527f2220793d22363035222066696c6c3d22234630463646432220666f6e742d736960208201527f7a653d2234302220646f6d696e616e742d626173656c696e653d2263656e747260408201527f616c2220746578742d616e63686f723d226d6964646c65223e230000000000006060820152607a0190565b8561199f565b7f3c2f746578743e3c7265637420636c6173733d2269206b206f2220783d22323181527f352220793d22363522206d61736b3d2275726c28236129222072783d2232302260208201527f2f3e3c636972636c6520636c6173733d226a206b206f222063793d223635222060408201527f6d61736b3d2275726c28236129222f3e3c636972636c6520636c6173733d226a60608201527f206b206f222063793d2236383522206d61736b3d2275726c28236129222f3e3c60808201527f2f7376673e00000000000000000000000000000000000000000000000000000060a082015260a50190565b949350505050565b600060208284031215615c0357600080fd5b815161158f8161169e565b7f7b226e616d65223a22417574686f727368697020546f6b656e20230000000000815260008451615c4681601b850160208901611596565b7f222c226465736372697074696f6e223a225468697320746f6b656e20616c6c6f601b918401918201527f777320312070757a7a6c6520746f20626520616464656420746f204375727461603b8201527f2e204f6e636520697420686173206265656e20757365642c2069742063616e20605b8201527f6e65766572206265207573656420616761696e2e222c22696d6167655f646174607b8201527f61223a22646174613a696d6167652f7376672b786d6c3b6261736536342c0000609b8201528451615d1b8160b9840160208901611596565b7f222c2261747472696275746573223a5b7b2274726169745f74797065223a225560b992909101918201527f736564222c2276616c7565223a0000000000000000000000000000000000000060d98201528351615d7f8160e6840160208801611596565b61199460e6828401017f7d5d7d0000000000000000000000000000000000000000000000000000000000815260030190565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251615de981601d850160208701611596565b91909101601d0192915050565b7f3030303030300000000000000000000000000000000000000000000000000000815260008251615e2e816006850160208701611596565b9190910160060192915050565b7f3030303030000000000000000000000000000000000000000000000000000000815260008251615e73816005850160208701611596565b9190910160050192915050565b7f3030303000000000000000000000000000000000000000000000000000000000815260008251615eb8816004850160208701611596565b9190910160040192915050565b7f3030300000000000000000000000000000000000000000000000000000000000815260008251615efd816003850160208701611596565b9190910160030192915050565b61030360f41b815260008251615f27816002850160208701611596565b9190910160020192915050565b600360fc1b815260008251615f50816001850160208701611596565b9190910160010192915050565b8082018082111561047b57634e487b7160e01b600052601160045260246000fd5b600082615f8d57615f8d6117eb565b50049056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa164736f6c6343000811000a

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

00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce000000000000000000000000000000000000000000000000000000000003f4800000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5000000000000000000000000afdc1a3ef3992f53c10fc798d242e15e2f0df51a0000000000000000000000008fc68a56f9682312953a1730ae62afd1a99fdc4f0000000000000000000000007ed52863829ab99354f3a0503a622e82acd5f7d3000000000000000000000000b8c2c29ee19d8307cb7255e1cd9cbde883a267d5000000000000000000000000f296178d553c8ec21a2fbd2c5dda8ca9ac905a000000000000000000000000000734d56da60852a03e2aafae8a36ffd8c12b32f10000000000000000000000001e79b045dc29eae9fdc69673c9dcd7c53e5e159d000000000000000000000000230d31eec85f4063a405b0f95bde509c0d0a8b5d000000000000000000000000ea23c259b637f72d80697d4a4d1302df9f64530b000000000000000000000000b0623c91c65621df716ab8afe5f66656b21a9108000000000000000000000000068484f7bd2b7d7c5a698d89e75ddcaf3a92b87900000000000000000000000012518c3c808ef33e496fd42033bd312919cd3fe1000000000000000000000000b578405df1f9d4dfdd46a0bd152d518d4c5fe0ac000000000000000000000000b3fd340cb00f7d1b27556e7231a93ca6ffa0bd57000000000000000000000000174787a207bf4ed4d8db0945602e49f42c146474000000000000000000000000fee555e9367b83fb0952a945539faae54f0560a4000000000000000000000000e7aa7af667016837733f3ca3809bde04697730ef000000000000000000000000d84365dad6e6db6fa2d431992acb1e050789be6900000000000000000000000046622e91f95f274f4f76460b38d1f5e00905f767000000000000000000000000152ac2bc1821c5c9eca56d1f35d8b0d8b61187f5000000000000000000000000b958d9fa0417e116f446d0a06d0326805ad73bd5000000000000000000000000bad58e133138549936d2576ebc33251be841d3e90000000000000000000000006dacb7352b4ec1e2b979a05e3cf1f126ad641110000000000000000000000000040dbc0811377dcfa98ffabbe7f6b2f411986931000000000000000000000000d6da3f2b9ec0e51ba0c6aee4080a8179246962ed

-----Decoded View---------------
Arg [0] : _curta (address): 0x00000000eCf2b58C296B47caC8C51467c0e307cE
Arg [1] : _issueLength (uint256): 259200
Arg [2] : _authors (address[]): 0xA85572Cd96f1643458f17340b6f0D6549Af482F5,0xA85572Cd96f1643458f17340b6f0D6549Af482F5,0xA85572Cd96f1643458f17340b6f0D6549Af482F5,0xA85572Cd96f1643458f17340b6f0D6549Af482F5,0xA85572Cd96f1643458f17340b6f0D6549Af482F5,0xaFDc1A3EF3992f53C10fC798d242E15E2F0DF51A,0x8FC68A56f9682312953a1730Ae62AFD1a99FdC4F,0x7eD52863829AB99354F3a0503A622e82AcD5F7d3,0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5,0xF296178d553C8Ec21A2fBD2c5dDa8CA9ac905A00,0x0734d56DA60852A03e2Aafae8a36FFd8c12B32f1,0x1E79b045Dc29eAe9fdc69673c9DCd7C53E5E159D,0x230d31EEC85F4063a405B0F95bdE509C0d0A8b5D,0xea23c259b637f72D80697D4A4D1302df9f64530B,0xB0623C91c65621df716aB8aFE5f66656B21A9108,0x068484F7BD2b7D7C5a698d89e75ddcaf3a92B879,0x12518c3c808ef33E496Fd42033BD312919CD3FE1,0xB578405Df1F9D4dFdD46a0BD152D518d4c5Fe0aC,0xb3Fd340Cb00f7d1b27556E7231a93CA6ffa0Bd57,0x174787a207BF4eD4D8db0945602e49f42c146474,0xfEe555E9367B83fB0952A945539FAAE54f0560A4,0xE7aa7AF667016837733F3CA3809bdE04697730eF,0xd84365dAd6e6dB6fa2d431992acB1e050789bE69,0x46622E91F95F274f4f76460B38d1F5E00905f767,0x152Ac2bC1821C5C9ecA56D1F35D8b0D8b61187F5,0xB958d9FA0417E116F446D0A06D0326805Ad73Bd5,0xBad58e133138549936D2576ebC33251bE841d3e9,0x6dacb7352B4eC1e2B979a05E3cF1F126AD641110,0x040dBC0811377DcFA98FFaBBE7F6b2F411986931,0xd6Da3f2B9eC0E51BA0c6AEe4080a8179246962ed

-----Encoded View---------------
34 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000ecf2b58c296b47cac8c51467c0e307ce
Arg [1] : 000000000000000000000000000000000000000000000000000000000003f480
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 000000000000000000000000000000000000000000000000000000000000001e
Arg [4] : 000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5
Arg [5] : 000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5
Arg [6] : 000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5
Arg [7] : 000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5
Arg [8] : 000000000000000000000000a85572cd96f1643458f17340b6f0d6549af482f5
Arg [9] : 000000000000000000000000afdc1a3ef3992f53c10fc798d242e15e2f0df51a
Arg [10] : 0000000000000000000000008fc68a56f9682312953a1730ae62afd1a99fdc4f
Arg [11] : 0000000000000000000000007ed52863829ab99354f3a0503a622e82acd5f7d3
Arg [12] : 000000000000000000000000b8c2c29ee19d8307cb7255e1cd9cbde883a267d5
Arg [13] : 000000000000000000000000f296178d553c8ec21a2fbd2c5dda8ca9ac905a00
Arg [14] : 0000000000000000000000000734d56da60852a03e2aafae8a36ffd8c12b32f1
Arg [15] : 0000000000000000000000001e79b045dc29eae9fdc69673c9dcd7c53e5e159d
Arg [16] : 000000000000000000000000230d31eec85f4063a405b0f95bde509c0d0a8b5d
Arg [17] : 000000000000000000000000ea23c259b637f72d80697d4a4d1302df9f64530b
Arg [18] : 000000000000000000000000b0623c91c65621df716ab8afe5f66656b21a9108
Arg [19] : 000000000000000000000000068484f7bd2b7d7c5a698d89e75ddcaf3a92b879
Arg [20] : 00000000000000000000000012518c3c808ef33e496fd42033bd312919cd3fe1
Arg [21] : 000000000000000000000000b578405df1f9d4dfdd46a0bd152d518d4c5fe0ac
Arg [22] : 000000000000000000000000b3fd340cb00f7d1b27556e7231a93ca6ffa0bd57
Arg [23] : 000000000000000000000000174787a207bf4ed4d8db0945602e49f42c146474
Arg [24] : 000000000000000000000000fee555e9367b83fb0952a945539faae54f0560a4
Arg [25] : 000000000000000000000000e7aa7af667016837733f3ca3809bde04697730ef
Arg [26] : 000000000000000000000000d84365dad6e6db6fa2d431992acb1e050789be69
Arg [27] : 00000000000000000000000046622e91f95f274f4f76460b38d1f5e00905f767
Arg [28] : 000000000000000000000000152ac2bc1821c5c9eca56d1f35d8b0d8b61187f5
Arg [29] : 000000000000000000000000b958d9fa0417e116f446d0a06d0326805ad73bd5
Arg [30] : 000000000000000000000000bad58e133138549936d2576ebc33251be841d3e9
Arg [31] : 0000000000000000000000006dacb7352b4ec1e2b979a05e3cf1f126ad641110
Arg [32] : 000000000000000000000000040dbc0811377dcfa98ffabbe7f6b2f411986931
Arg [33] : 000000000000000000000000d6da3f2b9ec0e51ba0c6aee4080a8179246962ed


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.