ETH Price: $3,583.82 (+4.15%)
 

Overview

Max Total Supply

1,711,893 BMP

Holders

20,748

Market

Volume (24H)

2.4076 ETH

Min Price (24H)

$1.08 @ 0.000300 ETH

Max Price (24H)

$313.94 @ 0.087600 ETH
Balance
100 BMP
0x27031f529Dde7Adf72CA721768382570F7385D3F
Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

Contract Name:
BitmapPunks721

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 1000 runs

Other Settings:
cancun EvmVersion
File 1 of 6 : BitmapPunks721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

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

import {BitmapBT404Mirror} from "../bt404/BitmapBT404Mirror.sol";

contract BitmapPunks721 is BitmapBT404Mirror {
    constructor(address _traitRegistry, address _traitOwner) BitmapBT404Mirror(tx.origin) {
        _initializeBT404Mirror(tx.origin);
        _initializeTraitsMetadata(_traitRegistry, _traitOwner);
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        string memory _name = name();
        (string memory attributesJson, string memory imageURI) =
            _getTokenAttributesAndImage(tokenId);

        return string.concat(
            "data:application/json;base64,",
            Base64.encode(
                bytes(
                    string.concat(
                        '{"external_url":"https://bitmappunks.com","description":"A fully-onchain, ultra-large, hybrid collection.","name":"',
                        _name,
                        " #",
                        LibString.toString(tokenId),
                        '","attributes":',
                        attributesJson,
                        ',"image":"',
                        imageURI,
                        '"}'
                    )
                )
            )
        );
    }
}

File 2 of 6 : 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 - <[email protected]>.
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.
            }
        }
    }
}

File 3 of 6 : 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)))
        }
    }
}

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

import {TraitsMetadata} from "../TraitsMetadata.sol";

import {BT404Mirror} from "../bt404/BT404Mirror.sol";

contract BitmapBT404Mirror is BT404Mirror, TraitsMetadata {
    constructor(address deployer) BT404Mirror(deployer) {}

    modifier bitmapFallback() {
        BT404NFTStorage storage $ = _getBT404NFTStorage();
        uint256 fnSelector = _calldataload(0x00) >> 224;

        // `afterMintBatch(address,uint256,uint256)`.
        if (fnSelector == 0x778e1229) {
            if (msg.sender != $.baseERC20) revert SenderNotBase();

            address to = address(uint160(_calldataload(0x04)));
            uint256 fromTokenId = _calldataload(0x24);
            uint256 toTokenId = _calldataload(0x44);

            _addTokenBatch(to, fromTokenId, toTokenId);

            assembly ("memory-safe") {
                mstore(0x00, 0x01)
                return(0x00, 0x20)
            }
        }

        _;
    }

    fallback() external payable override bt404NFTFallback bitmapFallback {}
}

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

interface ITraitRegistry {
    function registerCollection(address owner) external;

    function getCollectionTraitTypeCount() external view returns (uint256);

    function generateRandomTraits(uint256[] memory randomSeeds)
        external
        view
        returns (uint256[] memory traitIds);

    function getAttibutesJson(uint256[] memory traitIds) external view returns (string memory);

    function getImageURIOf(uint256[] memory traitIds) external view returns (string memory);
}

abstract contract TraitsMetadata {
    /*╭─────────────────────────────────────────────────────────────╮*/
    /*│                           EVENTS                            │*/
    /*╰─────────────────────────────────────────────────────────────╯*/

    event TraitRegistrySet(address traitRegistry);
    event TokenBatchAdded(
        uint256 batchIndex,
        uint256 fromTokenId,
        uint256 toTokenId,
        address minter,
        uint256 timestamp
    );

    /*╭─────────────────────────────────────────────────────────────╮*/
    /*│                        CUSTOM ERRORS                        │*/
    /*╰─────────────────────────────────────────────────────────────╯*/

    error TokenNotMinted();

    /*╭─────────────────────────────────────────────────────────────╮*/
    /*│                           STORAGE                           │*/
    /*╰─────────────────────────────────────────────────────────────╯*/

    struct TokenBatch {
        uint32 fromTokenId;
        uint32 toTokenId;
        address minter;
        uint32 timestamp;
    }

    struct TraitsMetadataStorage {
        ITraitRegistry traitRegistry;
        uint64 batchCount;
        mapping(uint64 batchIndex => TokenBatch batch) mintBatches;
    }

    /// @dev Returns a storage pointer for this contract.
    function _getTraitsMetadataStorage()
        internal
        pure
        virtual
        returns (TraitsMetadataStorage storage $)
    {
        assembly ("memory-safe") {
            // `keccak256(abi.encode(uint256(keccak256("bmp.storage.TraitsMetadata")) - 1)) & ~bytes32(uint256(0xff))`
            $.slot := 0xae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a00
        }
    }

    function _initializeTraitsMetadata(address _traitRegistry, address _traitOwner) internal {
        _getTraitsMetadataStorage().traitRegistry = ITraitRegistry(_traitRegistry);
        emit TraitRegistrySet(_traitRegistry);

        ITraitRegistry(_traitRegistry).registerCollection(_traitOwner);
    }

    /*╭─────────────────────────────────────────────────────────────╮*/
    /*│                            VIEW                             │*/
    /*╰─────────────────────────────────────────────────────────────╯*/

    function tokenTraits(uint256 tokenId) public view returns (uint256[] memory traitIds) {
        TraitsMetadataStorage storage $ = _getTraitsMetadataStorage();

        ITraitRegistry traitHub = $.traitRegistry;
        uint256 traitCount = traitHub.getCollectionTraitTypeCount();
        if (traitCount == 0) return traitIds;

        TokenBatch memory batch = _findTokenBatch($, tokenId);

        uint256[] memory randomSeeds = new uint256[](traitCount);
        _getTraitRandomSeeds(
            randomSeeds,
            tokenId,
            keccak256(
                abi.encodePacked(batch.fromTokenId, batch.toTokenId, batch.minter, batch.timestamp)
            )
        );

        traitIds = traitHub.generateRandomTraits(randomSeeds);
    }

    function traitRegistry() public view returns (address) {
        return address(_getTraitsMetadataStorage().traitRegistry);
    }

    /*╭─────────────────────────────────────────────────────────────╮*/
    /*│                      INTERNAL HELPERS                       │*/
    /*╰─────────────────────────────────────────────────────────────╯*/

    function _getTokenAttributesAndImage(uint256 tokenId)
        internal
        view
        virtual
        returns (string memory attributesJson, string memory imageURI)
    {
        TraitsMetadataStorage storage $ = _getTraitsMetadataStorage();
        uint256[] memory traitIds = tokenTraits(tokenId);

        address _traitRegistry = address($.traitRegistry);

        attributesJson = _readStringByArray(_traitRegistry, 0xbc58599a, traitIds);
        imageURI = _readStringByArray(_traitRegistry, 0x4c182a01, traitIds);
    }

    function _addTokenBatch(address minter, uint256 fromTokenId, uint256 toTokenId)
        internal
        virtual
    {
        TraitsMetadataStorage storage $ = _getTraitsMetadataStorage();

        uint256 batchIndex = $.batchCount++;

        $.mintBatches[uint64(batchIndex)] = TokenBatch({
            fromTokenId: uint32(fromTokenId),
            toTokenId: uint32(toTokenId),
            minter: minter,
            timestamp: uint32(block.timestamp)
        });

        emit TokenBatchAdded(batchIndex, fromTokenId, toTokenId, minter, block.timestamp);
    }

    function _findTokenBatch(TraitsMetadataStorage storage $, uint256 tokenId)
        internal
        view
        returns (TokenBatch memory batch)
    {
        uint256 start = 0;
        uint256 end = $.batchCount;

        mapping(uint64 => TokenBatch) storage batches = $.mintBatches;

        unchecked {
            while (start < end) {
                uint256 mid = (start + end) >> 1;
                batch = batches[uint64(mid)];

                if (batch.fromTokenId <= tokenId && tokenId <= batch.toTokenId) {
                    return batch;
                }

                if (tokenId < batch.fromTokenId) {
                    end = mid;
                } else {
                    start = mid + 1;
                }
            }

            revert TokenNotMinted();
        }
    }

    function _getTraitRandomSeeds(uint256[] memory randomSeeds, uint256 seed, bytes32 salt)
        internal
        pure
    {
        unchecked {
            uint256 count = randomSeeds.length;
            for (uint256 i = 0; i < count; ++i) {
                randomSeeds[i] = uint256(keccak256(abi.encodePacked(i, salt, seed)));
            }
        }
    }

    function _readStringByArray(address target, uint256 fnSelector, uint256[] memory array)
        private
        view
        returns (string memory result)
    {
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            mstore(ptr, fnSelector)

            let arrayLen := mload(array)
            mstore(add(ptr, 0x20), 0x20) // Offset of the array
            mstore(add(ptr, 0x40), arrayLen) // Length of the array

            // Copy array elements
            let arrayDataSize := mul(arrayLen, 0x20)
            mcopy(
                add(ptr, 0x60), // dst
                add(array, 0x20), // src
                arrayDataSize // length
            )

            // Selector + Offset + Length + ArrayElements
            let encodedSize := add(0x44, arrayDataSize)

            if iszero(staticcall(gas(), target, add(ptr, 0x1c), encodedSize, 0x00, 0x00)) {
                returndatacopy(ptr, 0x00, returndatasize())
                revert(ptr, returndatasize())
            }

            result := ptr

            returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata.
            returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string.
            returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string.

            let nextPtr := add(add(result, 0x20), mload(result))
            let padding := and(sub(32, and(nextPtr, 31)), 31)
            mstore(0x40, add(nextPtr, padding)) // Allocate memory.
        }
    }
}

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

