BaseBase
0x55...2f4e
Stack Score

Stack Score

STACK_SCORE

Colección
Precio de Piso
0,001 ETH
$2,345.34
Volumen de 24h
0 ETH
$2,345.34
Tamaño
7917
Coleccionables
Propietarios
7917
100 % Propietarios Únicos
¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.24+commit.e11b9ed9
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 24: AbstractNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {ERC721ConduitPreapproved_Solady, ERC721} from "./tokens/erc721/ERC721ConduitPreapproved_Solady.sol";
import {json} from "./onchain/json.sol";
import {Metadata} from "./onchain/Metadata.sol";
import {LibString} from "solady/utils/LibString.sol";
import {Solarray} from "solarray/Solarray.sol";
import {OnchainTraits, DynamicTraits} from "./dynamic-traits/OnchainTraits.sol";

// @authors Modified from: https://github.com/ProjectOpenSea/shipyard-core/blob/main/src/reference/AbstractNFT.sol
abstract contract AbstractNFT is OnchainTraits, ERC721ConduitPreapproved_Solady {
    string _name;
    string _symbol;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @notice Get the metdata URI for a given token ID
     * @param tokenId The token ID to get the tokenURI for
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        return _stringURI(tokenId);
    }

    /**
     * @notice Helper function to get the raw JSON metadata representing a given token ID
     * @param tokenId The token ID to get URI for
     */
    function _stringURI(uint256 tokenId) internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @notice Helper function to get both the static and dynamic attributes for a given token ID
     * @param tokenId The token ID to get the static and dynamic attributes for
     */
    function _attributes(uint256 tokenId) internal view virtual returns (string memory) {
        // get the static attributes
        string[] memory staticTraits = _staticAttributes(tokenId);
        // get the dynamic attributes
        string[] memory dynamicTraits = _dynamicAttributes(tokenId);

        // return the combined attributes as a property containing an array
        return json.rawProperty("attributes", json.arrayOf(staticTraits, dynamicTraits));
    }

    /**
     * @notice Helper function to get the static attributes for a given token ID
     * @param tokenId The token ID to get the static attributes for
     */
    function _staticAttributes(uint256 tokenId) internal view virtual returns (string[] memory);

    /**
     * @notice Helper function to get the raw SVG image for a given token ID
     * @param tokenId The token ID to get the dynamic attributes for
     */
    function _image(uint256 tokenId) internal view virtual returns (string memory);

    function supportsInterface(bytes4 interfaceId) public view virtual override(DynamicTraits, ERC721) returns (bool) {
        return DynamicTraits.supportsInterface(interfaceId) || ERC721.supportsInterface(interfaceId);
    }
}
Código Fuente del Contrato
Archivo 2 de 24: 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 0x0670 will turn "-_" into "+/".
                mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
                mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))

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

                let dataEnd := add(add(0x20, data), dataLength)
                let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot.
                mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits.

                // 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 }
                }
                mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`.
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                // 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)
                mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
                mstore(result, sub(encodedLength, o)) // Store the length.
            }
        }
    }

    /// @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 Decodes 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 }
                }
                mstore(0x40, add(end, 0x20)) // Allocate the memory.
                mstore(end, 0) // Zeroize the slot after the bytes.
                mstore(0x60, 0) // Restore the zero slot.
            }
        }
    }
}
Código Fuente del Contrato
Archivo 3 de 24: Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @dev The canonical OpenSea conduit.
address constant CONDUIT = 0x1E0049783F008A0085193E00003D00cd54003c71;
/// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
uint256 constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
/// @dev Pre-shifted and pre-masked constant.
uint256 constant SOLADY_ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;
/// @dev Solady ERC1155 master slot seed.
uint256 constant SOLADY_ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;
/// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
uint256 constant SOLADY_TRANSFER_SINGLE_EVENT_SIGNATURE =
    0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
/// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
uint256 constant SOLADY_TRANSFER_BATCH_EVENT_SIGNATURE =
    0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
/// @dev Solady ERC20 allowance slot seed.
uint256 constant SOLADY_ERC20_ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev Solady ERC20 balance slot seed.
uint256 constant SOLADY_ERC20_BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 constant SOLADY_ERC20_TRANSFER_EVENT_SIGNATURE =
    0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
uint256 constant SOLADY_ERC20_APPROVAL_EVENT_SIGNATURE = 0;
/// @dev Solady ERC20 nonces slot seed with signature prefix.
uint256 constant SOLADY_ERC20_NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 constant SOLADY_ERC20_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev Solady ERC20 version hash: `keccak256("1")`.
bytes32 constant SOLADY_ERC20_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 constant SOLADY_ERC20_PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
Código Fuente del Contrato
Archivo 4 de 24: DateTimeLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for date time operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol)
/// @author Modified from BokkyPooBahsDateTimeLibrary (https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary)
/// @dev
/// Conventions:
/// --------------------------------------------------------------------+
/// Unit      | Range                | Notes                            |
/// --------------------------------------------------------------------|
/// timestamp | 0..0x1e18549868c76ff | Unix timestamp.                  |
/// epochDay  | 0..0x16d3e098039     | Days since 1970-01-01.           |
/// year      | 1970..0xffffffff     | Gregorian calendar year.         |
/// month     | 1..12                | Gregorian calendar month.        |
/// day       | 1..31                | Gregorian calendar day of month. |
/// weekday   | 1..7                 | The day of the week (1-indexed). |
/// --------------------------------------------------------------------+
/// All timestamps of days are rounded down to 00:00:00 UTC.
library DateTimeLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Weekdays are 1-indexed, adhering to ISO 8601.

    uint256 internal constant MON = 1;
    uint256 internal constant TUE = 2;
    uint256 internal constant WED = 3;
    uint256 internal constant THU = 4;
    uint256 internal constant FRI = 5;
    uint256 internal constant SAT = 6;
    uint256 internal constant SUN = 7;

    // Months and days of months are 1-indexed, adhering to ISO 8601.

    uint256 internal constant JAN = 1;
    uint256 internal constant FEB = 2;
    uint256 internal constant MAR = 3;
    uint256 internal constant APR = 4;
    uint256 internal constant MAY = 5;
    uint256 internal constant JUN = 6;
    uint256 internal constant JUL = 7;
    uint256 internal constant AUG = 8;
    uint256 internal constant SEP = 9;
    uint256 internal constant OCT = 10;
    uint256 internal constant NOV = 11;
    uint256 internal constant DEC = 12;

    // These limits are large enough for most practical purposes.
    // Inputs that exceed these limits result in undefined behavior.

    uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff;
    uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039;
    uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    DATE TIME OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`).
    /// See: https://howardhinnant.github.io/date_algorithms.html
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedDate} to check if the inputs are supported.
    function dateToEpochDay(uint256 year, uint256 month, uint256 day)
        internal
        pure
        returns (uint256 epochDay)
    {
        /// @solidity memory-safe-assembly
        assembly {
            year := sub(year, lt(month, 3))
            let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day)
            let yoe := mod(year, 400)
            let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100))
            epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469)
        }
    }

    /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01.
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedDays} to check if the inputs is supported.
    function epochDayToDate(uint256 epochDay)
        internal
        pure
        returns (uint256 year, uint256 month, uint256 day)
    {
        /// @solidity memory-safe-assembly
        assembly {
            epochDay := add(epochDay, 719468)
            let doe := mod(epochDay, 146097)
            let yoe :=
                div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365)
            let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100)))
            let mp := div(add(mul(5, doy), 2), 153)
            day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1)
            month := byte(mp, shl(160, 0x030405060708090a0b0c0102))
            year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3))
        }
    }

    /// @dev Returns the unix timestamp from (`year`,`month`,`day`).
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedDate} to check if the inputs are supported.
    function dateToTimestamp(uint256 year, uint256 month, uint256 day)
        internal
        pure
        returns (uint256 result)
    {
        unchecked {
            result = dateToEpochDay(year, month, day) * 86400;
        }
    }

    /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp.
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedTimestamp} to check if the inputs are supported.
    function timestampToDate(uint256 timestamp)
        internal
        pure
        returns (uint256 year, uint256 month, uint256 day)
    {
        (year, month, day) = epochDayToDate(timestamp / 86400);
    }

    /// @dev Returns the unix timestamp from
    /// (`year`,`month`,`day`,`hour`,`minute`,`second`).
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedDateTime} to check if the inputs are supported.
    function dateTimeToTimestamp(
        uint256 year,
        uint256 month,
        uint256 day,
        uint256 hour,
        uint256 minute,
        uint256 second
    ) internal pure returns (uint256 result) {
        unchecked {
            result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second;
        }
    }

    /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`)
    /// from the given unix timestamp.
    /// Note: Inputs outside the supported ranges result in undefined behavior.
    /// Use {isSupportedTimestamp} to check if the inputs are supported.
    function timestampToDateTime(uint256 timestamp)
        internal
        pure
        returns (
            uint256 year,
            uint256 month,
            uint256 day,
            uint256 hour,
            uint256 minute,
            uint256 second
        )
    {
        unchecked {
            (year, month, day) = epochDayToDate(timestamp / 86400);
            uint256 secs = timestamp % 86400;
            hour = secs / 3600;
            secs = secs % 3600;
            minute = secs / 60;
            second = secs % 60;
        }
    }

    /// @dev Returns if the `year` is leap.
    function isLeapYear(uint256 year) internal pure returns (bool leap) {
        /// @solidity memory-safe-assembly
        assembly {
            leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year))
        }
    }

    /// @dev Returns number of days in given `month` of `year`.
    function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) {
        bool flag = isLeapYear(year);
        /// @solidity memory-safe-assembly
        assembly {
            // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`.
            // `result = daysInMonths[month - 1] + isLeapYear(year)`.
            result :=
                add(byte(month, shl(152, 0x1f1c1f1e1f1e1f1f1e1f1e1f)), and(eq(month, 2), flag))
        }
    }

    /// @dev Returns the weekday from the unix timestamp.
    /// Monday: 1, Tuesday: 2, ....., Sunday: 7.
    function weekday(uint256 timestamp) internal pure returns (uint256 result) {
        unchecked {
            result = ((timestamp / 86400 + 3) % 7) + 1;
        }
    }

    /// @dev Returns if (`year`,`month`,`day`) is a supported date.
    /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.
    /// - `1 <= month <= 12`.
    /// - `1 <= day <= daysInMonth(year, month)`.
    function isSupportedDate(uint256 year, uint256 month, uint256 day)
        internal
        pure
        returns (bool result)
    {
        uint256 md = daysInMonth(year, month);
        /// @solidity memory-safe-assembly
        assembly {
            result :=
                and(
                    lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)),
                    and(lt(sub(month, 1), 12), lt(sub(day, 1), md))
                )
        }
    }

    /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time.
    /// - `1970 <= year <= MAX_SUPPORTED_YEAR`.
    /// - `1 <= month <= 12`.
    /// - `1 <= day <= daysInMonth(year, month)`.
    /// - `hour < 24`.
    /// - `minute < 60`.
    /// - `second < 60`.
    function isSupportedDateTime(
        uint256 year,
        uint256 month,
        uint256 day,
        uint256 hour,
        uint256 minute,
        uint256 second
    ) internal pure returns (bool result) {
        if (isSupportedDate(year, month, day)) {
            /// @solidity memory-safe-assembly
            assembly {
                result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60)))
            }
        }
    }

    /// @dev Returns if `epochDay` is a supported unix epoch day.
    function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) {
        unchecked {
            result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1;
        }
    }

    /// @dev Returns if `timestamp` is a supported unix timestamp.
    function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) {
        unchecked {
            result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1;
        }
    }

    /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`.
    /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)`
    /// Note: `n` is 1-indexed for traditional consistency.
    /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior.
    function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd)
        internal
        pure
        returns (uint256 result)
    {
        uint256 d = dateToEpochDay(year, month, 1);
        uint256 md = daysInMonth(year, month);
        /// @solidity memory-safe-assembly
        assembly {
            let diff := sub(wd, add(mod(add(d, 3), 7), 1))
            let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff))
            result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n))))
        }
    }

    /// @dev Returns the unix timestamp of the most recent Monday.
    function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) {
        uint256 t = timestamp;
        /// @solidity memory-safe-assembly
        assembly {
            let day := div(t, 86400)
            result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599))
        }
    }

    /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday.
    /// To check whether it is a week day, just take the negation of the result.
    function isWeekEnd(uint256 timestamp) internal pure returns (bool result) {
        result = weekday(timestamp) > FRI;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              DATE TIME ARITHMETIC OPERATIONS               */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Adds `numYears` to the unix timestamp, and returns the result.
    /// Note: The result will share the same Gregorian calendar month,
    /// but different Gregorian calendar years for non-zero `numYears`.
    /// If the Gregorian calendar month of the result has less days
    /// than the Gregorian calendar month day of the `timestamp`,
    /// the result's month day will be the maximum possible value for the month.
    /// (e.g. from 29th Feb to 28th Feb)
    function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {
        (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
        result = _offsetted(year + numYears, month, day, timestamp);
    }

    /// @dev Adds `numMonths` to the unix timestamp, and returns the result.
    /// Note: If the Gregorian calendar month of the result has less days
    /// than the Gregorian calendar month day of the `timestamp`,
    /// the result's month day will be the maximum possible value for the month.
    /// (e.g. from 29th Feb to 28th Feb)
    function addMonths(uint256 timestamp, uint256 numMonths)
        internal
        pure
        returns (uint256 result)
    {
        (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
        month = _sub(month + numMonths, 1);
        result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp);
    }

    /// @dev Adds `numDays` to the unix timestamp, and returns the result.
    function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {
        result = timestamp + numDays * 86400;
    }

    /// @dev Adds `numHours` to the unix timestamp, and returns the result.
    function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {
        result = timestamp + numHours * 3600;
    }

    /// @dev Adds `numMinutes` to the unix timestamp, and returns the result.
    function addMinutes(uint256 timestamp, uint256 numMinutes)
        internal
        pure
        returns (uint256 result)
    {
        result = timestamp + numMinutes * 60;
    }

    /// @dev Adds `numSeconds` to the unix timestamp, and returns the result.
    function addSeconds(uint256 timestamp, uint256 numSeconds)
        internal
        pure
        returns (uint256 result)
    {
        result = timestamp + numSeconds;
    }

    /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.
    /// Note: The result will share the same Gregorian calendar month,
    /// but different Gregorian calendar years for non-zero `numYears`.
    /// If the Gregorian calendar month of the result has less days
    /// than the Gregorian calendar month day of the `timestamp`,
    /// the result's month day will be the maximum possible value for the month.
    /// (e.g. from 29th Feb to 28th Feb)
    function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) {
        (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
        result = _offsetted(year - numYears, month, day, timestamp);
    }

    /// @dev Subtracts `numYears` from the unix timestamp, and returns the result.
    /// Note: If the Gregorian calendar month of the result has less days
    /// than the Gregorian calendar month day of the `timestamp`,
    /// the result's month day will be the maximum possible value for the month.
    /// (e.g. from 29th Feb to 28th Feb)
    function subMonths(uint256 timestamp, uint256 numMonths)
        internal
        pure
        returns (uint256 result)
    {
        (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400);
        uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1);
        result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp);
    }

    /// @dev Subtracts `numDays` from the unix timestamp, and returns the result.
    function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) {
        result = timestamp - numDays * 86400;
    }

    /// @dev Subtracts `numHours` from the unix timestamp, and returns the result.
    function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) {
        result = timestamp - numHours * 3600;
    }

    /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result.
    function subMinutes(uint256 timestamp, uint256 numMinutes)
        internal
        pure
        returns (uint256 result)
    {
        result = timestamp - numMinutes * 60;
    }

    /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result.
    function subSeconds(uint256 timestamp, uint256 numSeconds)
        internal
        pure
        returns (uint256 result)
    {
        result = timestamp - numSeconds;
    }

    /// @dev Returns the difference in Gregorian calendar years
    /// between `fromTimestamp` and `toTimestamp`.
    /// Note: Even if the true time difference is less than a year,
    /// the difference can be non-zero is the timestamps are
    /// from different Gregorian calendar years
    function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        toTimestamp - fromTimestamp;
        (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400);
        (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400);
        result = _sub(toYear, fromYear);
    }

    /// @dev Returns the difference in Gregorian calendar months
    /// between `fromTimestamp` and `toTimestamp`.
    /// Note: Even if the true time difference is less than a month,
    /// the difference can be non-zero is the timestamps are
    /// from different Gregorian calendar months.
    function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        toTimestamp - fromTimestamp;
        (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400);
        (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400);
        result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth));
    }

    /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`.
    function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        result = (toTimestamp - fromTimestamp) / 86400;
    }

    /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`.
    function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        result = (toTimestamp - fromTimestamp) / 3600;
    }

    /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`.
    function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        result = (toTimestamp - fromTimestamp) / 60;
    }

    /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`.
    function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
        internal
        pure
        returns (uint256 result)
    {
        result = toTimestamp - fromTimestamp;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unchecked arithmetic for computing the total number of months.
    function _totalMonths(uint256 numYears, uint256 numMonths)
        private
        pure
        returns (uint256 total)
    {
        unchecked {
            total = numYears * 12 + numMonths;
        }
    }

    /// @dev Unchecked arithmetic for adding two numbers.
    function _add(uint256 a, uint256 b) private pure returns (uint256 c) {
        unchecked {
            c = a + b;
        }
    }

    /// @dev Unchecked arithmetic for subtracting two numbers.
    function _sub(uint256 a, uint256 b) private pure returns (uint256 c) {
        unchecked {
            c = a - b;
        }
    }

    /// @dev Returns the offsetted timestamp.
    function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp)
        private
        pure
        returns (uint256 result)
    {
        uint256 dm = daysInMonth(year, month);
        if (day >= dm) {
            day = dm;
        }
        result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400);
    }
}
Código Fuente del Contrato
Archivo 5 de 24: DynamicTraits.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.19;

import {IERC7496} from "./interfaces/IERC7496.sol";

