// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/security/ReentrancyGuard.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

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



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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/core/ToVInfos.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

interface ToVInfos {
    struct ToV {
        uint64 season;
        uint64 seasonTraitCount;
        uint256 dna;
    }
    
    struct ContractData {
        string name;
        string collectionName;
        string description;
        string image;
        string banner;
        string website;
        string royalties;
        string royaltiesRecipient;
    }
}
// File: contracts/lib/Base64.sol



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

/// @notice Library to encode strings in Base64.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <brecht@loopring.org>.
library Base64 {
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    function encode(bytes memory data, bool fileSafe, bool noPadding)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                // Multiply by 4/3 rounded up.
                // The `shl(2, ...)` is equivalent to multiplying by 4.
                let encodedLength := shl(2, div(add(dataLength, 2), 3))

                // Set `result` to point to the start of the free memory.
                result := mload(0x40)

                // Store the table into the scratch space.
                // Offsetted by -1 byte so that the `mload` will load the character.
                // We will rewrite the free memory pointer at `0x40` later with
                // the allocated size.
                // The magic constant 0x0230 will translate "-_" + "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230)))

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, encodedLength)

                // Run over the input, 3 bytes at a time.
                for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(0, mload(and(shr(18, input), 0x3F)))
                    mstore8(1, mload(and(shr(12, input), 0x3F)))
                    mstore8(2, mload(and(shr(6, input), 0x3F)))
                    mstore8(3, mload(and(input, 0x3F)))
                    mstore(ptr, mload(0x00))

                    ptr := add(ptr, 4) // Advance 4 bytes.

                    if iszero(lt(ptr, end)) { break }
                }

                // Allocate the memory for the string.
                // Add 31 and mask with `not(31)` to round the
                // free memory pointer up the next multiple of 32.
                mstore(0x40, and(add(end, 31), not(31)))

                // Equivalent to `o = [0, 2, 1][dataLength % 3]`.
                let o := div(2, mod(dataLength, 3))

                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore(sub(ptr, o), shl(240, 0x3d3d))
                // Set `o` to zero if there is padding.
                o := mul(iszero(iszero(noPadding)), o)
                // Zeroize the slot after the string.
                mstore(sub(ptr, o), 0)
                // Write the length of the string.
                mstore(result, sub(encodedLength, o))
            }
        }
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, false, false)`.
    function encode(bytes memory data) internal pure returns (string memory result) {
        result = encode(data, false, false);
    }

    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// Equivalent to `encode(data, fileSafe, false)`.
    function encode(bytes memory data, bool fileSafe)
        internal
        pure
        returns (string memory result)
    {
        result = encode(data, fileSafe, false);
    }

    /// @dev Encodes base64 encoded `data`.
    ///
    /// Supports:
    /// - RFC 4648 (both standard and file-safe mode).
    /// - RFC 3501 (63: ',').
    ///
    /// Does not support:
    /// - Line breaks.
    ///
    /// Note: For performance reasons,
    /// this function will NOT revert on invalid `data` inputs.
    /// Outputs for invalid inputs will simply be undefined behaviour.
    /// It is the user's responsibility to ensure that the `data`
    /// is a valid base64 encoded string.
    function decode(string memory data) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let dataLength := mload(data)

            if dataLength {
                let decodedLength := mul(shr(2, dataLength), 3)

                for {} 1 {} {
                    // If padded.
                    if iszero(and(dataLength, 3)) {
                        let t := xor(mload(add(data, dataLength)), 0x3d3d)
                        // forgefmt: disable-next-item
                        decodedLength := sub(
                            decodedLength,
                            add(iszero(byte(30, t)), iszero(byte(31, t)))
                        )
                        break
                    }
                    // If non-padded.
                    decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
                    break
                }
                result := mload(0x40)

                // Write the length of the bytes.
                mstore(result, decodedLength)

                // Skip the first slot, which stores the length.
                let ptr := add(result, 0x20)
                let end := add(ptr, decodedLength)

                // Load the table into the scratch space.
                // Constants are optimized for smaller bytecode with zero gas overhead.
                // `m` also doubles as the mask of the upper 6 bits.
                let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
                mstore(0x5b, m)
                mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
                mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)

                for {} 1 {} {
                    // Read 4 bytes.
                    data := add(data, 4)
                    let input := mload(data)

                    // Write 3 bytes.
                    // forgefmt: disable-next-item
                    mstore(ptr, or(
                        and(m, mload(byte(28, input))),
                        shr(6, or(
                            and(m, mload(byte(29, input))),
                            shr(6, or(
                                and(m, mload(byte(30, input))),
                                shr(6, mload(byte(31, input)))
                            ))
                        ))
                    ))

                    ptr := add(ptr, 3)

                    if iszero(lt(ptr, end)) { break }
                }

                // Allocate the memory for the string.
                // Add 31 and mask with `not(31)` to round the
                // free memory pointer up the next multiple of 32.
                mstore(0x40, and(add(end, 31), not(31)))
                // Zeroize the slot after the bytes.
                mstore(end, 0)
                // Restore the zero slot.
                mstore(0x60, 0)
            }
        }
    }
}

// File: contracts/lib/DynamicBuffer.sol



// SPDX-License-Identifier: MIT
// Copyright (c) 2021 the ethier authors (github.com/divergencetech/ethier)

pragma solidity >=0.8.0;

/// @title DynamicBuffer
/// @author David Huber (@cxkoda) and Simon Fremaux (@dievardump). See also
///         https://raw.githubusercontent.com/dievardump/solidity-dynamic-buffer
/// @notice This library is used to allocate a big amount of container memory
//          which will be subsequently filled without needing to reallocate
///         memory.
/// @dev First, allocate memory.
///      Then use `buffer.appendUnchecked(theBytes)` or `appendSafe()` if
///      bounds checking is required.
library DynamicBuffer {
    /// @notice Allocates container space for the DynamicBuffer
    /// @param capacity_ The intended max amount of bytes in the buffer
    /// @return buffer The memory location of the buffer
    /// @dev Allocates `capacity_ + 0x60` bytes of space
    ///      The buffer array starts at the first container data position,
    ///      (i.e. `buffer = container + 0x20`)
    function allocate(uint256 capacity_)
        internal
        pure
        returns (bytes memory buffer)
    {
        assembly {
            // Get next-free memory address
            let container := mload(0x40)

            // Allocate memory by setting a new next-free address
            {
                // Add 2 x 32 bytes in size for the two length fields
                // Add 32 bytes safety space for 32B chunked copy
                let size := add(capacity_, 0x60)
                let newNextFree := add(container, size)
                mstore(0x40, newNextFree)
            }

            // Set the correct container length
            {
                let length := add(capacity_, 0x40)
                mstore(container, length)
            }

            // The buffer starts at idx 1 in the container (0 is length)
            buffer := add(container, 0x20)

            // Init content with length 0
            mstore(buffer, 0)
        }

        return buffer;
    }

    /// @notice Appends data to buffer, and update buffer length
    /// @param buffer the buffer to append the data to
    /// @param data the data to append
    /// @dev Does not perform out-of-bound checks (container capacity)
    ///      for efficiency.
    function appendUnchecked(bytes memory buffer, bytes memory data)
        internal
        pure
    {
        assembly {
            let length := mload(data)
            for {
                data := add(data, 0x20)
                let dataEnd := add(data, length)
                let copyTo := add(buffer, add(mload(buffer), 0x20))
            } lt(data, dataEnd) {
                data := add(data, 0x20)
                copyTo := add(copyTo, 0x20)
            } {
                // Copy 32B chunks from data to buffer.
                // This may read over data array boundaries and copy invalid
                // bytes, which doesn't matter in the end since we will
                // later set the correct buffer length, and have allocated an
                // additional word to avoid buffer overflow.
                mstore(copyTo, mload(data))
            }

            // Update buffer length
            mstore(buffer, add(mload(buffer), length))
        }
    }

    /// @notice Appends data to buffer, and update buffer length
    /// @param buffer the buffer to append the data to
    /// @param data the data to append
    /// @dev Performs out-of-bound checks and calls `appendUnchecked`.
    function appendSafe(bytes memory buffer, bytes memory data) internal pure {
        checkOverflow(buffer, data.length);
        appendUnchecked(buffer, data);
    }

    /// @notice Appends data encoded as Base64 to buffer.
    /// @param fileSafe  Whether to replace '+' with '-' and '/' with '_'.
    /// @param noPadding Whether to strip away the padding.
    /// @dev Encodes `data` using the base64 encoding described in RFC 4648.
    /// See: https://datatracker.ietf.org/doc/html/rfc4648
    /// Author: Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
    /// Author: Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
    /// Author: Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos.
    function appendSafeBase64(
        bytes memory buffer,
        bytes memory data,
        bool fileSafe,
        bool noPadding
    ) internal pure {
        uint256 dataLength = data.length;

        if (data.length == 0) {
            return;
        }

        uint256 encodedLength;
        uint256 r;
        assembly {
            // For each 3 bytes block, we will have 4 bytes in the base64
            // encoding: `encodedLength = 4 * divCeil(dataLength, 3)`.
            // The `shl(2, ...)` is equivalent to multiplying by 4.
            encodedLength := shl(2, div(add(dataLength, 2), 3))

            r := mod(dataLength, 3)
            if noPadding {
                // if r == 0 => no modification
                // if r == 1 => encodedLength -= 2
                // if r == 2 => encodedLength -= 1
                encodedLength := sub(
                    encodedLength,
                    add(iszero(iszero(r)), eq(r, 1))
                )
            }
        }

        checkOverflow(buffer, encodedLength);

        assembly {
            let nextFree := mload(0x40)

            // Store the table into the scratch space.
            // Offsetted by -1 byte so that the `mload` will load the character.
            // We will rewrite the free memory pointer at `0x40` later with
            // the allocated size.
            mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
            mstore(
                0x3f,
                sub(
                    "ghijklmnopqrstuvwxyz0123456789-_",
                    // The magic constant 0x0230 will translate "-_" + "+/".
                    mul(iszero(fileSafe), 0x0230)
                )
            )

            // Skip the first slot, which stores the length.
            let ptr := add(add(buffer, 0x20), mload(buffer))
            let end := add(data, dataLength)

            // Run over the input, 3 bytes at a time.
            // prettier-ignore
            // solhint-disable-next-line no-empty-blocks
            for {} 1 {} {
                    data := add(data, 3) // Advance 3 bytes.
                    let input := mload(data)

                    // Write 4 bytes. Optimized for fewer stack operations.
                    mstore8(    ptr    , mload(and(shr(18, input), 0x3F)))
                    mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F)))
                    mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F)))
                    mstore8(add(ptr, 3), mload(and(        input , 0x3F)))
                    
                    ptr := add(ptr, 4) // Advance 4 bytes.
                    // prettier-ignore
                    if iszero(lt(data, end)) { break }
                }

            if iszero(noPadding) {
                // Offset `ptr` and pad with '='. We can simply write over the end.
                mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`.
                mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`.
            }

            mstore(buffer, add(mload(buffer), encodedLength))
            mstore(0x40, nextFree)
        }
    }

    /// @notice Returns the capacity of a given buffer.
    function capacity(bytes memory buffer) internal pure returns (uint256) {
        uint256 cap;
        assembly {
            cap := sub(mload(sub(buffer, 0x20)), 0x40)
        }
        return cap;
    }

    /// @notice Reverts if the buffer will overflow after appending a given
    /// number of bytes.
    function checkOverflow(bytes memory buffer, uint256 addedLength)
        internal
        pure
    {
        uint256 cap = capacity(buffer);
        uint256 newLength = buffer.length + addedLength;
        if (cap < newLength) {
            revert("DynamicBuffer: Appending out of bounds.");
        }
    }
}

// File: contracts/lib/SSTORE2.sol



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

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solmady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev We skip the first byte as it's a STOP opcode,
    /// which ensures the contract can't be called.
    uint256 internal constant DATA_OFFSET = 1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the storage contract.
    error DeploymentFailed();

    /// @dev The storage contract address is invalid.
    error InvalidPointer();

    /// @dev Attempt to read outside of the storage contract's bytecode bounds.
    error ReadOutOfBounds();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         WRITE LOGIC                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    function write(bytes memory data) internal returns (address pointer) {
        /// @solidity memory-safe-assembly
        assembly {
            let originalDataLength := mload(data)

            // Add 1 to data size since we are prefixing it with a STOP opcode.
            let dataSize := add(originalDataLength, DATA_OFFSET)

            /**
             * ------------------------------------------------------------------------------+
             * Opcode      | Mnemonic        | Stack                   | Memory              |
             * ------------------------------------------------------------------------------|
             * 61 codeSize | PUSH2 codeSize  | codeSize                |                     |
             * 80          | DUP1            | codeSize codeSize       |                     |
             * 60 0xa      | PUSH1 0xa       | 0xa codeSize codeSize   |                     |
             * 3D          | RETURNDATASIZE  | 0 0xa codeSize codeSize |                     |
             * 39          | CODECOPY        | codeSize                | [0..codeSize): code |
             * 3D          | RETURNDATASIZE  | 0 codeSize              | [0..codeSize): code |
             * F3          | RETURN          |                         | [0..codeSize): code |
             * 00          | STOP            |                         |                     |
             * ------------------------------------------------------------------------------+
             * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.
             * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
             */
            mstore(
                data,
                or(
                    0x61000080600a3d393df300,
                    // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2.
                    shl(0x40, dataSize)
                )
            )

            // Deploy a new contract with the generated creation code.
            pointer := create(0, add(data, 0x15), add(dataSize, 0xa))

            // If `pointer` is zero, revert.
            if iszero(pointer) {
                // Store the function selector of `DeploymentFailed()`.
                mstore(0x00, 0x30116425)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Restore original length of the variable size `data`.
            mstore(data, originalDataLength)
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract with `salt`
    /// and returns its deterministic address.
    function writeDeterministic(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let originalDataLength := mload(data)
            let dataSize := add(originalDataLength, DATA_OFFSET)

            mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize)))

            // Deploy a new contract with the generated creation code.
            pointer := create2(0, add(data, 0x15), add(dataSize, 0xa), salt)

            // If `pointer` is zero, revert.
            if iszero(pointer) {
                // Store the function selector of `DeploymentFailed()`.
                mstore(0x00, 0x30116425)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Restore original length of the variable size `data`.
            mstore(data, originalDataLength)
        }
    }

    /// @dev Returns the initialization code hash of the storage contract for `data`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let originalDataLength := mload(data)
            let dataSize := add(originalDataLength, DATA_OFFSET)

            mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize)))

            hash := keccak256(add(data, 0x15), add(dataSize, 0xa))

            // Restore original length of the variable size `data`.
            mstore(data, originalDataLength)
        }
    }

    /// @dev Returns the address of the storage contract for `data`
    /// deployed with `salt` by `deployer`.
    function predictDeterministicAddress(bytes memory data, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(data);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, hash)
            mstore(0x01, shl(96, deployer))
            mstore(0x15, salt)
            predicted := keccak256(0x00, 0x55)
            // Restore the part of the free memory pointer that has been overwritten.
            mstore(0x35, 0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         READ LOGIC                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns all the `data` from the bytecode of the storage contract at `pointer`.
    function read(address pointer) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            let pointerCodesize := extcodesize(pointer)
            if iszero(pointerCodesize) {
                // Store the function selector of `InvalidPointer()`.
                mstore(0x00, 0x11052bb4)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            // Offset all indices by 1 to skip the STOP opcode.
            let size := sub(pointerCodesize, DATA_OFFSET)

            // Get the pointer to the free memory and allocate
            // enough 32-byte words for the data and the length of the data,
            // then copy the code to the allocated memory.
            // Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
            data := mload(0x40)
            mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
            mstore(data, size)
            mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
            extcodecopy(pointer, add(data, 0x20), DATA_OFFSET, size)
        }
    }

    /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`,
    /// from the byte at `start`, to the end of the data stored.
    function read(address pointer, uint256 start) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            let pointerCodesize := extcodesize(pointer)
            if iszero(pointerCodesize) {
                // Store the function selector of `InvalidPointer()`.
                mstore(0x00, 0x11052bb4)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // If `!(pointer.code.size > start)`, reverts.
            // This also handles the case where `start + DATA_OFFSET` overflows.
            if iszero(gt(pointerCodesize, start)) {
                // Store the function selector of `ReadOutOfBounds()`.
                mstore(0x00, 0x84eb0dd1)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            let size := sub(pointerCodesize, add(start, DATA_OFFSET))

            // Get the pointer to the free memory and allocate
            // enough 32-byte words for the data and the length of the data,
            // then copy the code to the allocated memory.
            // Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
            data := mload(0x40)
            mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
            mstore(data, size)
            mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
            extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size)
        }
    }

    /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`,
    /// from the byte at `start`, to the byte at `end` (exclusive) of the data stored.
    function read(address pointer, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let pointerCodesize := extcodesize(pointer)
            if iszero(pointerCodesize) {
                // Store the function selector of `InvalidPointer()`.
                mstore(0x00, 0x11052bb4)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // If `!(pointer.code.size > end) || (start > end)`, revert.
            // This also handles the cases where
            // `end + DATA_OFFSET` or `start + DATA_OFFSET` overflows.
            if iszero(
                and(
                    gt(pointerCodesize, end), // Within bounds.
                    iszero(gt(start, end)) // Valid range.
                )
            ) {
                // Store the function selector of `ReadOutOfBounds()`.
                mstore(0x00, 0x84eb0dd1)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }
            let size := sub(end, start)

            // Get the pointer to the free memory and allocate
            // enough 32-byte words for the data and the length of the data,
            // then copy the code to the allocated memory.
            // Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
            data := mload(0x40)
            mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
            mstore(data, size)
            mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
            extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size)
        }
    }
}

// File: contracts/lib/Strings.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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



//  _____         _     _ _                                                _   _             
// /__   \___  __| | __| (_) ___  ___     ___  _ __    /\   /\__ _  ___ __ _| |_(_) ___  _ __  
//   / /\/ _ \/ _` |/ _` | |/ _ \/ __|   / _ \| '_ \   \ \ / / _` |/ __/ _` | __| |/ _ \| '_ \ 
//  / / |  __/ (_| | (_| | |  __/\__ \  | (_) | | | |   \ V / (_| | (_| (_| | |_| | (_) | | | |
//  \/   \___|\__,_|\__,_|_|\___||___/   \___/|_| |_|    \_/ \__,_|\___\__,_|\__|_|\___/|_| |_|
// // // // // // // //                             =########             
// // // // // // // //       :********.   :**********#######**.          
// // // // // // // //     -+*********+++++*************====##.          
// // // // // // // //   -+*#*****====******************++--##.          
// // // // // // // //   +##****==--++********************++--           
// // // // // // // //   +##****--++************************             
// // // // // // // //   -=+##**==**************************--           
// // // // // // // //     -++##***************##*******##****.          
// // // // // // // //       :+****************%%*******%%****.          
// // // // // // // //         .##*************##=-----=##****.          
// // // // // // // //         .##*************-------======**.          
// // // // // // // //         .##***********--------*%%%%%%--           
// // // // // // // //         .##***********----------=%#----           
// // // // // // // //            ##***********---------=---             
// // // // // // // //            ..*#**********************             
// // // // // // // //              *######**************+..             
// // // // // // // //            ==*##########***********++             
// // // // // // // //          --****###########*********##====.        
// // // // // // // //       .--********#########***********####+-:      
// // // // // // // //     .:=********#########***************####+-:    
// // // // // // // //     =**********#######*****************######*::  
// // // // // // // //   ..=**********#######*******************#######  
// // // // // // // //   +**********#######*********************#######  
// // // // // // // //   +**********#######*********************#########
// // // // // // // // ***********#########*********************#########
/**
 * @title TeddiesOnVacationRenderer
 * @author numo <@numo_0> <info@numo.art>
 */

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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./core/ToVInfos.sol";
import "./lib/DynamicBuffer.sol";
import "./lib/Base64.sol";
import "./lib/SSTORE2.sol";
import "./lib/Strings.sol";

contract TeddiesOnVacationRenderer is Ownable, ReentrancyGuard {
    using DynamicBuffer for bytes;

    error InvalidSeason();

    struct TraitDTO {
        string name;
        string mimetype;
        bytes data;
        bool hide;
        bool useExistingData;
        uint existingDataIndex;
    }

    struct Trait {
        string name;
        string mimetype;
        bool hide;
    }

    struct OneOfOne { 
        uint256 tokenId;
        uint256 dna;
    }

    bool private shouldWrapSVG = true;
    string private backgroundColor = "transparent";
    string private placeholderImage = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMTIwMCAxMjAwIiB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGltYWdlIHdpZHRoPSIxMjAwIiBoZWlnaHQ9IjEyMDAiIGhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjNhV1IwYUQwaU1USXdNQ0lnYUdWcFoyaDBQU0l4TWpBd0lpQjJhV1YzUW05NFBTSXdJREFnTVRJd01DQXhNakF3SWlCMlpYSnphVzl1UFNJeExqSWlJSGh0Ykc1elBTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHlNREF3TDNOMlp5SWdjM1I1YkdVOUltSmhZMnRuY205MWJtUXRZMjlzYjNJNmRISmhibk53WVhKbGJuUTdZbUZqYTJkeWIzVnVaQzFwYldGblpUcDFjbXdvWkdGMFlUcHBiV0ZuWlM5d2JtYzdZbUZ6WlRZMExHbFdRazlTZHpCTFIyZHZRVUZCUVU1VFZXaEZWV2RCUVVGRFFVRkJRVUZuUTBGWlFVRkJRbnBsYm5Jd1FVRkJRVUZZVGxOU01FbEJjbk0wWXpaUlFVRkNVRlpLVWtWR1ZWZEpWMlJzTURGMlNFVlZVV2h3SzNGSWRTdDFOMWxxUldwdFRWUktlVWhvVVVWRWFVTjFTVk5KV2tOR1JrOVlRMUFyVlVWVFFXZHBTV05uYUVWeU9FRndTa0ZSWmpZeGFuZzBOR3BtTW5neWNEWnpOR1JOTDNNNU9YQlJNRzFxWVRka05UWXpNM0Z5Y1hKMFNGZHNPVGcxYlZONmQzbFBiVzFUTkhablUzQmhWRGhxZDBGcmRIVkVUVUp6Y1hBNVVrdDVORVZHTUVaRlMwbHZRV3QxUWtaa1dIaGtRVVJrVEVKR1ZYZDNUWGQyU2taT1RVRjBabXhLVmxOV1pVaHBSVnBCVEhnMlNtcEhObXR3VGtscE0wMDBkMjlyUWtoRVJFTkJVbnBXUVhoVVFWaE9jME53Um1sa1JVcFljbkZOY1hGRGFHaGlXbFkwT0VKd1JWVkNSVEJHUldkc0wxTkRVa1JCZW1kc2RFcHJXWFpSVDFoMVVIVTVVMUJNVXpKUFMwUkNSVkZGV1c5UmEzTlRXbWhQWTNoRFIwWjFUSEpOVlZKd1RtZG9SbEZJUVdONVRYVk1LMUJ4YlV4MU5FazJORFF3WkhaalJFNXJObVJ2ZDJkVmNIbDZaVUZoUVhGeFMzRkNRbFl3TVdsdkx6a3JXRXMyZEdoVWIwTTJSRzV3TjJVMlowZEVSVkI0YkVGTVVEbGFSRGxXVTJ4S1FrTnlkMGx4UTFOQlZVOW5aa3hXVUVOSmNVa3djbmt4YW5GeFFUVkZORmxLUWtsQlRYZDZiemRNV1ZKRVdXYzNTV3BETTNSdllWb3daRE5hZUZaVVFraFVkRFpuZVRWa2R6aDNiMmhwU1ZoU1dYVkJhVVpNZFRkNVQzRmtVa1ZYU1ZWQ1YxcGhaMlJDTVVsUmVrNUtkbU56Tm1KT09XUjRUVEZEYUhOWU5sUmljblZPVWxWek1VWlZTbGRSUzFWMkszZEROR2wwV1VVMWRTOWpVbXhTVVRGaFVrRkdaakZCUTNGeVF6QTJRelJLT1cxTVJVUkJWbkJMZVRCR2FsRnZVbTAxYkU0MGIydDFaVkU0U25kVlIzZFNablV6YTJ0d1JXOVZhVWxLUkVkQmVXNVpLeXR1ZUd0Q2IzSkhkemgzVGpoM1RYSmpRa3hGYm1rd01VWkZjWFZEYlVZMVFUSnRhVzVCVVM5UGVsQmFNMU5tWWpsbFR6RjROWFJ2UTNKelpuWTRUSE1yZW1jNFVrNVhkblpyVTBwT1YwWnNkRVJhTWpBd1FrdG9VbkpPTkdoc1VrRnZiMDVDWTJoWFVXeFNhRkYzSzJGdWN5OVFhRFppUnpkUmR6RjNRV2M1Y25CSlEwdHNUMWhFYkhSaU9XTTNTMnRNUjJOdmFVZEVhMXBtYTJWbk9UTTJXbTkwUTJkaGNtUndlRFZZZVZkNFZqWXpTR3hsWkdSTU5qbFhlR1Y1YVU1Q1kxZzJPV0pJVGtoVlFtTlFVa3N5VnpOVmVuUnhUSEkwTXpWMmQybEZkWFptVUd0NlZrUTBhVUZ2TkdjMmVtWldZbGRKZVc5SE1scEthR0pSVUd0SFUyaFZiVXBIV2sxQlVWRmhRbGR4YnpRcmFFcFNOM1F4ZUdab2NIWnJibVJJUVVaRFZWTkJVMWd6WkZVd1JYVlFUSEF2VlhWRVZqTlBSRGd4ZG1ZdlZrUTNhM0JHTXk5NmFGQkJXVGhrVW1kQlRHUXlOWG8xWkRRNVJHNDNLMkpUWWpSUmNsQkNNbUp6SzNkaGRVdzRlSGxtYm0xalFVRldUM1ZtU0dsUU1rOTBhSE5UVTFkalVYaHlkM05JWlhRM1ZraEhNVkJDYVRCaFRDdFpWa1pPUWxReFl6TTFNazkxWW1vMVRWVXZia3czWXpWSGVIcGhlVXhIVkVGTVZFeG9UbFo2YXlzMmEySnVWMUZxTTJaTFlUTm1UR1ZpY0dsSVlXWTFTMHgyUXpOTlVXYzJjSGRaVDJjNWVsVmlZbk5zVjJ0WFQxbGlaMUpOWVRsWE9UazFSak0zVGtWMlltUlVWM0IxVkhCT2FrZERVek5uVUROMmRFWkxTRGRXVDJGaVVVczFZVk5pT0hWNGJXMUViVXhJT1hkdGJISkdaa0V4YlZwcGIybGhSMlF4TW0wNE5VOXRPRGN5UkhBeWRHSlVkM1pqYms1MVVGQjROa2x0UkZaSVlqTlBWMWdyZFZORFVsaElNelpHZUZscFdrUm1iRXMzTUZOTk5uRjZiemRpTTNFek9XNWpZMGhtVDA1eVl6Um1ZbTFLZUZKTFVHTlRjVXBrTm1SMmMxWnFWMVFyZURGcFlqRjFTSHA1YlhsTk1ITlFNek5uYVd4RWFHZE1iV3BsUzNGWU5uWnZWVEF3VEVWS2JHSnhkRTB4Ynpka1JrZEpjRFpFVFdwUE9HcENUMnBPTUVKaGJqbDRNMVpoVmpVNEx6RXdRM2h4TlVONWVtTjJZMDlpV0RVdlQwcEVRbUZJTkZCSWMxcGplR3BqZGtrcll6VlBkWEJDYTFseWNUaG9SRkV2TDNOblltRTJkRlJNZVZNNVJucHpWRFZhT1VadlRFdHlSSG8wWmtJNFpYaFhTMnhvUzA4M2NHcDRhWGhOZFVwMVYwUlVUV3BtVHk4dmNEZHZLMHhLTWk5UFVWQTBhRkozZWpka2JHeGhWbkpYU1hsS1VrZGhURWRrTVc1TU1tTTJjalEzY21rNWNubzNaVGxRS3pkS1NFazVlVFJVY21rcldFcE9VVk5RWkRScGVFVjJTVGt6TWxoeWNFTk5OM1l6Y1hkT2J6QjFSVEIwZG1KR1JuVmhaV2ROZFVsNFZtZ3ZSeTlUTDBSak1rbHlkemhTVmxoNGVqa3dTMjR2T1ZoamNrb3ZjV2RUTWlzM2FrRTNVRTFMT1M5M01UTnJZbE5ZZEZOd1RsbG1LMWxqWjFGalNtNHdZblJuT0hWQ1pqWXZRMEUyUVZaNEwwVXZkMG9yZEVOTlMyeERjRko2VVVGQlFVRkNTbEpWTlVWeWEwcG5aMmM5UFNrN1ltRmphMmR5YjNWdVpDMXlaWEJsWVhRNmJtOHRjbVZ3WldGME8ySmhZMnRuY205MWJtUXRjMmw2WlRwamIyNTBZV2x1TzJKaFkydG5jbTkxYm1RdGNHOXphWFJwYjI0NlkyVnVkR1Z5TzJsdFlXZGxMWEpsYm1SbGNtbHVaem90ZDJWaWEybDBMVzl3ZEdsdGFYcGxMV052Ym5SeVlYTjBPeTF0Y3kxcGJuUmxjbkJ2YkdGMGFXOXVMVzF2WkdVNmJtVmhjbVZ6ZEMxdVpXbG5hR0p2Y2p0cGJXRm5aUzF5Wlc1a1pYSnBibWM2TFcxdmVpMWpjbWx6Y0MxbFpHZGxjenRwYldGblpTMXlaVzVrWlhKcGJtYzZjR2w0Wld4aGRHVmtPeUkrUEM5emRtYysiPjwvaW1hZ2U+PC9zdmc+";
    string[] private layerNames =   [   
                                        unicode"1/1", 
                                        unicode"Handheld", 
                                        unicode"Head", 
                                        unicode"Head", // mask & helmet
                                        unicode"Eyes", 
                                        unicode"Snout", 
                                        unicode"Body", 
                                        unicode"Type", 
                                        unicode"Background"
                                    ];

    bytes32 public SEASON1_ONE_OF_ONE_IDS_HASH = 0x1980099be486d3bcdb0216af2c717f8dae73c16812d94dc1ac4c1972c9f78195;
    bytes32 public SEASON2_ONE_OF_ONE_IDS_HASH = 0x82998fb94fcc4699e39f14bcb230450ef96ee7f522d72bfb40f6dc283b545417;
    bytes32 public SEASON3_ONE_OF_ONE_IDS_HASH = 0xfdce05748e2384f41f03ee9eba3b81b6a7af2786dabb77f01597948f00cb82f7;

    uint256 public constant NUM_LAYERS = 9;
    uint256 public constant NUM_SEASONS = 4;
    uint16[][NUM_LAYERS] WEIGHTS;
    uint16[NUM_SEASONS][NUM_LAYERS] WEIGHTS_START_INDEX;

    // After contract sealed, no changes are possible anymore
    bool public isContractSealed;
    bool[NUM_SEASONS] public isRevealed;

    // on-chain data for the art
    mapping(uint => address[]) private _traitDataPointers;
    // on-chain data for the metadata
    mapping(uint => mapping(uint => Trait)) private _traitDetails;
    // information about the one of ones for each season
    mapping(uint => OneOfOne[]) public oneOfOnes;

    constructor() {
        // Start index for each season per trait
        WEIGHTS_START_INDEX[0] = [0, 4, 7, 10];
        WEIGHTS_START_INDEX[1] = [23, 35, 45, 23];
        WEIGHTS_START_INDEX[2] = [36, 43, 51, 36];
        WEIGHTS_START_INDEX[3] = [2, 23, 41, 1];
        WEIGHTS_START_INDEX[4] = [23, 28, 33, 23];
        WEIGHTS_START_INDEX[5] = [25, 28, 30, 25];
        WEIGHTS_START_INDEX[6] = [20, 33, 43, 20];
        WEIGHTS_START_INDEX[7] = [7, 7, 7, 7];
        WEIGHTS_START_INDEX[8] = [5, 5, 5, 5];
    
        
        WEIGHTS[0] = [/* 1 */ 2500, 2500, 2500, 2500,
                      /* 2 */ 3334, 3333, 3333,
                      /* 3 */ 3334, 3333, 3333];
        WEIGHTS[1] = [/* Basic */   170, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 125, 125, 175,
                      /* 1 */       100, 80, 80, 100, 100, 100, 80, 80, 80, 100, 80, 100,
                      /* 2 */       118, 90, 110, 90, 120, 112, 120, 80, 120, 120,
                      /* 3 */       77, 77, 77, 77, 77, 77, 77, 78, 77, 77, 77, 77, 77, 78,
                      /* None */    5000];
        WEIGHTS[2] = [/* Basic */   277, 277, 277, 277, 277, 227, 227, 227, 227, 227, 177, 277, 277, 227, 187, 177, 177, 277, 137, 137, 137, 137, 137, 227, 177, 277, 277, 277, 1195, 177, 227, 227, 127, 227, 177, 177,
                      /* 1 */       150, 150, 200, 200, 200, 150, 150,
                      /* 2 */       150, 150, 150, 150, 150, 150, 150, 150,
                      /* 3 */       160, 160, 130, 130, 115, 115, 130, 130, 130];
        WEIGHTS[3] = [/* Basic */   63, 13,
                      /* 1 */       20, 6, 20, 6, 20, 20, 20, 6, 20, 6, 20, 20, 6, 20, 20, 7, 20, 20, 7, 20, 20,
                      /* 2 */       23, 10, 15, 9, 20, 25, 25, 9, 25, 24, 10, 15, 20, 10, 24, 25, 10, 25,
                      /* 3 */       47, 14, 47, 47, 14, 47, 14, 47, 47,
                      /* None */    9600];
        WEIGHTS[4] = [/* Basic */   436, 386, 636, 432, 386, 386, 386, 386, 386, 436, 386, 386, 386, 336, 386, 386, 386, 386, 436, 280, 330, 380, 380,
                      /* 1 */       200, 200, 150, 200, 150,
                      /* 2 */       180, 180, 180, 180, 180,
                      /* 3 */       150, 150, 150, 150, 150, 150];
        WEIGHTS[5] = [/* Basic */   594, 444, 434, 194, 434, 394, 424, 444, 394, 444, 284, 294, 294, 284, 444, 344, 434, 444, 384, 384, 394, 394, 284, 244, 194,
                      /* 1 */       250, 250, 200,
                      /* 2 */       350, 350,
                      /* 3 */       100, 100, 100, 100, 100, 100, 100];
        WEIGHTS[6] = [/* Basic */   460, 305, 410, 460, 365, 365, 415, 415, 415, 660, 415, 395, 285, 285, 285, 285, 285, 415, 415, 415,
                      /* 1 */       150, 200, 200, 150, 200, 200, 150, 150, 150, 200, 150, 200, 150,
                      /* 2 */       225, 225, 225, 225, 225, 155, 225, 260, 260, 225,
                      /* 3 */       187, 190, 187, 187, 187, 187, 187, 187, 190, 187, 187, 187];
        WEIGHTS[7] = [/* Basic */   5000, 900, 500, 900, 900, 900, 900,
                      /* Special */ 10000];
        WEIGHTS[8] = [/* Basic */ 1625, 3500, 1625, 1625, 1625];
    }

    /**
     * Checks if the contract is sealed.
     */
    modifier whenUnsealed() {
        require(!isContractSealed, "Contract is sealed");
        _;
    }

    /**
     * Function to retrieve the metadata & art for a given token.
     * @param tokenId The tokenID to get the data for
     * @param tovData Addition information for retrieving the correct data
     * @param contractData The contract information of the calling contract
     */
    function tokenURI(uint256 tokenId, ToVInfos.ToV memory tovData, ToVInfos.ContractData memory contractData) 
        public 
        view 
        returns (string memory) 
    {
        bool oneOfOne;
        uint256 tovDna;

        (oneOfOne, tovDna) = isOneOfOneTokenId(tokenId, tovData.season);

        uint16[NUM_LAYERS] memory dna;
        if (oneOfOne) {
            dna = getRarityWeights(tovDna);
        } else {            
            dna = getRarityWeights(tovData.dna);
            dna[0] = 0;
        }

        bytes memory jsonBytes = DynamicBuffer.allocate(1024 * 128);

        jsonBytes.appendSafe(
            abi.encodePacked(
                '{"name":"',
                contractData.name,
                " #",
                Strings.toString(tokenId),
                '","description":"',
                contractData.description,
                '",'
            )
        );

        if (!isRevealed[tovData.season-1]) {
            jsonBytes.appendSafe(
                abi.encodePacked(
                    '"image":"',
                    placeholderImage,
                    '"}'
                )
            );
        } else {
            string memory svgCode = "";
            if (shouldWrapSVG) {
                string memory svgString = getSVG(dna, tovData.season);

                svgCode = string(
                    abi.encodePacked(
                        "data:image/svg+xml;base64,",
                        Base64.encode(
                            abi.encodePacked(
                                '<svg width="100%" height="100%" viewBox="0 0 1200 1200" version="1.2" xmlns="http://www.w3.org/2000/svg"><image width="1200" height="1200" href="',
                                svgString,
                                '"></image></svg>'
                            )
                        )
                    )
                );
            } else {
                svgCode = getSVG(dna, tovData.season);
            }

            jsonBytes.appendSafe(
                abi.encodePacked(
                    '"image_data":"',
                    svgCode,
                    '",'
                )
            );

            jsonBytes.appendSafe(
                abi.encodePacked(
                    '"attributes":',
                    getMetaData(dna, tovData.season),
                    "}"
                )
            );
        }

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

    /**
     * Gets the SVG image of a certain DNA.
     * @param _dna The DNA used to retrieve the art of it
     * @param _season The current season of the DNA
     */
    function getSVG(uint16[NUM_LAYERS] memory _dna, uint256 _season) public view returns (string memory) {
        uint256 traitIndex;
        bool oneOfOne = hasOneOfOne(_dna[0]);
        bool handheld = hasHandHeld(_dna[1]);
        bool mask = hasMask(_dna[3]);
        bool eyesOnTop;

        bytes memory svgBytes = DynamicBuffer.allocate(1024 * 128);

        svgBytes.appendSafe('<svg width="1200" height="1200" viewBox="0 0 1200 1200" version="1.2" xmlns="http://www.w3.org/2000/svg" style="background-color:');
        svgBytes.appendSafe(
            abi.encodePacked(
                backgroundColor,
                ";background-image:url("
            )
        );

        // Put eyes on top if necessary/defined
        (traitIndex, ) = getTraitIndex(_dna[4], 4, _season);

        if (!oneOfOne && !mask && ((traitIndex > 18 && traitIndex < 23) 
        || (_season == 1 && traitIndex == 27 ))) {
            svgBytes.appendSafe(
                abi.encodePacked(
                    "data:",
                    _traitDetails[4][traitIndex].mimetype,
                    ";base64,",
                    Base64.encode(SSTORE2.read(_traitDataPointers[4][traitIndex])),
                    "),url("
                )
            );
            eyesOnTop = true;
        }

        uint16[][NUM_LAYERS] memory weights = WEIGHTS;
        uint256 weightsLength;
        uint256 layerIndexFactor = oneOfOne ? 0 : NUM_LAYERS - 1;

        if (!oneOfOne) {
            for (uint8 i = 1; i < NUM_LAYERS - 1; i++) {
                weightsLength = weights[i].length;
                    
                (traitIndex, ) = getTraitIndex(_dna[i], i, _season);            
                
                // Add layer only for a valid index
                if (traitIndex < weightsLength) {
                    // Make sure there is no mask, eyes or snout when DNA has mask trait
                    // Skip eyes trait if eyes is on top because already appended above
                    if (((i == 2 || i == 4 || i == 5) && !mask) || (i < 4 || (i > 5 && i < 8))) {
                        if (!handheld && i == 1) continue;
                        if (mask && i == 2) continue;
                        if (!mask && i == 3) continue;
                        if (eyesOnTop && i == 4) continue;
                        svgBytes.appendSafe(
                            abi.encodePacked(
                                "data:",
                                _traitDetails[i][traitIndex].mimetype,
                                ";base64,",
                                Base64.encode(SSTORE2.read(_traitDataPointers[i][traitIndex])),
                                "),url("
                            )
                        );
                    }
                }
            }
        }

        (traitIndex, ) = getTraitIndex(_dna[layerIndexFactor], uint8(layerIndexFactor), _season);

        svgBytes.appendSafe(
            abi.encodePacked(
                "data:",
                _traitDetails[layerIndexFactor][traitIndex].mimetype,
                ";base64,",
                Base64.encode(SSTORE2.read(_traitDataPointers[layerIndexFactor][traitIndex])),
                ');background-repeat:no-repeat;background-size:contain;background-position:center;image-rendering:-webkit-optimize-contrast;-ms-interpolation-mode:nearest-neighbor;image-rendering:-moz-crisp-edges;image-rendering:pixelated;"></svg>'
            )
        );

        return string(
            abi.encodePacked(
                "data:image/svg+xml;base64,",
                Base64.encode(svgBytes)
            )
        );
    }

    /**
     * Gets the metadata of a certain tokenId.
     * @param _tokenId The DNA used to retrieve the metadata of it
     * @param _tovData Data of the ToV
     */
    function getMetaDataFromTokenID(uint256 _tokenId, ToVInfos.ToV memory _tovData) public view returns (string memory) {
        if (isRevealed[_tovData.season-1]) {
            bool oneOfOne;
            uint256 tovDna;
            (oneOfOne, tovDna) = isOneOfOneTokenId(_tokenId, _tovData.season);

            uint16[NUM_LAYERS] memory dna;
            if (oneOfOne) {
                dna = getRarityWeights(tovDna);
            } else {            
                dna = getRarityWeights(_tovData.dna);
                dna[0] = 0;
            }
            return getMetaData(dna, _tovData.season);
        }
        return "";
    }

    /**
     * Gets the metadata of a certain DNA.
     * @param _dna The DNA used to retrieve the metadata of it
     * @param _season The current season of the DNA
     */
    function getMetaData(uint16[NUM_LAYERS] memory _dna, uint256 _season) public view returns (string memory) {
        bytes memory metadataBytes = DynamicBuffer.allocate(1024 * 128);
        uint256 traitIndex;
        uint64 seasonTraitCount;
        bool afterFirstTrait;
        bool oneOfOne = hasOneOfOne(_dna[0]);
        bool handheld = hasHandHeld(_dna[1]);
        bool mask = hasMask(_dna[3]);
        bool isSeasonTrait;
        uint8 layerIndexFactor = oneOfOne ? 0 : 1;
        
        uint16[][NUM_LAYERS] memory weights = WEIGHTS;
        uint256 weightsLength;

        metadataBytes.appendSafe("[");
        
        for (uint8 i = layerIndexFactor; i < NUM_LAYERS; i++) {
            weightsLength = weights[i].length;

            (traitIndex, isSeasonTrait) = getTraitIndex(_dna[i], i, _season);

            // Add layer only for a valid index
            if (traitIndex < weightsLength) {   
                // Make sure there is no mask, eyes or snout when DNA has mask trait
                if (((oneOfOne && i == 0) || (!oneOfOne && i > 0)) && (_traitDetails[i][traitIndex].hide == false) &&
                (((i == 2 || i == 4 || i == 5) && !mask) || (i < 4 || (i > 5 && i < 9)))) {
                    if (!handheld && i == 1) continue;
                    if (mask && i == 2) continue;
                    if (!mask && i == 3) continue;
                    if (afterFirstTrait) {
                        metadataBytes.appendSafe(",");
                    }

                    if (isSeasonTrait) {
                        ++seasonTraitCount;
                    }

                    metadataBytes.appendSafe(
                        abi.encodePacked(
                            '{"trait_type":"',
                            layerNames[i],
                            '","value":"',
                            _traitDetails[i][traitIndex].name,
                            '"}'
                        )
                    );
                    if (afterFirstTrait == false) {
                        afterFirstTrait = true;
                    }
                    isSeasonTrait = false;
                }

                if (i == NUM_LAYERS - 1) {
                    if (!oneOfOne) {
                        metadataBytes.appendSafe(
                            abi.encodePacked(
                                ',{"trait_type":"',
                                "Season Traits",
                                '","value":"',
                                Strings.toString(seasonTraitCount),
                                '"}'
                            )
                        );
                    }

                    metadataBytes.appendSafe(
                        abi.encodePacked(
                            ',{"trait_type":"',
                            "Season",
                            '","value":"',
                            Strings.toString(_season),
                            '"}'
                        )
                    );
                  
                    metadataBytes.appendSafe("]");
                }
            }
        }       

        return string(metadataBytes);
    }

    /**
     * Checks if a certain tokenID is a one of one and returns the DNA of it.
     * @param _tokenId  The tokenID to check
     * @param _season The season of the one of one
     * @return bool Returns if an one of one was found
     * @return uint256 The DNA of the one of one 
     */
    function isOneOfOneTokenId(uint256 _tokenId, uint256 _season) public view returns (bool, uint256) {
        OneOfOne[] memory seasonOneOfOnes = oneOfOnes[_season];
        for (uint256 i = 0; i < seasonOneOfOnes.length; i++) {
            if (seasonOneOfOnes[i].tokenId == _tokenId) {
                return (true, seasonOneOfOnes[i].dna);
            }
        }
        return (false, 0);
    }

    /**
     * Gets the rarity weights of a certain DNA.
     * @param _dna DNA to get the weights.
     */
    function getRarityWeights(uint256 _dna) internal pure returns (uint16[NUM_LAYERS] memory rarityWeights) {
        for (uint256 i = 0; i < rarityWeights.length; i++) {
            rarityWeights[i] = uint16(_dna % 10000);
            _dna >>= 14;
        }
        return rarityWeights;
    }

    /**
     * Retrieves the trait index for a certain trait weight and season.
     * @param _traitWeight The trait weight to determine the trait index for
     * @param _index Determines which trait type
     * @param _season The season of the trait index
     * @return uint256 returns the trait index
     * @return bool returns if the trait is a season trait
     */
    function getTraitIndex(uint16 _traitWeight, uint8 _index, uint256 _season) public view returns (uint256, bool) {
        uint16 i;
        uint16 lowerBound;
        uint16 percentage;
        uint16[] memory currentWeights = WEIGHTS[_index];
        uint16[NUM_SEASONS] memory startIndices = WEIGHTS_START_INDEX[_index];
        uint256 maxIndex;
        bool seasonTrait;

        if (_season == NUM_SEASONS) {
            maxIndex = currentWeights.length;
           
            // Traits only from the last season except for Background
            if (_index == 8) {
                i = 0;
            } else {
                i = startIndices[_season-1];
            }

            _traitWeight = uint16(multiplyByFactor(_traitWeight, _index));
        } else {
            // take start index of the next season as max of the current season.
            if (_season == 3) {
                maxIndex = currentWeights.length;
            } else {
                maxIndex = startIndices[_season];
            }
            // Traits only from the certain season for one on ones
            if (_index == 0) {
                i = startIndices[_season-1];
            } else {
                i = 0;                
            }
        }

        for (; i < maxIndex; i++) {
            percentage = currentWeights[i];  
    
            if (_traitWeight >= lowerBound && _traitWeight < lowerBound + percentage) {
                if (i >= startIndices[_season-1] && i < maxIndex && _index != 7 && _index != 8) {
                    seasonTrait = true;
                }

                return (i, seasonTrait);
            }
            lowerBound += percentage;
            
            // jump to the correct season index
            // startIndices[0] - 1 stands for the base trait max index
            // Don't jump for OneOfOnes, Background and Type traits
            if((_index != 0 && _index != 7 && _index != 8) && i == (startIndices[0] - 1)) {
                i = startIndices[_season-1] - 1;
            }
        }
        
        // If not found, return index higher than available layers. Will get filtered out.
        return (currentWeights.length, false);
    }

    function hasMask(uint256 _traitWeight) public view returns (bool) {
        return _traitWeight < (10000 - WEIGHTS[3][50]);
    }

    function hasHandHeld(uint256 _traitWeight) public view returns (bool) {
        return _traitWeight < (10000 - WEIGHTS[1][59]);
    }
    
    function hasOneOfOne(uint256 _traitWeight) public pure returns (bool) {
        return _traitWeight > 0;
    }

    /**
     * A factor to keep the trait weight in correct range for the last season.
     * @param _value The value which should be transformed
     * @param _index Determines the trait type
     */
    function multiplyByFactor(uint256 _value, uint256 _index) public pure returns (uint) {
        if (_index == 1) {
            return (_value * 648) / 1000;
        } else if (_index == 2) {
            return (_value * 36) / 100;
        } else if (_index == 3) {
            return (_value * 24) / 100;
        } else if (_index == 4) {
            return (_value * 27) / 100;
        } else if (_index == 5) {
            return (_value * 21) / 100;
        } else if (_index == 6) {
            return (_value * 675) / 1000;
        } else {
            return _value;
        }
    }

    // =========================================================================
    //                             Owner Functions
    // =========================================================================

    /**
     * Function to add the different metadata and art for the layers (trait types) on-chain
     * @param layerIndex Describes which trait type
     * @param traits Array of the metadata and art of the layer
     */
    function addLayer(uint layerIndex, TraitDTO[] calldata traits) public onlyOwner whenUnsealed {
        require(WEIGHTS[layerIndex].length == traits.length, "Traits length is incorrect");
        address[] memory dataPointers = new address[](traits.length);
        for (uint i = 0; i < traits.length; i++) {
            if (traits[i].useExistingData) {
                dataPointers[i] = dataPointers[traits[i].existingDataIndex];
            } else {
                dataPointers[i] = SSTORE2.write(traits[i].data);
            }
            _traitDetails[layerIndex][i] = Trait(traits[i].name, traits[i].mimetype, traits[i].hide);
        }
        _traitDataPointers[layerIndex] = dataPointers;
        return;
    }

    /**
     * Function to add/Update the metadata and art of one trait on-chain.
     * @param layerIndex Describes which trait type
     * @param traitIndex The certain trait which should be updated
     * @param trait Metadata and art of the new/updated trait
     */
    function addTrait(uint layerIndex, uint traitIndex, TraitDTO calldata trait) public onlyOwner whenUnsealed {
        _traitDetails[layerIndex][traitIndex] = Trait(trait.name, trait.mimetype, trait.hide);
        address[] memory dataPointers = _traitDataPointers[layerIndex];
        if (trait.useExistingData) {
            dataPointers[traitIndex] = dataPointers[trait.existingDataIndex];
        } else {
            dataPointers[traitIndex] = SSTORE2.write(trait.data);
        }
        _traitDataPointers[layerIndex] = dataPointers;
        return;
    }

    /**
     * Set the unique hash for the encrypted one of ones. 
     * @param _hash Holds the hash for the one of ones
     * @param _season One of ones for a certain season
     */
    function setOneOfOneHash(bytes32 _hash, uint256 _season) public onlyOwner whenUnsealed {
        if (_season == 1) {
            SEASON1_ONE_OF_ONE_IDS_HASH = _hash;
        } else if (_season == 2) {
            SEASON2_ONE_OF_ONE_IDS_HASH = _hash;
        } else {
            SEASON3_ONE_OF_ONE_IDS_HASH = _hash;
        }
    }

    /**
     * Set the one of one data.
     * @param _oneOfOnes Holds the data for the one of ones
     * @param _season One of ones for a certain season
     */
    function commitOneOfOnes(OneOfOne[] calldata _oneOfOnes, uint256 _season) public onlyOwner whenUnsealed {
        bytes32 hashedOneOfOnes = keccak256(abi.encode(_oneOfOnes));
        if (SEASON1_ONE_OF_ONE_IDS_HASH == hashedOneOfOnes ||
            SEASON2_ONE_OF_ONE_IDS_HASH == hashedOneOfOnes ||
            SEASON3_ONE_OF_ONE_IDS_HASH == hashedOneOfOnes ) {
            oneOfOnes[_season] =  _oneOfOnes;
        }
    }
    
    /**
     * Reveals or unreveals a certain season.
     * @param _season The season which should be revealed
     * @param _reveal Value if reveal or unreveal
     */
    function setRevealSeason(uint256 _season, bool _reveal) public onlyOwner {
        if (_season <= 0 || _season > NUM_SEASONS) revert InvalidSeason();
        isRevealed[_season-1] = _reveal;
    }

    /**
     * Seals the contract, so no changes are possible anymore.
     */
    function sealRendererContract() external whenUnsealed onlyOwner {
        isContractSealed = true;
    }
    function toggleWrapSVG() external onlyOwner {
        shouldWrapSVG = !shouldWrapSVG;
    }
    function setBackgroundColor(string calldata color) external onlyOwner {
        backgroundColor = color;
    }
    function setPlaceholderImage(string calldata placeholder) external onlyOwner {
        placeholderImage = placeholder;
    }    
}