/// @title BT404Mirror
/// @notice BT404Mirror provides an interface for interacting with the
/// NFT tokens in a BT404 implementation.
///
/// @author FlooringLab
/// @author Modified from DN404(https://github.com/Vectorized/dn404/src/DN404Mirror.sol)
///
/// @dev Note:
/// - The ERC721 data is stored in the base BT404 contract.
contract BT404Mirror {
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                           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 The ownership is transferred from `oldOwner` to `newOwner`.
    /// This is for marketplace signaling purposes. This contract has a `pullOwner()`
    /// function that will sync the owner from the base contract.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev Emitted when `owner` lock or unlock token `id`.
    event UpdateLockState(address indexed owner, uint256 indexed id, bool lockStatus);

    /// @dev Emitted when token `idX` and `idY` exchanged.
    event Exchange(uint256 indexed idX, uint256 indexed idY, uint256 exchangeFee);

    /// @dev Emitted when token `id` offered for sale.
    event Offer(uint256 indexed id, address indexed to, uint256 minPrice, address offerToken);

    /// @dev Emitted when token `id` offered for sale.
    event CancelOffer(uint256 indexed id, address indexed owner);

    /// @dev Emitted when token `id` offered for sale.
    event Bid(uint256 indexed id, address indexed from, uint256 price, address bidToken);

    /// @dev Emitted when token `id` offered for sale.
    event CancelBid(uint256 indexed id, address indexed from);

    /// @dev Emitted when token `id` bought.
    event Bought(
        uint256 indexed id,
        address indexed from,
        address indexed to,
        uint256 price,
        address token,
        address maker
    );

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

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

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

    /// @dev `keccak256(bytes("UpdateLockState(address,uint256,bool)"))`.
    uint256 internal constant _UPDATE_LOCK_STATE_EVENT_SIGNATURE =
        0xcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac;

    /// @dev `keccak256(bytes("Exchange(uint256,uint256,uint256)"))`
    uint256 internal constant _EXCHANGE_EVENT_SIGNATURE =
        0xbc43d7c0945f5a13a7bfa8ca7309e55f903f01d66c38c6d1353fe7ff9335d776;

    /// @dev `keccak256(bytes("Offer(uint256,address,uint256,address)"))`
    uint256 private constant _OFFER_EVENT_SIGNATURE =
        0xc56f8610599b5a39311e36563ef3386394748f787ef5efc116d960d77def8050;

    /// @dev `keccak256(bytes("CancelOffer(uint256,address)"))`
    uint256 private constant _CANCEL_OFFER_EVENT_SIGNATURE =
        0xc4caef7e3533865382e608c341581a5e2a1b0d1ac37b0aaf58023ccd4eedfd8e;

    /// @dev `keccak256(bytes("Bid(uint256,address,uint256,address)"))`
    uint256 private constant _BID_EVENT_SIGNATURE =
        0xec85e6e86fabc4c703529b570fb5eb567dad69ddbf7901bc0fd28b38b93de7f3;

    /// @dev `keccak256(bytes("CancelBid(uint256,address)"))`
    uint256 private constant _CANCEL_BID_EVENT_SIGNATURE =
        0x874afcdd5e90b2329b3c1601e613dcdc6abb6deb62ce61339a8337b48c053e51;

    /// @dev `keccak256(bytes("Bought(uint256,address,address,uint256,address,address)"))`
    uint256 private constant _BOUGHT_EVENT_SIGNATURE =
        0xd9882bc1ac8e78c918b907fa0ff79cc9d866091c5eb450ebed79e9d147541d5b;

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                        CUSTOM ERRORS                       */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Thrown when a call for an NFT function did not originate
    /// from the base BT404 contract.
    error SenderNotBase();

    /// @dev Thrown when a call for an NFT function did not originate from the deployer.
    error SenderNotDeployer();

    /// @dev Thrown when transferring an NFT to a contract address that
    /// does not implement ERC721Receiver.
    error TransferToNonERC721ReceiverImplementer();

    /// @dev Thrown when linking to the BT404 base contract and the
    /// BT404 supportsInterface check fails or the call reverts.
    error CannotLink();

    /// @dev Thrown when a linkMirrorContract call is received and the
    /// NFT mirror contract has already been linked to a BT404 base contract.
    error AlreadyLinked();

    /// @dev Thrown when retrieving the base BT404 address when a link has not
    /// been established.
    error NotLinked();

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

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

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                          STORAGE                           */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Struct contain the NFT mirror contract storage.
    struct BT404NFTStorage {
        address baseERC20;
        bool locked;
        address deployer;
        address owner;
    }

    /// @dev Returns a storage pointer for BT404NFTStorage.
    function _getBT404NFTStorage() internal pure virtual returns (BT404NFTStorage storage $) {
        /// @solidity memory-safe-assembly
        assembly {
            // `uint72(bytes9(keccak256("DN404_MIRROR_STORAGE")))`.
            $.slot := 0x3602298b8c10b01230 // Truncate to 9 bytes to reduce bytecode size.
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                       REENTRANCY GUARD                     */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        BT404NFTStorage storage $ = _getBT404NFTStorage();
        if ($.locked) revert Reentrancy();
        $.locked = true;
        _;
        $.locked = false;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                        CONSTRUCTOR                         */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    constructor(address deployer) {
        // For non-proxies, we will store the deployer so that only the deployer can
        // link the base contract.
        _getBT404NFTStorage().deployer = deployer;
    }

    function _initializeBT404Mirror(address deployer) internal {
        // For non-proxies, we will store the deployer so that only the deployer can
        // link the base contract.
        _getBT404NFTStorage().deployer = deployer;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                     ERC721 OPERATIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the token collection name from the base BT404 contract.
    function name() public view virtual returns (string memory result) {
        return _readString(0x06fdde03, 0); // `name()`.
    }

    /// @dev Returns the token collection symbol from the base BT404 contract.
    function symbol() public view virtual returns (string memory result) {
        return _readString(0x95d89b41, 0); // `symbol()`.
    }

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id` from
    /// the base BT404 contract.
    function tokenURI(uint256 id) public view virtual returns (string memory result) {
        return _readString(0xc87b56dd, id); // `tokenURI()`.
    }

    /// @dev Returns the total NFT supply from the base BT404 contract.
    function totalSupply() public view virtual returns (uint256 result) {
        return _readWord(0xe2c79281, 0, 0); // `totalNFTSupply()`.
    }

    /// @dev Returns the number of NFT tokens owned by `nftOwner` from the base BT404 contract.
    ///
    /// Requirements:
    /// - `nftOwner` must not be the zero address.
    function balanceOf(address nftOwner) public view virtual returns (uint256 result) {
        return _readWord(0xf5b100ea, uint160(nftOwner), 0); // `balanceOfNFT(address)`.
    }

    /// @dev Returns the owner of token `id` from the base BT404 contract.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        return address(uint160(_readWord(0x6352211e, id, 0))); // `ownerOf(uint256)`.
    }

    /// @dev Returns the owner of token `id` from the base BT404 contract.
    /// Returns `address(0)` instead of reverting if the token does not exist.
    function ownerAt(uint256 id) public view virtual returns (address result) {
        return address(uint160(_readWord(0x24359879, id, 0))); // `ownerAt(uint256)`.
    }

    /// @dev Sets `spender` as the approved account to manage token `id` in
    /// the base BT404 contract.
    ///
    /// 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 spender, uint256 id) public virtual {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            spender := shr(96, shl(96, spender))
            let m := mload(0x40)
            mstore(0x00, 0xd10b6e0c) // `approveNFT(address,uint256,address)`.
            mstore(0x20, spender)
            mstore(0x40, id)
            mstore(0x60, caller())
            if iszero(
                and(
                    gt(returndatasize(), 0x1f),
                    call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
            // Emit the {Approval} event.
            log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, shr(96, mload(0x0c)), spender, id)
        }
    }

    /// @dev Returns the account approved to manage token `id` from
    /// the base BT404 contract.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address) {
        return address(uint160(_readWord(0x081812fc, id, 0))); // `getApproved(uint256)`.
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller in
    /// the base BT404 contract.
    ///
    /// Emits an {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool approved) public virtual {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            operator := shr(96, shl(96, operator))
            let m := mload(0x40)
            mstore(0x00, 0x813500fc) // `setApprovalForAll(address,bool,address)`.
            mstore(0x20, operator)
            mstore(0x40, iszero(iszero(approved)))
            mstore(0x60, caller())
            if iszero(
                and(eq(mload(0x00), 1), call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x20))
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            // Emit the {ApprovalForAll} event.
            // The `approved` value is already at 0x40.
            log3(0x40, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), operator)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `nftOwner` from
    /// the base BT404 contract.
    function isApprovedForAll(address nftOwner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        // `isApprovedForAll(address,address)`.
        return _readWord(0xe985e9c5, uint160(nftOwner), uint160(operator)) != 0;
    }

    /// @dev Returns the owned token ids of `account` from the base BT404 contract.
    function ownedIds(address account, uint256 begin, uint256 end)
        public
        view
        virtual
        returns (uint256[] memory)
    {
        return _ownedIds(account, begin, end, false);
    }

    /// @dev Returns the locked token ids of `account` from the base BT404 contract.
    function lockedIds(address account, uint256 begin, uint256 end)
        public
        view
        virtual
        returns (uint256[] memory)
    {
        return _ownedIds(account, begin, end, true);
    }

    /// @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 virtual {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            from := shr(96, shl(96, from))
            to := shr(96, shl(96, to))
            let m := mload(0x40)
            mstore(m, 0xe5eb36c8) // `transferFromNFT(address,address,uint256,address)`.
            mstore(add(m, 0x20), from)
            mstore(add(m, 0x40), to)
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), caller())
            if iszero(
                and(eq(mload(m), 1), call(gas(), base, callvalue(), add(m, 0x1c), 0x84, m, 0x20))
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            // Emit the `from` unlock event.
            mstore(m, 0x00)
            log3(m, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, from, id)
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
            // Emit the `to` lock event
            mstore(m, 0x01)
            log3(m, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, to, id)
        }
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public 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
        virtual
    {
        transferFrom(from, to, id);

        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    function updateLockState(uint256[] memory ids, bool lock) public virtual {
        address base = baseERC20();

        (bool success, bytes memory result) = base.call(
            abi.encodeWithSignature(
                "setNFTLockState(uint256,uint256[])",
                uint256(uint160(msg.sender)) << 96 | (lock ? 1 : 0),
                ids
            )
        );

        // @solidity memory-safe-assembly
        assembly {
            if iszero(and(eq(mload(add(result, 0x20)), 1), success)) {
                revert(add(result, 0x20), mload(result))
            }
            let idLen := mload(ids)

            mstore(0x00, lock)
            for {
                let s := add(ids, 0x20)
                let end := add(s, shl(5, idLen))
            } iszero(eq(s, end)) { s := add(s, 0x20) } {
                log3(0x00, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, caller(), mload(s))
            }
        }
    }

    function exchange(uint256 idX, uint256 idY) public virtual returns (uint256 exchangeFee) {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(0x00, 0x2c5966af) // `exchangeNFT(uint256,uint256,address)`.
            mstore(0x20, idX)
            mstore(0x40, idY)
            mstore(0x60, caller())
            if iszero(
                and(
                    gt(returndatasize(), 0x5F),
                    call(gas(), base, callvalue(), 0x1c, 0x64, 0x00, 0x60)
                )
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            // store return value
            let x := mload(0x00)
            let y := mload(0x20)
            exchangeFee := mload(0x40)

            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, x, y, idX)
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, y, caller(), idY)
            // Emit the {Exchange} event.
            log3(0x40, 0x20, _EXCHANGE_EVENT_SIGNATURE, idX, idY)
            // Emit the `caller` lock event.
            mstore(0x40, 0x01)
            log3(0x40, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, caller(), idY)

            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    struct NFTOrder {
        uint256 id;
        uint256 price;
        address token;
        address trader;
    }

    function offerForSale(NFTOrder[] memory orders) public virtual nonReentrant {
        _callBaseRetWord(
            abi.encodeWithSignature(
                "offerForSale(address,(uint256,uint256,address,address)[])", msg.sender, orders
            )
        );

        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(orders, add(0x20, shl(5, mload(orders))))
                let end := add(s, mul(0x80, mload(orders)))
            } iszero(eq(s, end)) { s := add(s, 0x80) } {
                log3(add(s, 0x20), 0x40, _OFFER_EVENT_SIGNATURE, mload(s), mload(add(s, 0x60)))
            }
        }
    }

    function acceptOffer(NFTOrder[] memory orders) public payable virtual nonReentrant {
        _callBaseRetWord(
            abi.encodeWithSignature(
                "acceptOffer(address,(uint256,uint256,address,address)[])", msg.sender, orders
            )
        );
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(orders, add(0x20, shl(5, mload(orders))))
                let end := add(s, mul(0x80, mload(orders)))
            } iszero(eq(s, end)) { s := add(s, 0x80) } {
                let from := mload(add(s, 0x60))
                mstore(0x00, 0x01)
                log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, caller(), mload(s))
                log3(0x00, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, caller(), mload(s))
                log4(add(s, 0x20), 0x60, _BOUGHT_EVENT_SIGNATURE, mload(s), from, caller())
            }
        }
    }

    function cancelOffer(uint256[] memory ids) public virtual nonReentrant {
        _callBaseRetWord(abi.encodeWithSignature("cancelOffer(address,uint256[])", msg.sender, ids));

        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(ids, 0x20)
                let end := add(s, shl(5, mload(ids)))
            } iszero(eq(s, end)) { s := add(s, 0x20) } {
                log3(codesize(), 0x00, _CANCEL_OFFER_EVENT_SIGNATURE, mload(s), caller())
            }
        }
    }

    function bidForBuy(NFTOrder[] memory orders) public payable virtual nonReentrant {
        _callBaseRetWord(
            abi.encodeWithSignature(
                "bidForBuy(address,(uint256,uint256,address,address)[])", msg.sender, orders
            )
        );

        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(orders, add(0x20, shl(5, mload(orders))))
                let end := add(s, mul(0x80, mload(orders)))
            } iszero(eq(s, end)) { s := add(s, 0x80) } {
                log3(add(s, 0x20), 0x40, _BID_EVENT_SIGNATURE, mload(s), caller())
            }
        }
    }

    function acceptBid(NFTOrder[] memory orders) public virtual nonReentrant {
        _callBaseRetWord(
            abi.encodeWithSignature(
                "acceptBid(address,(uint256,uint256,address,address)[])", msg.sender, orders
            )
        );
        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(orders, add(0x20, shl(5, mload(orders))))
                let end := add(s, mul(0x80, mload(orders)))
            } iszero(eq(s, end)) { s := add(s, 0x80) } {
                let to := mload(add(s, 0x60))
                mstore(0x00, 0x01)
                log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, caller(), to, mload(s))
                log3(0x00, 0x20, _UPDATE_LOCK_STATE_EVENT_SIGNATURE, to, mload(s))
                log4(add(s, 0x20), 0x60, _BOUGHT_EVENT_SIGNATURE, mload(s), caller(), to)
            }
        }
    }

    function cancelBid(uint256[] memory ids) public virtual nonReentrant {
        _callBaseRetWord(abi.encodeWithSignature("cancelBid(address,uint256[])", msg.sender, ids));

        /// @solidity memory-safe-assembly
        assembly {
            for {
                let s := add(ids, 0x20)
                let end := add(s, shl(5, mload(ids)))
            } iszero(eq(s, end)) { s := add(s, 0x20) } {
                log3(codesize(), 0x00, _CANCEL_BID_EVENT_SIGNATURE, mload(s), caller())
            }
        }
    }

    /// @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))
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                  OWNER SYNCING OPERATIONS                  */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the `owner` of the contract, for marketplace signaling purposes.
    function owner() public view virtual returns (address) {
        return _getBT404NFTStorage().owner;
    }

    /// @dev Permissionless function to pull the owner from the base BT404 contract
    /// if it implements ownable, for marketplace signaling purposes.
    function pullOwner() public virtual {
        address newOwner;
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x8da5cb5b) // `owner()`.
            if and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x20)) {
                newOwner := shr(96, mload(0x0c))
            }
        }
        BT404NFTStorage storage $ = _getBT404NFTStorage();
        address oldOwner = $.owner;
        if (oldOwner != newOwner) {
            $.owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                     MIRROR OPERATIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the address of the base BT404 contract.
    function baseERC20() public view virtual returns (address base) {
        base = _getBT404NFTStorage().baseERC20;
        if (base == address(0)) revert NotLinked();
    }

    /// @dev Fallback modifier to execute calls from the base BT404 contract.
    modifier bt404NFTFallback() virtual {
        BT404NFTStorage storage $ = _getBT404NFTStorage();

        uint256 fnSelector = _calldataload(0x00) >> 224;

        // `logTransfer(uint256[])`.
        if (fnSelector == 0x263c69d6) {
            if (msg.sender != $.baseERC20) revert SenderNotBase();
            assembly ("memory-safe") {
                let o := add(0x24, calldataload(0x04)) // Packed logs offset.
                let end := add(o, shl(5, calldataload(sub(o, 0x20))))

                for {} iszero(eq(o, end)) { o := add(0x20, o) } {
                    let d := calldataload(o) // Entry in the packed logs.
                    let a := shr(96, d) // The address.
                    let b := and(1, d) // Whether it is a burn.
                    log4(
                        codesize(),
                        0x00,
                        _TRANSFER_EVENT_SIGNATURE,
                        mul(a, b), // `from`.
                        mul(a, iszero(b)), // `to`.
                        shr(168, shl(160, d)) // `id`.
                    )
                }
                mstore(0x00, 0x01)
                return(0x00, 0x20)
            }
        }
        // `logDirectTransfer(address,address,uint256[])`.
        if (fnSelector == 0x144027d3) {
            if (msg.sender != $.baseERC20) revert SenderNotBase();
            assembly ("memory-safe") {
                let from := calldataload(0x04)
                let to := calldataload(0x24)
                let o := add(0x24, calldataload(0x44)) // Direct logs offset.
                let end := add(o, shl(5, calldataload(sub(o, 0x20))))

                for {} iszero(eq(o, end)) { o := add(0x20, o) } {
                    log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, calldataload(o))
                }
                mstore(0x00, 0x01)
                return(0x00, 0x20)
            }
        }
        // `linkMirrorContract(address)`.
        if (fnSelector == 0x0f4599e5) {
            if ($.deployer != address(0)) {
                if (address(uint160(_calldataload(0x04))) != $.deployer) {
                    revert SenderNotDeployer();
                }
            }
            if ($.baseERC20 != address(0)) revert AlreadyLinked();
            $.baseERC20 = msg.sender;
            assembly ("memory-safe") {
                mstore(0x00, 0x01)
                return(0x00, 0x20)
            }
        }
        _;
    }

    /// @dev Fallback function for calls from base BT404 contract.
    fallback() external payable virtual bt404NFTFallback {}

    receive() external payable virtual {}

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      PRIVATE HELPERS                       */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Helper to read owned ids of a account from the base BT404 contract
    function _ownedIds(address account, uint256 begin, uint256 end, bool locked)
        private
        view
        returns (uint256[] memory result)
    {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(0x00, 0xf9b4b328) // `ownedIds(uint256,uint256,uint256)`.
            mstore(0x20, or(shl(96, account), iszero(iszero(locked))))
            mstore(0x40, begin)
            mstore(0x60, end)
            if iszero(staticcall(gas(), base, 0x1c, 0x64, 0x00, 0x00)) {
                returndatacopy(result, 0x00, returndatasize())
                revert(result, returndatasize())
            }
            returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the array in returndata.
            returndatacopy(result, mload(0x00), 0x20) // Copy the length of the array.
            returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), shl(5, mload(result))) // Copy the array elements.
            mstore(0x40, add(add(result, 0x20), shl(5, mload(result)))) // Allocate memory.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Helper to read a string from the base BT404 contract.
    function _readString(uint256 fnSelector, uint256 arg0)
        private
        view
        returns (string memory result)
    {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(0x00, fnSelector)
            mstore(0x20, arg0)
            if iszero(staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x00)) {
                returndatacopy(result, 0x00, returndatasize())
                revert(result, returndatasize())
            }
            returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata.
            returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string.
            returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string.
            mstore(0x40, add(add(result, 0x20), mload(result))) // Allocate memory.
        }
    }

    /// @dev Helper to read a word from the base BT404 contract.
    function _readWord(uint256 fnSelector, uint256 arg0, uint256 arg1)
        private
        view
        returns (uint256 result)
    {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(0x00, fnSelector)
            mstore(0x20, arg0)
            mstore(0x40, arg1)
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x44, 0x00, 0x20))
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            mstore(0x40, m) // Restore the free memory pointer.
            result := mload(0x00)
        }
    }

    /// @dev Helper to call a function and return a word value.
    function _callBaseRetWord(bytes memory _calldata) private returns (uint256 result) {
        address base = baseERC20();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            if iszero(
                and(
                    gt(returndatasize(), 0x1f),
                    call(
                        gas(), base, callvalue(), add(_calldata, 0x20), mload(_calldata), 0x00, 0x20
                    )
                )
            ) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
            result := mload(0x00)
        }
    }

    /// @dev Returns the calldata value at `offset`.
    function _calldataload(uint256 offset) internal pure returns (uint256 value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := calldataload(offset)
        }
    }

    /// @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)
            }
        }
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/",
    "@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "@openzeppelin/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_traitRegistry","type":"address"},{"internalType":"address","name":"_traitOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyLinked","type":"error"},{"inputs":[],"name":"CannotLink","type":"error"},{"inputs":[],"name":"NotLinked","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"SenderNotBase","type":"error"},{"inputs":[],"name":"SenderNotDeployer","type":"error"},{"inputs":[],"name":"TokenNotMinted","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","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":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"address","name":"bidToken","type":"address"}],"name":"Bid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"maker","type":"address"}],"name":"Bought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"}],"name":"CancelBid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"CancelOffer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"idX","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"idY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exchangeFee","type":"uint256"}],"name":"Exchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"minPrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"offerToken","type":"address"}],"name":"Offer","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":false,"internalType":"uint256","name":"batchIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"TokenBatchAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"traitRegistry","type":"address"}],"name":"TraitRegistrySet","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":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"lockStatus","type":"bool"}],"name":"UpdateLockState","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"trader","type":"address"}],"internalType":"struct BT404Mirror.NFTOrder[]","name":"orders","type":"tuple[]"}],"name":"acceptBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"trader","type":"address"}],"internalType":"struct BT404Mirror.NFTOrder[]","name":"orders","type":"tuple[]"}],"name":"acceptOffer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftOwner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseERC20","outputs":[{"internalType":"address","name":"base","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"trader","type":"address"}],"internalType":"struct BT404Mirror.NFTOrder[]","name":"orders","type":"tuple[]"}],"name":"bidForBuy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"cancelBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"cancelOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"idX","type":"uint256"},{"internalType":"uint256","name":"idY","type":"uint256"}],"name":"exchange","outputs":[{"internalType":"uint256","name":"exchangeFee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftOwner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"lockedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"trader","type":"address"}],"internalType":"struct BT404Mirror.NFTOrder[]","name":"orders","type":"tuple[]"}],"name":"offerForSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"ownedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerAt","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":[],"name":"pullOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenTraits","outputs":[{"internalType":"uint256[]","name":"traitIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"traitRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"bool","name":"lock","type":"bool"}],"name":"updateLockState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561000f575f5ffd5b5060405161294238038061294283398101604081905261002e91610145565b683602298b8c10b0123180546001600160a01b03199081163291821617179055610058828261005f565b5050610176565b817fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a0080546001600160a01b0319166001600160a01b0392831617905560405190831681527fb82d1657656997646f6785ef206dde6db783ae54e4a1b0eb109e144c68ac941c9060200160405180910390a16040516320e202bb60e11b81526001600160a01b0382811660048301528316906341c40576906024015f604051808303815f87803b158015610110575f5ffd5b505af1158015610122573d5f5f3e3d5ffd5b505050505050565b80516001600160a01b0381168114610140575f5ffd5b919050565b5f5f60408385031215610156575f5ffd5b61015f8361012a565b915061016d6020840161012a565b90509250929050565b6127bf806101835f395ff3fe6080604052600436106101d0575f3560e01c806370a08231116100f6578063b88d4fde11610094578063ccf0e51511610063578063ccf0e515146107c5578063d4b7eac3146107e4578063e05c57bf14610803578063e985e9c514610822576101d7565b8063b88d4fde1461072c578063bf598e1e1461074b578063c87b56dd1461076a578063cc117b7114610789576101d7565b806395d89b41116100d057806395d89b41146106d257806397e5311c146106e6578063a22cb465146106fa578063a444be1f14610719576101d7565b806370a082311461066f5780637ec824181461068e5780638da5cb5b146106ad576101d7565b806325d4fe1a1161016e578063616b95e81161013d578063616b95e8146105f15780636352211e14610610578063680a9f1f1461062f5780636cef16e61461065b576101d7565b806325d4fe1a1461058157806342842e0e146105a0578063442f085d146105bf5780634ae9617c146105d2576101d7565b8063095ea7b3116101aa578063095ea7b31461050257806318160ddd1461052157806323b872dd146105435780632435987914610562576101d7565b806301ffc9a71461045957806306fdde03146104aa578063081812fc146104cb576101d7565b366101d757005b683602298b8c10b012305f3560e01c63263c69d68190036102895781546001600160a01b0316331461021c57604051631b1e598960e11b815260040160405180910390fd5b600435602401602081033560051b81015b80821461027e5781358060601c816001168260a01b60a81c811583028284027fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a450505081602001915061022d565b505060015f5260205ff35b8063144027d30361031c5781546001600160a01b031633146102be57604051631b1e598960e11b815260040160405180910390fd5b600435602435604435602401602081033560051b81015b80821461030f57813583857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48160200191506102d5565b5050505060015f5260205ff35b80630f4599e5036103f55760018201546001600160a01b03161561038b5760018201546001600160a01b03166004356001600160a01b03161461038b576040517fc59ec47a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160a01b0316156103cd576040517fbf656a4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff19163317825560015f908152602090f35b683602298b8c10b012305f3560e01c63778e12298190036104575781546001600160a01b0316331461043a57604051631b1e598960e11b815260040160405180910390fd5b60043560243560443561044e838383610841565b60015f5260205ff35b005b348015610464575f5ffd5b50610495610473366004611f14565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b5575f5ffd5b506104be6109be565b6040516104a19190611f5a565b3480156104d6575f5ffd5b506104ea6104e5366004611f8f565b6109d3565b6040516001600160a01b0390911681526020016104a1565b34801561050d575f5ffd5b5061045761051c366004611fc1565b6109e9565b34801561052c575f5ffd5b50610535610a69565b6040519081526020016104a1565b34801561054e575f5ffd5b5061045761055d366004611fe9565b610a79565b34801561056d575f5ffd5b506104ea61057c366004611f8f565b610b5a565b34801561058c575f5ffd5b5061045761059b36600461211e565b610b6a565b3480156105ab575f5ffd5b506104576105ba366004611fe9565b610c64565b6104576105cd366004612150565b610c95565b3480156105dd575f5ffd5b506104576105ec366004612150565b610de4565b3480156105fc575f5ffd5b5061045761060b36600461211e565b610f33565b34801561061b575f5ffd5b506104ea61062a366004611f8f565b61101d565b34801561063a575f5ffd5b5061064e610649366004612229565b61102d565b6040516104a19190612293565b348015610666575f5ffd5b50610457611044565b34801561067a575f5ffd5b506105356106893660046122a5565b611100565b348015610699575f5ffd5b506104576106a83660046122cd565b611119565b3480156106b8575f5ffd5b50683602298b8c10b01232546001600160a01b03166104ea565b3480156106dd575f5ffd5b506104be61125d565b3480156106f1575f5ffd5b506104ea61126d565b348015610705575f5ffd5b50610457610714366004612318565b6112bb565b610457610727366004612150565b611338565b348015610737575f5ffd5b50610457610746366004612340565b61142c565b348015610756575f5ffd5b5061064e610765366004612229565b611486565b348015610775575f5ffd5b506104be610784366004611f8f565b611494565b348015610794575f5ffd5b507fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a00546001600160a01b03166104ea565b3480156107d0575f5ffd5b506104576107df366004612150565b61150f565b3480156107ef575f5ffd5b506105356107fe3660046123d5565b611607565b34801561080e575f5ffd5b5061064e61081d366004611f8f565b611702565b34801561082d575f5ffd5b5061049561083c3660046123f5565b611940565b7fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a0080545f90600160a01b900467ffffffffffffffff168260146108838361241d565b825467ffffffffffffffff9182166101009390930a9283029282021916919091179091556040805160808101825263ffffffff808916825287811660208084019182526001600160a01b03808d1685870190815242808616606088019081529989165f81815260018e019095529388902096518754955192519a518716600160e01b026001600160e01b039b90941668010000000000000000029a909a169186166401000000000267ffffffffffffffff1990951699909516989098179290921790951695909517949094179055519092507f0d8157cdc5b3cbbdfedf31ab98451daa7b92ba13165a0da1cae42e69c1e8f53b916109af918491889188918b91948552602085019390935260408401919091526001600160a01b03166060830152608082015260a00190565b60405180910390a15050505050565b60606109ce6306fdde035f61196b565b905090565b5f6109e363081812fc835f6119c1565b92915050565b5f6109f261126d565b90508260601b60601c925060405163d10b6e0c5f5283602052826040523360605260205f6064601c34865af1601f3d1116610a2f573d5f823e3d81fd5b80604052505f6060528183600c5160601c7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a4505050565b5f6109ce63e2c792815f5f6119c1565b5f610a8261126d565b90508360601b60601c93508260601b60601c925060405163e5eb36c881528460208201528360408201528260608201523360808201526020816084601c840134865af1600182511416610ad7573d5f823e3d81fd5b5f815282857fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac602084a38284867fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a46001815282847fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac602084a35050505050565b5f6109e36324359879835f6119c1565b683602298b8c10b012308054600160a01b900460ff1615610b9e5760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610c0e90610bc69033908590602401612455565b60408051601f198184030181529190526020810180516001600160e01b03167f2da2a85900000000000000000000000000000000000000000000000000000000179052611a04565b5060208201825160051b81015b808214610c54573382517fc4caef7e3533865382e608c341581a5e2a1b0d1ac37b0aaf58023ccd4eedfd8e5f38a3602082019150610c1b565b5050805460ff60a01b1916905550565b610c6f838383610a79565b813b15610c9057610c9083838360405180602001604052805f815250611a42565b505050565b683602298b8c10b012308054600160a01b900460ff1615610cc95760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610d3990610cf19033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167f53ffa07100000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015160015f52825133827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48251337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3338184517fd9882bc1ac8e78c918b907fa0ff79cc9d866091c5eb450ebed79e9d147541d5b606060208801a450608082019150610d4c565b683602298b8c10b012308054600160a01b900460ff1615610e185760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610e8890610e409033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167fb6ebe10300000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015160015f52825181337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48251817fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3803384517fd9882bc1ac8e78c918b907fa0ff79cc9d866091c5eb450ebed79e9d147541d5b606060208801a450608082019150610e9b565b683602298b8c10b012308054600160a01b900460ff1615610f675760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610fd790610f8f9033908590602401612455565b60408051601f198184030181529190526020810180516001600160e01b03167fa38beee100000000000000000000000000000000000000000000000000000000179052611a04565b5060208201825160051b81015b808214610c54573382517f874afcdd5e90b2329b3c1601e613dcdc6abb6deb62ce61339a8337b48c053e515f38a3602082019150610fe4565b5f6109e3636352211e835f6119c1565b606061103c8484846001611acb565b949350505050565b5f5f61104e61126d565b9050638da5cb5b5f5260205f6004601c845afa601f3d11161561107457600c5160601c91505b683602298b8c10b0123254683602298b8c10b01230906001600160a01b0390811690841681146110fa5760028201805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0386811691821790925560405190918316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35b50505050565b5f6109e363f5b100ea836001600160a01b03165f6119c1565b5f61112261126d565b90505f5f826001600160a01b03168461113b575f61113e565b60015b60ff166060336001600160a01b0316901b1786604051602401611162929190612504565b60408051601f198184030181529181526020820180516001600160e01b03167fb79cc1bd00000000000000000000000000000000000000000000000000000000179052516111b09190612533565b5f604051808303815f865af19150503d805f81146111e9576040519150601f19603f3d011682016040523d82523d5f602084013e6111ee565b606091505b50915091508160016020830151141661120957805160208201fd5b8451845f52602086018160051b81015b808214611253578151337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3602082019150611219565b5050505050505050565b60606109ce6395d89b415f61196b565b683602298b8c10b01230546001600160a01b0316806112b8576040517f5b2a47ae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90565b5f6112c461126d565b90508260601b60601c925060405163813500fc5f52836020528215156040523360605260205f6064601c34865af160015f511416611304573d5f823e3d81fd5b83337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206040a360405250505f60605250565b683602298b8c10b012308054600160a01b900460ff161561136c5760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b1781556040516113dc906113949033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167fb5a1305b00000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c54573382517fec85e6e86fabc4c703529b570fb5eb567dad69ddbf7901bc0fd28b38b93de7f3604060208601a36080820191506113ef565b611437858585610a79565b833b1561147f5761147f85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611a4292505050565b5050505050565b606061103c8484845f611acb565b60605f61149f6109be565b90505f5f6114ac85611b40565b915091506114e6836114bd87611ba6565b84846040516020016114d2949392919061253e565b604051602081830303815290604052611be8565b6040516020016114f691906126a6565b6040516020818303038152906040529350505050919050565b683602298b8c10b012308054600160a01b900460ff16156115435760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b1781556040516115b39061156b9033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167f73e63d8900000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015182517fc56f8610599b5a39311e36563ef3386394748f787ef5efc116d960d77def8050604060208601a36080820191506115c6565b5f5f61161161126d565b9050604051632c5966af5f5284602052836040523360605260605f6064601c34865af1605f3d1116611645573d5f823e3d81fd5b5f5160205160405194508681837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48533827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a4505083857fbc43d7c0945f5a13a7bfa8ca7309e55f903f01d66c38c6d1353fe7ff9335d77660206040a3600160405283337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60206040a3604052505f60605292915050565b7fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a008054604080517fb923d2fb0000000000000000000000000000000000000000000000000000000081529051606093926001600160a01b0316915f91839163b923d2fb9160048083019260209291908290030181865afa158015611788573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117ac91906126d7565b9050805f036117bd57505050919050565b5f6117c88487611bf5565b90505f8267ffffffffffffffff8111156117e4576117e4612023565b60405190808252806020026020018201604052801561180d578160200160208202803683370190505b5090506118af8188845f0151856020015186604001518760600151604051602001611894949392919060e094851b7fffffffff00000000000000000000000000000000000000000000000000000000908116825293851b8416600482015260609290921b6bffffffffffffffffffffffff1916600883015290921b16601c82015260200190565b60405160208183030381529060405280519060200120611d2b565b6040517f430361820000000000000000000000000000000000000000000000000000000081526001600160a01b038516906343036182906118f4908490600401612293565b5f60405180830381865afa15801561190e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261193591908101906126ee565b979650505050505050565b5f61196263e985e9c5846001600160a01b0316846001600160a01b03166119c1565b15159392505050565b60605f61197661126d565b90506040519150835f52826020525f5f6024601c845afa611999573d5f833e3d82fd5b60205f5f3e60205f51833e815160205f5101602084013e815160208301016040525092915050565b5f5f6119cb61126d565b9050604051855f52846020528360405260205f6044601c855afa601f3d11166119f6573d5f823e3d81fd5b60405250505f519392505050565b5f5f611a0e61126d565b905060405160205f85516020870134865af1601f3d1116611a31573d5f823e3d81fd5b60405250505f606081905251919050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611a89578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611aaa573d15611aaa573d5f843e3d83fd5b508060e01b825114611ac35763d1a57ed65f526004601cfd5b505050505050565b60605f611ad661126d565b9050604051915063f9b4b3285f528215158660601b1760205284604052836060525f5f6064601c845afa611b0c573d5f833e3d82fd5b60205f5f3e60205f51833e815160051b60205f5101602084013e815160051b60208301016040525f60605250949350505050565b6060807fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a005f611b6e85611702565b82549091506001600160a01b0316611b8b8163bc58599a84611d8f565b9450611b9c81634c182a0184611d8f565b9350505050915091565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480611bbf575050819003601f19909101908152919050565b60606109e3825f5f611e07565b604080516080810182525f80825260208201819052918101829052606081019190915282545f90600160a01b900467ffffffffffffffff16600185015b81831015611cf95781830160011c67ffffffffffffffff81165f90815260208381526040918290208251608081018452905463ffffffff80821680845264010000000083048216948401949094526801000000000000000082046001600160a01b031694830194909452600160e01b900490921660608301529095508610801590611cc75750846020015163ffffffff168611155b15611cd557505050506109e3565b845163ffffffff16861015611cec57809250611cf3565b8060010193505b50611c32565b6040517fd03ce9df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82515f5b8181101561147f57604080516020810183905290810184905260608101859052608001604051602081830303815290604052805190602001205f1c858281518110611d7c57611d7c612775565b6020908102919091010152600101611d2f565b606060405183815282516020808301528060408301526020810290508060208501606084015e6044015f8082601c8501895afa611dce573d5f833e3d82fd5b50905060205f803e60205f51823e805160205f5101602083013e80516020820101601f8082166020031680820160405250509392505050565b606083518015611f0c576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020830181810183886020010180515f82525b60038a0199508951603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518452600484019350828410611e825790526020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f60208284031215611f24575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611f53575f5ffd5b9392505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215611f9f575f5ffd5b5035919050565b80356001600160a01b0381168114611fbc575f5ffd5b919050565b5f5f60408385031215611fd2575f5ffd5b611fdb83611fa6565b946020939093013593505050565b5f5f5f60608486031215611ffb575f5ffd5b61200484611fa6565b925061201260208501611fa6565b929592945050506040919091013590565b634e487b7160e01b5f52604160045260245ffd5b6040516080810167ffffffffffffffff8111828210171561205a5761205a612023565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561208957612089612023565b604052919050565b5f67ffffffffffffffff8211156120aa576120aa612023565b5060051b60200190565b5f82601f8301126120c3575f5ffd5b81356120d66120d182612091565b612060565b8082825260208201915060208360051b8601019250858311156120f7575f5ffd5b602085015b838110156121145780358352602092830192016120fc565b5095945050505050565b5f6020828403121561212e575f5ffd5b813567ffffffffffffffff811115612144575f5ffd5b61103c848285016120b4565b5f60208284031215612160575f5ffd5b813567ffffffffffffffff811115612176575f5ffd5b8201601f81018413612186575f5ffd5b80356121946120d182612091565b8082825260208201915060208360071b8501019250868311156121b5575f5ffd5b6020840193505b8284101561221f57608084880312156121d3575f5ffd5b6121db612037565b84358152602080860135908201526121f560408601611fa6565b604082015261220660608601611fa6565b60608201528252608093909301926020909101906121bc565b9695505050505050565b5f5f5f6060848603121561223b575f5ffd5b61224484611fa6565b95602085013595506040909401359392505050565b5f8151808452602084019350602083015f5b8281101561228957815186526020958601959091019060010161226b565b5093949350505050565b602081525f611f536020830184612259565b5f602082840312156122b5575f5ffd5b611f5382611fa6565b80358015158114611fbc575f5ffd5b5f5f604083850312156122de575f5ffd5b823567ffffffffffffffff8111156122f4575f5ffd5b612300858286016120b4565b92505061230f602084016122be565b90509250929050565b5f5f60408385031215612329575f5ffd5b61233283611fa6565b915061230f602084016122be565b5f5f5f5f5f60808688031215612354575f5ffd5b61235d86611fa6565b945061236b60208701611fa6565b935060408601359250606086013567ffffffffffffffff81111561238d575f5ffd5b8601601f8101881361239d575f5ffd5b803567ffffffffffffffff8111156123b3575f5ffd5b8860208284010111156123c4575f5ffd5b959894975092955050506020019190565b5f5f604083850312156123e6575f5ffd5b50508035926020909101359150565b5f5f60408385031215612406575f5ffd5b61240f83611fa6565b915061230f60208401611fa6565b5f67ffffffffffffffff821667ffffffffffffffff810361244c57634e487b7160e01b5f52601160045260245ffd5b60010192915050565b6001600160a01b0383168152604060208201525f61103c6040830184612259565b5f604082016001600160a01b0385168352604060208401528084518083526060850191506020860192505f5b818110156124f857835180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152506080830192506020840193506001810190506124a2565b50909695505050505050565b828152604060208201525f61103c6040830184612259565b5f81518060208401855e5f93019283525090919050565b5f611f53828461251c565b7f7b2265787465726e616c5f75726c223a2268747470733a2f2f6269746d61707081527f756e6b732e636f6d222c226465736372697074696f6e223a22412066756c6c7960208201527f2d6f6e636861696e2c20756c7472612d6c617267652c2068796272696420636f60408201527f6c6c656374696f6e2e222c226e616d65223a220000000000000000000000000060608201525f6125e1607383018761251c565b7f20230000000000000000000000000000000000000000000000000000000000008152612611600282018761251c565b90507f222c2261747472696275746573223a00000000000000000000000000000000008152612643600f82018661251c565b90507f2c22696d616765223a22000000000000000000000000000000000000000000008152612675600a82018561251c565b7f227d0000000000000000000000000000000000000000000000000000000000008152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f611f53601d83018461251c565b5f602082840312156126e7575f5ffd5b5051919050565b5f602082840312156126fe575f5ffd5b815167ffffffffffffffff811115612714575f5ffd5b8201601f81018413612724575f5ffd5b80516127326120d182612091565b8082825260208201915060208360051b850101925086831115612753575f5ffd5b6020840193505b8284101561221f57835182526020938401939091019061275a565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212204a6db0638fbe6041dd2bb5d16c9519ae98c7bdd1583b05e06cb758fb07803cc364736f6c634300081c0033000000000000000000000000fe17b9cc26120654bccee694d2762ee32f8d355300000000000000000000000072016206489914de6e2f5c31193af7811fd6ae07

Deployed Bytecode

0x6080604052600436106101d0575f3560e01c806370a08231116100f6578063b88d4fde11610094578063ccf0e51511610063578063ccf0e515146107c5578063d4b7eac3146107e4578063e05c57bf14610803578063e985e9c514610822576101d7565b8063b88d4fde1461072c578063bf598e1e1461074b578063c87b56dd1461076a578063cc117b7114610789576101d7565b806395d89b41116100d057806395d89b41146106d257806397e5311c146106e6578063a22cb465146106fa578063a444be1f14610719576101d7565b806370a082311461066f5780637ec824181461068e5780638da5cb5b146106ad576101d7565b806325d4fe1a1161016e578063616b95e81161013d578063616b95e8146105f15780636352211e14610610578063680a9f1f1461062f5780636cef16e61461065b576101d7565b806325d4fe1a1461058157806342842e0e146105a0578063442f085d146105bf5780634ae9617c146105d2576101d7565b8063095ea7b3116101aa578063095ea7b31461050257806318160ddd1461052157806323b872dd146105435780632435987914610562576101d7565b806301ffc9a71461045957806306fdde03146104aa578063081812fc146104cb576101d7565b366101d757005b683602298b8c10b012305f3560e01c63263c69d68190036102895781546001600160a01b0316331461021c57604051631b1e598960e11b815260040160405180910390fd5b600435602401602081033560051b81015b80821461027e5781358060601c816001168260a01b60a81c811583028284027fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a450505081602001915061022d565b505060015f5260205ff35b8063144027d30361031c5781546001600160a01b031633146102be57604051631b1e598960e11b815260040160405180910390fd5b600435602435604435602401602081033560051b81015b80821461030f57813583857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48160200191506102d5565b5050505060015f5260205ff35b80630f4599e5036103f55760018201546001600160a01b03161561038b5760018201546001600160a01b03166004356001600160a01b03161461038b576040517fc59ec47a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81546001600160a01b0316156103cd576040517fbf656a4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff19163317825560015f908152602090f35b683602298b8c10b012305f3560e01c63778e12298190036104575781546001600160a01b0316331461043a57604051631b1e598960e11b815260040160405180910390fd5b60043560243560443561044e838383610841565b60015f5260205ff35b005b348015610464575f5ffd5b50610495610473366004611f14565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b5575f5ffd5b506104be6109be565b6040516104a19190611f5a565b3480156104d6575f5ffd5b506104ea6104e5366004611f8f565b6109d3565b6040516001600160a01b0390911681526020016104a1565b34801561050d575f5ffd5b5061045761051c366004611fc1565b6109e9565b34801561052c575f5ffd5b50610535610a69565b6040519081526020016104a1565b34801561054e575f5ffd5b5061045761055d366004611fe9565b610a79565b34801561056d575f5ffd5b506104ea61057c366004611f8f565b610b5a565b34801561058c575f5ffd5b5061045761059b36600461211e565b610b6a565b3480156105ab575f5ffd5b506104576105ba366004611fe9565b610c64565b6104576105cd366004612150565b610c95565b3480156105dd575f5ffd5b506104576105ec366004612150565b610de4565b3480156105fc575f5ffd5b5061045761060b36600461211e565b610f33565b34801561061b575f5ffd5b506104ea61062a366004611f8f565b61101d565b34801561063a575f5ffd5b5061064e610649366004612229565b61102d565b6040516104a19190612293565b348015610666575f5ffd5b50610457611044565b34801561067a575f5ffd5b506105356106893660046122a5565b611100565b348015610699575f5ffd5b506104576106a83660046122cd565b611119565b3480156106b8575f5ffd5b50683602298b8c10b01232546001600160a01b03166104ea565b3480156106dd575f5ffd5b506104be61125d565b3480156106f1575f5ffd5b506104ea61126d565b348015610705575f5ffd5b50610457610714366004612318565b6112bb565b610457610727366004612150565b611338565b348015610737575f5ffd5b50610457610746366004612340565b61142c565b348015610756575f5ffd5b5061064e610765366004612229565b611486565b348015610775575f5ffd5b506104be610784366004611f8f565b611494565b348015610794575f5ffd5b507fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a00546001600160a01b03166104ea565b3480156107d0575f5ffd5b506104576107df366004612150565b61150f565b3480156107ef575f5ffd5b506105356107fe3660046123d5565b611607565b34801561080e575f5ffd5b5061064e61081d366004611f8f565b611702565b34801561082d575f5ffd5b5061049561083c3660046123f5565b611940565b7fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a0080545f90600160a01b900467ffffffffffffffff168260146108838361241d565b825467ffffffffffffffff9182166101009390930a9283029282021916919091179091556040805160808101825263ffffffff808916825287811660208084019182526001600160a01b03808d1685870190815242808616606088019081529989165f81815260018e019095529388902096518754955192519a518716600160e01b026001600160e01b039b90941668010000000000000000029a909a169186166401000000000267ffffffffffffffff1990951699909516989098179290921790951695909517949094179055519092507f0d8157cdc5b3cbbdfedf31ab98451daa7b92ba13165a0da1cae42e69c1e8f53b916109af918491889188918b91948552602085019390935260408401919091526001600160a01b03166060830152608082015260a00190565b60405180910390a15050505050565b60606109ce6306fdde035f61196b565b905090565b5f6109e363081812fc835f6119c1565b92915050565b5f6109f261126d565b90508260601b60601c925060405163d10b6e0c5f5283602052826040523360605260205f6064601c34865af1601f3d1116610a2f573d5f823e3d81fd5b80604052505f6060528183600c5160601c7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f38a4505050565b5f6109ce63e2c792815f5f6119c1565b5f610a8261126d565b90508360601b60601c93508260601b60601c925060405163e5eb36c881528460208201528360408201528260608201523360808201526020816084601c840134865af1600182511416610ad7573d5f823e3d81fd5b5f815282857fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac602084a38284867fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a46001815282847fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac602084a35050505050565b5f6109e36324359879835f6119c1565b683602298b8c10b012308054600160a01b900460ff1615610b9e5760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610c0e90610bc69033908590602401612455565b60408051601f198184030181529190526020810180516001600160e01b03167f2da2a85900000000000000000000000000000000000000000000000000000000179052611a04565b5060208201825160051b81015b808214610c54573382517fc4caef7e3533865382e608c341581a5e2a1b0d1ac37b0aaf58023ccd4eedfd8e5f38a3602082019150610c1b565b5050805460ff60a01b1916905550565b610c6f838383610a79565b813b15610c9057610c9083838360405180602001604052805f815250611a42565b505050565b683602298b8c10b012308054600160a01b900460ff1615610cc95760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610d3990610cf19033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167f53ffa07100000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015160015f52825133827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48251337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3338184517fd9882bc1ac8e78c918b907fa0ff79cc9d866091c5eb450ebed79e9d147541d5b606060208801a450608082019150610d4c565b683602298b8c10b012308054600160a01b900460ff1615610e185760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610e8890610e409033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167fb6ebe10300000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015160015f52825181337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48251817fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3803384517fd9882bc1ac8e78c918b907fa0ff79cc9d866091c5eb450ebed79e9d147541d5b606060208801a450608082019150610e9b565b683602298b8c10b012308054600160a01b900460ff1615610f675760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b178155604051610fd790610f8f9033908590602401612455565b60408051601f198184030181529190526020810180516001600160e01b03167fa38beee100000000000000000000000000000000000000000000000000000000179052611a04565b5060208201825160051b81015b808214610c54573382517f874afcdd5e90b2329b3c1601e613dcdc6abb6deb62ce61339a8337b48c053e515f38a3602082019150610fe4565b5f6109e3636352211e835f6119c1565b606061103c8484846001611acb565b949350505050565b5f5f61104e61126d565b9050638da5cb5b5f5260205f6004601c845afa601f3d11161561107457600c5160601c91505b683602298b8c10b0123254683602298b8c10b01230906001600160a01b0390811690841681146110fa5760028201805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0386811691821790925560405190918316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35b50505050565b5f6109e363f5b100ea836001600160a01b03165f6119c1565b5f61112261126d565b90505f5f826001600160a01b03168461113b575f61113e565b60015b60ff166060336001600160a01b0316901b1786604051602401611162929190612504565b60408051601f198184030181529181526020820180516001600160e01b03167fb79cc1bd00000000000000000000000000000000000000000000000000000000179052516111b09190612533565b5f604051808303815f865af19150503d805f81146111e9576040519150601f19603f3d011682016040523d82523d5f602084013e6111ee565b606091505b50915091508160016020830151141661120957805160208201fd5b8451845f52602086018160051b81015b808214611253578151337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60205fa3602082019150611219565b5050505050505050565b60606109ce6395d89b415f61196b565b683602298b8c10b01230546001600160a01b0316806112b8576040517f5b2a47ae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b90565b5f6112c461126d565b90508260601b60601c925060405163813500fc5f52836020528215156040523360605260205f6064601c34865af160015f511416611304573d5f823e3d81fd5b83337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160206040a360405250505f60605250565b683602298b8c10b012308054600160a01b900460ff161561136c5760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b1781556040516113dc906113949033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167fb5a1305b00000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c54573382517fec85e6e86fabc4c703529b570fb5eb567dad69ddbf7901bc0fd28b38b93de7f3604060208601a36080820191506113ef565b611437858585610a79565b833b1561147f5761147f85858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250611a4292505050565b5050505050565b606061103c8484845f611acb565b60605f61149f6109be565b90505f5f6114ac85611b40565b915091506114e6836114bd87611ba6565b84846040516020016114d2949392919061253e565b604051602081830303815290604052611be8565b6040516020016114f691906126a6565b6040516020818303038152906040529350505050919050565b683602298b8c10b012308054600160a01b900460ff16156115435760405163558a1e0360e11b815260040160405180910390fd5b805460ff60a01b1916600160a01b1781556040516115b39061156b9033908590602401612476565b60408051601f198184030181529190526020810180516001600160e01b03167f73e63d8900000000000000000000000000000000000000000000000000000000179052611a04565b50815160051b6020018201825160800281015b808214610c5457606082015182517fc56f8610599b5a39311e36563ef3386394748f787ef5efc116d960d77def8050604060208601a36080820191506115c6565b5f5f61161161126d565b9050604051632c5966af5f5284602052836040523360605260605f6064601c34865af1605f3d1116611645573d5f823e3d81fd5b5f5160205160405194508681837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a48533827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f38a4505083857fbc43d7c0945f5a13a7bfa8ca7309e55f903f01d66c38c6d1353fe7ff9335d77660206040a3600160405283337fcc3a1bd7e528af8582cd3578d82ae22e309de7c3663c9d0fa5b5ce79c1a346ac60206040a3604052505f60605292915050565b7fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a008054604080517fb923d2fb0000000000000000000000000000000000000000000000000000000081529051606093926001600160a01b0316915f91839163b923d2fb9160048083019260209291908290030181865afa158015611788573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117ac91906126d7565b9050805f036117bd57505050919050565b5f6117c88487611bf5565b90505f8267ffffffffffffffff8111156117e4576117e4612023565b60405190808252806020026020018201604052801561180d578160200160208202803683370190505b5090506118af8188845f0151856020015186604001518760600151604051602001611894949392919060e094851b7fffffffff00000000000000000000000000000000000000000000000000000000908116825293851b8416600482015260609290921b6bffffffffffffffffffffffff1916600883015290921b16601c82015260200190565b60405160208183030381529060405280519060200120611d2b565b6040517f430361820000000000000000000000000000000000000000000000000000000081526001600160a01b038516906343036182906118f4908490600401612293565b5f60405180830381865afa15801561190e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261193591908101906126ee565b979650505050505050565b5f61196263e985e9c5846001600160a01b0316846001600160a01b03166119c1565b15159392505050565b60605f61197661126d565b90506040519150835f52826020525f5f6024601c845afa611999573d5f833e3d82fd5b60205f5f3e60205f51833e815160205f5101602084013e815160208301016040525092915050565b5f5f6119cb61126d565b9050604051855f52846020528360405260205f6044601c855afa601f3d11166119f6573d5f823e3d81fd5b60405250505f519392505050565b5f5f611a0e61126d565b905060405160205f85516020870134865af1601f3d1116611a31573d5f823e3d81fd5b60405250505f606081905251919050565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a08401528015611a89578060c08401826020870160045afa505b60208360a48301601c86015f8a5af1611aaa573d15611aaa573d5f843e3d83fd5b508060e01b825114611ac35763d1a57ed65f526004601cfd5b505050505050565b60605f611ad661126d565b9050604051915063f9b4b3285f528215158660601b1760205284604052836060525f5f6064601c845afa611b0c573d5f833e3d82fd5b60205f5f3e60205f51833e815160051b60205f5101602084013e815160051b60208301016040525f60605250949350505050565b6060807fae051faf52657fcd53347f0e4cbde96efc859c0144969f104459b1251f1e0a005f611b6e85611702565b82549091506001600160a01b0316611b8b8163bc58599a84611d8f565b9450611b9c81634c182a0184611d8f565b9350505050915091565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480611bbf575050819003601f19909101908152919050565b60606109e3825f5f611e07565b604080516080810182525f80825260208201819052918101829052606081019190915282545f90600160a01b900467ffffffffffffffff16600185015b81831015611cf95781830160011c67ffffffffffffffff81165f90815260208381526040918290208251608081018452905463ffffffff80821680845264010000000083048216948401949094526801000000000000000082046001600160a01b031694830194909452600160e01b900490921660608301529095508610801590611cc75750846020015163ffffffff168611155b15611cd557505050506109e3565b845163ffffffff16861015611cec57809250611cf3565b8060010193505b50611c32565b6040517fd03ce9df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82515f5b8181101561147f57604080516020810183905290810184905260608101859052608001604051602081830303815290604052805190602001205f1c858281518110611d7c57611d7c612775565b6020908102919091010152600101611d2f565b606060405183815282516020808301528060408301526020810290508060208501606084015e6044015f8082601c8501895afa611dce573d5f833e3d82fd5b50905060205f803e60205f51823e805160205f5101602083013e80516020820101601f8082166020031680820160405250509392505050565b606083518015611f0c576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020830181810183886020010180515f82525b60038a0199508951603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f518452600484019350828410611e825790526020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b5f60208284031215611f24575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611f53575f5ffd5b9392505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f60208284031215611f9f575f5ffd5b5035919050565b80356001600160a01b0381168114611fbc575f5ffd5b919050565b5f5f60408385031215611fd2575f5ffd5b611fdb83611fa6565b946020939093013593505050565b5f5f5f60608486031215611ffb575f5ffd5b61200484611fa6565b925061201260208501611fa6565b929592945050506040919091013590565b634e487b7160e01b5f52604160045260245ffd5b6040516080810167ffffffffffffffff8111828210171561205a5761205a612023565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561208957612089612023565b604052919050565b5f67ffffffffffffffff8211156120aa576120aa612023565b5060051b60200190565b5f82601f8301126120c3575f5ffd5b81356120d66120d182612091565b612060565b8082825260208201915060208360051b8601019250858311156120f7575f5ffd5b602085015b838110156121145780358352602092830192016120fc565b5095945050505050565b5f6020828403121561212e575f5ffd5b813567ffffffffffffffff811115612144575f5ffd5b61103c848285016120b4565b5f60208284031215612160575f5ffd5b813567ffffffffffffffff811115612176575f5ffd5b8201601f81018413612186575f5ffd5b80356121946120d182612091565b8082825260208201915060208360071b8501019250868311156121b5575f5ffd5b6020840193505b8284101561221f57608084880312156121d3575f5ffd5b6121db612037565b84358152602080860135908201526121f560408601611fa6565b604082015261220660608601611fa6565b60608201528252608093909301926020909101906121bc565b9695505050505050565b5f5f5f6060848603121561223b575f5ffd5b61224484611fa6565b95602085013595506040909401359392505050565b5f8151808452602084019350602083015f5b8281101561228957815186526020958601959091019060010161226b565b5093949350505050565b602081525f611f536020830184612259565b5f602082840312156122b5575f5ffd5b611f5382611fa6565b80358015158114611fbc575f5ffd5b5f5f604083850312156122de575f5ffd5b823567ffffffffffffffff8111156122f4575f5ffd5b612300858286016120b4565b92505061230f602084016122be565b90509250929050565b5f5f60408385031215612329575f5ffd5b61233283611fa6565b915061230f602084016122be565b5f5f5f5f5f60808688031215612354575f5ffd5b61235d86611fa6565b945061236b60208701611fa6565b935060408601359250606086013567ffffffffffffffff81111561238d575f5ffd5b8601601f8101881361239d575f5ffd5b803567ffffffffffffffff8111156123b3575f5ffd5b8860208284010111156123c4575f5ffd5b959894975092955050506020019190565b5f5f604083850312156123e6575f5ffd5b50508035926020909101359150565b5f5f60408385031215612406575f5ffd5b61240f83611fa6565b915061230f60208401611fa6565b5f67ffffffffffffffff821667ffffffffffffffff810361244c57634e487b7160e01b5f52601160045260245ffd5b60010192915050565b6001600160a01b0383168152604060208201525f61103c6040830184612259565b5f604082016001600160a01b0385168352604060208401528084518083526060850191506020860192505f5b818110156124f857835180518452602081015160208501526001600160a01b0360408201511660408501526001600160a01b036060820151166060850152506080830192506020840193506001810190506124a2565b50909695505050505050565b828152604060208201525f61103c6040830184612259565b5f81518060208401855e5f93019283525090919050565b5f611f53828461251c565b7f7b2265787465726e616c5f75726c223a2268747470733a2f2f6269746d61707081527f756e6b732e636f6d222c226465736372697074696f6e223a22412066756c6c7960208201527f2d6f6e636861696e2c20756c7472612d6c617267652c2068796272696420636f60408201527f6c6c656374696f6e2e222c226e616d65223a220000000000000000000000000060608201525f6125e1607383018761251c565b7f20230000000000000000000000000000000000000000000000000000000000008152612611600282018761251c565b90507f222c2261747472696275746573223a00000000000000000000000000000000008152612643600f82018661251c565b90507f2c22696d616765223a22000000000000000000000000000000000000000000008152612675600a82018561251c565b7f227d0000000000000000000000000000000000000000000000000000000000008152600201979650505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f611f53601d83018461251c565b5f602082840312156126e7575f5ffd5b5051919050565b5f602082840312156126fe575f5ffd5b815167ffffffffffffffff811115612714575f5ffd5b8201601f81018413612724575f5ffd5b80516127326120d182612091565b8082825260208201915060208360051b850101925086831115612753575f5ffd5b6020840193505b8284101561221f57835182526020938401939091019061275a565b634e487b7160e01b5f52603260045260245ffdfea26469706673582212204a6db0638fbe6041dd2bb5d16c9519ae98c7bdd1583b05e06cb758fb07803cc364736f6c634300081c0033

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

000000000000000000000000fe17b9cc26120654bccee694d2762ee32f8d355300000000000000000000000072016206489914de6e2f5c31193af7811fd6ae07

-----Decoded View---------------
Arg [0] : _traitRegistry (address): 0xFE17B9CC26120654BccEe694d2762ee32f8d3553
Arg [1] : _traitOwner (address): 0x72016206489914DE6E2f5C31193Af7811Fd6AE07

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000fe17b9cc26120654bccee694d2762ee32f8d3553
Arg [1] : 00000000000000000000000072016206489914de6e2f5c31193af7811fd6ae07


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

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