library DynamicTraitsStorage {
    struct Layout {
        /// @dev A mapping of token ID to a mapping of trait key to trait value.
        mapping(uint256 tokenId => mapping(bytes32 traitKey => bytes32 traitValue)) _traits;
        /// @dev An offchain string URI that points to a JSON file containing trait metadata.
        string _traitMetadataURI;
    }

    bytes32 internal constant STORAGE_SLOT = keccak256("contracts.storage.erc7496-dynamictraits");

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

/**
 * @title DynamicTraits
 *
 * @dev Implementation of [ERC-7496](https://eips.ethereum.org/EIPS/eip-7496) Dynamic Traits.
 * Uses a storage layout pattern for upgradeable contracts.
 *
 * Requirements:
 * - Overwrite `setTrait` with access role restriction.
 * - Expose a function for `setTraitMetadataURI` with access role restriction if desired.
 */
contract DynamicTraits is IERC7496 {
    using DynamicTraitsStorage for DynamicTraitsStorage.Layout;

    /**
     * @notice Get the value of a trait for a given token ID.
     * @param tokenId The token ID to get the trait value for
     * @param traitKey The trait key to get the value of
     */
    function getTraitValue(uint256 tokenId, bytes32 traitKey) public view virtual returns (bytes32 traitValue) {
        // Return the trait value.
        return DynamicTraitsStorage.layout()._traits[tokenId][traitKey];
    }

    /**
     * @notice Get the values of traits for a given token ID.
     * @param tokenId The token ID to get the trait values for
     * @param traitKeys The trait keys to get the values of
     */
    function getTraitValues(uint256 tokenId, bytes32[] calldata traitKeys)
        public
        view
        virtual
        returns (bytes32[] memory traitValues)
    {
        // Set the length of the traitValues return array.
        uint256 length = traitKeys.length;
        traitValues = new bytes32[](length);

        // Assign each trait value to the corresopnding key.
        for (uint256 i = 0; i < length;) {
            bytes32 traitKey = traitKeys[i];
            traitValues[i] = getTraitValue(tokenId, traitKey);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Get the URI for the trait metadata
     */
    function getTraitMetadataURI() external view virtual returns (string memory labelsURI) {
        // Return the trait metadata URI.
        return DynamicTraitsStorage.layout()._traitMetadataURI;
    }

    /**
     * @notice Set the value of a trait for a given token ID.
     *         Reverts if the trait value is unchanged.
     * @dev    IMPORTANT: Override this method with access role restriction.
     * @param tokenId The token ID to set the trait value for
     * @param traitKey The trait key to set the value of
     * @param newValue The new trait value to set
     */
    function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 newValue) public virtual {
        // Revert if the new value is the same as the existing value.
        bytes32 existingValue = DynamicTraitsStorage.layout()._traits[tokenId][traitKey];
        if (existingValue == newValue) {
            revert TraitValueUnchanged();
        }

        // Set the new trait value.
        _setTrait(tokenId, traitKey, newValue);

        // Emit the event noting the update.
        emit TraitUpdated(traitKey, tokenId, newValue);
    }

    /**
     * @notice Set the trait value (without emitting an event).
     * @param tokenId The token ID to set the trait value for
     * @param traitKey The trait key to set the value of
     * @param newValue The new trait value to set
     */
    function _setTrait(uint256 tokenId, bytes32 traitKey, bytes32 newValue) internal virtual {
        // Set the new trait value.
        DynamicTraitsStorage.layout()._traits[tokenId][traitKey] = newValue;
    }

    /**
     * @notice Set the URI for the trait metadata.
     * @param uri The new URI to set.
     */
    function _setTraitMetadataURI(string memory uri) internal virtual {
        // Set the new trait metadata URI.
        DynamicTraitsStorage.layout()._traitMetadataURI = uri;

        // Emit the event noting the update.
        emit TraitMetadataURIUpdated();
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC7496).interfaceId;
    }
}
Código Fuente del Contrato
Archivo 6 de 24: ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
///   This is for more safety by default.
///   Use the `tryRecover` variants if you need to get the zero address back
///   upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The order of the secp256k1 elliptic curve.
    uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;

    /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
    uint256 private constant _HALF_N_PLUS_1 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;

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

    /// @dev The signature is invalid.
    error InvalidSignature();

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

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            for {} 1 {} {
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                if eq(mload(signature), 64) {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(mload(signature), 65) {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    break
                }
                result := 0
                break
            }
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            for {} 1 {} {
                if eq(signature.length, 64) {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(signature.length, 65) {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    break
                }
                result := 0
                break
            }
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        result, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            result :=
                mload(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                )
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            for {} 1 {} {
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                if eq(mload(signature), 64) {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(mload(signature), 65) {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    break
                }
                result := 0
                break
            }
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    result, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            for {} 1 {} {
                if eq(signature.length, 64) {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    break
                }
                if eq(signature.length, 65) {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                    break
                }
                result := 0
                break
            }
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    result, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    1, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(
                staticcall(
                    gas(), // Amount of gas left for the transaction.
                    1, // Address of `ecrecover`.
                    0x00, // Start of input.
                    0x80, // Size of input.
                    0x40, // Start of output.
                    0x20 // Size of output.
                )
            )
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

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

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CANONICAL HASH FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // The following functions returns the hash of the signature in it's canonicalized format,
    // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
    // If `s` is greater than `N / 2` then it will be converted to `N - s`
    // and the `v` value will be flipped.
    // If the signature has an invalid length, or if `v` is invalid,
    // a uniquely corrupt hash will be returned.
    // These functions are useful for "poor-mans-VRF".

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            let l := mload(signature)
            for {} 1 {} {
                mstore(0x00, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                let v := mload(add(signature, 0x41))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }

            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHashCalldata(bytes calldata signature)
        internal
        pure
        returns (bytes32 result)
    {
        // @solidity memory-safe-assembly
        assembly {
            let l := signature.length
            for {} 1 {} {
                mstore(0x00, calldataload(signature.offset)) // `r`.
                let s := calldataload(add(signature.offset, 0x20))
                let v := calldataload(add(signature.offset, 0x21))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }
            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                calldatacopy(mload(0x40), signature.offset, l)
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(mload(0x40), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            let v := add(shr(255, vs), 27)
            let s := shr(1, shl(1, vs))
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
        // @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            if iszero(lt(s, _HALF_N_PLUS_1)) {
                v := xor(v, 7)
                s := sub(N, s)
            }
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}
Código Fuente del Contrato
Archivo 7 de 24: ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
///   to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
///   the balance of an owner MUST always be equal to their number of ownership slots.
///   The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
//    (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An account can hold up to 4294967295 tokens.
    uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;

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

    /// @dev Only the token owner or an approved account can manage the token.
    error NotOwnerNorApproved();

    /// @dev The token does not exist.
    error TokenDoesNotExist();

    /// @dev The token already exists.
    error TokenAlreadyExists();

    /// @dev Cannot query the balance for the zero address.
    error BalanceQueryForZeroAddress();

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

    /// @dev The token must be owned by `from`.
    error TransferFromIncorrectOwner();

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC721Receiver interface.
    error TransferToNonERC721ReceiverImplementer();

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

    /// @dev Emitted when token `id` is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    /// @dev Emitted when `owner` enables `account` to manage the `id` token.
    event Approval(address indexed owner, address indexed account, uint256 indexed id);

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

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

    /// @dev The ownership data slot of `id` is given by:
    /// ```
    ///     mstore(0x00, id)
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
    /// ```
    /// Bits Layout:
    /// - [0..159]   `addr`
    /// - [160..255] `extraData`
    ///
    /// The approved address slot is given by: `add(1, ownershipSlot)`.
    ///
    /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
    ///
    /// The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x1c)
    /// ```
    /// Bits Layout:
    /// - [0..31]   `balance`
    /// - [32..255] `aux`
    ///
    /// The `operator` approval slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
    ///     mstore(0x00, owner)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x30)
    /// ```
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    /// @dev Pre-shifted and pre-masked constant.
    uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC721 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the token collection name.
    function name() public view virtual returns (string memory);

    /// @dev Returns the token collection symbol.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
    function tokenURI(uint256 id) public view virtual returns (string memory);

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

    /// @dev Returns the owner of token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        result = _ownerOf(id);
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(result) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the number of tokens owned by `owner`.
    ///
    /// Requirements:
    /// - `owner` must not be the zero address.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the `owner` is the zero address.
            if iszero(owner) {
                mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            if iszero(shl(96, sload(ownershipSlot))) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            result := sload(add(1, ownershipSlot))
        }
    }

    /// @dev Sets `account` as the approved account to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - The caller must be the owner of the token,
    ///   or an approved operator for the token owner.
    ///
    /// Emits an {Approval} event.
    function approve(address account, uint256 id) public payable virtual {
        _approve(msg.sender, account, id);
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x30))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits an {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
        }
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 id) public payable virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // Revert if the caller is not the owner, nor approved.
                if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        payable
        virtual
    {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL QUERY FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
        }
    }

    /// @dev Returns the owner of token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _ownerOf(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            INTERNAL DATA HITCHHIKING FUNCTIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance, no events are emitted for the hitchhiking setters.
    // Please emit your own events if required.

    /// @dev Returns the auxiliary data for `owner`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _getAux(address owner) internal view virtual returns (uint224 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := shr(32, sload(keccak256(0x0c, 0x1c)))
        }
    }

    /// @dev Set the auxiliary data for `owner` to `value`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _setAux(address owner, uint224 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            let balanceSlot := keccak256(0x0c, 0x1c)
            let packed := sload(balanceSlot)
            sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
        }
    }

    /// @dev Returns the extra data for token `id`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Sets the extra data for token `id` to `value`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _setExtraData(uint256 id, uint96 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let packed := sload(ownershipSlot)
            sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 id) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Revert if the token already exists.
            if shl(96, ownershipPacked) {
                mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
                revert(0x1c, 0x04)
            }
            // Update with the owner.
            sstore(ownershipSlot, or(ownershipPacked, to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`.
    /// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing).
    ///
    /// Requirements:
    ///
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Update with the owner and extra data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Equivalent to `_safeMint(to, id, "")`.
    function _safeMint(address to, uint256 id) internal virtual {
        _safeMint(to, id, "");
    }

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);
        if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_burn(address(0), id)`.
    function _burn(uint256 id) internal virtual {
        _burn(address(0), id);
    }

    /// @dev Destroys token `id`, using `by`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _burn(address by, uint256 id) internal virtual {
        address owner = ownerOf(id);
        _beforeTokenTransfer(owner, address(0), id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Reload the owner in case it is changed in `_beforeTokenTransfer`.
            owner := shr(96, shl(96, ownershipPacked))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Load and check the token approval.
            {
                mstore(0x00, owner)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Clear the owner.
            sstore(ownershipSlot, xor(ownershipPacked, owner))
            // Decrement the balance of `owner`.
            {
                let balanceSlot := keccak256(0x0c, 0x1c)
                sstore(balanceSlot, sub(sload(balanceSlot), 1))
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
        }
        _afterTokenTransfer(owner, address(0), id);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL APPROVAL FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function _isApprovedOrOwner(address account, uint256 id)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            // Clear the upper 96 bits.
            account := shr(96, shl(96, account))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := shr(96, shl(96, sload(ownershipSlot)))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Check if `account` is the `owner`.
            if iszero(eq(account, owner)) {
                mstore(0x00, owner)
                // Check if `account` is approved to manage the token.
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    result := eq(account, sload(add(1, ownershipSlot)))
                }
            }
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _getApproved(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Equivalent to `_approve(address(0), account, id)`.
    function _approve(address account, uint256 id) internal virtual {
        _approve(address(0), account, id);
    }

    /// @dev Sets `account` as the approved account to manage token `id`, using `by`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - If `by` is not the zero address, `by` must be the owner
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Approval} event.
    function _approve(address by, address account, uint256 id) internal virtual {
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            account := and(bitmaskAddress, account)
            by := and(bitmaskAddress, by)
            // Load the owner of the token.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := and(bitmaskAddress, sload(ownershipSlot))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // If `by` is not the zero address, do the authorization check.
            // Revert if `by` is not the owner, nor approved.
            if iszero(or(iszero(by), eq(by, owner))) {
                mstore(0x00, owner)
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Sets `account` as the approved account to manage `id`.
            sstore(add(1, ownershipSlot), account)
            // Emit the {Approval} event.
            log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits an {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            operator := shr(96, shl(96, operator))
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
            mstore(0x00, by)
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_transfer(address(0), from, to, id)`.
    function _transfer(address from, address to, uint256 id) internal virtual {
        _transfer(address(0), from, to, id);
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _transfer(address by, address from, address to, uint256 id) internal virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            by := and(bitmaskAddress, by)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
    function _safeTransfer(address from, address to, uint256 id) internal virtual {
        _safeTransfer(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(address(0), from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
    function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
        _safeTransfer(by, from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(by, from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any token transfers, including minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /// @dev Hook that is called after any token transfers, including minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            let onERC721ReceivedSelector := 0x150b7a02
            mstore(m, onERC721ReceivedSelector)
            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 0x80)
            let n := mload(data)
            mstore(add(m, 0xa0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it.
            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }
}
Código Fuente del Contrato
Archivo 8 de 24: ERC721ConduitPreapproved_Solady.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {ERC721} from "solady/tokens/ERC721.sol";
import {
    CONDUIT, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, SOLADY_ERC721_MASTER_SLOT_SEED_MASKED
} from "../../lib/Constants.sol";
import {IPreapprovalForAll} from "../../interfaces/IPreapprovalForAll.sol";

abstract contract ERC721ConduitPreapproved_Solady is ERC721, IPreapprovalForAll {
    constructor() {
        emit PreapprovalForAll(CONDUIT, true);
    }

    function transferFrom(address from, address to, uint256 id) public payable virtual override {
        _transfer(_by(from), from, to, id);
    }

    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        bool approved = super.isApprovedForAll(owner, operator);
        return (operator == CONDUIT) ? !approved : approved;
    }

    function setApprovalForAll(address operator, bool isApproved) public virtual override {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            let isConduit := eq(operator, CONDUIT)
            // if isConduit, flip isApproved, otherwise leave as is
            let storedValue :=
                or(
                    // isConduit && !isApproved
                    and(isConduit, iszero(isApproved)),
                    // !isConduit && isApproved
                    and(iszero(isConduit), isApproved)
                )
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, SOLADY_ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), storedValue)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
        }
    }

    function _by(address from) internal view virtual returns (address result) {
        if (msg.sender == CONDUIT) {
            if (isApprovedForAll(from, CONDUIT)) {
                return address(0);
            }
        }
        return msg.sender;
    }
}
Código Fuente del Contrato
Archivo 9 de 24: EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
Código Fuente del Contrato
Archivo 10 de 24: IERC5192.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IERC5192 {
    /// @notice Emitted when the locking status is changed to locked.
    /// @dev If a token is minted and the status is locked, this event should be emitted.
    /// @param tokenId The identifier for a token.
    event Locked(uint256 tokenId);

    /// @notice Emitted when the locking status is changed to unlocked.
    /// @dev If a token is minted and the status is unlocked, this event should be emitted.
    /// @param tokenId The identifier for a token.
    event Unlocked(uint256 tokenId);

    /// @notice Returns the locking status of an Soulbound Token
    /// @dev SBTs assigned to zero address are considered invalid, and queries
    /// about them do throw.
    /// @param tokenId The identifier for an SBT.
    function locked(uint256 tokenId) external view returns (bool);
}
Código Fuente del Contrato
Archivo 11 de 24: IERC7496.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.19;

interface IERC7496 {
    /* Events */
    event TraitUpdated(bytes32 indexed traitKey, uint256 tokenId, bytes32 traitValue);
    event TraitUpdatedRange(bytes32 indexed traitKey, uint256 fromTokenId, uint256 toTokenId);
    event TraitUpdatedRangeUniformValue(
        bytes32 indexed traitKey, uint256 fromTokenId, uint256 toTokenId, bytes32 traitValue
    );
    event TraitUpdatedList(bytes32 indexed traitKey, uint256[] tokenIds);
    event TraitUpdatedListUniformValue(bytes32 indexed traitKey, uint256[] tokenIds, bytes32 traitValue);
    event TraitMetadataURIUpdated();

    /* Getters */
    function getTraitValue(uint256 tokenId, bytes32 traitKey) external view returns (bytes32 traitValue);
    function getTraitValues(uint256 tokenId, bytes32[] calldata traitKeys)
        external
        view
        returns (bytes32[] memory traitValues);
    function getTraitMetadataURI() external view returns (string memory uri);

    /* Setters */
    function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 traitValue) external;

    /* Errors */
    error TraitValueUnchanged();
}
Código Fuente del Contrato
Archivo 12 de 24: IPreapprovalForAll.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IPreapprovalForAll {
    /// @notice Emitted when a token contract preapproves (or revokes) all token transfers from a specific address, if
    ///         the preapproval is configurable. This allows offchain indexers to correctly reflect token approvals
    ///         which can later be revoked.
    event PreapprovalForAll(address indexed operator, bool indexed approved);
}
Código Fuente del Contrato
Archivo 13 de 24: 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)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

    /// @dev The length of the string is more than 32 bytes.
    error TooBigForSmallString();

    /// @dev The input string must be a 7-bit ASCII.
    error StringNot7BitASCII();

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

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

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
    uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;

    /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;

    /// @dev Lookup for '0123456789'.
    uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;

    /// @dev Lookup for '0123456789abcdefABCDEF'.
    uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;

    /// @dev Lookup for '01234567'.
    uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
    uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;

    /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
    uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;

    /// @dev Lookup for ' \t\n\r\x0b\x0c'.
    uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory result) {
        /// @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.
            result := add(mload(0x40), 0x80)
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

            let end := result // Cache the end of the memory to calculate the length later.
            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 {} {
                result := add(result, w) // `sub(result, 1)`.
                // Store the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(result, add(48, mod(temp, 10)))
                temp := div(temp, 10) // Keep dividing `temp` until zero.
                if iszero(temp) { break }
            }
            let n := sub(end, result)
            result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
            mstore(result, n) // Store the length.
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory result) {
        if (value >= 0) return toString(uint256(value));
        unchecked {
            result = toString(~uint256(value) + 1);
        }
        /// @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 n := mload(result) // Load the string length.
            mstore(result, 0x2d) // Store the '-' character.
            result := sub(result, 1) // Move back the string pointer by a byte.
            mstore(result, add(n, 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 result)
    {
        result = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is not prefixed with "0x" and is 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 result)
    {
        /// @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.
            result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

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

            let start := sub(result, 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 {} {
                result := add(result, w) // `sub(result, 2)`.
                mstore8(add(result, 1), mload(and(temp, 15)))
                mstore8(result, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(result, start)) { break }
            }
            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }
            let n := sub(end, result)
            result := sub(result, 0x20)
            mstore(result, n) // Store the length.
        }
    }

    /// @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 result) {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x".
    /// The output excludes leading "0" from the `toHexString` output.
    /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
    function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
            let n := add(mload(result), 2) // Compute the length.
            mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
            result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
            mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
    /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
    function toMinimalHexStringNoPrefix(uint256 value)
        internal
        pure
        returns (string memory result)
    {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
            let n := mload(result) // Get the length.
            result := add(result, o) // Move the pointer, accounting for leading zero.
            mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
        }
    }

    /// @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 result) {
        /// @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.
            result := add(mload(0x40), 0x80)
            mstore(0x40, add(result, 0x20)) // Allocate memory.
            mstore(result, 0) // Zeroize the slot after the string.

            let end := result // Cache the end to calculate the length later.
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            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 {} {
                result := add(result, w) // `sub(result, 2)`.
                mstore8(add(result, 1), mload(and(temp, 15)))
                mstore8(result, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }
            let n := sub(end, result)
            result := sub(result, 0x20)
            mstore(result, n) // Store the length.
        }
    }

    /// @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 toHexStringChecksummed(address value) internal pure returns (string memory result) {
        result = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(result, 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 result) {
        result = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store 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 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            // Allocate 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(result, 0x80))
            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.

            result := add(result, 2)
            mstore(result, 40) // Store the length.
            let o := add(result, 0x20)
            mstore(add(o, 40), 0) // Zeroize the slot after the string.
            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 result) {
        result = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let n := add(mload(result), 2) // Compute the length.
            mstore(result, 0x3078) // Store the "0x" prefix.
            result := sub(result, 2) // Move the pointer.
            mstore(result, n) // Store 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 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(raw)
            result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(result, add(n, n)) // Store the length of the output.

            mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
            let o := add(result, 0x20)
            let end := add(raw, n)
            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, add(o, 0x20)) // Allocate 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 }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            let mask := shl(7, div(not(0), 255))
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string,
    /// AND all characters are in the `allowed` lookup.
    /// Note: If `s` is empty, returns true regardless of `allowed`.
    function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            if mload(s) {
                let allowed_ := shr(128, shl(128, allowed))
                let o := add(s, 0x20)
                for { let end := add(o, mload(s)) } 1 {} {
                    result := and(result, shr(byte(0, mload(o)), allowed_))
                    o := add(o, 1)
                    if iszero(and(result, lt(o, end))) { break }
                }
            }
        }
    }

    /// @dev Converts the bytes in the 7-bit ASCII string `s` to
    /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
    /// To save runtime gas, you can cache the result in an immutable variable.
    function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                let o := add(s, 0x20)
                for { let end := add(o, mload(s)) } 1 {} {
                    result := or(result, shl(byte(0, mload(o)), 1))
                    o := add(o, 1)
                    if iszero(lt(o, end)) { break }
                }
                if shr(128, result) {
                    mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

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

    // For performance and bytecode compactness, byte string operations are restricted
    // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
    // Usage of byte string operations on charsets with runes spanning two or more bytes
    // can lead to undefined behavior.

    /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
    function replace(string memory subject, string memory needle, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let needleLen := mload(needle)
            let replacementLen := mload(replacement)
            let d := sub(result, subject) // Memory difference.
            let i := add(subject, 0x20) // Subject bytes pointer.
            let end := add(i, mload(subject))
            if iszero(gt(needleLen, mload(subject))) {
                let subjectSearchEnd := add(sub(end, needleLen), 1)
                let h := 0 // The hash of `needle`.
                if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
                let s := mload(add(needle, 0x20))
                for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
                    let t := mload(i)
                    // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(i, needleLen), h)) {
                                mstore(add(i, d), t)
                                i := add(i, 1)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let j := 0 } 1 {} {
                            mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
                            j := add(j, 0x20)
                            if iszero(lt(j, replacementLen)) { break }
                        }
                        d := sub(add(d, replacementLen), needleLen)
                        if needleLen {
                            i := add(i, needleLen)
                            if iszero(lt(i, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(add(i, d), t)
                    i := add(i, 1)
                    if iszero(lt(i, subjectSearchEnd)) { break }
                }
            }
            let n := add(sub(d, add(result, 0x20)), end)
            // Copy the rest of the string one word at a time.
            for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
            let o := add(i, d)
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
            mstore(result, n) // Store the length.
        }
    }

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

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
                let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
                let s := mload(add(needle, 0x20))

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

                if iszero(lt(needleLen, 0x20)) {
                    for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, needleLen), 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 `needle` in `subject`,
    /// needleing from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function indexOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, needle, 0);
    }

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

                let fromMax := sub(mload(subject), needleLen)
                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(needle, 0x20), needleLen) } 1 {} {
                    if eq(keccak256(subject, needleLen), 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 `needle` in `subject`,
    /// needleing from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
    function lastIndexOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, needle, type(uint256).max);
    }

    /// @dev Returns true if `needle` is found in `subject`, false otherwise.
    function contains(string memory subject, string memory needle) internal pure returns (bool) {
        return indexOf(subject, needle) != NOT_FOUND;
    }

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

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

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLen := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLen))) {
                result := mload(0x40)
                subject := add(subject, 0x20)
                let o := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let j := 0 } 1 {} {
                        mstore(add(o, j), mload(add(subject, j)))
                        j := add(j, 0x20)
                        if iszero(lt(j, subjectLen)) { break }
                    }
                    o := add(o, subjectLen)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
                mstore(result, sub(o, add(result, 0x20))) // Store the length.
            }
        }
    }

    /// @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 subjectLen := mload(subject)
            if iszero(gt(subjectLen, end)) { end := subjectLen }
            if iszero(gt(subjectLen, start)) { start := subjectLen }
            if lt(start, end) {
                result := mload(0x40)
                let n := sub(end, start)
                let i := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let j := and(add(n, 0x1f), w) } 1 {} {
                    mstore(add(result, j), mload(add(i, j)))
                    j := add(j, w) // `sub(j, 0x20)`.
                    if iszero(j) { break }
                }
                let o := add(add(result, 0x20), n)
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
                mstore(result, n) // Store the length.
            }
        }
    }

    /// @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, type(uint256).max);
    }

    /// @dev Returns all the indices of `needle` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory needle)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLen := mload(needle)
            if iszero(gt(searchLen, mload(subject))) {
                result := mload(0x40)
                let i := add(subject, 0x20)
                let o := add(result, 0x20)
                let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
                let h := 0 // The hash of `needle`.
                if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
                let s := mload(add(needle, 0x20))
                for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
                    let t := mload(i)
                    // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(i, searchLen), h)) {
                                i := add(i, 1)
                                if iszero(lt(i, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
                        o := add(o, 0x20)
                        i := add(i, searchLen) // Advance `i` by `searchLen`.
                        if searchLen {
                            if iszero(lt(i, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    i := add(i, 1)
                    if iszero(lt(i, subjectSearchEnd)) { break }
                }
                mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(o, 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(0x1f)
            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))
            for { let prevIndex := 0 } 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let l := sub(index, prevIndex)
                    mstore(element, l) // Store the length of the element.
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(l, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the string.
                    // Allocate memory for the length and the bytes, rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(l, 0x3f), w)))
                    mstore(indexPtr, element) // Store the `element` into the array.
                }
                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 {
            result := mload(0x40)
            let w := not(0x1f)
            let aLen := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(aLen, 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLen := mload(b)
            let output := add(result, aLen)
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLen, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLen := add(aLen, bLen)
            let last := add(add(result, 0x20), totalLen)
            mstore(last, 0) // Zeroize the slot after the string.
            mstore(result, totalLen) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate memory.
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(subject)
            if n {
                result := mload(0x40)
                let o := add(result, 0x20)
                let d := sub(subject, result)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                for { let end := add(o, n) } 1 {} {
                    let b := byte(0, mload(add(d, o)))
                    mstore8(o, xor(and(shr(b, flags), 0x20), b))
                    o := add(o, 1)
                    if eq(o, end) { break }
                }
                mstore(result, n) // Store the length.
                mstore(o, 0) // Zeroize the slot after the string.
                mstore(0x40, add(o, 0x20)) // Allocate memory.
            }
        }
    }

    /// @dev Returns a string from a small bytes32 string.
    /// `s` must be null-terminated, or behavior will be undefined.
    function fromSmallString(bytes32 s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let n := 0
            for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
            mstore(result, n) // Store the length.
            let o := add(result, 0x20)
            mstore(o, s) // Store the bytes of the string.
            mstore(add(o, n), 0) // Zeroize the slot after the string.
            mstore(0x40, add(result, 0x40)) // Allocate memory.
        }
    }

    /// @dev Returns the small string, with all bytes after the first null byte zeroized.
    function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
            mstore(0x00, s)
            mstore(result, 0x00)
            result := mload(0x00)
        }
    }

    /// @dev Returns the string as a normalized null-terminated small string.
    function toSmallString(string memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(s)
            if iszero(lt(result, 33)) {
                mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                revert(0x1c, 0x04)
            }
            result := shl(shl(3, sub(32, result)), mload(add(s, result)))
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    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 {
            result := mload(0x40)
            let end := add(s, mload(s))
            let o := add(result, 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))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(o, c)
                    o := add(o, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(o, mload(and(t, 0x1f)))
                o := add(o, shr(5, t))
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(result, sub(o, add(result, 0x20))) // Store the length.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
    function escapeJSON(string memory s, bool addDoubleQuotes)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let o := add(result, 0x20)
            if addDoubleQuotes {
                mstore8(o, 34)
                o := add(1, o)
            }
            // 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))
            for { let end := add(s, mload(s)) } 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(o, c)
                        o := add(o, 1)
                        continue
                    }
                    mstore8(o, 0x5c) // "\\".
                    mstore8(add(o, 1), c)
                    o := add(o, 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(o, mload(0x19)) // "\\u00XX".
                    o := add(o, 6)
                    continue
                }
                mstore8(o, 0x5c) // "\\".
                mstore8(add(o, 1), mload(add(c, 8)))
                o := add(o, 2)
            }
            if addDoubleQuotes {
                mstore8(o, 34)
                o := add(1, o)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(result, sub(o, add(result, 0x20))) // Store the length.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        result = escapeJSON(s, false);
    }

    /// @dev Encodes `s` so that it can be safely used in a URI,
    /// just like `encodeURIComponent` in JavaScript.
    /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
    /// See: https://datatracker.ietf.org/doc/html/rfc2396
    /// See: https://datatracker.ietf.org/doc/html/rfc3986
    function encodeURIComponent(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            // Store "0123456789ABCDEF" in scratch space.
            // Uppercased to be consistent with JavaScript's implementation.
            mstore(0x0f, 0x30313233343536373839414243444546)
            let o := add(result, 0x20)
            for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // If not in `[0-9A-Z-a-z-_.!~*'()]`.
                if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
                    mstore8(o, 0x25) // '%'.
                    mstore8(add(o, 1), mload(and(shr(4, c), 15)))
                    mstore8(add(o, 2), mload(and(c, 15)))
                    o := add(o, 3)
                    continue
                }
                mstore8(o, c)
                o := add(o, 1)
            }
            mstore(result, sub(o, add(result, 0x20))) // Store the length.
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate memory.
        }
    }

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

    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
    function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // These should be evaluated on compile time, as far as possible.
            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
            let x := not(or(m, or(b, add(m, and(b, m)))))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
        }
    }

    /// @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 behavior is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40) // Grab the free memory pointer.
            mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(result, 0) // Zeroize the length slot.
            mstore(add(result, 0x1f), packed) // Store the length and bytes.
            mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
        }
    }

    /// @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 aLen := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    or( // Load the length and the bytes of `a` and `b`.
                    shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
                    // `totalLen != 0 && totalLen < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLen, 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 behavior is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            resultA := mload(0x40) // Grab the free memory pointer.
            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 retUnpaddedSize := 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, retUnpaddedSize), 0)
            mstore(retStart, 0x20) // Store the return offset.
            // End the transaction, returning the string.
            return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
        }
    }
}
Código Fuente del Contrato
Archivo 14 de 24: Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {json} from "./json.sol";
import {LibString} from "solady/utils/LibString.sol";
import {Solarray} from "solarray/Solarray.sol";
import {Base64} from "solady/utils/Base64.sol";

