ETH Price: $1,890.76 (-11.81%)
 

Overview

ETH Balance

0.034 ETH

Eth Value

$64.29 (@ $1,890.76/ETH)

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...243863752026-02-04 22:11:2324 hrs ago1770243083IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000024480.77593654
Set Approval For...243799782026-02-04 0:43:1146 hrs ago1770165791IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000115582.15833747
Set Approval For...243735942026-02-03 3:18:232 days ago1770088703IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.00000580.18385045
Set Approval For...243729262026-02-03 1:03:112 days ago1770080591IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000036261.14906675
Set Approval For...243717572026-02-02 21:07:473 days ago1770066467IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000014730.46697062
Set Approval For...243716542026-02-02 20:47:113 days ago1770065231IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000006450.20455055
Set Approval For...243715942026-02-02 20:35:113 days ago1770064511IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000009560.30320365
Set Approval For...243714712026-02-02 20:10:233 days ago1770063023IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000035321.11926557
Set Approval For...243663892026-02-02 3:08:233 days ago1770001703IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000004310.13627068
Set Approval For...243614832026-02-01 10:41:354 days ago1769942495IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000044511.41037886
Set Approval For...243614822026-02-01 10:41:234 days ago1769942483IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000043031.36000678
Set Approval For...243541382026-01-31 10:03:595 days ago1769853839IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000111082.07743638
Set Approval For...243518592026-01-31 2:26:475 days ago1769826407IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000002430.07704673
Set Approval For...243256482026-01-27 10:43:359 days ago1769510615IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.0000310.57983618
Set Approval For...243191972026-01-26 13:06:5910 days ago1769432819IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000004050.12859996
Set Approval For...243191962026-01-26 13:06:4710 days ago1769432807IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000003940.12465582
Set Approval For...243090672026-01-25 3:14:3511 days ago1769310875IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000001350.04291686
Set Approval For...242931882026-01-22 22:03:3514 days ago1769119415IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000109932.05605736
Set Approval For...242931072026-01-22 21:47:2314 days ago1769118443IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000002550.04773879
Safe Transfer Fr...242854522026-01-21 20:07:5915 days ago1769026079IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000043250.62667833
Set Approval For...242806582026-01-21 4:05:3515 days ago1768968335IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000017470.55302136
Safe Transfer Fr...242443872026-01-16 2:46:1120 days ago1768531571IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000018760.54509777
Safe Transfer Fr...242443862026-01-16 2:45:5920 days ago1768531559IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000039320.54752185
Safe Transfer Fr...242443762026-01-16 2:43:5920 days ago1768531439IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000072710.5435445
Safe Transfer Fr...242406132026-01-15 14:09:2321 days ago1768486163IN
0xBBBBA1eE...03a9cBBBB
0 ETH0.000058740.81788208
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x53ffa071225779352025-05-28 1:15:47253 days ago1748394947
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071225621692025-05-25 20:19:11256 days ago1748204351
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071225139752025-05-19 2:16:11262 days ago1747620971
0xBBBBA1eE...03a9cBBBB
0.005 ETH
0x53ffa071224649532025-05-12 4:51:59269 days ago1747025519
0xBBBBA1eE...03a9cBBBB
0.029 ETH
0x53ffa071224495042025-05-10 0:41:47271 days ago1746837707
0xBBBBA1eE...03a9cBBBB
0.05 ETH
0x53ffa071224434782025-05-09 4:14:23272 days ago1746764063
0xBBBBA1eE...03a9cBBBB
0.0049 ETH
0x53ffa071223982832025-05-02 19:44:59279 days ago1746215099
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071223982432025-05-02 19:36:47279 days ago1746214607
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071223982342025-05-02 19:34:47279 days ago1746214487
0xBBBBA1eE...03a9cBBBB
0.0288 ETH
0x53ffa071223981412025-05-02 19:15:59279 days ago1746213359
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071223981362025-05-02 19:14:59279 days ago1746213299
0xBBBBA1eE...03a9cBBBB
0.01 ETH
0x53ffa071221913652025-04-03 22:27:47308 days ago1743719267
0xBBBBA1eE...03a9cBBBB
0.01 ETH
0x53ffa071221868692025-04-03 7:22:47308 days ago1743664967
0xBBBBA1eE...03a9cBBBB
0.0065 ETH
0x53ffa071221749922025-04-01 15:36:23310 days ago1743521783
0xBBBBA1eE...03a9cBBBB
0.008 ETH
0x53ffa071221227362025-03-25 8:30:11317 days ago1742891411
0xBBBBA1eE...03a9cBBBB
0.00058 ETH
0x53ffa071221105022025-03-23 15:30:47319 days ago1742743847
0xBBBBA1eE...03a9cBBBB
0.01 ETH
0x53ffa071220387942025-03-13 15:18:35329 days ago1741879115
0xBBBBA1eE...03a9cBBBB
0.008 ETH
0x53ffa071220352212025-03-13 3:20:11329 days ago1741836011
0xBBBBA1eE...03a9cBBBB
0.0042 ETH
0x53ffa071220095342025-03-09 13:12:59333 days ago1741525979
0xBBBBA1eE...03a9cBBBB
0.0065 ETH
0x53ffa071220037572025-03-08 17:50:47334 days ago1741456247
0xBBBBA1eE...03a9cBBBB
0.007 ETH
0x53ffa071219955642025-03-07 14:21:35335 days ago1741357295
0xBBBBA1eE...03a9cBBBB
0.0088 ETH
0x53ffa071219944572025-03-07 10:38:47335 days ago1741343927
0xBBBBA1eE...03a9cBBBB
0.01 ETH
0x53ffa071219748502025-03-04 16:54:47338 days ago1741107287
0xBBBBA1eE...03a9cBBBB
0.008 ETH
0x53ffa071219717572025-03-04 6:32:47338 days ago1741069967
0xBBBBA1eE...03a9cBBBB
0.0049 ETH
0x53ffa071219717032025-03-04 6:21:47338 days ago1741069307
0xBBBBA1eE...03a9cBBBB
0.0049 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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
// 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,
                        '"}'
                    )
                )
            )
        );
    }
}

// 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 {}
}

// 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

API
[{"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


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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