enum DisplayType {
    String,
    Number,
    Date,
    BoostPercent,
    BoostNumber,
    Hidden
}

library Metadata {
    string private constant NULL = "";

    using LibString for string;

    function attribute(string memory traitType, string memory value) internal pure returns (string memory) {
        return json.objectOf(Solarray.strings(json.property("trait_type", traitType), json.property("value", value)));
    }

    function attribute(string memory traitType, string memory value, DisplayType displayType)
        internal
        pure
        returns (string memory)
    {
        return json.objectOf(
            Solarray.strings(
                json.property("trait_type", traitType),
                json.property("value", value),
                json.property("display_type", toString(displayType))
            )
        );
    }

    function toString(DisplayType displayType) internal pure returns (string memory) {
        if (displayType == DisplayType.String) {
            return "string";
        } else if (displayType == DisplayType.Number) {
            return "number";
        } else if (displayType == DisplayType.Date) {
            return "date";
        } else if (displayType == DisplayType.BoostNumber) {
            return "boost_number";
        } else if (displayType == DisplayType.BoostPercent) {
            return "boost_percent";
        } else {
            return "hidden";
        }
    }

    function dataURI(string memory dataType, string memory encoding, string memory content)
        internal
        pure
        returns (string memory)
    {
        return string.concat(
            "data:", dataType, ";", bytes(encoding).length > 0 ? string.concat(encoding, ",") : NULL, content
        );
    }

    function dataURI(string memory dataType, string memory content) internal pure returns (string memory) {
        return dataURI(dataType, NULL, content);
    }

    function jsonDataURI(string memory content, string memory encoding) internal pure returns (string memory) {
        return dataURI("application/json", encoding, content);
    }

    function jsonDataURI(string memory content) internal pure returns (string memory) {
        return jsonDataURI(content, NULL);
    }

    function base64JsonDataURI(string memory content) internal pure returns (string memory) {
        return jsonDataURI(Base64.encode(bytes(content)), "base64");
    }

    function svgDataURI(string memory content, string memory encoding, bool escape)
        internal
        pure
        returns (string memory)
    {
        string memory uri = dataURI("image/svg+xml", encoding, content);

        if (escape) {
            return uri.escapeJSON();
        } else {
            return uri;
        }
    }

    function svgDataURI(string memory content) internal pure returns (string memory) {
        return svgDataURI(content, "utf8", true);
    }

    function base64SvgDataURI(string memory content) internal pure returns (string memory) {
        return svgDataURI(Base64.encode(bytes(content)), "base64", false);
    }
}
Código Fuente del Contrato
Archivo 15 de 24: OnchainTraits.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {DynamicTraits} from "./DynamicTraits.sol";
import {Metadata} from "../onchain/Metadata.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {SSTORE2} from "solady/utils/SSTORE2.sol";
import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import {
    TraitLabelStorage,
    TraitLabelStorageLib,
    TraitLabel,
    TraitLabelLib,
    Editors,
    StoredTraitLabel,
    AllowedEditor,
    EditorsLib,
    StoredTraitLabelLib
} from "./lib/TraitLabelLib.sol";

library OnchainTraitsStorage {
    struct Layout {
        /// @notice An enumerable set of all trait keys that have been set.
        EnumerableSet.Bytes32Set _traitKeys;
        /// @notice A mapping of traitKey to OnchainTraitsStorage.layout()._traitLabelStorage metadata.
        mapping(bytes32 traitKey => TraitLabelStorage traitLabelStorage) _traitLabelStorage;
        /// @notice An enumerable set of all accounts allowed to edit traits with a "Custom" editor privilege.
        EnumerableSet.AddressSet _customEditors;
    }

    bytes32 internal constant STORAGE_SLOT = keccak256("contracts.storage.erc7496-dynamictraits.onchaintraits");

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

abstract contract OnchainTraits is Ownable, DynamicTraits {
    using OnchainTraitsStorage for OnchainTraitsStorage.Layout;
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @notice Thrown when the caller does not have the privilege to set a trait
    error InsufficientPrivilege();
    /// @notice Thrown when trying to set a trait that does not exist
    error TraitDoesNotExist(bytes32 traitKey);

    constructor() {
        _initializeOwner(msg.sender);
    }

    // ABSTRACT

    /// @notice Helper to determine if a given address has the AllowedEditor.TokenOwner privilege.
    function _isOwnerOrApproved(uint256 tokenId, address addr) internal view virtual returns (bool);

    // CUSTOM EDITORS

    /**
     * @notice Check if an address is a custom editor
     * @param editor The address to check
     */
    function isCustomEditor(address editor) external view returns (bool) {
        return OnchainTraitsStorage.layout()._customEditors.contains(editor);
    }

    /**
     * @notice Add or remove an address as a custom editor
     * @param editor The address to add or remove
     * @param insert Whether to add or remove the address
     */
    function updateCustomEditor(address editor, bool insert) external onlyOwner {
        if (insert) {
            OnchainTraitsStorage.layout()._customEditors.add(editor);
        } else {
            OnchainTraitsStorage.layout()._customEditors.remove(editor);
        }
    }

    /**
     * @notice Get the list of custom editors. This may revert if there are too many editors.
     */
    function getCustomEditors() external view returns (address[] memory) {
        return OnchainTraitsStorage.layout()._customEditors.values();
    }

    /**
     * @notice Get the number of custom editors
     */
    function getCustomEditorsLength() external view returns (uint256) {
        return OnchainTraitsStorage.layout()._customEditors.length();
    }

    /**
     * @notice Get the custom editor at a given index
     * @param index The index of the custom editor to get
     */
    function getCustomEditorAt(uint256 index) external view returns (address) {
        return OnchainTraitsStorage.layout()._customEditors.at(index);
    }

    // LABELS URI

    /**
     * @notice Get the onchain URI for the trait metadata, encoded as a JSON data URI
     */
    function getTraitMetadataURI() external view virtual override returns (string memory) {
        return Metadata.jsonDataURI(_getTraitMetadataJson());
    }

    /**
     * @notice Get the raw JSON for the trait metadata
     */
    function _getTraitMetadataJson() internal view returns (string memory) {
        bytes32[] memory keys = OnchainTraitsStorage.layout()._traitKeys.values();
        return TraitLabelStorageLib.toLabelJson(OnchainTraitsStorage.layout()._traitLabelStorage, keys);
    }

    /**
     * @notice Return trait label storage information at a given key.
     */
    function traitLabelStorage(bytes32 traitKey) external view returns (TraitLabelStorage memory) {
        return OnchainTraitsStorage.layout()._traitLabelStorage[traitKey];
    }

    /**
     * @notice Set a trait for a given traitKey and tokenId. Checks that the caller has permission to set the trait,
     *         and, if the TraitLabel specifies that the trait value must be validated, checks that the trait value
     *         is valid.
     * @param tokenId The token ID to get the trait value for
     * @param traitKey The trait key to get the value of
     * @param newValue The new trait value
     */
    function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 newValue) public virtual override {
        TraitLabelStorage memory labelStorage = OnchainTraitsStorage.layout()._traitLabelStorage[traitKey];
        StoredTraitLabel storedTraitLabel = labelStorage.storedLabel;
        if (!StoredTraitLabelLib.exists(storedTraitLabel)) {
            revert TraitDoesNotExist(traitKey);
        }
        _verifySetterPrivilege(labelStorage.allowedEditors, tokenId);

        if (labelStorage.valuesRequireValidation) {
            TraitLabelLib.validateAcceptableValue(StoredTraitLabelLib.load(storedTraitLabel), traitKey, newValue);
        }
        DynamicTraits.setTrait(tokenId, traitKey, newValue);
    }

    /**
     * @notice Set the TraitLabel for a given traitKey. This will overwrite any existing TraitLabel for the traitKey.
     *         Traits may not be set without a corresponding TraitLabel. OnlyOwner.
     * @param traitKey The trait key to set the value of
     * @param _traitLabel The trait label to set
     */
    function setTraitLabel(bytes32 traitKey, TraitLabel calldata _traitLabel) external virtual onlyOwner {
        _setTraitLabel(traitKey, _traitLabel);
    }

    /**
     * @notice Set the OnchainTraitsStorage.layout()._traitLabelStorage for a traitKey. Packs SSTORE2 value along with allowedEditors, required?, and
     *         valuesRequireValidation? into a single storage slot for more efficient validation when setting trait values.
     */
    function _setTraitLabel(bytes32 traitKey, TraitLabel memory _traitLabel) internal virtual {
        OnchainTraitsStorage.layout()._traitKeys.add(traitKey);
        OnchainTraitsStorage.layout()._traitLabelStorage[traitKey] = TraitLabelStorage({
            allowedEditors: _traitLabel.editors,
            required: _traitLabel.required,
            valuesRequireValidation: _traitLabel.acceptableValues.length > 0,
            storedLabel: TraitLabelLib.store(_traitLabel)
        });
    }

    /**
     * @notice Checks that the caller has permission to set a trait for a given allowed Editors set and token ID.
     *         Reverts with InsufficientPrivilege if the caller does not have permission.
     * @param editors The allowed editors for this trait
     * @param tokenId The token ID the trait is being set for
     */
    function _verifySetterPrivilege(Editors editors, uint256 tokenId) internal view {
        // anyone
        if (EditorsLib.contains(editors, AllowedEditor.Anyone)) {
            // short circuit
            return;
        }

        // tokenOwner
        if (EditorsLib.contains(editors, AllowedEditor.TokenOwner)) {
            if (_isOwnerOrApproved(tokenId, msg.sender)) {
                // short circuit
                return;
            }
        }
        // customEditor
        if (EditorsLib.contains(editors, AllowedEditor.Custom)) {
            if (OnchainTraitsStorage.layout()._customEditors.contains(msg.sender)) {
                // short circuit
                return;
            }
        }
        // contractOwner
        if (EditorsLib.contains(editors, AllowedEditor.ContractOwner)) {
            if (owner() == msg.sender) {
                // short circuit
                return;
            }
        }

        if (EditorsLib.contains(editors, AllowedEditor.Self)) {
            if (address(this) == msg.sender) {
                // short circuit
                return;
            }
        }

        revert InsufficientPrivilege();
    }

    /**
     * @notice Gets the individual JSON objects for each dynamic trait set on this token by iterating over all
     *         possible traitKeys and checking if the trait is set on the token. This is extremely inefficient
     *         and should only be called offchain when rendering metadata.
     * @param tokenId The token ID to get the dynamic trait attributes for
     * @return An array of JSON objects, each representing a dynamic trait set on the token
     */
    function _dynamicAttributes(uint256 tokenId) internal view virtual returns (string[] memory) {
        bytes32[] memory keys = OnchainTraitsStorage.layout()._traitKeys.values();
        uint256 keysLength = keys.length;

        string[] memory attributes = new string[](keysLength);
        // keep track of how many traits are actually set
        uint256 num;
        for (uint256 i = 0; i < keysLength;) {
            bytes32 key = keys[i];
            bytes32 trait = getTraitValue(tokenId, key);
            // check that the trait is set, otherwise, skip it
            if (trait != bytes32(0)) {
                attributes[num] =
                    TraitLabelStorageLib.toAttributeJson(OnchainTraitsStorage.layout()._traitLabelStorage, key, trait);
                unchecked {
                    ++num;
                }
            }
            unchecked {
                ++i;
            }
        }
        ///@solidity memory-safe-assembly
        assembly {
            // update attributes with actual length
            mstore(attributes, num)
        }

        return attributes;
    }
}
Código Fuente del Contrato
Archivo 16 de 24: Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

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

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

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

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

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

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}
Código Fuente del Contrato
Archivo 17 de 24: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

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

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}
Código Fuente del Contrato
Archivo 18 de 24: 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/solady/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)
/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)
library SSTORE2 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The proxy initialization code.
    uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;

    /// @dev Hash of the `_CREATE3_PROXY_INITCODE`.
    /// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
    bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
        0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;

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

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         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 n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.
            /**
             * ---------------------------------------------------+
             * Opcode | Mnemonic       | Stack     | Memory       |
             * ---------------------------------------------------|
             * 61 l   | PUSH2 l        | l         |              |
             * 80     | DUP1           | l l       |              |
             * 60 0xa | PUSH1 0xa      | 0xa l l   |              |
             * 3D     | RETURNDATASIZE | 0 0xa l l |              |
             * 39     | CODECOPY       | l         | [0..l): code |
             * 3D     | RETURNDATASIZE | 0 l       | [0..l): code |
             * F3     | RETURN         |           | [0..l): 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.
             */
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create(0, add(data, 0x15), add(n, 0xb))
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract with `salt`
    /// and returns its normal CREATE2 deterministic address.
    function writeCounterfactual(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    /// This uses the so-called "CREATE3" workflow,
    /// which means that `pointer` is agnostic to `data, and only depends on `salt`.
    function writeDeterministic(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.
            let proxy := create2(0, 0x10, 0x10, salt)
            if iszero(proxy) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, proxy) // Store the proxy's address.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)

            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            if iszero(
                mul( // The arguments of `mul` are evaluated last to first.
                    extcodesize(pointer),
                    call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
                )
            ) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    ADDRESS CALCULATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @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 n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
            mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
            hash := keccak256(add(data, 0x15), add(n, 0xb))
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`
    function predictCounterfactualAddress(bytes memory data, bytes32 salt)
        internal
        view
        returns (address pointer)
    {
        pointer = predictCounterfactualAddress(data, salt, address(this));
    }

    /// @dev Returns the CREATE2 address of the storage contract for `data`
    /// deployed with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictCounterfactualAddress(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)
        }
    }

    /// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.
    function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
        pointer = predictDeterministicAddress(salt, address(this));
    }

    /// @dev Returns the "CREATE3" deterministic address for `salt` with `deployer`.
    function predictDeterministicAddress(bytes32 salt, address deployer)
        internal
        pure
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, deployer) // Store `deployer`.
            mstore8(0x0b, 0xff) // Store the prefix.
            mstore(0x20, salt) // Store the salt.
            mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.

            mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.
            mstore(0x40, m) // Restore the free memory pointer.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)
        }
    }

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

    /// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.
    function read(address pointer) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
            extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
            mstore(data, n) // Store the length.
            mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.
    function read(address pointer, uint256 start) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(sub(extcodesize(pointer), 0x01), 0xffffffffff)
            extcodecopy(pointer, add(data, 0x1f), start, add(n, 0x21))
            mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the data on `pointer` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `pointer` MUST be deployed via the SSTORE2 write functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `pointer` does not have any code.
    function read(address pointer, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
            if iszero(and(0xff, mload(add(data, d)))) {
                let n := sub(extcodesize(pointer), 0x01)
                returndatacopy(returndatasize(), returndatasize(), shr(64, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(data, d) // Store the length.
            mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.
        }
    }
}
Código Fuente del Contrato
Archivo 19 de 24: SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

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

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

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

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

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

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `1` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}
Código Fuente del Contrato
Archivo 20 de 24: Solarray.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;

library Solarray {
    
    function uint8s(uint8 a) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](1);
		arr[0] = a;
        return arr;
    }


    function uint8s(uint8 a,uint8 b) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint8s(uint8 a,uint8 b,uint8 c) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint8s(uint8 a,uint8 b,uint8 c,uint8 d) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint8s(uint8 a,uint8 b,uint8 c,uint8 d,uint8 e) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint8s(uint8 a,uint8 b,uint8 c,uint8 d,uint8 e,uint8 f) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint8s(uint8 a,uint8 b,uint8 c,uint8 d,uint8 e,uint8 f,uint8 g) internal pure returns (uint8[] memory) {
        uint8[] memory arr = new uint8[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function uint16s(uint16 a) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](1);
		arr[0] = a;
        return arr;
    }


    function uint16s(uint16 a,uint16 b) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint16s(uint16 a,uint16 b,uint16 c) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint16s(uint16 a,uint16 b,uint16 c,uint16 d) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint16s(uint16 a,uint16 b,uint16 c,uint16 d,uint16 e) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint16s(uint16 a,uint16 b,uint16 c,uint16 d,uint16 e,uint16 f) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint16s(uint16 a,uint16 b,uint16 c,uint16 d,uint16 e,uint16 f,uint16 g) internal pure returns (uint16[] memory) {
        uint16[] memory arr = new uint16[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function uint32s(uint32 a) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](1);
		arr[0] = a;
        return arr;
    }


    function uint32s(uint32 a,uint32 b) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint32s(uint32 a,uint32 b,uint32 c) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint32s(uint32 a,uint32 b,uint32 c,uint32 d) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint32s(uint32 a,uint32 b,uint32 c,uint32 d,uint32 e) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint32s(uint32 a,uint32 b,uint32 c,uint32 d,uint32 e,uint32 f) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint32s(uint32 a,uint32 b,uint32 c,uint32 d,uint32 e,uint32 f,uint32 g) internal pure returns (uint32[] memory) {
        uint32[] memory arr = new uint32[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }
    
    function uint40s(uint40 a) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](1);
		arr[0] = a;
        return arr;
    }


    function uint40s(uint40 a,uint40 b) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint40s(uint40 a,uint40 b,uint40 c) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint40s(uint40 a,uint40 b,uint40 c,uint40 d) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint40s(uint40 a,uint40 b,uint40 c,uint40 d,uint40 e) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint40s(uint40 a,uint40 b,uint40 c,uint40 d,uint40 e,uint40 f) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint40s(uint40 a,uint40 b,uint40 c,uint40 d,uint40 e,uint40 f,uint40 g) internal pure returns (uint40[] memory) {
        uint40[] memory arr = new uint40[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }

    function uint64s(uint64 a) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](1);
		arr[0] = a;
        return arr;
    }


    function uint64s(uint64 a,uint64 b) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint64s(uint64 a,uint64 b,uint64 c) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint64s(uint64 a,uint64 b,uint64 c,uint64 d) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint64s(uint64 a,uint64 b,uint64 c,uint64 d,uint64 e) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint64s(uint64 a,uint64 b,uint64 c,uint64 d,uint64 e,uint64 f) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint64s(uint64 a,uint64 b,uint64 c,uint64 d,uint64 e,uint64 f,uint64 g) internal pure returns (uint64[] memory) {
        uint64[] memory arr = new uint64[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function uint128s(uint128 a) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](1);
		arr[0] = a;
        return arr;
    }


    function uint128s(uint128 a,uint128 b) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint128s(uint128 a,uint128 b,uint128 c) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint128s(uint128 a,uint128 b,uint128 c,uint128 d) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint128s(uint128 a,uint128 b,uint128 c,uint128 d,uint128 e) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint128s(uint128 a,uint128 b,uint128 c,uint128 d,uint128 e,uint128 f) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint128s(uint128 a,uint128 b,uint128 c,uint128 d,uint128 e,uint128 f,uint128 g) internal pure returns (uint128[] memory) {
        uint128[] memory arr = new uint128[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function uint256s(uint256 a) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](1);
		arr[0] = a;
        return arr;
    }


    function uint256s(uint256 a,uint256 b) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function uint256s(uint256 a,uint256 b,uint256 c) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function uint256s(uint256 a,uint256 b,uint256 c,uint256 d) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function uint256s(uint256 a,uint256 b,uint256 c,uint256 d,uint256 e) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function uint256s(uint256 a,uint256 b,uint256 c,uint256 d,uint256 e,uint256 f) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function uint256s(uint256 a,uint256 b,uint256 c,uint256 d,uint256 e,uint256 f,uint256 g) internal pure returns (uint256[] memory) {
        uint256[] memory arr = new uint256[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int8s(int8 a) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](1);
		arr[0] = a;
        return arr;
    }


    function int8s(int8 a,int8 b) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int8s(int8 a,int8 b,int8 c) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int8s(int8 a,int8 b,int8 c,int8 d) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int8s(int8 a,int8 b,int8 c,int8 d,int8 e) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int8s(int8 a,int8 b,int8 c,int8 d,int8 e,int8 f) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int8s(int8 a,int8 b,int8 c,int8 d,int8 e,int8 f,int8 g) internal pure returns (int8[] memory) {
        int8[] memory arr = new int8[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int16s(int16 a) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](1);
		arr[0] = a;
        return arr;
    }


    function int16s(int16 a,int16 b) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int16s(int16 a,int16 b,int16 c) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int16s(int16 a,int16 b,int16 c,int16 d) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int16s(int16 a,int16 b,int16 c,int16 d,int16 e) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int16s(int16 a,int16 b,int16 c,int16 d,int16 e,int16 f) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int16s(int16 a,int16 b,int16 c,int16 d,int16 e,int16 f,int16 g) internal pure returns (int16[] memory) {
        int16[] memory arr = new int16[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int32s(int32 a) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](1);
		arr[0] = a;
        return arr;
    }


    function int32s(int32 a,int32 b) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int32s(int32 a,int32 b,int32 c) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int32s(int32 a,int32 b,int32 c,int32 d) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int32s(int32 a,int32 b,int32 c,int32 d,int32 e) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int32s(int32 a,int32 b,int32 c,int32 d,int32 e,int32 f) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int32s(int32 a,int32 b,int32 c,int32 d,int32 e,int32 f,int32 g) internal pure returns (int32[] memory) {
        int32[] memory arr = new int32[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int64s(int64 a) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](1);
		arr[0] = a;
        return arr;
    }


    function int64s(int64 a,int64 b) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int64s(int64 a,int64 b,int64 c) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int64s(int64 a,int64 b,int64 c,int64 d) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int64s(int64 a,int64 b,int64 c,int64 d,int64 e) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int64s(int64 a,int64 b,int64 c,int64 d,int64 e,int64 f) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int64s(int64 a,int64 b,int64 c,int64 d,int64 e,int64 f,int64 g) internal pure returns (int64[] memory) {
        int64[] memory arr = new int64[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int128s(int128 a) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](1);
		arr[0] = a;
        return arr;
    }


    function int128s(int128 a,int128 b) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int128s(int128 a,int128 b,int128 c) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int128s(int128 a,int128 b,int128 c,int128 d) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int128s(int128 a,int128 b,int128 c,int128 d,int128 e) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int128s(int128 a,int128 b,int128 c,int128 d,int128 e,int128 f) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int128s(int128 a,int128 b,int128 c,int128 d,int128 e,int128 f,int128 g) internal pure returns (int128[] memory) {
        int128[] memory arr = new int128[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function int256s(int256 a) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](1);
		arr[0] = a;
        return arr;
    }


    function int256s(int256 a,int256 b) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function int256s(int256 a,int256 b,int256 c) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function int256s(int256 a,int256 b,int256 c,int256 d) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function int256s(int256 a,int256 b,int256 c,int256 d,int256 e) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function int256s(int256 a,int256 b,int256 c,int256 d,int256 e,int256 f) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function int256s(int256 a,int256 b,int256 c,int256 d,int256 e,int256 f,int256 g) internal pure returns (int256[] memory) {
        int256[] memory arr = new int256[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytes1s(bytes1 a) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](1);
		arr[0] = a;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b,bytes1 c) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b,bytes1 c,bytes1 d) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b,bytes1 c,bytes1 d,bytes1 e) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b,bytes1 c,bytes1 d,bytes1 e,bytes1 f) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytes1s(bytes1 a,bytes1 b,bytes1 c,bytes1 d,bytes1 e,bytes1 f,bytes1 g) internal pure returns (bytes1[] memory) {
        bytes1[] memory arr = new bytes1[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytes8s(bytes8 a) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](1);
		arr[0] = a;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b,bytes8 c) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b,bytes8 c,bytes8 d) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b,bytes8 c,bytes8 d,bytes8 e) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b,bytes8 c,bytes8 d,bytes8 e,bytes8 f) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytes8s(bytes8 a,bytes8 b,bytes8 c,bytes8 d,bytes8 e,bytes8 f,bytes8 g) internal pure returns (bytes8[] memory) {
        bytes8[] memory arr = new bytes8[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytes16s(bytes16 a) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](1);
		arr[0] = a;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b,bytes16 c) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b,bytes16 c,bytes16 d) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b,bytes16 c,bytes16 d,bytes16 e) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b,bytes16 c,bytes16 d,bytes16 e,bytes16 f) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytes16s(bytes16 a,bytes16 b,bytes16 c,bytes16 d,bytes16 e,bytes16 f,bytes16 g) internal pure returns (bytes16[] memory) {
        bytes16[] memory arr = new bytes16[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytes20s(bytes20 a) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](1);
		arr[0] = a;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b,bytes20 c) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b,bytes20 c,bytes20 d) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b,bytes20 c,bytes20 d,bytes20 e) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b,bytes20 c,bytes20 d,bytes20 e,bytes20 f) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytes20s(bytes20 a,bytes20 b,bytes20 c,bytes20 d,bytes20 e,bytes20 f,bytes20 g) internal pure returns (bytes20[] memory) {
        bytes20[] memory arr = new bytes20[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytes32s(bytes32 a) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](1);
		arr[0] = a;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b,bytes32 c) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b,bytes32 c,bytes32 d) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b,bytes32 c,bytes32 d,bytes32 e) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b,bytes32 c,bytes32 d,bytes32 e,bytes32 f) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytes32s(bytes32 a,bytes32 b,bytes32 c,bytes32 d,bytes32 e,bytes32 f,bytes32 g) internal pure returns (bytes32[] memory) {
        bytes32[] memory arr = new bytes32[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bytess(bytes memory a) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](1);
		arr[0] = a;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b,bytes memory c) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b,bytes memory c,bytes memory d) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b,bytes memory c,bytes memory d,bytes memory e) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b,bytes memory c,bytes memory d,bytes memory e,bytes memory f) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bytess(bytes memory a,bytes memory b,bytes memory c,bytes memory d,bytes memory e,bytes memory f,bytes memory g) internal pure returns (bytes[] memory) {
        bytes[] memory arr = new bytes[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function addresses(address a) internal pure returns (address[] memory) {
        address[] memory arr = new address[](1);
		arr[0] = a;
        return arr;
    }


    function addresses(address a,address b) internal pure returns (address[] memory) {
        address[] memory arr = new address[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function addresses(address a,address b,address c) internal pure returns (address[] memory) {
        address[] memory arr = new address[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function addresses(address a,address b,address c,address d) internal pure returns (address[] memory) {
        address[] memory arr = new address[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function addresses(address a,address b,address c,address d,address e) internal pure returns (address[] memory) {
        address[] memory arr = new address[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function addresses(address a,address b,address c,address d,address e,address f) internal pure returns (address[] memory) {
        address[] memory arr = new address[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function addresses(address a,address b,address c,address d,address e,address f,address g) internal pure returns (address[] memory) {
        address[] memory arr = new address[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function bools(bool a) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](1);
		arr[0] = a;
        return arr;
    }


    function bools(bool a,bool b) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function bools(bool a,bool b,bool c) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function bools(bool a,bool b,bool c,bool d) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function bools(bool a,bool b,bool c,bool d,bool e) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function bools(bool a,bool b,bool c,bool d,bool e,bool f) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function bools(bool a,bool b,bool c,bool d,bool e,bool f,bool g) internal pure returns (bool[] memory) {
        bool[] memory arr = new bool[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }


    function strings(string memory a) internal pure returns (string[] memory) {
        string[] memory arr = new string[](1);
		arr[0] = a;
        return arr;
    }


    function strings(string memory a,string memory b) internal pure returns (string[] memory) {
        string[] memory arr = new string[](2);
		arr[0] = a;
		arr[1] = b;
        return arr;
    }


    function strings(string memory a,string memory b,string memory c) internal pure returns (string[] memory) {
        string[] memory arr = new string[](3);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
        return arr;
    }


    function strings(string memory a,string memory b,string memory c,string memory d) internal pure returns (string[] memory) {
        string[] memory arr = new string[](4);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
        return arr;
    }


    function strings(string memory a,string memory b,string memory c,string memory d,string memory e) internal pure returns (string[] memory) {
        string[] memory arr = new string[](5);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
        return arr;
    }


    function strings(string memory a,string memory b,string memory c,string memory d,string memory e,string memory f) internal pure returns (string[] memory) {
        string[] memory arr = new string[](6);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
        return arr;
    }


    function strings(string memory a,string memory b,string memory c,string memory d,string memory e,string memory f,string memory g) internal pure returns (string[] memory) {
        string[] memory arr = new string[](7);
		arr[0] = a;
		arr[1] = b;
		arr[2] = c;
		arr[3] = d;
		arr[4] = e;
		arr[5] = f;
		arr[6] = g;
        return arr;
    }

}

Código Fuente del Contrato
Archivo 21 de 24: StackScore.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

// External libraries
import {LibString} from "solady/utils/LibString.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {ECDSA} from "solady/utils/ECDSA.sol";
import {Solarray} from "solarray/Solarray.sol";
// Internal imports
import {json} from "./onchain/json.sol";
import {Metadata, DisplayType} from "./onchain/Metadata.sol";
import {TraitLib} from "./dynamic-traits/lib/TraitLabelLib.sol";
import {AbstractNFT} from "./AbstractNFT.sol";
import {StackScoreRenderer} from "./StackScoreRenderer.sol";
import {IERC5192} from "./interfaces/IERC5192.sol";

/// @title StackScore
/// @notice A fully onchain, dynamic, soulbound token for reputation scores
/// @notice Each score is rendered as an onchain SVG.
/// @author strangechances (g@stack.so)
contract StackScore is AbstractNFT, IERC5192, ReentrancyGuard {
    /// @notice The current version of the contract.
    string public constant version = "1";
    /// @notice The name of the token.
    string internal constant _tokenName = "Stack Score";
    /// @notice The description of the token.
    string internal constant _tokenDescription = "A dynamic, onchain, soulbound reputation score";
    /// @notice The signer address.
    /// @dev The signer address is used to verify the score signature.
    address public signer;
    /// @notice The mint fee.
    uint256 public mintFee = 0.001 ether;
    /// @notice The referral fee percentage, in basis points.
    /// @dev This is a percentage of the mint fee, in basis points (100 basis points is 1%).
    uint256 public referralBps = 5000;
    /// @notice Address to token ID mapping.
    /// @dev Prevents multiple tokens from being minted for the same address.
    mapping(address => uint256) public addressToTokenId;
    /// @notice Signature mapping, to prevent replay attacks.
    /// @dev This is used to prevent replay attacks.
    mapping(bytes32 => bool) internal signatures;
    /// @notice The current token ID.
    uint256 internal currentId;
    /// @notice The renderer contract.
    /// @dev This contract is used to render the SVG image.
    StackScoreRenderer internal renderer;
    /// @notice The mint fee recipient.
    /// @dev This address receives the mint fee, minus any referral fee.
    address public mintFeeRecipient;

    /// @notice Emitted when the score is updated.
    event ScoreUpdated(address account, uint256 tokenId, uint256 oldScore, uint256 newScore);
    /// @notice Emitted when a token is minted.
    event Minted(address to, uint256 tokenId);
    /// @notice Emitted when a referral is paid.
    event ReferralPaid(address referrer, address referred, uint256 amount);
    /// @notice Emitted when the mint fee is updated.
    event MintFeeUpdated(uint256 oldFee, uint256 newFee);
    /// @notice Emitted when the mint fee recipient is updated.
    event MintFeeRecipientUpdated(address oldRecipient, address newRecipient);
    /// @notice Emitted when the referral fee is updated.
    /// @dev This is a percentage of the mint fee, in basis points (100 basis points is 1%).
    event ReferralBpsUpdated(uint256 oldBps, uint256 newBps);
    /// @notice Emitted when the renderer contract is updated.
    event RendererUpdated(address oldRenderer, address newRenderer);
    /// @notice Emitted when the signer address is updated.
    event SignerUpdated(address oldSigner, address newSigner);

    /// @notice Error thrown when the token is locked upon transfer.
    /// @dev The token is Soulbound according to the ERC-5192 standard.
    error TokenLocked(uint256 tokenId);
    /// @notice Error thrown when the signature is invalid.
    error InvalidSignature();
    /// @notice Error thrown when the signature is already used.
    error SignatureAlreadyUsed();
    /// @notice Error thrown when the mint fee is insufficient.
    error InsufficientFee();
    /// @notice Error thrown when the sender is not the token owner.
    /// @dev For example, when a non-owner tries to update a score's color palette.
    error OnlyTokenOwner();
    /// @notice Error thrown when a given timestamp is older than the last update for a score.
    error TimestampTooOld();
    /// @notice Error thrown if mint called for the second time for the same address.
    error OneTokenPerAddress();

    /// @notice Constructor
    /// @dev Set the name and symbol of the token.
    constructor(address initialOwner) AbstractNFT(_tokenName, "STACK_SCORE") {
        _initializeOwner(initialOwner);
    }

    /// @notice Mint a new soulbound token.
    /// @dev Mint a new token and lock it.
    /// @dev The mint fee is sent to the mint fee recipient.
    /// @dev Does not require a signature, since there is no score.
    /// @param to The address to mint the token to.
    /// @return The token ID.
    function mint(address to) payable public nonReentrant returns (uint256) {
        _assertSufficientFee();

        SafeTransferLib.safeTransferETH(mintFeeRecipient, msg.value);
        _mintTo(to);

        return currentId;
    }

    /// @notice Mint a new soulbound token with a referral.
    /// @dev Mint a new token, lock it, and send a referral fee.
    /// @dev Does not need to check a signature, since there is no score.
    /// @param to The address to mint the token to.
    /// @param referrer The address to send the referral fee to.
    /// @return The token ID.
    function mintWithReferral(
        address to,
        address referrer
    ) payable public nonReentrant returns (uint256) {
        _assertSufficientFee();

        uint256 referralAmount = _getReferralAmount(msg.value);
        SafeTransferLib.safeTransferETH(mintFeeRecipient, msg.value - referralAmount);
        emit ReferralPaid(referrer, to, referralAmount);
        SafeTransferLib.safeTransferETH(referrer, referralAmount);
        _mintTo(to);

        return currentId;
    }

    /// @notice Mint a new soulbound token with a score.
    /// @dev Mint a new token, lock it, and update the score.
    /// @param to The address to mint the token to.
    /// @param score The score to set.
    /// @param signature The signature to verify.
    /// @return The token ID.
    function mintWithScore(
        address to,
        uint256 score,
        uint256 timestamp,
        bytes memory signature
    ) payable public returns (uint256) {
        mint(to);
        updateScore(currentId, score, timestamp, signature);
        return currentId;
    }

    /// @notice Mint a new soulbound token with a score and palette.
    /// @dev Mint a new token, lock it, update the score, and update the palette.
    /// @param to The address to mint the token to.
    /// @param score The score to set.
    /// @param palette The palette index to set.
    /// @param signature The signature to verify.
    /// @return The token ID.
    function mintWithScoreAndPalette(
        address to,
        uint256 score,
        uint256 timestamp,
        uint256 palette,
        bytes memory signature
    ) payable public returns (uint256) {
        mint(to);
        updateScore(currentId, score, timestamp, signature);
        updatePalette(currentId, palette);
        return currentId;
    }

    /// @notice Mint a new soulbound token with a score and referral.
    /// @dev Mint a new token, lock it, update the score, and send a referral fee.
    /// @param to The address to mint the token to.
    /// @param score The score to set.
    /// @param referrer The address to send the referral fee to.
    /// @param signature The signature to verify.
    /// @return The token ID.
    function mintWithScoreAndReferral(
        address to,
        uint256 score,
        uint256 timestamp,
        address referrer,
        bytes memory signature
    ) payable public returns (uint256) {
        mintWithReferral(to, referrer);
        updateScore(currentId, score, timestamp, signature);
        return currentId;
    }

    /// @notice Mint a new soulbound token with a score, referral, and palette.
    /// @dev Mint a new token, lock it, update the score, send a referral fee, and update the palette.
    /// @param to The address to mint the token to.
    /// @param score The score to set.
    /// @param referrer The address to send the referral fee to.
    /// @param palette The palette index to set.
    /// @param signature The signature to verify.
    /// @return The token ID.
    function mintWithScoreAndReferralAndPalette(
        address to,
        uint256 score,
        uint256 timestamp,
        address referrer,
        uint256 palette,
        bytes memory signature
    ) payable public returns (uint256) {
        mintWithReferral(to, referrer);
        updateScore(currentId, score, timestamp, signature);
        updatePalette(currentId, palette);
        return currentId;
    }

    /// @notice Mint a new soulbound token with a referral and palette.
    /// @dev Mint a new token, lock it, send a referral fee, and update the palette.
    /// @dev Does not require a signature, since there is no score.
    /// @param to The address to mint the token to.
    /// @param referrer The address to send the referral fee to.
    /// @param palette The palette index to set.
    /// @return The token ID.
    function mintWithReferralAndPalette(
        address to,
        address referrer,
        uint256 palette
    ) payable public nonReentrant returns (uint256) {
        mintWithReferral(to, referrer);
        updatePalette(currentId, palette);
        return currentId;
    }

    /// @notice Mint a new soulbound token with a palette.
    /// @dev Mint a new token, lock it, and update the palette.
    /// @dev Does not require a signature, since there is no score.
    /// @param to The address to mint the token to.
    /// @param palette The palette index to set.
    function mintWithPalette(
        address to,
        uint256 palette
    ) payable public nonReentrant returns (uint256) {
        mint(to);
        updatePalette(currentId, palette);
        return currentId;
    }

    /// @notice Update the score for a given token ID.
    /// @dev The score is signed by the signer for the account.
    /// @param tokenId The token ID to update.
    /// @param newScore The new score.
    /// @param signature The signature to verify.
    function updateScore(uint256 tokenId, uint256 newScore, uint256 timestamp, bytes memory signature) public {
        uint256 oldScore = uint256(getTraitValue(tokenId, "score"));
        if (newScore == 0 && oldScore == 0) {
            // No need to update the score if it's already 0.
            return;
        }
        address account = ownerOf(tokenId);
        _assertValidTimestamp(tokenId, timestamp);
        _assertValidScoreSignature(account, newScore, timestamp, signature);
        this.setTrait(tokenId, "updatedAt", bytes32(timestamp));
        this.setTrait(tokenId, "score", bytes32(newScore));
        emit ScoreUpdated(account, tokenId, oldScore, newScore);
    }

    /// @notice Update the palette index for a given token ID.
    /// @dev The palette index is the index of the palette to use for rendering.
    /// @dev Only the owner can update the palette index.
    /// @param tokenId The token ID to update.
    function updatePalette(uint256 tokenId, uint256 paletteIndex) public {
        _assertTokenOwner(tokenId);
        this.setTrait(tokenId, "paletteIndex", bytes32(paletteIndex));
    }

    /// @notice Check if a token is locked.
    /// @dev The token is Soulbound according to the ERC-5192 standard.
    /// @param tokenId The token ID to check.
    function locked(uint256 tokenId) public pure override returns (bool) {
        return true;
    }

    /// @notice Get the score for a given account.
    /// @param account The account to get the score for.
    /// @return The score.
    function getScore(address account) public view returns (uint256) {
        return uint256(getTraitValue(addressToTokenId[account], "score"));
    }

    /// @notice Get the updated at timestamp for a given token ID.
    /// @dev The updated at timestamp is the timestamp of the last score update.
    /// @param tokenId The token ID to get the updated at timestamp for.
    /// @return The updated at timestamp.
    function getUpdatedAt(uint256 tokenId) public view returns (uint256) {
        return uint256(getTraitValue(tokenId, "updatedAt"));
    }

    /// @notice Get the score and last updated timestamp for a given account.
    /// @dev The score is the reputation score aggregated from Stack leaderboards, and the last updated timestamp.
    /// @param account The account to get the score and last updated timestamp for.
    /// @return The score and last updated timestamp.
    function getScoreAndLastUpdated(address account) public view returns (uint256, uint256) {
        uint256 tokenId = addressToTokenId[account];
        return (
            uint256(getTraitValue(tokenId, "score")),
            uint256(getTraitValue(tokenId, "updatedAt"))
        );
    }

    /// @notice Get the palette index for a given token ID.
    /// @param tokenId The token ID to get the palette index for.
    /// @return The palette index.
    function getPaletteIndex(uint256 tokenId) public view returns (uint256) {
        return uint256(getTraitValue(tokenId, "paletteIndex"));
    }

    /// @notice Get the current token ID.
    /// @return The current token ID.
    function getCurrentId() public view returns (uint256) {
        return currentId;
    }

    /// @notice Get the renderer contract address.
    /// @return The renderer contract address.
    function getRenderer() public view returns (address) {
        return address(renderer);
    }

    /// @notice Set the renderer contract address.
    /// @dev Only the owner can set the renderer contract address.
    /// @param _renderer The renderer contract address.
    function setRenderer(address _renderer) public onlyOwner {
        address oldRenderer = address(renderer);
        renderer = StackScoreRenderer(_renderer);
        emit RendererUpdated(oldRenderer, _renderer);
    }

    /// @notice Set the signer address.
    /// @dev Only the owner can set the signer address.
    /// @param _signer The signer address.
    function setSigner(address _signer) public onlyOwner {
        address oldSigner = signer;
        signer = _signer;
        emit SignerUpdated(oldSigner, _signer);
    }

    /// @notice Set the mint fee.
    /// @dev Only the owner can set the mint fee.
    function setMintFee(uint256 fee) public onlyOwner {
        uint256 oldFee = mintFee;
        mintFee = fee;
        emit MintFeeUpdated(oldFee, mintFee);
    }

    /// @notice Set the referral fee percentage.
    /// @dev Only the owner can set the referral fee percentage.
    /// @param bps The referral fee percentage, in basis points.
    function setReferralBps(uint256 bps) public onlyOwner {
        referralBps = bps;
        emit ReferralBpsUpdated(referralBps, bps);
    }

    /// @notice Set the mint fee recipient.
    /// @dev Only the owner can set the mint fee recipient.
    /// @param _mintFeeRecipient The mint fee recipient address.
    function setMintFeeRecipient(address _mintFeeRecipient) public onlyOwner {
        address oldFeeRecipient = mintFeeRecipient;
        mintFeeRecipient = _mintFeeRecipient;
        emit MintFeeRecipientUpdated(oldFeeRecipient, mintFeeRecipient);
    }

    function _getReferralAmount(uint256 amount) internal view returns (uint256) {
        return amount * referralBps / 10000;
    }

    function _assertOneTokenPerAddress(address to) internal view {
        if (balanceOf(to) > 0) {
            revert OneTokenPerAddress();
        }
    }

    /// @notice Mint a new soulbound token.
    /// @dev Mint a new token, lock it.
    /// @param to The address to mint the token to.
    function _mintTo(address to) internal {
        _assertOneTokenPerAddress(to);

        unchecked {
            _mint(to, ++currentId);
        }

        addressToTokenId[to] = currentId;

        emit Minted(to, currentId);
        emit Locked(currentId);
    }

    /// @notice Verify the signature for the score.
    /// @dev The function throws an error if the signature is invalid, or has been used before.
    /// @param account The account to verify the score for.
    /// @param score The score to verify.
    /// @param signature The signature to verify.
    function _assertValidScoreSignature(address account, uint256 score, uint256 timestamp, bytes memory signature) internal {
        if (signatures[keccak256(signature)]) {
            revert SignatureAlreadyUsed();
        }
        signatures[keccak256(signature)] = true;
        bytes32 hash = ECDSA.toEthSignedMessageHash(
            keccak256(abi.encodePacked(account, score, timestamp))
        );
        if (ECDSA.recover(hash, signature) != signer) {
            revert InvalidSignature();
        }
    }

    /// @notice Verify the sender is the owner of the token.
    /// @dev The function throws an error if the sender is not the owner of the token.
    /// @param tokenId The token ID to verify the owner for.
    function _assertTokenOwner(uint256 tokenId) internal view {
        if (msg.sender != ownerOf(tokenId)) {
            revert OnlyTokenOwner();
        }
    }

    /// @notice Verify the timestamp is not too old.
    /// @dev The function throws an error if the timestamp is too old.
    /// @param tokenId The token ID to verify the timestamp for.
    /// @param timestamp The timestamp to verify.
    function _assertValidTimestamp(uint256 tokenId, uint256 timestamp) internal view {
        uint256 lastUpdatedAt = uint256(getTraitValue(tokenId, "updatedAt"));
        // Ensure the score is newer than the last update.
        if (lastUpdatedAt > timestamp) {
            revert TimestampTooOld();
        }
    }

    function _assertSufficientFee() internal view {
        if (msg.value < mintFee) {
            revert InsufficientFee();
        }
    }

    /// @notice Get the URI for the trait metadata
    /// @param tokenId The token ID to get URI for
    /// @return The trait metadata URI.
    function _stringURI(uint256 tokenId) internal view override returns (string memory) {
        return json.objectOf(
            Solarray.strings(
                json.property("name", _tokenName),
                json.property("description", _tokenDescription),
                json.property("image", Metadata.base64SvgDataURI(_image(tokenId))),
                _attributes(tokenId)
            )
        );
    }

    /// @notice Helper function to get the static attributes for a given token ID
    /// @dev The static attributes are the name and description.
    /// @param tokenId The token ID to get the static attributes for
    /// @return The static attributes.
    function _staticAttributes(uint256 tokenId) internal view virtual override returns (string[] memory) {
        return Solarray.strings(
            Metadata.attribute({traitType: "Score Version", value: version})
        );
    }

    /// @notice Run checks before token transfers
    /// @dev Only allow transfers from the zero address, since the token is soulbound.
    /// @param from The address the token is being transferred from
    /// @param tokenId The token ID
    function _beforeTokenTransfer(address from, address, uint256 tokenId) internal pure override {
        // if the token is being transferred from an address
        if (from != address(0)) {
            revert TokenLocked(tokenId);
        }
    }

    /// @notice Helper function to get the raw SVG image for a given token ID
    /// @dev The SVG image is rendered by the renderer contract.
    /// @param tokenId The token ID to get the dynamic attributes for
    /// @return The SVG image.
    function _image(uint256 tokenId) internal view virtual override returns (string memory) {
        address account = ownerOf(tokenId);
        uint256 paletteIndex = uint256(getTraitValue(tokenId, "paletteIndex"));
        uint256 score = uint256(getTraitValue(tokenId, "score"));
        uint256 updatedAt = uint256(getTraitValue(tokenId, "updatedAt"));
        return renderer.getSVG(score, account, paletteIndex, updatedAt);
    }

    /// @notice Check if the sender is the owner of the token or an approved operator.
    /// @param tokenId The token ID to check.
    /// @param addr The address to check.
    /// @return True if the address is the owner or an approved operator.
    function _isOwnerOrApproved(uint256 tokenId, address addr) internal view override returns (bool) {
        return addr == ownerOf(tokenId);
    }
}
Código Fuente del Contrato
Archivo 22 de 24: StackScoreRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

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

/// @notice SVG renderer for Stack Scores
/// @dev SVGs are rendered using a palette of colors, a score, address, and timestamp.
/// @author strangechances
contract StackScoreRenderer {
    /// @notice The color palettes to use for the SVGs.
    /// @dev A palette is 3 colors in the order: top, bottom, middle.
    /// @dev The colors are stored as bytes3 to save gas
    bytes3[3][11] private COLOR_PALETTES = [
        [bytes3(0x4F4C42), bytes3(0xACA695), bytes3(0xDBD9D1)],
        [bytes3(0x1D3C86), bytes3(0x2960E7), bytes3(0x4DB0F3)],
        [bytes3(0x0331A6), bytes3(0x5E83AA), bytes3(0xD2B481)],
        [bytes3(0x412C3F), bytes3(0xDA6A87), bytes3(0xDF819A)],
        [bytes3(0x0B241A), bytes3(0x5BC793), bytes3(0x5E83AA)],
        [bytes3(0xEB90BE), bytes3(0xEC5E3B), bytes3(0xF9D85B)],
        [bytes3(0xA13120), bytes3(0xEB5640), bytes3(0xEBDE8F)],
        [bytes3(0x0331A6), bytes3(0xB6BEC6), bytes3(0xF6CB82)],
        [bytes3(0x3B7BF6), bytes3(0xB7CDCE), bytes3(0xF09ABD)],
        [bytes3(0x3579BD), bytes3(0xDEAC91), bytes3(0xEC6C3F)],
        [bytes3(0x301C28), bytes3(0xCFA37F), bytes3(0xD7482C)]
    ];

    string[] private MONTHS = [
        "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
    ];

    /// @notice Get the color at a given index in a given palette as a hex string
    /// @dev The color is returned as a hex string without a prefix
    /// @param paletteIndex The index of the palette to get the color from
    /// @param colorIndex The index of the color to get
    function getColorAsHexString(uint paletteIndex, uint colorIndex) public view returns (string memory) {
        return LibString.toHexStringNoPrefix(
            uint24(COLOR_PALETTES[paletteIndex][colorIndex]),
            3
        );
    }

    /// @notice Get the timestamp as a string in the format "MMM DD YYYY HH:MM UTC"
    /// @param timestamp The timestamp to convert to a string
    function getTimestampString(uint256 timestamp) public view returns (string memory) {
        (
            uint year,
            uint month,
            uint day,
            uint hour,
            uint minute,
        ) = DateTimeLib.timestampToDateTime(timestamp);

        return string(abi.encodePacked(
            MONTHS[month - 1], " ",
            LibString.toString(day), " ",
            LibString.toString(year), " ",
            LibString.toString(hour), ":",
            LibString.toString(minute), " UTC"
        ));
    }

    /// @notice Generate the SVG for a given palette index
    /// @param paletteIndex The index of the palette to use
    /// @return The SVG as a string
    function generateColors(uint256 paletteIndex) private view returns (string memory) {
        return string(
            abi.encodePacked(
                '<g filter="url(#B)">'
                '<path fill="#',
                getColorAsHexString(paletteIndex, 0),
                '" d="M-230.25-60h900v330h-900z"/>'
                '</g>',
                '<g style="mix-blend-mode:overlay" class="B">'
                '<path d="M391 202.95c-.312 0-.588-.048-.828-.144s-.468-.262-.684-.498-.436-.57-.66-.99c-.112-.212-.21-.374-.294-.486a.89.89 0 0 0-.276-.252.74.74 0 0 0-.366-.084c-.224 0-.404.074-.54.222s-.204.354-.204.63c0 .296.078.532.234.708s.374.286.666.33l-.036.54c-.424-.048-.758-.208-1.002-.48s-.366-.642-.366-1.098c0-.288.052-.536.156-.744a1.09 1.09 0 0 1 .438-.48 1.21 1.21 0 0 1 .642-.168c.22 0 .408.036.564.108s.296.192.432.36.284.398.444.702c.196.372.4.66.612.864s.396.312.564.324v-2.364H391v3zm.096-5.098c0 .308-.088.574-.264.798s-.436.398-.768.522-.732.18-1.188.18-.852-.06-1.188-.18-.594-.298-.774-.522a1.25 1.25 0 0 1-.27-.798c0-.304.09-.568.27-.792s.438-.402.774-.522a3.43 3.43 0 0 1 1.188-.186 3.43 3.43 0 0 1 1.188.186c.332.12.588.292.768.516s.264.49.264.798zm-.504 0c0-.2-.068-.372-.204-.516s-.338-.254-.594-.33a3.29 3.29 0 0 0-.918-.114 3.29 3.29 0 0 0-.918.114c-.26.076-.46.188-.6.336s-.21.314-.21.51.07.368.21.516.34.254.6.33a3.29 3.29 0 0 0 .918.114 3.29 3.29 0 0 0 .918-.114c.256-.076.454-.186.594-.33s.204-.316.204-.516zm-3.264-.6l.024-.48 3.06 1.68-.024.48-3.06-1.68zm3.672-1.497c-.312 0-.588-.048-.828-.144s-.468-.262-.684-.498-.436-.57-.66-.99c-.112-.212-.21-.374-.294-.486a.89.89 0 0 0-.276-.252.74.74 0 0 0-.366-.084c-.224 0-.404.074-.54.222s-.204.354-.204.63c0 .296.078.532.234.708s.374.286.666.33l-.036.54c-.424-.048-.758-.208-1.002-.48s-.366-.642-.366-1.098c0-.288.052-.536.156-.744a1.09 1.09 0 0 1 .438-.48 1.21 1.21 0 0 1 .642-.168c.22 0 .408.036.564.108s.296.192.432.36.284.398.444.702c.196.372.4.66.612.864s.396.312.564.324v-2.364H391v3zm-.924-5.626v2.088h-.468l-2.868-2.052v-.552h2.832v-.516h.504v.516H391v.516h-.924zm-.504 0h-2.04l2.04 1.464v-1.464zm1.428-3.034v.324h-.72v-.72h.636l1.02.378v.3l-.936-.282zm-4.26-5.851v-.72l3.576-1.524h-3.576v-.516H391v.72l-3.576 1.524H391v.516h-4.26zm2.52-4.726l-2.52 1.284v-.588l1.92-.948-1.92-.948v-.588l2.52 1.272H391v.516h-1.74zm1.836-3.861c0 .336-.09.624-.27.864s-.436.416-.768.54-.726.18-1.182.18-.856-.06-1.188-.18-.594-.304-.774-.54-.27-.528-.27-.864a1.35 1.35 0 0 1 .372-.96c.244-.264.586-.44 1.026-.528l.036.552c-.3.068-.53.184-.69.348a.81.81 0 0 0-.24.588c0 .224.068.414.204.57s.328.274.588.354.568.12.936.12.674-.04.93-.12.452-.198.588-.354.198-.346.198-.57c0-.252-.088-.462-.264-.63s-.43-.288-.762-.348l.03-.546c.468.08.836.254 1.104.522a1.37 1.37 0 0 1 .396 1.002zm.318-7.4c0 .132-.072.222-.186.222-.132 0-.198-.066-.342-.066-.312 0-.54.174-.828.45-.714.69-1.056 1.104-2.016 1.104-.672 0-1.326-.486-1.506-1.122-.036-.126-.048-.252-.048-.384 0-.36.132-.738.366-1.038.144-.186.264-.33.264-.414 0-.108-.114-.228-.3-.474-.126-.168-.204-.354-.264-.528a1.35 1.35 0 0 1-.066-.432c0-.858.678-1.482 1.566-1.482.516 0 1.044.234 1.59.78.72.72 1.464 1.962 1.686 2.85a2.35 2.35 0 0 1 .084.534z" fill-opacity=".5"/>'
                '</g>'
                '<g style="mix-blend-mode:overlay" class="B">'
                '<path d="M391.096 417.384c0 .296-.06.562-.18.798s-.296.418-.516.558a1.65 1.65 0 0 1-.762.246l-.036-.54c.212-.028.392-.088.54-.18a.95.95 0 0 0 .336-.366c.076-.152.114-.328.114-.528s-.024-.362-.072-.498-.126-.244-.222-.312a.59.59 0 0 0-.354-.108c-.144 0-.264.032-.36.096s-.194.182-.282.354-.178.416-.27.744c-.088.304-.182.546-.282.726s-.22.306-.36.39-.322.12-.534.12c-.236 0-.446-.056-.63-.168a1.14 1.14 0 0 1-.426-.492c-.104-.216-.156-.468-.156-.756s.058-.53.174-.738a1.32 1.32 0 0 1 .48-.504c.204-.124.438-.202.702-.234l.036.54a1.19 1.19 0 0 0-.462.156.78.78 0 0 0-.312.312c-.076.132-.114.292-.114.48 0 .264.064.474.192.63s.288.234.492.234c.136 0 .246-.028.33-.084s.158-.16.222-.3a5.15 5.15 0 0 0 .216-.636c.1-.352.21-.628.33-.828a1.13 1.13 0 0 1 .42-.444c.164-.092.366-.138.606-.138.22 0 .418.062.594.186s.306.298.402.522a1.89 1.89 0 0 1 .144.762zm-3.864-3.274v1.302h-.492v-3.12h.492v1.302H391v.516h-3.768zm-.492-3.507v-.696l4.26-1.296v.528l-3.792 1.116 3.792 1.116v.528l-4.26-1.296zm2.58.462v-1.62l.492-.18v1.98l-.492-.18zm1.776-4.42c0 .336-.09.624-.27.864s-.436.416-.768.54-.726.18-1.182.18-.856-.06-1.188-.18-.594-.304-.774-.54-.27-.528-.27-.864a1.35 1.35 0 0 1 .372-.96c.244-.264.586-.44 1.026-.528l.036.552c-.3.068-.53.184-.69.348a.81.81 0 0 0-.24.588c0 .224.068.414.204.57s.328.274.588.354.568.12.936.12.674-.04.93-.12.452-.198.588-.354.198-.346.198-.57c0-.252-.088-.462-.264-.63s-.43-.288-.762-.348l.03-.546c.468.08.836.254 1.104.522a1.37 1.37 0 0 1 .396 1.002zm-4.356-2.146v-.516h1.884l-1.884-1.728v-.636l1.788 1.62 2.472-1.74v.636l-2.112 1.464.408.384H391v.516h-4.26zm.492-8.377v1.302h-.492v-3.12h.492v1.302H391v.516h-3.768zm-.492-2.596v-2.592h.492v2.076h1.38v-2.004h.492v2.004h1.404v-2.124H391v2.64h-4.26zm4.356-4.869c0 .336-.09.624-.27.864s-.436.416-.768.54-.726.18-1.182.18-.856-.06-1.188-.18-.594-.304-.774-.54-.27-.528-.27-.864a1.35 1.35 0 0 1 .372-.96c.244-.264.586-.44 1.026-.528l.036.552c-.3.068-.53.184-.69.348a.81.81 0 0 0-.24.588c0 .224.068.414.204.57s.328.274.588.354.568.12.936.12.674-.04.93-.12.452-.198.588-.354.198-.346.198-.57c0-.252-.088-.462-.264-.63s-.43-.288-.762-.348l.03-.546c.468.08.836.254 1.104.522a1.37 1.37 0 0 1 .396 1.002zm-4.356-2.194v-.516h2.07l-.198.264v-2.28l.198.264h-2.07v-.516H391v.516h-2.094l.198-.264v2.28l-.198-.264H391v.516h-4.26zm0-3.61v-.72l3.576-1.524h-3.576v-.516H391v.72l-3.576 1.524H391v.516h-4.26zm4.356-4.977c0 .336-.086.622-.258.858s-.424.41-.756.534-.734.18-1.206.18-.88-.06-1.212-.18-.59-.302-.762-.534-.258-.522-.258-.858a1.39 1.39 0 0 1 .258-.852c.172-.236.426-.414.762-.534s.736-.186 1.212-.186.874.062 1.206.186.584.298.756.534a1.39 1.39 0 0 1 .258.852zm-.504 0a.82.82 0 0 0-.198-.558c-.132-.156-.326-.274-.582-.354s-.568-.12-.936-.12-.68.04-.936.12-.456.198-.588.354-.204.338-.204.558a.83.83 0 0 0 .204.564c.132.152.328.268.588.348s.568.12.936.12.68-.04.936-.12.45-.196.582-.348.198-.344.198-.564zm-3.852-2.398v-.516h4.02l-.252.258v-2.382H391v2.64h-4.26zm4.356-4.798c0 .336-.086.622-.258.858s-.424.41-.756.534-.734.18-1.206.18-.88-.06-1.212-.18-.59-.302-.762-.534-.258-.522-.258-.858a1.39 1.39 0 0 1 .258-.852c.172-.236.426-.414.762-.534s.736-.186 1.212-.186.874.062 1.206.186.584.298.756.534a1.39 1.39 0 0 1 .258.852zm-.504 0a.82.82 0 0 0-.198-.558c-.132-.156-.326-.274-.582-.354s-.568-.12-.936-.12-.68.04-.936.12-.456.198-.588.354-.204.338-.204.558a.83.83 0 0 0 .204.564c.132.152.328.268.588.348s.568.12.936.12.68-.04.936-.12.45-.196.582-.348.198-.344.198-.564zm.504-3.585a1.36 1.36 0 0 1-.252.81c-.172.236-.424.42-.756.552s-.736.198-1.212.198a3.43 3.43 0 0 1-1.188-.186c-.336-.128-.594-.31-.774-.546a1.38 1.38 0 0 1-.27-.852c0-.38.124-.7.372-.96s.58-.438.996-.534l.036.54c-.276.072-.494.188-.654.348a.81.81 0 0 0-.246.606.83.83 0 0 0 .204.564c.132.156.328.276.588.36s.568.12.936.12.674-.042.93-.126.452-.206.588-.366.198-.352.198-.576c0-.192-.054-.362-.162-.51a1.06 1.06 0 0 0-.444-.354 1.66 1.66 0 0 0-.642-.144v1.02h-.492v-1.5H391v.48l-1.008.024.06-.168c.332.096.59.24.774.432s.27.448.27.768zm-.588-2.29v-1.062h-3.276v1.062h-.492v-2.64h.492v1.062h3.276v-1.062H391v2.64h-.492zm-3.768-3.658v-2.592h.492v2.076h1.38v-2.004h.492v2.004h1.404v-2.124H391v2.64h-4.26zm4.356-4.923c0 .296-.06.562-.18.798s-.296.418-.516.558a1.65 1.65 0 0 1-.762.246l-.036-.54c.212-.028.392-.088.54-.18a.95.95 0 0 0 .336-.366c.076-.152.114-.328.114-.528s-.024-.362-.072-.498-.126-.244-.222-.312a.59.59 0 0 0-.354-.108c-.144 0-.264.032-.36.096s-.194.182-.282.354-.178.416-.27.744c-.088.304-.182.546-.282.726s-.22.306-.36.39-.322.12-.534.12c-.236 0-.446-.056-.63-.168a1.14 1.14 0 0 1-.426-.492c-.104-.216-.156-.468-.156-.756s.058-.53.174-.738a1.32 1.32 0 0 1 .48-.504c.204-.124.438-.202.702-.234l.036.54a1.19 1.19 0 0 0-.462.156.78.78 0 0 0-.312.312c-.076.132-.114.292-.114.48 0 .264.064.474.192.63s.288.234.492.234c.136 0 .246-.028.33-.084s.158-.16.222-.3a5.15 5.15 0 0 0 .216-.636c.1-.352.21-.628.33-.828a1.13 1.13 0 0 1 .42-.444c.164-.092.366-.138.606-.138.22 0 .418.062.594.186s.306.298.402.522a1.89 1.89 0 0 1 .144.762zm-.588-5.809v-1.062h-3.276v1.062h-.492v-2.64h.492v1.062h3.276v-1.062H391v2.64h-.492zm-3.768-3.538v-.72l3.576-1.524h-3.576v-.516H391v.72l-3.576 1.524H391v.516h-4.26zm4.356-4.99c0 .336-.09.624-.27.864s-.436.416-.768.54-.726.18-1.182.18-.856-.06-1.188-.18-.594-.304-.774-.54-.27-.528-.27-.864a1.35 1.35 0 0 1 .372-.96c.244-.264.586-.44 1.026-.528l.036.552c-.3.068-.53.184-.69.348a.81.81 0 0 0-.24.588c0 .224.068.414.204.57s.328.274.588.354.568.12.936.12.674-.04.93-.12.452-.198.588-.354.198-.346.198-.57c0-.252-.088-.462-.264-.63s-.43-.288-.762-.348l.03-.546c.468.08.836.254 1.104.522a1.37 1.37 0 0 1 .396 1.002zm-.816-3.225v-.72h.72v.72h-.72z" fill-opacity=".5"/>'
                '</g>'
                '<g filter="url(#C)">'
                '<path fill="#',
                getColorAsHexString(paletteIndex, 1),
                '" d="M-247.5 195h900v150h-900z"/>'
                '</g>'
                '<g filter="url(#A)">'
                '<path fill="#',
                getColorAsHexString(paletteIndex, 2),
                '" d="M-230.25 270h900v330.75h-900z"/>'
                '</g>'
            )
        );
    }

    /// @notice Get the SVG for a given score, account, palette index, and last updated timestamp
    /// @dev The SVG is returned as a string
    /// @param score The score to render
    /// @param account The account to render
    /// @param paletteIndex The index of the palette to use
    /// @param lastUpdated The timestamp when the score was last updated
    /// @return The SVG as a string
    function getSVG(
        uint256 score,
        address account,
        uint256 paletteIndex,
        uint256 lastUpdated
    ) public view returns (string memory) {
        return string(
            abi.encodePacked(
                '<svg xmlns="http://www.w3.org/2000/svg" width="405" height="540" fill="none" xmlns:v="https://vecta.io/nano">'
                '<style><![CDATA[.B{fill:#fff}.C{color-interpolation-filters:sRGB}.D{flood-opacity:0}.E{font-family:"DM Mono", monospace}.F{dominant-baseline:hanging}]]></style>'
                '<defs><style>@import url(\'https://fonts.googleapis.com/css2?family=DM+Mono%3Awght@300400&amp;display=swap\');</style></defs>'
                '<g clip-path="url(#E)">'
                '<rect width="405" height="540" rx="24" fill="color(display-p3 0.0353 0.0392 0.0471)"/>',
                generateColors(paletteIndex),
                '<g style="mix-blend-mode:overlay" class="B E F">'
                '<text x="24" y="24" font-size="7.5" font-weight="300">LAST UPDATED ',
                getTimestampString(lastUpdated),
                '</text>'
                '</g>',
                '<g style="mix-blend-mode:overlay" class="B E F">'
                '<text x="24" y="175.5" font-size="108" font-weight="300">',
                LibString.toString(score),
                '</text>'
                '</g>'
                '<g style="mix-blend-mode:overlay" class="B E F">'
                '<text x="24" y="282" font-size="9">',
                LibString.toHexString(account),
                '</text>'
                '</g>'
                '<g style="mix-blend-mode:overlay" filter="url(#D)">'
                '<rect x="2.625" y="2.625" width="399.75" height="534.75" rx="22.875" stroke="#fff" stroke-width="5.25"/>'
                '</g>'
                '<g style="mix-blend-mode:overlay" class="B">'
                '<path d="M40.32 470.518c-2.592 0-4.864-.48-6.816-1.44-1.952-.992-3.504-2.368-4.656-4.128s-1.824-3.792-2.016-6.096l4.32-.288c.224 1.696.72 3.136 1.488 4.32a7.35 7.35 0 0 0 3.072 2.688c1.312.608 2.88.912 4.704.912 2.4 0 4.256-.464 5.568-1.392s1.968-2.256 1.968-3.984c0-1.12-.256-2.08-.768-2.88s-1.472-1.536-2.88-2.208-3.44-1.344-6.096-2.016c-2.688-.672-4.8-1.408-6.336-2.208-1.504-.8-2.608-1.76-3.312-2.88-.672-1.152-1.008-2.592-1.008-4.32 0-1.888.448-3.568 1.344-5.04.928-1.472 2.24-2.608 3.936-3.408 1.728-.832 3.744-1.248 6.048-1.248 2.432 0 4.544.464 6.336 1.392 1.824.928 3.264 2.208 4.32 3.84 1.088 1.632 1.76 3.504 2.016 5.616l-4.32.288c-.192-1.408-.64-2.64-1.344-3.696-.704-1.088-1.648-1.92-2.832-2.496-1.184-.608-2.608-.912-4.272-.912-2.112 0-3.792.512-5.04 1.536-1.248.992-1.872 2.304-1.872 3.936 0 1.088.24 1.968.72 2.64.512.672 1.376 1.28 2.592 1.824 1.248.512 3.12 1.072 5.616 1.68 2.816.704 5.056 1.536 6.72 2.496 1.696.96 2.912 2.08 3.648 3.36.736 1.248 1.104 2.72 1.104 4.416 0 1.952-.512 3.664-1.536 5.136-1.024 1.44-2.448 2.56-4.272 3.36-1.792.8-3.84 1.2-6.144 1.2zm26.035-.768c-2.304 0-4.016-.528-5.136-1.584-1.088-1.056-1.632-2.704-1.632-4.944v-24.864h4.032v24.768c0 1.12.24 1.92.72 2.4.512.448 1.312.672 2.4.672h3.648v3.552h-4.032zm-10.512-25.44h14.544v3.552H55.843v-3.552zm27.146 26.016c-2.656 0-4.752-.608-6.288-1.824s-2.304-2.912-2.304-5.088.64-3.888 1.92-5.136 3.312-2.128 6.096-2.64l9.024-1.68c0-2.144-.512-3.744-1.536-4.8-.992-1.056-2.496-1.584-4.512-1.584-1.728 0-3.12.384-4.176 1.152-1.024.768-1.728 1.904-2.112 3.408l-4.272-.336c.48-2.464 1.648-4.416 3.504-5.856 1.888-1.472 4.24-2.208 7.056-2.208 3.232 0 5.712.912 7.44 2.736 1.76 1.824 2.64 4.368 2.64 7.632v10.512c0 .576.096.992.288 1.248.224.224.576.336 1.056.336h1.008v3.552c-.192.032-.416.048-.672.048-.256.032-.544.048-.864.048-1.76 0-3.008-.416-3.744-1.248s-1.104-2.208-1.104-4.128l.48.096c-.32 1.632-1.344 3.008-3.072 4.128-1.728 1.088-3.68 1.632-5.856 1.632zm.384-3.552c1.664 0 3.104-.32 4.32-.96 1.216-.672 2.144-1.568 2.784-2.688.64-1.152.96-2.432.96-3.84v-1.824l-8.256 1.536c-1.664.288-2.848.768-3.552 1.44-.672.64-1.008 1.504-1.008 2.592 0 1.184.416 2.112 1.248 2.784.832.64 2 .96 3.504.96zm29.594 3.552c-2.336 0-4.384-.544-6.144-1.632s-3.12-2.64-4.08-4.656-1.44-4.352-1.44-7.008.48-4.992 1.44-7.008 2.32-3.568 4.08-4.656 3.808-1.632 6.144-1.632c1.952 0 3.68.352 5.184 1.056a8.89 8.89 0 0 1 3.744 3.024c.96 1.312 1.584 2.896 1.872 4.752l-4.224.288c-.288-1.664-1.024-2.96-2.208-3.888-1.152-.928-2.608-1.392-4.368-1.392-2.336 0-4.16.832-5.472 2.496s-1.968 3.984-1.968 6.96.656 5.296 1.968 6.96 3.136 2.496 5.472 2.496c1.76 0 3.216-.48 4.368-1.44 1.184-.992 1.92-2.432 2.208-4.32l4.224.288c-.288 1.888-.912 3.536-1.872 4.944-.96 1.376-2.208 2.448-3.744 3.216s-3.264 1.152-5.184 1.152zm16.17-34.656h4.032v22.32l12.384-13.68h5.376l-10.032 10.8 10.464 14.64h-4.896l-8.304-11.904-4.992 5.28v6.624h-4.032v-34.08zM40.32 513.518c-2.592 0-4.864-.48-6.816-1.44-1.952-.992-3.504-2.368-4.656-4.128s-1.824-3.792-2.016-6.096l4.32-.288c.224 1.696.72 3.136 1.488 4.32a7.35 7.35 0 0 0 3.072 2.688c1.312.608 2.88.912 4.704.912 2.4 0 4.256-.464 5.568-1.392s1.968-2.256 1.968-3.984c0-1.12-.256-2.08-.768-2.88s-1.472-1.536-2.88-2.208-3.44-1.344-6.096-2.016c-2.688-.672-4.8-1.408-6.336-2.208-1.504-.8-2.608-1.76-3.312-2.88-.672-1.152-1.008-2.592-1.008-4.32 0-1.888.448-3.568 1.344-5.04.928-1.472 2.24-2.608 3.936-3.408 1.728-.832 3.744-1.248 6.048-1.248 2.432 0 4.544.464 6.336 1.392 1.824.928 3.264 2.208 4.32 3.84 1.088 1.632 1.76 3.504 2.016 5.616l-4.32.288c-.192-1.408-.64-2.64-1.344-3.696-.704-1.088-1.648-1.92-2.832-2.496-1.184-.608-2.608-.912-4.272-.912-2.112 0-3.792.512-5.04 1.536-1.248.992-1.872 2.304-1.872 3.936 0 1.088.24 1.968.72 2.64.512.672 1.376 1.28 2.592 1.824 1.248.512 3.12 1.072 5.616 1.68 2.816.704 5.056 1.536 6.72 2.496 1.696.96 2.912 2.08 3.648 3.36.736 1.248 1.104 2.72 1.104 4.416 0 1.952-.512 3.664-1.536 5.136-1.024 1.44-2.448 2.56-4.272 3.36-1.792.8-3.84 1.2-6.144 1.2zm28.725-.192c-2.336 0-4.384-.544-6.144-1.632s-3.12-2.64-4.08-4.656-1.44-4.352-1.44-7.008.48-4.992 1.44-7.008 2.32-3.568 4.08-4.656 3.808-1.632 6.144-1.632c1.952 0 3.68.352 5.184 1.056a8.89 8.89 0 0 1 3.744 3.024c.96 1.312 1.584 2.896 1.872 4.752l-4.224.288c-.288-1.664-1.024-2.96-2.208-3.888-1.152-.928-2.608-1.392-4.368-1.392-2.336 0-4.16.832-5.472 2.496s-1.968 3.984-1.968 6.96.656 5.296 1.968 6.96 3.136 2.496 5.472 2.496c1.76 0 3.216-.48 4.368-1.44 1.184-.992 1.92-2.432 2.208-4.32l4.224.288c-.288 1.888-.912 3.536-1.872 4.944-.96 1.376-2.208 2.448-3.744 3.216s-3.264 1.152-5.184 1.152zm26.25 0c-2.336 0-4.384-.544-6.144-1.632s-3.12-2.624-4.08-4.608c-.96-2.016-1.44-4.368-1.44-7.056s.48-5.024 1.44-7.008c.96-2.016 2.32-3.568 4.08-4.656s3.808-1.632 6.144-1.632c2.304 0 4.336.544 6.096 1.632s3.12 2.64 4.08 4.656c.96 1.984 1.44 4.32 1.44 7.008s-.48 5.04-1.44 7.056c-.96 1.984-2.32 3.52-4.08 4.608s-3.792 1.632-6.096 1.632zm0-3.84c2.336 0 4.144-.832 5.424-2.496 1.312-1.696 1.968-4.016 1.968-6.96s-.656-5.248-1.968-6.912c-1.28-1.696-3.088-2.544-5.424-2.544s-4.16.832-5.472 2.496-1.968 3.984-1.968 6.96.656 5.296 1.968 6.96 3.136 2.496 5.472 2.496zm17.483-22.176h3.552l.192 6.432-.384-.144c.32-2.144 1.008-3.728 2.064-4.752s2.496-1.536 4.32-1.536h2.496v3.84h-2.448c-1.28 0-2.352.24-3.216.72-.832.448-1.472 1.136-1.92 2.064-.416.928-.624 2.08-.624 3.456v15.36h-4.032v-25.44zm26.955 26.016c-2.368 0-4.432-.544-6.192-1.632s-3.12-2.624-4.08-4.608c-.928-2.016-1.392-4.368-1.392-7.056 0-2.656.464-4.992 1.392-7.008.96-2.016 2.304-3.568 4.032-4.656s3.744-1.632 6.048-1.632c2.208 0 4.16.528 5.856 1.584 1.696 1.024 3.008 2.544 3.936 4.56.96 2.016 1.44 4.416 1.44 7.2v1.2h-18.48c.128 2.688.832 4.736 2.112 6.144 1.312 1.376 3.088 2.064 5.328 2.064 1.632 0 2.976-.368 4.032-1.104 1.088-.768 1.856-1.824 2.304-3.168l4.32.336c-.672 2.336-1.968 4.224-3.888 5.664-1.888 1.408-4.144 2.112-6.768 2.112zm6.624-15.6c-.192-2.368-.896-4.144-2.112-5.328-1.184-1.216-2.752-1.824-4.704-1.824-1.984 0-3.616.608-4.896 1.824-1.248 1.216-2.032 2.992-2.352 5.328h14.064z"/>'
                '</g>'
                '<g style="mix-blend-mode:overlay" class="B">'
                '<path fill-rule="evenodd" d="M347.842 474c-4.883 0-8.842 3.959-8.842 8.842v24.316c0 4.883 3.959 8.842 8.842 8.842h24.316c4.883 0 8.842-3.959 8.842-8.842v-24.316c0-4.883-3.959-8.842-8.842-8.842h-24.316zm13.069 16.799a2.33 2.33 0 0 0-.911 1.85v16.059a1.46 1.46 0 0 0 1.458 1.458h12.25a1.46 1.46 0 0 0 1.459-1.458v-27.69c0-.484-.556-.758-.939-.463l-13.317 10.244z"/>'
                '</g>'
                '<g style="mix-blend-mode:overlay">'
                '<rect x="6.375" y="6.375" width="392.25" height="527.25" rx="17.625" stroke="#fff" stroke-width=".75"/>'
                '</g>'
                '<g style="mix-blend-mode:overlay">'
                '<path stroke="#fff" stroke-width=".75" d="M24 269.625h357"/>'
                '</g>'
                '</g>'
                '<defs>'
                '<filter id="A" x="-356.25" y="144" width="1152" height="582.75" filterUnits="userSpaceOnUse" class="C"><feFlood class="D"/><feBlend in="SourceGraphic"/><feGaussianBlur stdDeviation="63"/></filter>'
                '<filter id="B" x="-356.25" y="-186" width="1152" height="582" filterUnits="userSpaceOnUse" class="C"><feFlood class="D"/><feBlend in="SourceGraphic"/><feGaussianBlur stdDeviation="63"/></filter>'
                '<filter id="C" x="-373.5" y="69" width="1152" height="402" filterUnits="userSpaceOnUse" class="C"><feFlood class="D"/><feBlend in="SourceGraphic"/><feGaussianBlur stdDeviation="63"/></filter>'
                '<filter id="D" x="-3" y="-3" width="411" height="546" filterUnits="userSpaceOnUse" class="C"><feFlood class="D"/><feBlend in="SourceGraphic"/><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="E">'
                '<rect width="405" height="540" rx="24" fill-opacity="1" class="B"/>'
                '</clipPath></defs></svg>'
            )
        );
    }
}
Código Fuente del Contrato
Archivo 23 de 24: TraitLabelLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {DisplayType, Metadata} from "../../onchain/Metadata.sol";
import {json} from "../../onchain/json.sol";
import {Solarray} from "solarray/Solarray.sol";
import {LibString} from "solady/utils/LibString.sol";
import {SSTORE2} from "solady/utils/SSTORE2.sol";

///@notice Bitmap type for storing allowed editors
type Editors is uint8;

///@notice alias type for storing and loading TraitLabels using SSTORE2
type StoredTraitLabel is address;

///@notice Enumeration of allowed editor roles
enum AllowedEditor {
    Anyone,
    Self,
    TokenOwner,
    Custom,
    ContractOwner
}

///@notice Struct associating a bytes32 traitValue to its string representation
struct FullTraitValue {
    bytes32 traitValue;
    string fullTraitValue;
}

///@notice Struct representing a trait label
struct TraitLabel {
    // The full trait key string if different from the bytes32 traitKey
    string fullTraitKey;
    // The string label for the trait
    string traitLabel;
    // The list of acceptable values for the trait, if it must be validated
    string[] acceptableValues;
    // The list of full trait values, if the trait value should be converted to a different string
    FullTraitValue[] fullTraitValues;
    // The display type for the trait
    DisplayType displayType;
    // The list of editors allowed to set the trait
    Editors editors;
    // Whether the trait is required to have a value
    bool required;
}

// Pack allowedEditors and valueRequiresValidation (for writes) plus storageAddress (for reads) into a single slot
struct TraitLabelStorage {
    // The bitmap of editors allowed to set the trait
    Editors allowedEditors;
    // true if TraitLabel.required == true
    bool required;
    // true if TraitLabel.acceptableValues.length != 0
    bool valuesRequireValidation;
    // The address of the TraitLabel in contract storage, aliased as a StoredTraitLabel
    StoredTraitLabel storedLabel;
}

library TraitLabelStorageLib {
    /**
     * @notice Decode a TraitLabel from contract storage
     * @param labelStorage TraitLabelStorage
     */
    function toTraitLabel(TraitLabelStorage memory labelStorage) internal view returns (TraitLabel memory) {
        return StoredTraitLabelLib.load(labelStorage.storedLabel);
    }

    /**
     * @notice Given a trait key and value, render it as a properly formatted JSON attribute
     * @param traitLabelStorage Storage mapping of trait keys to TraitLabelStorage
     * @param traitKey Trait key
     * @param traitValue Trait value
     */
    function toAttributeJson(
        mapping(bytes32 traitKey => TraitLabelStorage traitLabelStorage) storage traitLabelStorage,
        bytes32 traitKey,
        bytes32 traitValue
    ) internal view returns (string memory) {
        // read and decode the trait label from contract storage
        TraitLabelStorage storage labelStorage = traitLabelStorage[traitKey];
        TraitLabel memory traitLabel = toTraitLabel(labelStorage);

        string memory actualTraitValue;
        // convert traitValue if possible

        if (traitLabel.fullTraitValues.length != 0) {
            // try to find matching FullTraitValue
            uint256 length = traitLabel.fullTraitValues.length;
            for (uint256 i = 0; i < length;) {
                FullTraitValue memory fullTraitValue = traitLabel.fullTraitValues[i];
                if (fullTraitValue.traitValue == traitValue) {
                    actualTraitValue = fullTraitValue.fullTraitValue;
                    break;
                }
                unchecked {
                    ++i;
                }
            }
        }
        // if no match, use traitValue as-is
        if (bytes(actualTraitValue).length == 0) {
            actualTraitValue = TraitLib.toString(traitValue, traitLabel.displayType);
        }
        // render the attribute as JSON
        return Metadata.attribute({
            traitType: traitLabel.traitLabel,
            value: actualTraitValue,
            displayType: traitLabel.displayType
        });
    }

    /**
     * @notice Given trait keys, render their labels as a properly formatted JSON array
     * @param traitLabelStorage Storage mapping of trait keys to TraitLabelStorage
     * @param keys Trait keys to render labels for
     */
    function toLabelJson(mapping(bytes32 => TraitLabelStorage) storage traitLabelStorage, bytes32[] memory keys)
        internal
        view
        returns (string memory)
    {
        string[] memory result = new string[](keys.length);
        uint256 i;
        for (i; i < keys.length;) {
            bytes32 key = keys[i];
            TraitLabel memory traitLabel = TraitLabelStorageLib.toTraitLabel(traitLabelStorage[key]); //.toTraitLabel();
            result[i] = TraitLabelLib.toLabelJson(traitLabel, key);
            unchecked {
                ++i;
            }
        }
        return json.arrayOf(result);
    }
}

library FullTraitValueLib {
    /**
     * @notice Convert a FullTraitValue to a JSON object
     */
    function toJson(FullTraitValue memory fullTraitValue) internal pure returns (string memory) {
        return json.objectOf(
            Solarray.strings(
                // TODO: is hex string appropriate here? doesn't make sense to render hashes as strings otherwise
                json.property("traitValue", LibString.toHexString(uint256(fullTraitValue.traitValue))),
                json.property("fullTraitValue", fullTraitValue.fullTraitValue)
            )
        );
    }

    /**
     * @notice Convert an array of FullTraitValues to a JSON array of objects
     */
    function toJson(FullTraitValue[] memory fullTraitValues) internal pure returns (string memory) {
        string[] memory result = new string[](fullTraitValues.length);
        for (uint256 i = 0; i < fullTraitValues.length;) {
            result[i] = toJson(fullTraitValues[i]);
            unchecked {
                ++i;
            }
        }
        return json.arrayOf(result);
    }
}

library TraitLabelLib {
    error InvalidTraitValue(bytes32 traitKey, bytes32 traitValue);

    /**
     * @notice Store a TraitLabel in contract storage using SSTORE2 and return the StoredTraitLabel
     */
    function store(TraitLabel memory self) internal returns (StoredTraitLabel) {
        return StoredTraitLabel.wrap(SSTORE2.write(abi.encode(self)));
    }

    /**
     * @notice Validate a trait value against a TraitLabel's acceptableValues
     * @param label TraitLabel
     * @param traitKey Trait key
     * @param traitValue Trait value
     */
    function validateAcceptableValue(TraitLabel memory label, bytes32 traitKey, bytes32 traitValue) internal pure {
        string[] memory acceptableValues = label.acceptableValues;
        uint256 length = acceptableValues.length;
        DisplayType displayType = label.displayType;
        if (length != 0) {
            string memory stringValue = TraitLib.toString(traitValue, displayType);
            bytes32 hashedValue = keccak256(abi.encodePacked(stringValue));
            for (uint256 i = 0; i < length;) {
                if (hashedValue == keccak256(abi.encodePacked(acceptableValues[i]))) {
                    return;
                }
                unchecked {
                    ++i;
                }
            }
            revert InvalidTraitValue(traitKey, traitValue);
        }
    }

    /**
     * @notice Convert a TraitLabel to a JSON object
     * @param label TraitLabel
     * @param traitKey Trait key for the label
     */
    function toLabelJson(TraitLabel memory label, bytes32 traitKey) internal pure returns (string memory) {
        return json.objectOf(
            Solarray.strings(
                json.property("traitKey", TraitLib.asString(traitKey)),
                json.property(
                    "fullTraitKey",
                    bytes(label.fullTraitKey).length == 0 ? TraitLib.asString(traitKey) : label.fullTraitKey
                ),
                json.property("traitLabel", label.traitLabel),
                json.rawProperty("acceptableValues", TraitLib.toJson(label.acceptableValues)),
                json.rawProperty("fullTraitValues", FullTraitValueLib.toJson(label.fullTraitValues)),
                json.property("displayType", Metadata.toString(label.displayType)),
                json.rawProperty("editors", EditorsLib.toJson(label.editors))
            )
        );
    }
}

library TraitLib {
    /**
     * @notice Convert a bytes32 trait value to a string
     * @param key The trait value to convert
     * @param displayType The display type of the trait value
     */
    function toString(bytes32 key, DisplayType displayType) internal pure returns (string memory) {
        if (
            displayType == DisplayType.Number || displayType == DisplayType.BoostNumber
                || displayType == DisplayType.BoostPercent
        ) {
            return LibString.toString(uint256(key));
        } else {
            return asString(key);
        }
    }

    /**
     * @notice Convert a bytes32 to a string
     * @param key The bytes32 to convert
     */
    function asString(bytes32 key) internal pure returns (string memory) {
        uint256 len = _bytes32StringLength(key);
        string memory result;
        ///@solidity memory-safe-assembly
        assembly {
            // assign result to free memory pointer
            result := mload(0x40)
            // increment free memory pointer by two words
            mstore(0x40, add(0x40, result))
            // store length at result
            mstore(result, len)
            // store key at next word
            mstore(add(result, 0x20), key)
        }
        return result;
    }

    /**
     * @notice Get the "length" of a bytes32 by counting number of non-zero leading bytes
     */
    function _bytes32StringLength(bytes32 str) internal pure returns (uint256) {
        // only meant to be called in a view context, so this optimizes for bytecode size over performance
        for (uint256 i; i < 32;) {
            if (str[i] == 0) {
                return i;
            }
            unchecked {
                ++i;
            }
        }
        return 32;
    }

    /**
     * @notice Convert an array of strings to a JSON array of strings
     */
    function toJson(string[] memory acceptableValues) internal pure returns (string memory) {
        return json.arrayOf(json.quote(acceptableValues));
    }
}

library EditorsLib {
    /**
     * @notice Convert an array of AllowedEditor enum values to an Editors bitmap
     */
    function aggregate(AllowedEditor[] memory editors) internal pure returns (Editors) {
        uint256 editorsLength = editors.length;
        uint256 result;
        for (uint256 i = 0; i < editorsLength;) {
            result |= 1 << uint8(editors[i]);
            unchecked {
                ++i;
            }
        }
        return Editors.wrap(uint8(result));
    }

    /**
     * @notice Convert an Editors bitmap to an array of AllowedEditor enum values
     */
    function expand(Editors editors) internal pure returns (AllowedEditor[] memory allowedEditors) {
        uint8 _editors = Editors.unwrap(editors);
        if (_editors & 1 == 1) {
            allowedEditors = new AllowedEditor[](1);
            allowedEditors[0] = AllowedEditor.Anyone;
            return allowedEditors;
        }
        // optimistically allocate 4 slots
        AllowedEditor[] memory result = new AllowedEditor[](4);
        uint256 num;
        for (uint256 i = 1; i < 5;) {
            bool set = _editors & (1 << i) != 0;
            if (set) {
                result[num] = AllowedEditor(i);
                unchecked {
                    ++num;
                }
            }
            unchecked {
                ++i;
            }
        }
        ///@solidity memory-safe-assembly
        assembly {
            mstore(result, num)
        }
        return result;
    }

    /**
     * @notice Convert an AllowedEditor enum value to its corresponding bit in an Editors bitmap
     */
    function toBitMap(AllowedEditor editor) internal pure returns (uint8) {
        return uint8(1 << uint256(editor));
    }

    /**
     * @notice Check if an Editors bitmap contains a given AllowedEditor
     */
    function contains(Editors self, AllowedEditor editor) internal pure returns (bool) {
        return Editors.unwrap(self) & toBitMap(editor) != 0;
    }

    /**
     * @notice Convert an Editors bitmap to a JSON array of numbers
     */
    function toJson(Editors editors) internal pure returns (string memory) {
        return toJson(expand(editors));
    }

    /**
     * @notice Convert an array of AllowedEditors to a JSON array of numbers
     */
    function toJson(AllowedEditor[] memory editors) internal pure returns (string memory) {
        string[] memory result = new string[](editors.length);
        for (uint256 i = 0; i < editors.length;) {
            result[i] = LibString.toString(uint8(editors[i]));
            unchecked {
                ++i;
            }
        }
        return json.arrayOf(result);
    }
}

library StoredTraitLabelLib {
    /**
     * @notice Check that a StoredTraitLabel is not the zero address, ie, that it exists
     */
    function exists(StoredTraitLabel storedTraitLabel) internal pure returns (bool) {
        return StoredTraitLabel.unwrap(storedTraitLabel) != address(0);
    }

    /**
     * @notice Load a TraitLabel from contract storage using SSTORE2
     */
    function load(StoredTraitLabel storedTraitLabel) internal view returns (TraitLabel memory) {
        bytes memory data = SSTORE2.read(StoredTraitLabel.unwrap(storedTraitLabel));
        return abi.decode(data, (TraitLabel));
    }
}

using EditorsLib for Editors global;
using StoredTraitLabelLib for StoredTraitLabel global;
Código Fuente del Contrato
Archivo 24 de 24: json.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

/**
 * @title JSON
 * @author emo.eth
 * @notice TODO: overrides for common types that automatically stringify
 */
library json {
    string private constant NULL = "";

    using LibString for string;

    /**
     * @notice enclose a string in {braces}
     *         Note: does not escape quotes in value
     * @param  value string to enclose in braces
     * @return string of {value}
     */
    function object(string memory value) internal pure returns (string memory) {
        return string.concat("{", value, "}");
    }

    /**
     * @notice enclose a string in [brackets]
     *         Note: does not escape quotes in value
     * @param value string to enclose in brackets
     * @return string of [value]
     */
    function array(string memory value) internal pure returns (string memory) {
        return string.concat("[", value, "]");
    }

    /**
     * @notice enclose name and value with quotes, and place a colon "between":"them".
     *         Note: escapes quotes in name and value
     * @param name name of property
     * @param value value of property
     * @return string of "name":"value"
     */
    function property(string memory name, string memory value) internal pure returns (string memory) {
        return string.concat('"', name.escapeJSON(), '":"', value.escapeJSON(), '"');
    }

    /**
     * @notice enclose name with quotes, but not rawValue, and place a colon "between":them
     *         Note: escapes quotes in name, but not value (which may itself be a JSON object, array, etc)
     * @param name name of property
     * @param rawValue raw value of property, which will not be enclosed in quotes
     * @return string of "name":value
     */
    function rawProperty(string memory name, string memory rawValue) internal pure returns (string memory) {
        return string.concat('"', name.escapeJSON(), '":', rawValue);
    }

    /**
     * @notice comma-join an array of properties and {"enclose":"them","in":"braces"}
     *         Note: does not escape quotes in properties, as it assumes they are already escaped
     * @param properties array of '"name":"value"' properties to join
     * @return string of {"name":"value","name":"value",...}
     */
    function objectOf(string[] memory properties) internal pure returns (string memory) {
        return object(_commaJoin(properties));
    }

    /**
     * @notice comma-join an array of values and enclose them [in,brackets]
     *         Note: does not escape quotes in values, as it assumes they are already escaped
     * @param values array of values to join
     * @return string of [value,value,...]
     */
    function arrayOf(string[] memory values) internal pure returns (string memory) {
        return array(_commaJoin(values));
    }

    /**
     * @notice comma-join two arrays of values and [enclose,them,in,brackets]
     *         Note: does not escape quotes in values, as it assumes they are already escaped
     * @param values1 first array of values to join
     * @param values2 second array of values to join
     * @return string of [values1_0,values1_1,values2_0,values2_1...]
     */
    function arrayOf(string[] memory values1, string[] memory values2) internal pure returns (string memory) {
        if (values1.length == 0) {
            return arrayOf(values2);
        } else if (values2.length == 0) {
            return arrayOf(values1);
        }
        return array(string.concat(_commaJoin(values1), ",", _commaJoin(values2)));
    }

    /**
     * @notice enclose a string in double "quotes", escaping any existing quotes
     * @param str string to enclose in quotes
     * @return string of "value"
     */
    function quote(string memory str) internal pure returns (string memory) {
        return string.concat('"', str.escapeJSON(), '"');
    }

    /**
     * @notice enclose each string in an array in double "quotes", escaping any existing quotes
     * @param strs array of strings, each to escape and enclose in quotes
     */
    function quote(string[] memory strs) internal pure returns (string[] memory) {
        string[] memory result = new string[](strs.length);
        for (uint256 i = 0; i < strs.length;) {
            result[i] = quote(strs[i]);
            unchecked {
                ++i;
            }
        }
        return result;
    }

    /**
     * @notice comma-join an array of strings
     * @param values array of strings to join
     * @return string of value,value,...
     */
    function _commaJoin(string[] memory values) internal pure returns (string memory) {
        return _join(values, ",");
    }

    /**
     * @notice join two strings with a comma
     * @param value1 first string
     * @param value2 second string
     * @return string of value1,value2
     */
    function _commaJoin(string memory value1, string memory value2) internal pure returns (string memory) {
        return string.concat(value1, ",", value2);
    }

    /**
     * @notice join an array of strings with a specified separator
     * @param values array of strings to join
     * @param separator separator to join with
     * @return string of value<separator>value<separator>...
     */
    function _join(string[] memory values, string memory separator) internal pure returns (string memory) {
        if (values.length == 0) {
            return NULL;
        }
        string memory result = values[0];
        for (uint256 i = 1; i < values.length; ++i) {
            result = string.concat(result, separator, values[i]);
        }
        return result;
    }
}
Configuraciones
{
  "compilationTarget": {
    "src/StackScore.sol": "StackScore"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    ":ds-test/=lib/solarray/lib/forge-std/lib/ds-test/src/",
    ":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    ":forge-std/=lib/forge-std/src/",
    ":openzeppelin-contracts/=lib/openzeppelin-contracts/",
    ":solady/=lib/solady/src/",
    ":solarray/=lib/solarray/src/"
  ]
}
ABI
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"InsufficientFee","type":"error"},{"inputs":[],"name":"InsufficientPrivilege","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[{"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"internalType":"bytes32","name":"traitValue","type":"bytes32"}],"name":"InvalidTraitValue","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OneTokenPerAddress","type":"error"},{"inputs":[],"name":"OnlyTokenOwner","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"SignatureAlreadyUsed","type":"error"},{"inputs":[],"name":"TimestampTooOld","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenLocked","type":"error"},{"inputs":[{"internalType":"bytes32","name":"traitKey","type":"bytes32"}],"name":"TraitDoesNotExist","type":"error"},{"inputs":[],"name":"TraitValueUnchanged","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","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":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"newRecipient","type":"address"}],"name":"MintFeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"MintFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"bool","name":"approved","type":"bool"}],"name":"PreapprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBps","type":"uint256"}],"name":"ReferralBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"address","name":"referred","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReferralPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRenderer","type":"address"},{"indexed":false,"internalType":"address","name":"newRenderer","type":"address"}],"name":"RendererUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldScore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newScore","type":"uint256"}],"name":"ScoreUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSigner","type":"address"},{"indexed":false,"internalType":"address","name":"newSigner","type":"address"}],"name":"SignerUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"TraitMetadataURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"traitValue","type":"bytes32"}],"name":"TraitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"TraitUpdatedList","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"bytes32","name":"traitValue","type":"bytes32"}],"name":"TraitUpdatedListUniformValue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"}],"name":"TraitUpdatedRange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"traitValue","type":"bytes32"}],"name":"TraitUpdatedRangeUniformValue","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Unlocked","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getCustomEditorAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCustomEditors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCustomEditorsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getPaletteIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRenderer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getScore","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getScoreAndLastUpdated","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTraitMetadataURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"traitKey","type":"bytes32"}],"name":"getTraitValue","outputs":[{"internalType":"bytes32","name":"traitValue","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32[]","name":"traitKeys","type":"bytes32[]"}],"name":"getTraitValues","outputs":[{"internalType":"bytes32[]","name":"traitValues","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getUpdatedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"editor","type":"address"}],"name":"isCustomEditor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"palette","type":"uint256"}],"name":"mintWithPalette","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"referrer","type":"address"}],"name":"mintWithReferral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"palette","type":"uint256"}],"name":"mintWithReferralAndPalette","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintWithScore","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"palette","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintWithScoreAndPalette","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintWithScoreAndReferral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"palette","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintWithScoreAndReferralAndPalette","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","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":"payable","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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setMintFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mintFeeRecipient","type":"address"}],"name":"setMintFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bps","type":"uint256"}],"name":"setReferralBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_renderer","type":"address"}],"name":"setRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"internalType":"bytes32","name":"newValue","type":"bytes32"}],"name":"setTrait","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"traitKey","type":"bytes32"},{"components":[{"internalType":"string","name":"fullTraitKey","type":"string"},{"internalType":"string","name":"traitLabel","type":"string"},{"internalType":"string[]","name":"acceptableValues","type":"string[]"},{"components":[{"internalType":"bytes32","name":"traitValue","type":"bytes32"},{"internalType":"string","name":"fullTraitValue","type":"string"}],"internalType":"struct FullTraitValue[]","name":"fullTraitValues","type":"tuple[]"},{"internalType":"enum DisplayType","name":"displayType","type":"uint8"},{"internalType":"Editors","name":"editors","type":"uint8"},{"internalType":"bool","name":"required","type":"bool"}],"internalType":"struct TraitLabel","name":"_traitLabel","type":"tuple"}],"name":"setTraitLabel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"bytes32","name":"traitKey","type":"bytes32"}],"name":"traitLabelStorage","outputs":[{"components":[{"internalType":"Editors","name":"allowedEditors","type":"uint8"},{"internalType":"bool","name":"required","type":"bool"},{"internalType":"bool","name":"valuesRequireValidation","type":"bool"},{"internalType":"StoredTraitLabel","name":"storedLabel","type":"address"}],"internalType":"struct TraitLabelStorage","name":"","type":"tuple"}],"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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"editor","type":"address"},{"internalType":"bool","name":"insert","type":"bool"}],"name":"updateCustomEditor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"paletteIndex","type":"uint256"}],"name":"updatePalette","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"newScore","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"updateScore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]