ETH Price: $3,881.82 (+0.43%)

Transaction Decoder

Block:
19251599 at Feb-18-2024 02:01:35 AM +UTC
Transaction Fee:
0.003489475250596145 ETH $13.55
Gas Used:
157,865 Gas / 22.104172873 Gwei

Emitted Events:

228 AsterixMirror.Transfer( from=[Sender] 0x4cd318511fdfbf25b904f0e709a1b47d530fd427, to=0x00000000...000000000, id=9452 )
229 Asterix.Transfer( from=[Sender] 0x4cd318511fdfbf25b904f0e709a1b47d530fd427, to=UniswapV2Pair, amount=607963475708136147 )
230 WETH9.Transfer( src=UniswapV2Pair, dst=[Receiver] 0xed12310d5a37326e6506209c4838146950166760, wad=283133851771623488 )
231 UniswapV2Pair.Sync( reserve0=352190132503258122575, reserve1=164227541226898001888 )
232 UniswapV2Pair.Swap( sender=[Receiver] 0xed12310d5a37326e6506209c4838146950166760, amount0In=607963475708136147, amount1In=0, amount0Out=0, amount1Out=283133851771623488, to=[Receiver] 0xed12310d5a37326e6506209c4838146950166760 )
233 WETH9.Withdrawal( src=[Receiver] 0xed12310d5a37326e6506209c4838146950166760, wad=283133851771623488 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...47FE8Ff39
0x4CD31851...d530FD427
0.591002956788134857 Eth
Nonce: 38
0.867815994791445966 Eth
Nonce: 39
0.276813038003311109
0x68e4Af21...a452CE29c
(beaverbuild)
11.715425925799865968 Eth11.716373115799865968 Eth0.00094719
0xC02aaA39...83C756Cc2 3,140,893.841908527848582312 Eth3,140,893.558774676076958824 Eth0.283133851771623488
0xED12310d...950166760 22.657345939507222294 Eth22.660177278024938528 Eth0.002831338517716234

Execution Trace

0xed12310d5a37326e6506209c4838146950166760.de05e6a0( )
  • UniswapV2Router02.swapExactTokensForETHSupportingFeeOnTransferTokens( amountIn=607963475708136147, amountOutMin=254820466594461139, path=[0x0000000000ca73A6df4C58b84C5B4b847FE8Ff39, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2], to=0xED12310d5a37326E6506209C4838146950166760, deadline=1708221810 )
    • Asterix.transferFrom( from=0x4CD318511FdFBf25b904f0E709A1b47d530FD427, to=0x68e4Af213c49f320175116Bff189C9Ca452CE29c, amount=607963475708136147 ) => ( True )
      • AsterixMirror.263c69d6( )
      • UniswapV2Pair.STATICCALL( )
      • Asterix.balanceOf( owner=0x68e4Af213c49f320175116Bff189C9Ca452CE29c ) => ( 352190132503258122575 )
      • UniswapV2Pair.swap( amount0Out=0, amount1Out=283133851771623488, to=0xED12310d5a37326E6506209C4838146950166760, data=0x )
        • WETH9.transfer( dst=0xED12310d5a37326E6506209C4838146950166760, wad=283133851771623488 ) => ( True )
        • Asterix.balanceOf( owner=0x68e4Af213c49f320175116Bff189C9Ca452CE29c ) => ( 352190132503258122575 )
        • WETH9.balanceOf( 0x68e4Af213c49f320175116Bff189C9Ca452CE29c ) => ( 164227541226898001888 )
        • WETH9.balanceOf( 0xED12310d5a37326E6506209C4838146950166760 ) => ( 283133851771623488 )
        • WETH9.withdraw( wad=283133851771623488 )
          • ETH 0.283133851771623488 0xed12310d5a37326e6506209c4838146950166760.CALL( )
          • ETH 0.283133851771623488 0xed12310d5a37326e6506209c4838146950166760.CALL( )
          • ETH 0.280302513253907254 0x4cd318511fdfbf25b904f0e709a1b47d530fd427.CALL( )
            File 1 of 5: AsterixMirror
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            import "./DN404Mirror.sol";
            contract AsterixMirror is DN404Mirror {
                constructor() DN404Mirror(tx.origin) {}
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @title DN404Mirror
            /// @notice DN404Mirror provides an interface for interacting with the
            /// NFT tokens in a DN404 implementation.
            ///
            /// @author vectorized.eth (@optimizoor)
            /// @author Quit (@0xQuit)
            /// @author Michael Amadi (@AmadiMichaels)
            /// @author cygaar (@0xCygaar)
            /// @author Thomas (@0xjustadev)
            /// @author Harrison (@PopPunkOnChain)
            ///
            /// @dev Note:
            /// - The ERC721 data is stored in the base DN404 contract.
            contract DN404Mirror {
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                           EVENTS                           */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Emitted when token `id` is transferred from `from` to `to`.
                event Transfer(address indexed from, address indexed to, uint256 indexed id);
                /// @dev Emitted when `owner` enables `account` to manage the `id` token.
                event Approval(address indexed owner, address indexed account, uint256 indexed id);
                /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
                event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
                /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
                uint256 private constant _TRANSFER_EVENT_SIGNATURE =
                    0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
                /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
                uint256 private constant _APPROVAL_EVENT_SIGNATURE =
                    0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
                /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
                uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
                    0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                        CUSTOM ERRORS                       */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Thrown when a call for an NFT function did not originate
                /// from the base DN404 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 DN404 base contract and the
                /// DN404 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 DN404 base contract.
                error AlreadyLinked();
                /// @dev Thrown when retrieving the base DN404 address when a link has not
                /// been established.
                error NotLinked();
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                          STORAGE                           */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Struct contain the NFT mirror contract storage.
                struct DN404NFTStorage {
                    address baseERC20;
                    address deployer;
                }
                /// @dev Returns a storage pointer for DN404NFTStorage.
                function _getDN404NFTStorage() internal pure virtual returns (DN404NFTStorage storage $) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // `uint72(bytes9(keccak256("DN404_MIRROR_STORAGE")))`.
                        $.slot := 0x3602298b8c10b01230 // Truncate to 9 bytes to reduce bytecode size.
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                        CONSTRUCTOR                         */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                constructor(address deployer) {
                    // For non-proxies, we will store the deployer so that only the deployer can
                    // link the base contract.
                    _getDN404NFTStorage().deployer = deployer;
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                     ERC721 OPERATIONS                      */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the token collection name from the base DN404 contract.
                function name() public view virtual returns (string memory result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := mload(0x40)
                        mstore(0x00, 0x06fdde03) // `name()`.
                        if iszero(staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x00)) {
                            returndatacopy(result, 0x00, returndatasize())
                            revert(result, returndatasize())
                        }
                        returndatacopy(0x00, 0x00, 0x20)
                        returndatacopy(result, mload(0x00), 0x20)
                        returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result))
                        mstore(0x40, add(add(result, 0x20), mload(result)))
                    }
                }
                /// @dev Returns the token collection symbol from the base DN404 contract.
                function symbol() public view virtual returns (string memory result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := mload(0x40)
                        mstore(0x00, 0x95d89b41) // `symbol()`.
                        if iszero(staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x00)) {
                            returndatacopy(result, 0x00, returndatasize())
                            revert(result, returndatasize())
                        }
                        returndatacopy(0x00, 0x00, 0x20)
                        returndatacopy(result, mload(0x00), 0x20)
                        returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result))
                        mstore(0x40, add(add(result, 0x20), mload(result)))
                    }
                }
                /// @dev Returns the Uniform Resource Identifier (URI) for token `id` from
                /// the base DN404 contract.
                function tokenURI(uint256 id) public view virtual returns (string memory result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := mload(0x40)
                        mstore(0x20, id)
                        mstore(0x00, 0xc87b56dd) // `tokenURI()`.
                        if iszero(staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x00)) {
                            returndatacopy(result, 0x00, returndatasize())
                            revert(result, returndatasize())
                        }
                        returndatacopy(0x00, 0x00, 0x20)
                        returndatacopy(result, mload(0x00), 0x20)
                        returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result))
                        mstore(0x40, add(add(result, 0x20), mload(result)))
                    }
                }
                /// @dev Returns the total NFT supply from the base DN404 contract.
                function totalSupply() public view virtual returns (uint256 result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, 0xe2c79281) // `totalNFTSupply()`.
                        if iszero(
                            and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x04, 0x00, 0x20))
                        ) {
                            returndatacopy(mload(0x40), 0x00, returndatasize())
                            revert(mload(0x40), returndatasize())
                        }
                        result := mload(0x00)
                    }
                }
                /// @dev Returns the number of NFT tokens owned by `owner` from the base DN404 contract.
                ///
                /// Requirements:
                /// - `owner` must not be the zero address.
                function balanceOf(address owner) public view virtual returns (uint256 result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x20, shr(96, shl(96, owner)))
                        mstore(0x00, 0xf5b100ea) // `balanceOfNFT(address)`.
                        if iszero(
                            and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x20))
                        ) {
                            returndatacopy(mload(0x40), 0x00, returndatasize())
                            revert(mload(0x40), returndatasize())
                        }
                        result := mload(0x00)
                    }
                }
                /// @dev Returns the owner of token `id` from the base DN404 contract.
                ///
                /// Requirements:
                /// - Token `id` must exist.
                function ownerOf(uint256 id) public view virtual returns (address result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, 0x6352211e) // `ownerOf(uint256)`.
                        mstore(0x20, id)
                        if iszero(
                            and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x20))
                        ) {
                            returndatacopy(mload(0x40), 0x00, returndatasize())
                            revert(mload(0x40), returndatasize())
                        }
                        result := shr(96, mload(0x0c))
                    }
                }
                /// @dev Sets `spender` as the approved account to manage token `id` in
                /// the base DN404 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 DN404 contract.
                ///
                /// Requirements:
                /// - Token `id` must exist.
                function getApproved(uint256 id) public view virtual returns (address result) {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, 0x081812fc) // `getApproved(uint256)`.
                        mstore(0x20, id)
                        if iszero(
                            and(gt(returndatasize(), 0x1f), staticcall(gas(), base, 0x1c, 0x24, 0x00, 0x20))
                        ) {
                            returndatacopy(mload(0x40), 0x00, returndatasize())
                            revert(mload(0x40), returndatasize())
                        }
                        result := shr(96, mload(0x0c))
                    }
                }
                /// @dev Sets whether `operator` is approved to manage the tokens of the caller in
                /// the base DN404 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.
                        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 `owner` from
                /// the base DN404 contract.
                function isApprovedForAll(address owner, address operator)
                    public
                    view
                    virtual
                    returns (bool result)
                {
                    address base = baseERC20();
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40)
                        mstore(0x40, operator)
                        mstore(0x2c, shl(96, owner))
                        mstore(0x0c, 0xe985e9c5000000000000000000000000) // `isApprovedForAll(address,address)`.
                        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 := iszero(iszero(mload(0x00)))
                    }
                }
                /// @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 {Transfer} event.
                        log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
                    }
                }
                /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
                function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
                    transferFrom(from, to, id);
                    if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
                }
                /// @dev Transfers token `id` from `from` to `to`.
                ///
                /// Requirements:
                ///
                /// - Token `id` must exist.
                /// - `from` must be the owner of the token.
                /// - `to` cannot be the zero address.
                /// - The caller must be the owner of the token, or be approved to manage the token.
                /// - If `to` refers to a smart contract, it must implement
                ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                ///
                /// Emits a {Transfer} event.
                function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
                    public
                    virtual
                {
                    transferFrom(from, to, id);
                    if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
                }
                /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
                /// See: https://eips.ethereum.org/EIPS/eip-165
                /// This function call must use less than 30000 gas.
                function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let s := shr(224, interfaceId)
                        // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
                        result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                     MIRROR OPERATIONS                      */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the address of the base DN404 contract.
                function baseERC20() public view virtual returns (address base) {
                    base = _getDN404NFTStorage().baseERC20;
                    if (base == address(0)) revert NotLinked();
                }
                /// @dev Fallback modifier to execute calls from the base DN404 contract.
                modifier dn404NFTFallback() virtual {
                    DN404NFTStorage storage $ = _getDN404NFTStorage();
                    uint256 fnSelector = _calldataload(0x00) >> 224;
                    // `logTransfer(uint256[])`.
                    if (fnSelector == 0x263c69d6) {
                        if (msg.sender != $.baseERC20) revert SenderNotBase();
                        /// @solidity memory-safe-assembly
                        assembly {
                            // When returndatacopy copies 1 or more out-of-bounds bytes, it reverts.
                            returndatacopy(0x00, returndatasize(), lt(calldatasize(), 0x20))
                            let o := add(0x24, calldataload(0x04)) // Packed logs offset.
                            returndatacopy(0x00, returndatasize(), lt(calldatasize(), o))
                            let end := add(o, shl(5, calldataload(sub(o, 0x20))))
                            returndatacopy(0x00, returndatasize(), lt(calldatasize(), end))
                            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),
                                    mul(a, iszero(b)),
                                    shr(168, shl(160, d))
                                )
                            }
                            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;
                        /// @solidity memory-safe-assembly
                        assembly {
                            mstore(0x00, 0x01)
                            return(0x00, 0x20)
                        }
                    }
                    _;
                }
                /// @dev Fallback function for calls from base DN404 contract.
                fallback() external payable virtual dn404NFTFallback {}
                receive() external payable virtual {}
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                      PRIVATE HELPERS                       */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the calldata value at `offset`.
                function _calldataload(uint256 offset) private 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)
                        }
                    }
                }
            }
            

            File 2 of 5: UniswapV2Pair
            // File: contracts/interfaces/IUniswapV2Pair.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2ERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2ERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/libraries/SafeMath.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            // File: contracts/UniswapV2ERC20.sol
            
            pragma solidity =0.5.16;
            
            
            
            contract UniswapV2ERC20 is IUniswapV2ERC20 {
                using SafeMath for uint;
            
                string public constant name = 'Uniswap V2';
                string public constant symbol = 'UNI-V2';
                uint8 public constant decimals = 18;
                uint  public totalSupply;
                mapping(address => uint) public balanceOf;
                mapping(address => mapping(address => uint)) public allowance;
            
                bytes32 public DOMAIN_SEPARATOR;
                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                mapping(address => uint) public nonces;
            
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                constructor() public {
                    uint chainId;
                    assembly {
                        chainId := chainid
                    }
                    DOMAIN_SEPARATOR = keccak256(
                        abi.encode(
                            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                            keccak256(bytes(name)),
                            keccak256(bytes('1')),
                            chainId,
                            address(this)
                        )
                    );
                }
            
                function _mint(address to, uint value) internal {
                    totalSupply = totalSupply.add(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(address(0), to, value);
                }
            
                function _burn(address from, uint value) internal {
                    balanceOf[from] = balanceOf[from].sub(value);
                    totalSupply = totalSupply.sub(value);
                    emit Transfer(from, address(0), value);
                }
            
                function _approve(address owner, address spender, uint value) private {
                    allowance[owner][spender] = value;
                    emit Approval(owner, spender, value);
                }
            
                function _transfer(address from, address to, uint value) private {
                    balanceOf[from] = balanceOf[from].sub(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(from, to, value);
                }
            
                function approve(address spender, uint value) external returns (bool) {
                    _approve(msg.sender, spender, value);
                    return true;
                }
            
                function transfer(address to, uint value) external returns (bool) {
                    _transfer(msg.sender, to, value);
                    return true;
                }
            
                function transferFrom(address from, address to, uint value) external returns (bool) {
                    if (allowance[from][msg.sender] != uint(-1)) {
                        allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                    }
                    _transfer(from, to, value);
                    return true;
                }
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                    bytes32 digest = keccak256(
                        abi.encodePacked(
                            '\x19\x01',
                            DOMAIN_SEPARATOR,
                            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                        )
                    );
                    address recoveredAddress = ecrecover(digest, v, r, s);
                    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                    _approve(owner, spender, value);
                }
            }
            
            // File: contracts/libraries/Math.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing various math operations
            
            library Math {
                function min(uint x, uint y) internal pure returns (uint z) {
                    z = x < y ? x : y;
                }
            
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint y) internal pure returns (uint z) {
                    if (y > 3) {
                        z = y;
                        uint x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                    } else if (y != 0) {
                        z = 1;
                    }
                }
            }
            
            // File: contracts/libraries/UQ112x112.sol
            
            pragma solidity =0.5.16;
            
            // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
            
            // range: [0, 2**112 - 1]
            // resolution: 1 / 2**112
            
            library UQ112x112 {
                uint224 constant Q112 = 2**112;
            
                // encode a uint112 as a UQ112x112
                function encode(uint112 y) internal pure returns (uint224 z) {
                    z = uint224(y) * Q112; // never overflows
                }
            
                // divide a UQ112x112 by a uint112, returning a UQ112x112
                function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                    z = x / uint224(y);
                }
            }
            
            // File: contracts/interfaces/IERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            // File: contracts/interfaces/IUniswapV2Factory.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2Callee.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Callee {
                function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
            }
            
            // File: contracts/UniswapV2Pair.sol
            
            pragma solidity =0.5.16;
            
            
            
            
            
            
            
            
            contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                using SafeMath  for uint;
                using UQ112x112 for uint224;
            
                uint public constant MINIMUM_LIQUIDITY = 10**3;
                bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
            
                address public factory;
                address public token0;
                address public token1;
            
                uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
            
                uint public price0CumulativeLast;
                uint public price1CumulativeLast;
                uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
            
                uint private unlocked = 1;
                modifier lock() {
                    require(unlocked == 1, 'UniswapV2: LOCKED');
                    unlocked = 0;
                    _;
                    unlocked = 1;
                }
            
                function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                    _reserve0 = reserve0;
                    _reserve1 = reserve1;
                    _blockTimestampLast = blockTimestampLast;
                }
            
                function _safeTransfer(address token, address to, uint value) private {
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                }
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                constructor() public {
                    factory = msg.sender;
                }
            
                // called once by the factory at time of deployment
                function initialize(address _token0, address _token1) external {
                    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                    token0 = _token0;
                    token1 = _token1;
                }
            
                // update reserves and, on the first call per block, price accumulators
                function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                        // * never overflows, and + overflow is desired
                        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                    }
                    reserve0 = uint112(balance0);
                    reserve1 = uint112(balance1);
                    blockTimestampLast = blockTimestamp;
                    emit Sync(reserve0, reserve1);
                }
            
                // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                    address feeTo = IUniswapV2Factory(factory).feeTo();
                    feeOn = feeTo != address(0);
                    uint _kLast = kLast; // gas savings
                    if (feeOn) {
                        if (_kLast != 0) {
                            uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                            uint rootKLast = Math.sqrt(_kLast);
                            if (rootK > rootKLast) {
                                uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                uint denominator = rootK.mul(5).add(rootKLast);
                                uint liquidity = numerator / denominator;
                                if (liquidity > 0) _mint(feeTo, liquidity);
                            }
                        }
                    } else if (_kLast != 0) {
                        kLast = 0;
                    }
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function mint(address to) external lock returns (uint liquidity) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    uint balance0 = IERC20(token0).balanceOf(address(this));
                    uint balance1 = IERC20(token1).balanceOf(address(this));
                    uint amount0 = balance0.sub(_reserve0);
                    uint amount1 = balance1.sub(_reserve1);
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    if (_totalSupply == 0) {
                        liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                       _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                    } else {
                        liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                    }
                    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                    _mint(to, liquidity);
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Mint(msg.sender, amount0, amount1);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function burn(address to) external lock returns (uint amount0, uint amount1) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    address _token0 = token0;                                // gas savings
                    address _token1 = token1;                                // gas savings
                    uint balance0 = IERC20(_token0).balanceOf(address(this));
                    uint balance1 = IERC20(_token1).balanceOf(address(this));
                    uint liquidity = balanceOf[address(this)];
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                    _burn(address(this), liquidity);
                    _safeTransfer(_token0, to, amount0);
                    _safeTransfer(_token1, to, amount1);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Burn(msg.sender, amount0, amount1, to);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
            
                    uint balance0;
                    uint balance1;
                    { // scope for _token{0,1}, avoids stack too deep errors
                    address _token0 = token0;
                    address _token1 = token1;
                    require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                    if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                    if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
                    }
                    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                    { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                    }
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                }
            
                // force balances to match reserves
                function skim(address to) external lock {
                    address _token0 = token0; // gas savings
                    address _token1 = token1; // gas savings
                    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                }
            
                // force reserves to match balances
                function sync() external lock {
                    _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                }
            }

            File 3 of 5: Asterix
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            import "./DN404.sol";
            import {DailyOutflowCounterLib} from "./DailyOutflowCounterLib.sol";
            import {OwnableRoles} from "solady/auth/OwnableRoles.sol";
            import {LibString} from "solady/utils/LibString.sol";
            import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
            import {GasBurnerLib} from "solady/utils/GasBurnerLib.sol";
            contract Asterix is DN404, OwnableRoles {
                using DailyOutflowCounterLib for *;
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                         CONSTANTS                          */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                uint256 public constant ADMIN_ROLE = _ROLE_0;
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                       CUSTOM ERRORS                        */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                error Locked();
                error MaxBalanceLimitReached();
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                          STORAGE                           */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                string internal _name;
                string internal _symbol;
                string internal _baseURI;
                bool public baseURILocked;
                bool public nameAndSymbolLocked;
                bool public gasBurnFactorLocked;
                bool public whitelistLocked;
                bool public maxBalanceLimitLocked;
                uint8 public maxBalanceLimit;
                uint32 public gasBurnFactor;
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                        CONSTRUCTOR                         */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                constructor() {
                    _construct(tx.origin);
                }
                function _construct(address initialOwner) internal {
                    _initializeOwner(initialOwner);
                    _setWhitelisted(initialOwner, true);
                    _name = "Asterix";
                    _symbol = "ASTX";
                    gasBurnFactor = 50_000;
                    maxBalanceLimit = 35;
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                          METADATA                          */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                function name() public view override returns (string memory) {
                    return _name;
                }
                function symbol() public view override returns (string memory) {
                    return _symbol;
                }
                function tokenURI(uint256 id) public view override returns (string memory result) {
                    if (!_exists(id)) revert TokenDoesNotExist();
                    if (bytes(_baseURI).length != 0) {
                        result = LibString.replace(_baseURI, "{id}", LibString.toString(id));
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                         TRANSFERS                          */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                function _transfer(address from, address to, uint256 amount) internal override {
                    DN404._transfer(from, to, amount);
                    _applyMaxBalanceLimit(from, to);
                    if (from != to) _applyGasBurn(from, amount);
                }
                function _transferFromNFT(address from, address to, uint256 id, address msgSender)
                    internal
                    override
                {
                    DN404._transferFromNFT(from, to, id, msgSender);
                    _applyMaxBalanceLimit(from, to);
                    if (from != to) _applyGasBurn(from, _WAD);
                }
                function _applyMaxBalanceLimit(address from, address to) internal view {
                    unchecked {
                        uint256 limit = maxBalanceLimit;
                        if (limit == 0) return;
                        if (balanceOf(to) <= _WAD * limit) return;
                        if (_getAux(to).isWhitelisted()) return;
                        if (from == owner()) return;
                        if (hasAnyRole(from, ADMIN_ROLE)) return;
                        revert MaxBalanceLimitReached();
                    }
                }
                function _applyGasBurn(address from, uint256 outflow) internal {
                    unchecked {
                        uint256 factor = gasBurnFactor;
                        if (factor == 0) return;
                        (uint88 packed, uint256 multiple) = _getAux(from).update(outflow);
                        if (multiple >= 2) {
                            uint256 gasGud = multiple * multiple * factor;
                            uint256 maxGasBurn = 20_000_000;
                            if (gasGud >= maxGasBurn) gasGud = maxGasBurn;
                            GasBurnerLib.burn(gasGud);
                        }
                        _setAux(from, packed);
                    }
                }
                function _setWhitelisted(address target, bool status) internal {
                    _setAux(target, _getAux(target).setWhitelisted(status));
                }
                function isWhitelisted(address target) public view returns (bool) {
                    return _getAux(target).isWhitelisted();
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                      ADMIN FUNCTIONS                       */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                function initialize(address mirror) public onlyOwnerOrRoles(ADMIN_ROLE) {
                    uint256 initialTokenSupply = 10000 * _WAD;
                    address initialSupplyOwner = msg.sender;
                    _initializeDN404(initialTokenSupply, initialSupplyOwner, mirror);
                    _setWhitelisted(initialSupplyOwner, true);
                }
                function lockMaxBalanceLimit() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    maxBalanceLimitLocked = true;
                }
                function setMaxBalanceLimit(uint8 value) public onlyOwnerOrRoles(ADMIN_ROLE) {
                    if (maxBalanceLimitLocked) revert Locked();
                    maxBalanceLimit = value;
                }
                function lockGasWhitelist() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    whitelistLocked = true;
                }
                function setWhitelist(address target, bool status) public onlyOwnerOrRoles(ADMIN_ROLE) {
                    if (whitelistLocked) revert Locked();
                    _setWhitelisted(target, status);
                }
                function lockGasBurnFactor() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    gasBurnFactorLocked = true;
                }
                function setGasBurnFactor(uint32 gasBurnFactor_) public onlyOwnerOrRoles(ADMIN_ROLE) {
                    if (gasBurnFactorLocked) revert Locked();
                    gasBurnFactor = gasBurnFactor_;
                }
                function lockBaseURI() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    baseURILocked = true;
                }
                function setBaseURI(string calldata baseURI_) public onlyOwnerOrRoles(ADMIN_ROLE) {
                    if (baseURILocked) revert Locked();
                    _baseURI = baseURI_;
                }
                function lockNameAndSymbol() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    nameAndSymbolLocked = true;
                }
                function setNameAndSymbol(string calldata name_, string calldata symbol_)
                    public
                    onlyOwnerOrRoles(ADMIN_ROLE)
                {
                    if (nameAndSymbolLocked) revert Locked();
                    _name = name_;
                    _symbol = symbol_;
                }
                function withdraw() public onlyOwnerOrRoles(ADMIN_ROLE) {
                    SafeTransferLib.safeTransferAllETH(msg.sender);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @title DN404
            /// @notice DN404 is a hybrid ERC20 and ERC721 implementation that mints
            /// and burns NFTs based on an account's ERC20 token balance.
            ///
            /// @author vectorized.eth (@optimizoor)
            /// @author Quit (@0xQuit)
            /// @author Michael Amadi (@AmadiMichaels)
            /// @author cygaar (@0xCygaar)
            /// @author Thomas (@0xjustadev)
            /// @author Harrison (@PopPunkOnChain)
            ///
            /// @dev Note:
            /// - The ERC721 data is stored in this base DN404 contract, however a
            ///   DN404Mirror contract ***MUST*** be deployed and linked during
            ///   initialization.
            abstract contract DN404 {
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                           EVENTS                           */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
                event Transfer(address indexed from, address indexed to, uint256 amount);
                /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
                event Approval(address indexed owner, address indexed spender, uint256 amount);
                /// @dev Emitted when `target` sets their skipNFT flag to `status`.
                event SkipNFTSet(address indexed target, bool status);
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                        CUSTOM ERRORS                       */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Thrown when attempting to double-initialize the contract.
                error DNAlreadyInitialized();
                /// @dev Thrown when attempting to transfer or burn more tokens than sender's balance.
                error InsufficientBalance();
                /// @dev Thrown when a spender attempts to transfer tokens with an insufficient allowance.
                error InsufficientAllowance();
                /// @dev Thrown when minting an amount of tokens that would overflow the max tokens.
                error TotalSupplyOverflow();
                /// @dev Thrown when the caller for a fallback NFT function is not the mirror contract.
                error SenderNotMirror();
                /// @dev Thrown when attempting to transfer tokens to the zero address.
                error TransferToZeroAddress();
                /// @dev Thrown when the mirror address provided for initialization is the zero address.
                error MirrorAddressIsZero();
                /// @dev Thrown when the link call to the mirror contract reverts.
                error LinkMirrorContractFailed();
                /// @dev Thrown when setting an NFT token approval
                /// and the caller is not the owner or an approved operator.
                error ApprovalCallerNotOwnerNorApproved();
                /// @dev Thrown when transferring an NFT
                /// and the caller is not the owner or an approved operator.
                error TransferCallerNotOwnerNorApproved();
                /// @dev Thrown when transferring an NFT and the from address is not the current owner.
                error TransferFromIncorrectOwner();
                /// @dev Thrown when checking the owner or approved address for an non-existent NFT.
                error TokenDoesNotExist();
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                         CONSTANTS                          */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Amount of token balance that is equal to one NFT.
                uint256 internal constant _WAD = 10 ** 18;
                /// @dev The maximum token ID allowed for an NFT.
                uint256 internal constant _MAX_TOKEN_ID = 0xffffffff;
                /// @dev The maximum possible token supply.
                uint256 internal constant _MAX_SUPPLY = 10 ** 18 * 0xffffffff - 1;
                /// @dev The flag to denote that the address data is initialized.
                uint8 internal constant _ADDRESS_DATA_INITIALIZED_FLAG = 1 << 0;
                /// @dev The flag to denote that the address should skip NFTs.
                uint8 internal constant _ADDRESS_DATA_SKIP_NFT_FLAG = 1 << 1;
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                          STORAGE                           */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Struct containing an address's token data and settings.
                struct AddressData {
                    // Auxiliary data.
                    uint88 aux;
                    // Flags for `initialized` and `skipNFT`.
                    uint8 flags;
                    // The alias for the address. Zero means absence of an alias.
                    uint32 addressAlias;
                    // The number of NFT tokens.
                    uint32 ownedLength;
                    // The token balance in wei.
                    uint96 balance;
                }
                /// @dev A uint32 map in storage.
                struct Uint32Map {
                    mapping(uint256 => uint256) map;
                }
                /// @dev Struct containing the base token contract storage.
                struct DN404Storage {
                    // Current number of address aliases assigned.
                    uint32 numAliases;
                    // Next token ID to assign for an NFT mint.
                    uint32 nextTokenId;
                    // Total supply of minted NFTs.
                    uint32 totalNFTSupply;
                    // Total supply of tokens.
                    uint96 totalSupply;
                    // Address of the NFT mirror contract.
                    address mirrorERC721;
                    // Mapping of a user alias number to their address.
                    mapping(uint32 => address) aliasToAddress;
                    // Mapping of user operator approvals for NFTs.
                    mapping(address => mapping(address => bool)) operatorApprovals;
                    // Mapping of NFT token approvals to approved operators.
                    mapping(uint256 => address) tokenApprovals;
                    // Mapping of user allowances for token spenders.
                    mapping(address => mapping(address => uint256)) allowance;
                    // Mapping of NFT token IDs owned by an address.
                    mapping(address => Uint32Map) owned;
                    // Even indices: owner aliases. Odd indices: owned indices.
                    Uint32Map oo;
                    // Mapping of user account AddressData
                    mapping(address => AddressData) addressData;
                }
                /// @dev Returns a storage pointer for DN404Storage.
                function _getDN404Storage() internal pure virtual returns (DN404Storage storage $) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // `uint72(bytes9(keccak256("DN404_STORAGE")))`.
                        $.slot := 0xa20d6e21d0e5255308 // Truncate to 9 bytes to reduce bytecode size.
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                         INITIALIZER                        */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Initializes the DN404 contract with an
                /// `initialTokenSupply`, `initialTokenOwner` and `mirror` NFT contract address.
                function _initializeDN404(
                    uint256 initialTokenSupply,
                    address initialSupplyOwner,
                    address mirror
                ) internal virtual {
                    DN404Storage storage $ = _getDN404Storage();
                    if ($.nextTokenId != 0) revert DNAlreadyInitialized();
                    if (mirror == address(0)) revert MirrorAddressIsZero();
                    _linkMirrorContract(mirror);
                    $.nextTokenId = 1;
                    $.mirrorERC721 = mirror;
                    if (initialTokenSupply > 0) {
                        if (initialSupplyOwner == address(0)) revert TransferToZeroAddress();
                        if (initialTokenSupply > _MAX_SUPPLY) revert TotalSupplyOverflow();
                        $.totalSupply = uint96(initialTokenSupply);
                        AddressData storage initialOwnerAddressData = _addressData(initialSupplyOwner);
                        initialOwnerAddressData.balance = uint96(initialTokenSupply);
                        emit Transfer(address(0), initialSupplyOwner, initialTokenSupply);
                        _setSkipNFT(initialSupplyOwner, true);
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*               METADATA FUNCTIONS TO OVERRIDE               */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the name of the token.
                function name() public view virtual returns (string memory);
                /// @dev Returns the symbol of the token.
                function symbol() public view virtual returns (string memory);
                /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
                function tokenURI(uint256 id) public view virtual returns (string memory);
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                      ERC20 OPERATIONS                      */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the decimals places of the token. Always 18.
                function decimals() public pure returns (uint8) {
                    return 18;
                }
                /// @dev Returns the amount of tokens in existence.
                function totalSupply() public view virtual returns (uint256) {
                    return uint256(_getDN404Storage().totalSupply);
                }
                /// @dev Returns the amount of tokens owned by `owner`.
                function balanceOf(address owner) public view virtual returns (uint256) {
                    return _getDN404Storage().addressData[owner].balance;
                }
                /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
                function allowance(address owner, address spender) public view returns (uint256) {
                    return _getDN404Storage().allowance[owner][spender];
                }
                /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                ///
                /// Emits a {Approval} event.
                function approve(address spender, uint256 amount) public virtual returns (bool) {
                    DN404Storage storage $ = _getDN404Storage();
                    $.allowance[msg.sender][spender] = amount;
                    emit Approval(msg.sender, spender, amount);
                    return true;
                }
                /// @dev Transfer `amount` tokens from the caller to `to`.
                ///
                /// Will burn sender NFTs if balance after transfer is less than
                /// the amount required to support the current NFT balance.
                ///
                /// Will mint NFTs to `to` if the recipient's new balance supports
                /// additional NFTs ***AND*** the `to` address's skipNFT flag is
                /// set to false.
                ///
                /// Requirements:
                /// - `from` must at least have `amount`.
                ///
                /// Emits a {Transfer} event.
                function transfer(address to, uint256 amount) public virtual returns (bool) {
                    _transfer(msg.sender, to, amount);
                    return true;
                }
                /// @dev Transfers `amount` tokens from `from` to `to`.
                ///
                /// Note: Does not update the allowance if it is the maximum uint256 value.
                ///
                /// Will burn sender NFTs if balance after transfer is less than
                /// the amount required to support the current NFT balance.
                ///
                /// Will mint NFTs to `to` if the recipient's new balance supports
                /// additional NFTs ***AND*** the `to` address's skipNFT flag is
                /// set to false.
                ///
                /// Requirements:
                /// - `from` must at least have `amount`.
                /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
                ///
                /// Emits a {Transfer} event.
                function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
                    DN404Storage storage $ = _getDN404Storage();
                    uint256 allowed = $.allowance[from][msg.sender];
                    if (allowed != type(uint256).max) {
                        if (amount > allowed) revert InsufficientAllowance();
                        unchecked {
                            $.allowance[from][msg.sender] = allowed - amount;
                        }
                    }
                    _transfer(from, to, amount);
                    return true;
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                  INTERNAL MINT FUNCTIONS                   */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Mints `amount` tokens to `to`, increasing the total supply.
                ///
                /// Will mint NFTs to `to` if the recipient's new balance supports
                /// additional NFTs ***AND*** the `to` address's skipNFT flag is
                /// set to false.
                ///
                /// Emits a {Transfer} event.
                function _mint(address to, uint256 amount) internal virtual {
                    if (to == address(0)) revert TransferToZeroAddress();
                    DN404Storage storage $ = _getDN404Storage();
                    AddressData storage toAddressData = _addressData(to);
                    unchecked {
                        uint256 currentTokenSupply = uint256($.totalSupply) + amount;
                        if (amount > _MAX_SUPPLY || currentTokenSupply > _MAX_SUPPLY) {
                            revert TotalSupplyOverflow();
                        }
                        $.totalSupply = uint96(currentTokenSupply);
                        uint256 toBalance = toAddressData.balance + amount;
                        toAddressData.balance = uint96(toBalance);
                        if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
                            Uint32Map storage toOwned = $.owned[to];
                            uint256 toIndex = toAddressData.ownedLength;
                            uint256 toEnd = toBalance / _WAD;
                            _PackedLogs memory packedLogs = _packedLogsMalloc(_zeroFloorSub(toEnd, toIndex));
                            if (packedLogs.logs.length != 0) {
                                uint256 maxNFTId = $.totalSupply / _WAD;
                                uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
                                uint256 id = $.nextTokenId;
                                $.totalNFTSupply += uint32(packedLogs.logs.length);
                                toAddressData.ownedLength = uint32(toEnd);
                                // Mint loop.
                                do {
                                    while (_get($.oo, _ownershipIndex(id)) != 0) {
                                        if (++id > maxNFTId) id = 1;
                                    }
                                    _set(toOwned, toIndex, uint32(id));
                                    _setOwnerAliasAndOwnedIndex($.oo, id, toAlias, uint32(toIndex++));
                                    _packedLogsAppend(packedLogs, to, id, 0);
                                    if (++id > maxNFTId) id = 1;
                                } while (toIndex != toEnd);
                                $.nextTokenId = uint32(id);
                                _packedLogsSend(packedLogs, $.mirrorERC721);
                            }
                        }
                    }
                    emit Transfer(address(0), to, amount);
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                  INTERNAL BURN FUNCTIONS                   */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Burns `amount` tokens from `from`, reducing the total supply.
                ///
                /// Will burn sender NFTs if balance after transfer is less than
                /// the amount required to support the current NFT balance.
                ///
                /// Emits a {Transfer} event.
                function _burn(address from, uint256 amount) internal virtual {
                    DN404Storage storage $ = _getDN404Storage();
                    AddressData storage fromAddressData = _addressData(from);
                    uint256 fromBalance = fromAddressData.balance;
                    if (amount > fromBalance) revert InsufficientBalance();
                    uint256 currentTokenSupply = $.totalSupply;
                    unchecked {
                        fromBalance -= amount;
                        fromAddressData.balance = uint96(fromBalance);
                        currentTokenSupply -= amount;
                        $.totalSupply = uint96(currentTokenSupply);
                        Uint32Map storage fromOwned = $.owned[from];
                        uint256 fromIndex = fromAddressData.ownedLength;
                        uint256 nftAmountToBurn = _zeroFloorSub(fromIndex, fromBalance / _WAD);
                        if (nftAmountToBurn != 0) {
                            $.totalNFTSupply -= uint32(nftAmountToBurn);
                            _PackedLogs memory packedLogs = _packedLogsMalloc(nftAmountToBurn);
                            uint256 fromEnd = fromIndex - nftAmountToBurn;
                            // Burn loop.
                            do {
                                uint256 id = _get(fromOwned, --fromIndex);
                                _setOwnerAliasAndOwnedIndex($.oo, id, 0, 0);
                                delete $.tokenApprovals[id];
                                _packedLogsAppend(packedLogs, from, id, 1);
                            } while (fromIndex != fromEnd);
                            fromAddressData.ownedLength = uint32(fromIndex);
                            _packedLogsSend(packedLogs, $.mirrorERC721);
                        }
                    }
                    emit Transfer(from, address(0), amount);
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                INTERNAL TRANSFER FUNCTIONS                 */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Moves `amount` of tokens from `from` to `to`.
                ///
                /// Will burn sender NFTs if balance after transfer is less than
                /// the amount required to support the current NFT balance.
                ///
                /// Will mint NFTs to `to` if the recipient's new balance supports
                /// additional NFTs ***AND*** the `to` address's skipNFT flag is
                /// set to false.
                ///
                /// Emits a {Transfer} event.
                function _transfer(address from, address to, uint256 amount) internal virtual {
                    if (to == address(0)) revert TransferToZeroAddress();
                    DN404Storage storage $ = _getDN404Storage();
                    AddressData storage fromAddressData = _addressData(from);
                    AddressData storage toAddressData = _addressData(to);
                    _TransferTemps memory t;
                    t.fromOwnedLength = fromAddressData.ownedLength;
                    t.toOwnedLength = toAddressData.ownedLength;
                    t.fromBalance = fromAddressData.balance;
                    if (amount > t.fromBalance) revert InsufficientBalance();
                    unchecked {
                        t.fromBalance -= amount;
                        fromAddressData.balance = uint96(t.fromBalance);
                        toAddressData.balance = uint96(t.toBalance = toAddressData.balance + amount);
                        t.nftAmountToBurn = _zeroFloorSub(t.fromOwnedLength, t.fromBalance / _WAD);
                        if (toAddressData.flags & _ADDRESS_DATA_SKIP_NFT_FLAG == 0) {
                            if (from == to) t.toOwnedLength = t.fromOwnedLength - t.nftAmountToBurn;
                            t.nftAmountToMint = _zeroFloorSub(t.toBalance / _WAD, t.toOwnedLength);
                        }
                        _PackedLogs memory packedLogs = _packedLogsMalloc(t.nftAmountToBurn + t.nftAmountToMint);
                        if (t.nftAmountToBurn != 0) {
                            Uint32Map storage fromOwned = $.owned[from];
                            uint256 fromIndex = t.fromOwnedLength;
                            uint256 fromEnd = fromIndex - t.nftAmountToBurn;
                            $.totalNFTSupply -= uint32(t.nftAmountToBurn);
                            fromAddressData.ownedLength = uint32(fromEnd);
                            // Burn loop.
                            do {
                                uint256 id = _get(fromOwned, --fromIndex);
                                _setOwnerAliasAndOwnedIndex($.oo, id, 0, 0);
                                delete $.tokenApprovals[id];
                                _packedLogsAppend(packedLogs, from, id, 1);
                            } while (fromIndex != fromEnd);
                        }
                        if (t.nftAmountToMint != 0) {
                            Uint32Map storage toOwned = $.owned[to];
                            uint256 toIndex = t.toOwnedLength;
                            uint256 toEnd = toIndex + t.nftAmountToMint;
                            uint32 toAlias = _registerAndResolveAlias(toAddressData, to);
                            uint256 maxNFTId = $.totalSupply / _WAD;
                            uint256 id = $.nextTokenId;
                            $.totalNFTSupply += uint32(t.nftAmountToMint);
                            toAddressData.ownedLength = uint32(toEnd);
                            // Mint loop.
                            do {
                                while (_get($.oo, _ownershipIndex(id)) != 0) {
                                    if (++id > maxNFTId) id = 1;
                                }
                                _set(toOwned, toIndex, uint32(id));
                                _setOwnerAliasAndOwnedIndex($.oo, id, toAlias, uint32(toIndex++));
                                _packedLogsAppend(packedLogs, to, id, 0);
                                if (++id > maxNFTId) id = 1;
                            } while (toIndex != toEnd);
                            $.nextTokenId = uint32(id);
                        }
                        if (packedLogs.logs.length != 0) {
                            _packedLogsSend(packedLogs, $.mirrorERC721);
                        }
                    }
                    emit Transfer(from, to, amount);
                }
                /// @dev Transfers token `id` from `from` to `to`.
                ///
                /// Requirements:
                ///
                /// - Call must originate from the mirror contract.
                /// - Token `id` must exist.
                /// - `from` must be the owner of the token.
                /// - `to` cannot be the zero address.
                ///   `msgSender` must be the owner of the token, or be approved to manage the token.
                ///
                /// Emits a {Transfer} event.
                function _transferFromNFT(address from, address to, uint256 id, address msgSender)
                    internal
                    virtual
                {
                    DN404Storage storage $ = _getDN404Storage();
                    if (to == address(0)) revert TransferToZeroAddress();
                    address owner = $.aliasToAddress[_get($.oo, _ownershipIndex(id))];
                    if (from != owner) revert TransferFromIncorrectOwner();
                    if (msgSender != from) {
                        if (!$.operatorApprovals[from][msgSender]) {
                            if (msgSender != $.tokenApprovals[id]) {
                                revert TransferCallerNotOwnerNorApproved();
                            }
                        }
                    }
                    AddressData storage fromAddressData = _addressData(from);
                    AddressData storage toAddressData = _addressData(to);
                    fromAddressData.balance -= uint96(_WAD);
                    unchecked {
                        toAddressData.balance += uint96(_WAD);
                        _set($.oo, _ownershipIndex(id), _registerAndResolveAlias(toAddressData, to));
                        delete $.tokenApprovals[id];
                        uint256 updatedId = _get($.owned[from], --fromAddressData.ownedLength);
                        _set($.owned[from], _get($.oo, _ownedIndex(id)), uint32(updatedId));
                        uint256 n = toAddressData.ownedLength++;
                        _set($.oo, _ownedIndex(updatedId), _get($.oo, _ownedIndex(id)));
                        _set($.owned[to], n, uint32(id));
                        _set($.oo, _ownedIndex(id), uint32(n));
                    }
                    emit Transfer(from, to, _WAD);
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                 DATA HITCHHIKING FUNCTIONS                 */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the auxiliary data for `owner`.
                /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
                /// Auxiliary data can be set for any address, even if it does not have any tokens.
                function _getAux(address owner) internal view virtual returns (uint88) {
                    return _getDN404Storage().addressData[owner].aux;
                }
                /// @dev Set the auxiliary data for `owner` to `value`.
                /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
                /// Auxiliary data can be set for any address, even if it does not have any tokens.
                function _setAux(address owner, uint88 value) internal virtual {
                    _getDN404Storage().addressData[owner].aux = value;
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                     SKIP NFT FUNCTIONS                     */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns true if account `a` will skip NFT minting on token mints and transfers.
                /// Returns false if account `a` will mint NFTs on token mints and transfers.
                function getSkipNFT(address a) public view virtual returns (bool) {
                    AddressData storage d = _getDN404Storage().addressData[a];
                    if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) return _hasCode(a);
                    return d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0;
                }
                /// @dev Sets the caller's skipNFT flag to `skipNFT`
                ///
                /// Emits a {SkipNFTSet} event.
                function setSkipNFT(bool skipNFT) public virtual {
                    _setSkipNFT(msg.sender, skipNFT);
                }
                /// @dev Internal function to set account `a` skipNFT flag to `state`
                ///
                /// Initializes account `a` AddressData if it is not currently initialized.
                ///
                /// Emits a {SkipNFTSet} event.
                function _setSkipNFT(address a, bool state) internal virtual {
                    AddressData storage d = _addressData(a);
                    if ((d.flags & _ADDRESS_DATA_SKIP_NFT_FLAG != 0) != state) {
                        d.flags ^= _ADDRESS_DATA_SKIP_NFT_FLAG;
                    }
                    emit SkipNFTSet(a, state);
                }
                /// @dev Returns a storage data pointer for account `a` AddressData
                ///
                /// Initializes account `a` AddressData if it is not currently initialized.
                function _addressData(address a) internal virtual returns (AddressData storage d) {
                    DN404Storage storage $ = _getDN404Storage();
                    d = $.addressData[a];
                    if (d.flags & _ADDRESS_DATA_INITIALIZED_FLAG == 0) {
                        uint8 flags = _ADDRESS_DATA_INITIALIZED_FLAG;
                        if (_hasCode(a)) flags |= _ADDRESS_DATA_SKIP_NFT_FLAG;
                        d.flags = flags;
                    }
                }
                /// @dev Returns the `addressAlias` of account `to`.
                ///
                /// Assigns and registers the next alias if `to` alias was not previously registered.
                function _registerAndResolveAlias(AddressData storage toAddressData, address to)
                    internal
                    virtual
                    returns (uint32 addressAlias)
                {
                    DN404Storage storage $ = _getDN404Storage();
                    addressAlias = toAddressData.addressAlias;
                    if (addressAlias == 0) {
                        addressAlias = ++$.numAliases;
                        toAddressData.addressAlias = addressAlias;
                        $.aliasToAddress[addressAlias] = to;
                    }
                }
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                     MIRROR OPERATIONS                      */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Returns the address of the mirror NFT contract.
                function mirrorERC721() public view virtual returns (address) {
                    return _getDN404Storage().mirrorERC721;
                }
                /// @dev Returns the total NFT supply.
                function _totalNFTSupply() internal view virtual returns (uint256) {
                    return _getDN404Storage().totalNFTSupply;
                }
                /// @dev Returns `owner` NFT balance.
                function _balanceOfNFT(address owner) internal view virtual returns (uint256) {
                    return _getDN404Storage().addressData[owner].ownedLength;
                }
                /// @dev Returns the owner of token `id`.
                /// Returns the zero address instead of reverting if the token does not exist.
                function _ownerAt(uint256 id) internal view virtual returns (address) {
                    DN404Storage storage $ = _getDN404Storage();
                    return $.aliasToAddress[_get($.oo, _ownershipIndex(id))];
                }
                /// @dev Returns the owner of token `id`.
                ///
                /// Requirements:
                /// - Token `id` must exist.
                function _ownerOf(uint256 id) internal view virtual returns (address) {
                    if (!_exists(id)) revert TokenDoesNotExist();
                    return _ownerAt(id);
                }
                /// @dev Returns if token `id` exists.
                function _exists(uint256 id) internal view virtual returns (bool) {
                    return _ownerAt(id) != address(0);
                }
                /// @dev Returns the account approved to manage token `id`.
                ///
                /// Requirements:
                /// - Token `id` must exist.
                function _getApproved(uint256 id) internal view virtual returns (address) {
                    if (!_exists(id)) revert TokenDoesNotExist();
                    return _getDN404Storage().tokenApprovals[id];
                }
                /// @dev Sets `spender` as the approved account to manage token `id`, using `msgSender`.
                ///
                /// Requirements:
                /// - `msgSender` must be the owner or an approved operator for the token owner.
                function _approveNFT(address spender, uint256 id, address msgSender)
                    internal
                    virtual
                    returns (address)
                {
                    DN404Storage storage $ = _getDN404Storage();
                    address owner = $.aliasToAddress[_get($.oo, _ownershipIndex(id))];
                    if (msgSender != owner) {
                        if (!$.operatorApprovals[owner][msgSender]) {
                            revert ApprovalCallerNotOwnerNorApproved();
                        }
                    }
                    $.tokenApprovals[id] = spender;
                    return owner;
                }
                /// @dev Approve or remove the `operator` as an operator for `msgSender`,
                /// without authorization checks.
                function _setApprovalForAll(address operator, bool approved, address msgSender)
                    internal
                    virtual
                {
                    _getDN404Storage().operatorApprovals[msgSender][operator] = approved;
                }
                /// @dev Calls the mirror contract to link it to this contract.
                ///
                /// Reverts if the call to the mirror contract reverts.
                function _linkMirrorContract(address mirror) internal virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, 0x0f4599e5) // `linkMirrorContract(address)`.
                        mstore(0x20, caller())
                        if iszero(and(eq(mload(0x00), 1), call(gas(), mirror, 0, 0x1c, 0x24, 0x00, 0x20))) {
                            mstore(0x00, 0xd125259c) // `LinkMirrorContractFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
                /// @dev Fallback modifier to dispatch calls from the mirror NFT contract
                /// to internal functions in this contract.
                modifier dn404Fallback() virtual {
                    DN404Storage storage $ = _getDN404Storage();
                    uint256 fnSelector = _calldataload(0x00) >> 224;
                    // `isApprovedForAll(address,address)`.
                    if (fnSelector == 0xe985e9c5) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x44) revert();
                        address owner = address(uint160(_calldataload(0x04)));
                        address operator = address(uint160(_calldataload(0x24)));
                        _return($.operatorApprovals[owner][operator] ? 1 : 0);
                    }
                    // `ownerOf(uint256)`.
                    if (fnSelector == 0x6352211e) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x24) revert();
                        uint256 id = _calldataload(0x04);
                        _return(uint160(_ownerOf(id)));
                    }
                    // `transferFromNFT(address,address,uint256,address)`.
                    if (fnSelector == 0xe5eb36c8) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x84) revert();
                        address from = address(uint160(_calldataload(0x04)));
                        address to = address(uint160(_calldataload(0x24)));
                        uint256 id = _calldataload(0x44);
                        address msgSender = address(uint160(_calldataload(0x64)));
                        _transferFromNFT(from, to, id, msgSender);
                        _return(1);
                    }
                    // `setApprovalForAll(address,bool,address)`.
                    if (fnSelector == 0x813500fc) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x64) revert();
                        address spender = address(uint160(_calldataload(0x04)));
                        bool status = _calldataload(0x24) != 0;
                        address msgSender = address(uint160(_calldataload(0x44)));
                        _setApprovalForAll(spender, status, msgSender);
                        _return(1);
                    }
                    // `approveNFT(address,uint256,address)`.
                    if (fnSelector == 0xd10b6e0c) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x64) revert();
                        address spender = address(uint160(_calldataload(0x04)));
                        uint256 id = _calldataload(0x24);
                        address msgSender = address(uint160(_calldataload(0x44)));
                        _return(uint160(_approveNFT(spender, id, msgSender)));
                    }
                    // `getApproved(uint256)`.
                    if (fnSelector == 0x081812fc) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x24) revert();
                        uint256 id = _calldataload(0x04);
                        _return(uint160(_getApproved(id)));
                    }
                    // `balanceOfNFT(address)`.
                    if (fnSelector == 0xf5b100ea) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x24) revert();
                        address owner = address(uint160(_calldataload(0x04)));
                        _return(_balanceOfNFT(owner));
                    }
                    // `totalNFTSupply()`.
                    if (fnSelector == 0xe2c79281) {
                        if (msg.sender != $.mirrorERC721) revert SenderNotMirror();
                        if (msg.data.length < 0x04) revert();
                        _return(_totalNFTSupply());
                    }
                    // `implementsDN404()`.
                    if (fnSelector == 0xb7a94eb8) {
                        _return(1);
                    }
                    _;
                }
                /// @dev Fallback function for calls from mirror NFT contract.
                fallback() external payable virtual dn404Fallback {}
                receive() external payable virtual {}
                /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
                /*                      PRIVATE HELPERS                       */
                /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/
                /// @dev Struct containing packed log data for `Transfer` events to be
                /// emitted by the mirror NFT contract.
                struct _PackedLogs {
                    uint256[] logs;
                    uint256 offset;
                }
                /// @dev Initiates memory allocation for packed logs with `n` log items.
                function _packedLogsMalloc(uint256 n) private pure returns (_PackedLogs memory p) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let logs := add(mload(0x40), 0x40) // Offset by 2 words for `_packedLogsSend`.
                        mstore(logs, n)
                        let offset := add(0x20, logs)
                        mstore(0x40, add(offset, shl(5, n)))
                        mstore(p, logs)
                        mstore(add(0x20, p), offset)
                    }
                }
                /// @dev Adds a packed log item to `p` with address `a`, token `id` and burn flag `burnBit`.
                function _packedLogsAppend(_PackedLogs memory p, address a, uint256 id, uint256 burnBit)
                    private
                    pure
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let offset := mload(add(0x20, p))
                        mstore(offset, or(or(shl(96, a), shl(8, id)), burnBit))
                        mstore(add(0x20, p), add(offset, 0x20))
                    }
                }
                /// @dev Calls the `mirror` NFT contract to emit Transfer events for packed logs `p`.
                function _packedLogsSend(_PackedLogs memory p, address mirror) private {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let logs := mload(p)
                        let o := sub(logs, 0x40) // Start of calldata to send.
                        mstore(o, 0x263c69d6) // `logTransfer(uint256[])`.
                        mstore(add(o, 0x20), 0x20) // Offset of `logs` in the calldata to send.
                        let n := add(0x44, shl(5, mload(logs))) // Length of calldata to send.
                        if iszero(and(eq(mload(o), 1), call(gas(), mirror, 0, add(o, 0x1c), n, o, 0x20))) {
                            revert(o, 0x00)
                        }
                    }
                }
                /// @dev Struct of temporary variables for transfers.
                struct _TransferTemps {
                    uint256 nftAmountToBurn;
                    uint256 nftAmountToMint;
                    uint256 fromBalance;
                    uint256 toBalance;
                    uint256 fromOwnedLength;
                    uint256 toOwnedLength;
                }
                /// @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 Returns the calldata value at `offset`.
                function _calldataload(uint256 offset) private pure returns (uint256 value) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        value := calldataload(offset)
                    }
                }
                /// @dev Executes a return opcode to return `x` and end the current call frame.
                function _return(uint256 x) private pure {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, x)
                        return(0x00, 0x20)
                    }
                }
                /// @dev Returns `max(0, x - y)`.
                function _zeroFloorSub(uint256 x, uint256 y) private pure returns (uint256 z) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        z := mul(gt(x, y), sub(x, y))
                    }
                }
                /// @dev Returns `i << 1`.
                function _ownershipIndex(uint256 i) private pure returns (uint256) {
                    return i << 1;
                }
                /// @dev Returns `(i << 1) + 1`.
                function _ownedIndex(uint256 i) private pure returns (uint256) {
                    unchecked {
                        return (i << 1) + 1;
                    }
                }
                /// @dev Returns the uint32 value at `index` in `map`.
                function _get(Uint32Map storage map, uint256 index) private view returns (uint32 result) {
                    result = uint32(map.map[index >> 3] >> ((index & 7) << 5));
                }
                /// @dev Updates the uint32 value at `index` in `map`.
                function _set(Uint32Map storage map, uint256 index, uint32 value) private {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x20, map.slot)
                        mstore(0x00, shr(3, index))
                        let s := keccak256(0x00, 0x40) // Storage slot.
                        let o := shl(5, and(index, 7)) // Storage slot offset (bits).
                        let v := sload(s) // Storage slot value.
                        let m := 0xffffffff // Value mask.
                        sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
                    }
                }
                /// @dev Sets the owner alias and the owned index together.
                function _setOwnerAliasAndOwnedIndex(
                    Uint32Map storage map,
                    uint256 id,
                    uint32 ownership,
                    uint32 ownedIndex
                ) private {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let value := or(shl(32, ownedIndex), and(0xffffffff, ownership))
                        mstore(0x20, map.slot)
                        mstore(0x00, shr(2, id))
                        let s := keccak256(0x00, 0x40) // Storage slot.
                        let o := shl(6, and(id, 3)) // Storage slot offset (bits).
                        let v := sload(s) // Storage slot value.
                        let m := 0xffffffffffffffff // Value mask.
                        sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value)))))
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            library DailyOutflowCounterLib {
                uint256 internal constant WAD_TRUNCATED = 10 ** 18 >> 40;
                uint256 internal constant OUTFLOW_TRUNCATED_MASK = 0xffffffffffffff;
                uint256 internal constant DAY_BITPOS = 56;
                uint256 internal constant DAY_MASK = 0x7fffffff;
                uint256 internal constant OUTFLOW_TRUNCATE_SHR = 40;
                uint256 internal constant WHITELISTED_BITPOS = 87;
                function update(uint88 packed, uint256 outflow)
                    internal
                    view
                    returns (uint88 updated, uint256 multiple)
                {
                    unchecked {
                        if (isWhitelisted(packed)) {
                            return (packed, 0);
                        }
                        uint256 currentDay = (block.timestamp / 86400) & DAY_MASK;
                        uint256 packedDay = (uint256(packed) >> DAY_BITPOS) & DAY_MASK;
                        uint256 totalOutflowTruncated = uint256(packed) & OUTFLOW_TRUNCATED_MASK;
                        if (packedDay != currentDay) {
                            totalOutflowTruncated = 0;
                            packedDay = currentDay;
                        }
                        uint256 result = packedDay << DAY_BITPOS;
                        uint256 todaysOutflowTruncated =
                            totalOutflowTruncated + ((outflow >> OUTFLOW_TRUNCATE_SHR) & OUTFLOW_TRUNCATED_MASK);
                        result |= todaysOutflowTruncated & OUTFLOW_TRUNCATED_MASK;
                        updated = uint88(result);
                        multiple = todaysOutflowTruncated / WAD_TRUNCATED;
                    }
                }
                function isWhitelisted(uint88 packed) internal pure returns (bool) {
                    return packed >> WHITELISTED_BITPOS != 0;
                }
                function setWhitelisted(uint88 packed, bool status) internal pure returns (uint88) {
                    if (isWhitelisted(packed) != status) {
                        packed ^= uint88(1 << WHITELISTED_BITPOS);
                    }
                    return packed;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            import {Ownable} from "./Ownable.sol";
            /// @notice Simple single owner and multiroles authorization mixin.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
            /// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
            /// for compatibility, the nomenclature for the 2-step ownership handover and roles
            /// may be unique to this codebase.
            abstract contract OwnableRoles is Ownable {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                           EVENTS                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The `user`'s roles is updated to `roles`.
                /// Each bit of `roles` represents whether the role is set.
                event RolesUpdated(address indexed user, uint256 indexed roles);
                /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
                uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
                    0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                          STORAGE                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The role slot of `user` is given by:
                /// ```
                ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
                ///     let roleSlot := keccak256(0x00, 0x20)
                /// ```
                /// This automatically ignores the upper bits of the `user` in case
                /// they are not clean, as well as keep the `keccak256` under 32-bytes.
                ///
                /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`.
                uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     INTERNAL FUNCTIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Overwrite the roles directly without authorization guard.
                function _setRoles(address user, uint256 roles) internal virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, user)
                        // Store the new value.
                        sstore(keccak256(0x0c, 0x20), roles)
                        // Emit the {RolesUpdated} event.
                        log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
                    }
                }
                /// @dev Updates the roles directly without authorization guard.
                /// If `on` is true, each set bit of `roles` will be turned on,
                /// otherwise, each set bit of `roles` will be turned off.
                function _updateRoles(address user, uint256 roles, bool on) internal virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, user)
                        let roleSlot := keccak256(0x0c, 0x20)
                        // Load the current value.
                        let current := sload(roleSlot)
                        // Compute the updated roles if `on` is true.
                        let updated := or(current, roles)
                        // Compute the updated roles if `on` is false.
                        // Use `and` to compute the intersection of `current` and `roles`,
                        // `xor` it with `current` to flip the bits in the intersection.
                        if iszero(on) { updated := xor(current, and(current, roles)) }
                        // Then, store the new value.
                        sstore(roleSlot, updated)
                        // Emit the {RolesUpdated} event.
                        log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated)
                    }
                }
                /// @dev Grants the roles directly without authorization guard.
                /// Each bit of `roles` represents the role to turn on.
                function _grantRoles(address user, uint256 roles) internal virtual {
                    _updateRoles(user, roles, true);
                }
                /// @dev Removes the roles directly without authorization guard.
                /// Each bit of `roles` represents the role to turn off.
                function _removeRoles(address user, uint256 roles) internal virtual {
                    _updateRoles(user, roles, false);
                }
                /// @dev Throws if the sender does not have any of the `roles`.
                function _checkRoles(uint256 roles) internal view virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute the role slot.
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, caller())
                        // Load the stored value, and if the `and` intersection
                        // of the value and `roles` is zero, revert.
                        if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
                /// @dev Throws if the sender is not the owner,
                /// and does not have any of the `roles`.
                /// Checks for ownership first, then lazily checks for roles.
                function _checkOwnerOrRoles(uint256 roles) internal view virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // If the caller is not the stored owner.
                        // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                        if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                            // Compute the role slot.
                            mstore(0x0c, _ROLE_SLOT_SEED)
                            mstore(0x00, caller())
                            // Load the stored value, and if the `and` intersection
                            // of the value and `roles` is zero, revert.
                            if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                                revert(0x1c, 0x04)
                            }
                        }
                    }
                }
                /// @dev Throws if the sender does not have any of the `roles`,
                /// and is not the owner.
                /// Checks for roles first, then lazily checks for ownership.
                function _checkRolesOrOwner(uint256 roles) internal view virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute the role slot.
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, caller())
                        // Load the stored value, and if the `and` intersection
                        // of the value and `roles` is zero, revert.
                        if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                            // If the caller is not the stored owner.
                            // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                            if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                                revert(0x1c, 0x04)
                            }
                        }
                    }
                }
                /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
                /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
                /// Not recommended to be called on-chain.
                /// Made internal to conserve bytecode. Wrap it in a public function if needed.
                function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                            // We don't need to mask the values of `ordinals`, as Solidity
                            // cleans dirty upper bits when storing variables into memory.
                            roles := or(shl(mload(add(ordinals, i)), 1), roles)
                        }
                    }
                }
                /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
                /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
                /// Not recommended to be called on-chain.
                /// Made internal to conserve bytecode. Wrap it in a public function if needed.
                function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Grab the pointer to the free memory.
                        ordinals := mload(0x40)
                        let ptr := add(ordinals, 0x20)
                        let o := 0
                        // The absence of lookup tables, De Bruijn, etc., here is intentional for
                        // smaller bytecode, as this function is not meant to be called on-chain.
                        for { let t := roles } 1 {} {
                            mstore(ptr, o)
                            // `shr` 5 is equivalent to multiplying by 0x20.
                            // Push back into the ordinals array if the bit is set.
                            ptr := add(ptr, shl(5, and(t, 1)))
                            o := add(o, 1)
                            t := shr(o, roles)
                            if iszero(t) { break }
                        }
                        // Store the length of `ordinals`.
                        mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
                        // Allocate the memory.
                        mstore(0x40, ptr)
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                  PUBLIC UPDATE FUNCTIONS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Allows the owner to grant `user` `roles`.
                /// If the `user` already has a role, then it will be an no-op for the role.
                function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
                    _grantRoles(user, roles);
                }
                /// @dev Allows the owner to remove `user` `roles`.
                /// If the `user` does not have a role, then it will be an no-op for the role.
                function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
                    _removeRoles(user, roles);
                }
                /// @dev Allow the caller to remove their own roles.
                /// If the caller does not have a role, then it will be an no-op for the role.
                function renounceRoles(uint256 roles) public payable virtual {
                    _removeRoles(msg.sender, roles);
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   PUBLIC READ FUNCTIONS                    */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns the roles of `user`.
                function rolesOf(address user) public view virtual returns (uint256 roles) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute the role slot.
                        mstore(0x0c, _ROLE_SLOT_SEED)
                        mstore(0x00, user)
                        // Load the stored value.
                        roles := sload(keccak256(0x0c, 0x20))
                    }
                }
                /// @dev Returns whether `user` has any of `roles`.
                function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) {
                    return rolesOf(user) & roles != 0;
                }
                /// @dev Returns whether `user` has all of `roles`.
                function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) {
                    return rolesOf(user) & roles == roles;
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         MODIFIERS                          */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Marks a function as only callable by an account with `roles`.
                modifier onlyRoles(uint256 roles) virtual {
                    _checkRoles(roles);
                    _;
                }
                /// @dev Marks a function as only callable by the owner or by an account
                /// with `roles`. Checks for ownership first, then lazily checks for roles.
                modifier onlyOwnerOrRoles(uint256 roles) virtual {
                    _checkOwnerOrRoles(roles);
                    _;
                }
                /// @dev Marks a function as only callable by an account with `roles`
                /// or the owner. Checks for roles first, then lazily checks for ownership.
                modifier onlyRolesOrOwner(uint256 roles) virtual {
                    _checkRolesOrOwner(roles);
                    _;
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       ROLE CONSTANTS                       */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                // IYKYK
                uint256 internal constant _ROLE_0 = 1 << 0;
                uint256 internal constant _ROLE_1 = 1 << 1;
                uint256 internal constant _ROLE_2 = 1 << 2;
                uint256 internal constant _ROLE_3 = 1 << 3;
                uint256 internal constant _ROLE_4 = 1 << 4;
                uint256 internal constant _ROLE_5 = 1 << 5;
                uint256 internal constant _ROLE_6 = 1 << 6;
                uint256 internal constant _ROLE_7 = 1 << 7;
                uint256 internal constant _ROLE_8 = 1 << 8;
                uint256 internal constant _ROLE_9 = 1 << 9;
                uint256 internal constant _ROLE_10 = 1 << 10;
                uint256 internal constant _ROLE_11 = 1 << 11;
                uint256 internal constant _ROLE_12 = 1 << 12;
                uint256 internal constant _ROLE_13 = 1 << 13;
                uint256 internal constant _ROLE_14 = 1 << 14;
                uint256 internal constant _ROLE_15 = 1 << 15;
                uint256 internal constant _ROLE_16 = 1 << 16;
                uint256 internal constant _ROLE_17 = 1 << 17;
                uint256 internal constant _ROLE_18 = 1 << 18;
                uint256 internal constant _ROLE_19 = 1 << 19;
                uint256 internal constant _ROLE_20 = 1 << 20;
                uint256 internal constant _ROLE_21 = 1 << 21;
                uint256 internal constant _ROLE_22 = 1 << 22;
                uint256 internal constant _ROLE_23 = 1 << 23;
                uint256 internal constant _ROLE_24 = 1 << 24;
                uint256 internal constant _ROLE_25 = 1 << 25;
                uint256 internal constant _ROLE_26 = 1 << 26;
                uint256 internal constant _ROLE_27 = 1 << 27;
                uint256 internal constant _ROLE_28 = 1 << 28;
                uint256 internal constant _ROLE_29 = 1 << 29;
                uint256 internal constant _ROLE_30 = 1 << 30;
                uint256 internal constant _ROLE_31 = 1 << 31;
                uint256 internal constant _ROLE_32 = 1 << 32;
                uint256 internal constant _ROLE_33 = 1 << 33;
                uint256 internal constant _ROLE_34 = 1 << 34;
                uint256 internal constant _ROLE_35 = 1 << 35;
                uint256 internal constant _ROLE_36 = 1 << 36;
                uint256 internal constant _ROLE_37 = 1 << 37;
                uint256 internal constant _ROLE_38 = 1 << 38;
                uint256 internal constant _ROLE_39 = 1 << 39;
                uint256 internal constant _ROLE_40 = 1 << 40;
                uint256 internal constant _ROLE_41 = 1 << 41;
                uint256 internal constant _ROLE_42 = 1 << 42;
                uint256 internal constant _ROLE_43 = 1 << 43;
                uint256 internal constant _ROLE_44 = 1 << 44;
                uint256 internal constant _ROLE_45 = 1 << 45;
                uint256 internal constant _ROLE_46 = 1 << 46;
                uint256 internal constant _ROLE_47 = 1 << 47;
                uint256 internal constant _ROLE_48 = 1 << 48;
                uint256 internal constant _ROLE_49 = 1 << 49;
                uint256 internal constant _ROLE_50 = 1 << 50;
                uint256 internal constant _ROLE_51 = 1 << 51;
                uint256 internal constant _ROLE_52 = 1 << 52;
                uint256 internal constant _ROLE_53 = 1 << 53;
                uint256 internal constant _ROLE_54 = 1 << 54;
                uint256 internal constant _ROLE_55 = 1 << 55;
                uint256 internal constant _ROLE_56 = 1 << 56;
                uint256 internal constant _ROLE_57 = 1 << 57;
                uint256 internal constant _ROLE_58 = 1 << 58;
                uint256 internal constant _ROLE_59 = 1 << 59;
                uint256 internal constant _ROLE_60 = 1 << 60;
                uint256 internal constant _ROLE_61 = 1 << 61;
                uint256 internal constant _ROLE_62 = 1 << 62;
                uint256 internal constant _ROLE_63 = 1 << 63;
                uint256 internal constant _ROLE_64 = 1 << 64;
                uint256 internal constant _ROLE_65 = 1 << 65;
                uint256 internal constant _ROLE_66 = 1 << 66;
                uint256 internal constant _ROLE_67 = 1 << 67;
                uint256 internal constant _ROLE_68 = 1 << 68;
                uint256 internal constant _ROLE_69 = 1 << 69;
                uint256 internal constant _ROLE_70 = 1 << 70;
                uint256 internal constant _ROLE_71 = 1 << 71;
                uint256 internal constant _ROLE_72 = 1 << 72;
                uint256 internal constant _ROLE_73 = 1 << 73;
                uint256 internal constant _ROLE_74 = 1 << 74;
                uint256 internal constant _ROLE_75 = 1 << 75;
                uint256 internal constant _ROLE_76 = 1 << 76;
                uint256 internal constant _ROLE_77 = 1 << 77;
                uint256 internal constant _ROLE_78 = 1 << 78;
                uint256 internal constant _ROLE_79 = 1 << 79;
                uint256 internal constant _ROLE_80 = 1 << 80;
                uint256 internal constant _ROLE_81 = 1 << 81;
                uint256 internal constant _ROLE_82 = 1 << 82;
                uint256 internal constant _ROLE_83 = 1 << 83;
                uint256 internal constant _ROLE_84 = 1 << 84;
                uint256 internal constant _ROLE_85 = 1 << 85;
                uint256 internal constant _ROLE_86 = 1 << 86;
                uint256 internal constant _ROLE_87 = 1 << 87;
                uint256 internal constant _ROLE_88 = 1 << 88;
                uint256 internal constant _ROLE_89 = 1 << 89;
                uint256 internal constant _ROLE_90 = 1 << 90;
                uint256 internal constant _ROLE_91 = 1 << 91;
                uint256 internal constant _ROLE_92 = 1 << 92;
                uint256 internal constant _ROLE_93 = 1 << 93;
                uint256 internal constant _ROLE_94 = 1 << 94;
                uint256 internal constant _ROLE_95 = 1 << 95;
                uint256 internal constant _ROLE_96 = 1 << 96;
                uint256 internal constant _ROLE_97 = 1 << 97;
                uint256 internal constant _ROLE_98 = 1 << 98;
                uint256 internal constant _ROLE_99 = 1 << 99;
                uint256 internal constant _ROLE_100 = 1 << 100;
                uint256 internal constant _ROLE_101 = 1 << 101;
                uint256 internal constant _ROLE_102 = 1 << 102;
                uint256 internal constant _ROLE_103 = 1 << 103;
                uint256 internal constant _ROLE_104 = 1 << 104;
                uint256 internal constant _ROLE_105 = 1 << 105;
                uint256 internal constant _ROLE_106 = 1 << 106;
                uint256 internal constant _ROLE_107 = 1 << 107;
                uint256 internal constant _ROLE_108 = 1 << 108;
                uint256 internal constant _ROLE_109 = 1 << 109;
                uint256 internal constant _ROLE_110 = 1 << 110;
                uint256 internal constant _ROLE_111 = 1 << 111;
                uint256 internal constant _ROLE_112 = 1 << 112;
                uint256 internal constant _ROLE_113 = 1 << 113;
                uint256 internal constant _ROLE_114 = 1 << 114;
                uint256 internal constant _ROLE_115 = 1 << 115;
                uint256 internal constant _ROLE_116 = 1 << 116;
                uint256 internal constant _ROLE_117 = 1 << 117;
                uint256 internal constant _ROLE_118 = 1 << 118;
                uint256 internal constant _ROLE_119 = 1 << 119;
                uint256 internal constant _ROLE_120 = 1 << 120;
                uint256 internal constant _ROLE_121 = 1 << 121;
                uint256 internal constant _ROLE_122 = 1 << 122;
                uint256 internal constant _ROLE_123 = 1 << 123;
                uint256 internal constant _ROLE_124 = 1 << 124;
                uint256 internal constant _ROLE_125 = 1 << 125;
                uint256 internal constant _ROLE_126 = 1 << 126;
                uint256 internal constant _ROLE_127 = 1 << 127;
                uint256 internal constant _ROLE_128 = 1 << 128;
                uint256 internal constant _ROLE_129 = 1 << 129;
                uint256 internal constant _ROLE_130 = 1 << 130;
                uint256 internal constant _ROLE_131 = 1 << 131;
                uint256 internal constant _ROLE_132 = 1 << 132;
                uint256 internal constant _ROLE_133 = 1 << 133;
                uint256 internal constant _ROLE_134 = 1 << 134;
                uint256 internal constant _ROLE_135 = 1 << 135;
                uint256 internal constant _ROLE_136 = 1 << 136;
                uint256 internal constant _ROLE_137 = 1 << 137;
                uint256 internal constant _ROLE_138 = 1 << 138;
                uint256 internal constant _ROLE_139 = 1 << 139;
                uint256 internal constant _ROLE_140 = 1 << 140;
                uint256 internal constant _ROLE_141 = 1 << 141;
                uint256 internal constant _ROLE_142 = 1 << 142;
                uint256 internal constant _ROLE_143 = 1 << 143;
                uint256 internal constant _ROLE_144 = 1 << 144;
                uint256 internal constant _ROLE_145 = 1 << 145;
                uint256 internal constant _ROLE_146 = 1 << 146;
                uint256 internal constant _ROLE_147 = 1 << 147;
                uint256 internal constant _ROLE_148 = 1 << 148;
                uint256 internal constant _ROLE_149 = 1 << 149;
                uint256 internal constant _ROLE_150 = 1 << 150;
                uint256 internal constant _ROLE_151 = 1 << 151;
                uint256 internal constant _ROLE_152 = 1 << 152;
                uint256 internal constant _ROLE_153 = 1 << 153;
                uint256 internal constant _ROLE_154 = 1 << 154;
                uint256 internal constant _ROLE_155 = 1 << 155;
                uint256 internal constant _ROLE_156 = 1 << 156;
                uint256 internal constant _ROLE_157 = 1 << 157;
                uint256 internal constant _ROLE_158 = 1 << 158;
                uint256 internal constant _ROLE_159 = 1 << 159;
                uint256 internal constant _ROLE_160 = 1 << 160;
                uint256 internal constant _ROLE_161 = 1 << 161;
                uint256 internal constant _ROLE_162 = 1 << 162;
                uint256 internal constant _ROLE_163 = 1 << 163;
                uint256 internal constant _ROLE_164 = 1 << 164;
                uint256 internal constant _ROLE_165 = 1 << 165;
                uint256 internal constant _ROLE_166 = 1 << 166;
                uint256 internal constant _ROLE_167 = 1 << 167;
                uint256 internal constant _ROLE_168 = 1 << 168;
                uint256 internal constant _ROLE_169 = 1 << 169;
                uint256 internal constant _ROLE_170 = 1 << 170;
                uint256 internal constant _ROLE_171 = 1 << 171;
                uint256 internal constant _ROLE_172 = 1 << 172;
                uint256 internal constant _ROLE_173 = 1 << 173;
                uint256 internal constant _ROLE_174 = 1 << 174;
                uint256 internal constant _ROLE_175 = 1 << 175;
                uint256 internal constant _ROLE_176 = 1 << 176;
                uint256 internal constant _ROLE_177 = 1 << 177;
                uint256 internal constant _ROLE_178 = 1 << 178;
                uint256 internal constant _ROLE_179 = 1 << 179;
                uint256 internal constant _ROLE_180 = 1 << 180;
                uint256 internal constant _ROLE_181 = 1 << 181;
                uint256 internal constant _ROLE_182 = 1 << 182;
                uint256 internal constant _ROLE_183 = 1 << 183;
                uint256 internal constant _ROLE_184 = 1 << 184;
                uint256 internal constant _ROLE_185 = 1 << 185;
                uint256 internal constant _ROLE_186 = 1 << 186;
                uint256 internal constant _ROLE_187 = 1 << 187;
                uint256 internal constant _ROLE_188 = 1 << 188;
                uint256 internal constant _ROLE_189 = 1 << 189;
                uint256 internal constant _ROLE_190 = 1 << 190;
                uint256 internal constant _ROLE_191 = 1 << 191;
                uint256 internal constant _ROLE_192 = 1 << 192;
                uint256 internal constant _ROLE_193 = 1 << 193;
                uint256 internal constant _ROLE_194 = 1 << 194;
                uint256 internal constant _ROLE_195 = 1 << 195;
                uint256 internal constant _ROLE_196 = 1 << 196;
                uint256 internal constant _ROLE_197 = 1 << 197;
                uint256 internal constant _ROLE_198 = 1 << 198;
                uint256 internal constant _ROLE_199 = 1 << 199;
                uint256 internal constant _ROLE_200 = 1 << 200;
                uint256 internal constant _ROLE_201 = 1 << 201;
                uint256 internal constant _ROLE_202 = 1 << 202;
                uint256 internal constant _ROLE_203 = 1 << 203;
                uint256 internal constant _ROLE_204 = 1 << 204;
                uint256 internal constant _ROLE_205 = 1 << 205;
                uint256 internal constant _ROLE_206 = 1 << 206;
                uint256 internal constant _ROLE_207 = 1 << 207;
                uint256 internal constant _ROLE_208 = 1 << 208;
                uint256 internal constant _ROLE_209 = 1 << 209;
                uint256 internal constant _ROLE_210 = 1 << 210;
                uint256 internal constant _ROLE_211 = 1 << 211;
                uint256 internal constant _ROLE_212 = 1 << 212;
                uint256 internal constant _ROLE_213 = 1 << 213;
                uint256 internal constant _ROLE_214 = 1 << 214;
                uint256 internal constant _ROLE_215 = 1 << 215;
                uint256 internal constant _ROLE_216 = 1 << 216;
                uint256 internal constant _ROLE_217 = 1 << 217;
                uint256 internal constant _ROLE_218 = 1 << 218;
                uint256 internal constant _ROLE_219 = 1 << 219;
                uint256 internal constant _ROLE_220 = 1 << 220;
                uint256 internal constant _ROLE_221 = 1 << 221;
                uint256 internal constant _ROLE_222 = 1 << 222;
                uint256 internal constant _ROLE_223 = 1 << 223;
                uint256 internal constant _ROLE_224 = 1 << 224;
                uint256 internal constant _ROLE_225 = 1 << 225;
                uint256 internal constant _ROLE_226 = 1 << 226;
                uint256 internal constant _ROLE_227 = 1 << 227;
                uint256 internal constant _ROLE_228 = 1 << 228;
                uint256 internal constant _ROLE_229 = 1 << 229;
                uint256 internal constant _ROLE_230 = 1 << 230;
                uint256 internal constant _ROLE_231 = 1 << 231;
                uint256 internal constant _ROLE_232 = 1 << 232;
                uint256 internal constant _ROLE_233 = 1 << 233;
                uint256 internal constant _ROLE_234 = 1 << 234;
                uint256 internal constant _ROLE_235 = 1 << 235;
                uint256 internal constant _ROLE_236 = 1 << 236;
                uint256 internal constant _ROLE_237 = 1 << 237;
                uint256 internal constant _ROLE_238 = 1 << 238;
                uint256 internal constant _ROLE_239 = 1 << 239;
                uint256 internal constant _ROLE_240 = 1 << 240;
                uint256 internal constant _ROLE_241 = 1 << 241;
                uint256 internal constant _ROLE_242 = 1 << 242;
                uint256 internal constant _ROLE_243 = 1 << 243;
                uint256 internal constant _ROLE_244 = 1 << 244;
                uint256 internal constant _ROLE_245 = 1 << 245;
                uint256 internal constant _ROLE_246 = 1 << 246;
                uint256 internal constant _ROLE_247 = 1 << 247;
                uint256 internal constant _ROLE_248 = 1 << 248;
                uint256 internal constant _ROLE_249 = 1 << 249;
                uint256 internal constant _ROLE_250 = 1 << 250;
                uint256 internal constant _ROLE_251 = 1 << 251;
                uint256 internal constant _ROLE_252 = 1 << 252;
                uint256 internal constant _ROLE_253 = 1 << 253;
                uint256 internal constant _ROLE_254 = 1 << 254;
                uint256 internal constant _ROLE_255 = 1 << 255;
            }
            // 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();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         CONSTANTS                          */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The constant returned when the `search` is not found in the string.
                uint256 internal constant NOT_FOUND = type(uint256).max;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     DECIMAL OPERATIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns the base 10 decimal representation of `value`.
                function toString(uint256 value) internal pure returns (string memory str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                        // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                        // We will need 1 word for the trailing zeros padding, 1 word for the length,
                        // and 3 words for a maximum of 78 digits.
                        str := add(mload(0x40), 0x80)
                        // Update the free memory pointer to allocate.
                        mstore(0x40, add(str, 0x20))
                        // Zeroize the slot after the string.
                        mstore(str, 0)
                        // Cache the end of the memory to calculate the length later.
                        let end := str
                        let w := not(0) // Tsk.
                        // We write the string from rightmost digit to leftmost digit.
                        // The following is essentially a do-while loop that also handles the zero case.
                        for { let temp := value } 1 {} {
                            str := add(str, w) // `sub(str, 1)`.
                            // Write the character to the pointer.
                            // The ASCII index of the '0' character is 48.
                            mstore8(str, add(48, mod(temp, 10)))
                            // Keep dividing `temp` until zero.
                            temp := div(temp, 10)
                            if iszero(temp) { break }
                        }
                        let length := sub(end, str)
                        // Move the pointer 32 bytes leftwards to make room for the length.
                        str := sub(str, 0x20)
                        // Store the length.
                        mstore(str, length)
                    }
                }
                /// @dev Returns the base 10 decimal representation of `value`.
                function toString(int256 value) internal pure returns (string memory str) {
                    if (value >= 0) {
                        return toString(uint256(value));
                    }
                    unchecked {
                        str = toString(~uint256(value) + 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 length := mload(str) // Load the string length.
                        mstore(str, 0x2d) // Store the '-' character.
                        str := sub(str, 1) // Move back the string pointer by a byte.
                        mstore(str, add(length, 1)) // Update the string length.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   HEXADECIMAL OPERATIONS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns the hexadecimal representation of `value`,
                /// left-padded to an input length of `length` bytes.
                /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
                /// giving a total length of `length * 2 + 2` bytes.
                /// Reverts if `length` is too small for the output to contain all the digits.
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(value, length);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`,
                /// left-padded to an input length of `length` bytes.
                /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
                /// giving a total length of `length * 2` bytes.
                /// Reverts if `length` is too small for the output to contain all the digits.
                function toHexStringNoPrefix(uint256 value, uint256 length)
                    internal
                    pure
                    returns (string memory str)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
                        // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
                        // We add 0x20 to the total and round down to a multiple of 0x20.
                        // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
                        str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
                        // Allocate the memory.
                        mstore(0x40, add(str, 0x20))
                        // Zeroize the slot after the string.
                        mstore(str, 0)
                        // Cache the end to calculate the length later.
                        let end := str
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        let start := sub(str, add(length, length))
                        let w := not(1) // Tsk.
                        let temp := value
                        // We write the string from rightmost digit to leftmost digit.
                        // The following is essentially a do-while loop that also handles the zero case.
                        for {} 1 {} {
                            str := add(str, w) // `sub(str, 2)`.
                            mstore8(add(str, 1), mload(and(temp, 15)))
                            mstore8(str, mload(and(shr(4, temp), 15)))
                            temp := shr(8, temp)
                            if iszero(xor(str, start)) { break }
                        }
                        if temp {
                            mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                            revert(0x1c, 0x04)
                        }
                        // Compute the string's length.
                        let strLength := sub(end, str)
                        // Move the pointer and write the length.
                        str := sub(str, 0x20)
                        mstore(str, strLength)
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
                /// As address are 20 bytes long, the output will left-padded to have
                /// a length of `20 * 2 + 2` bytes.
                function toHexString(uint256 value) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is 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 str) {
                    str = toHexStringNoPrefix(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
                        str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
                        mstore(str, sub(strLength, o)) // Write 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 str) {
                    str = toHexStringNoPrefix(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
                        let strLength := mload(str) // Get the length.
                        str := add(str, o) // Move the pointer, accounting for leading zero.
                        mstore(str, sub(strLength, o)) // Write 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 str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                        // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
                        // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
                        str := add(mload(0x40), 0x80)
                        // Allocate the memory.
                        mstore(0x40, add(str, 0x20))
                        // Zeroize the slot after the string.
                        mstore(str, 0)
                        // Cache the end to calculate the length later.
                        let end := str
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        let w := not(1) // Tsk.
                        // We write the string from rightmost digit to leftmost digit.
                        // The following is essentially a do-while loop that also handles the zero case.
                        for { let temp := value } 1 {} {
                            str := add(str, w) // `sub(str, 2)`.
                            mstore8(add(str, 1), mload(and(temp, 15)))
                            mstore8(str, mload(and(shr(4, temp), 15)))
                            temp := shr(8, temp)
                            if iszero(temp) { break }
                        }
                        // Compute the string's length.
                        let strLength := sub(end, str)
                        // Move the pointer and write the length.
                        str := sub(str, 0x20)
                        mstore(str, strLength)
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
                /// and the alphabets are capitalized conditionally according to
                /// https://eips.ethereum.org/EIPS/eip-55
                function toHexStringChecksummed(address value) internal pure returns (string memory str) {
                    str = toHexString(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                        let o := add(str, 0x22)
                        let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                        let t := shl(240, 136) // `0b10001000 << 240`
                        for { let i := 0 } 1 {} {
                            mstore(add(i, i), mul(t, byte(i, hashed)))
                            i := add(i, 1)
                            if eq(i, 20) { break }
                        }
                        mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                        o := add(o, 0x20)
                        mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
                function toHexString(address value) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(value);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hexadecimal representation of `value`.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        str := mload(0x40)
                        // Allocate the memory.
                        // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                        // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                        // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                        mstore(0x40, add(str, 0x80))
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        str := add(str, 2)
                        mstore(str, 40)
                        let o := add(str, 0x20)
                        mstore(add(o, 40), 0)
                        value := shl(96, value)
                        // We write the string from rightmost digit to leftmost digit.
                        // The following is essentially a do-while loop that also handles the zero case.
                        for { let i := 0 } 1 {} {
                            let p := add(o, add(i, i))
                            let temp := byte(i, value)
                            mstore8(add(p, 1), mload(and(temp, 15)))
                            mstore8(p, mload(shr(4, temp)))
                            i := add(i, 1)
                            if eq(i, 20) { break }
                        }
                    }
                }
                /// @dev Returns the hex encoded string from the raw bytes.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexString(bytes memory raw) internal pure returns (string memory str) {
                    str = toHexStringNoPrefix(raw);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let strLength := add(mload(str), 2) // Compute the length.
                        mstore(str, 0x3078) // Write the "0x" prefix.
                        str := sub(str, 2) // Move the pointer.
                        mstore(str, strLength) // Write the length.
                    }
                }
                /// @dev Returns the hex encoded string from the raw bytes.
                /// The output is encoded using 2 hexadecimal digits per byte.
                function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let length := mload(raw)
                        str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                        mstore(str, add(length, length)) // Store the length of the output.
                        // Store "0123456789abcdef" in scratch space.
                        mstore(0x0f, 0x30313233343536373839616263646566)
                        let o := add(str, 0x20)
                        let end := add(raw, length)
                        for {} iszero(eq(raw, end)) {} {
                            raw := add(raw, 1)
                            mstore8(add(o, 1), mload(and(mload(raw), 15)))
                            mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                            o := add(o, 2)
                        }
                        mstore(o, 0) // Zeroize the slot after the string.
                        mstore(0x40, add(o, 0x20)) // Allocate the memory.
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   RUNE STRING OPERATIONS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns the number of UTF characters in the string.
                function runeCount(string memory s) internal pure returns (uint256 result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if mload(s) {
                            mstore(0x00, div(not(0), 255))
                            mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                            let o := add(s, 0x20)
                            let end := add(o, mload(s))
                            for { result := 1 } 1 { result := add(result, 1) } {
                                o := add(o, byte(0, mload(shr(250, mload(o)))))
                                if iszero(lt(o, end)) { break }
                            }
                        }
                    }
                }
                /// @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 {
                        let mask := shl(7, div(not(0), 255))
                        result := 1
                        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)
                        }
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   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 `search` replaced with `replacement`.
                function replace(string memory subject, string memory search, string memory replacement)
                    internal
                    pure
                    returns (string memory result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let subjectLength := mload(subject)
                        let searchLength := mload(search)
                        let replacementLength := mload(replacement)
                        subject := add(subject, 0x20)
                        search := add(search, 0x20)
                        replacement := add(replacement, 0x20)
                        result := add(mload(0x40), 0x20)
                        let subjectEnd := add(subject, subjectLength)
                        if iszero(gt(searchLength, subjectLength)) {
                            let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                            let h := 0
                            if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                            let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                            let s := mload(search)
                            for {} 1 {} {
                                let t := mload(subject)
                                // Whether the first `searchLength % 32` bytes of
                                // `subject` and `search` matches.
                                if iszero(shr(m, xor(t, s))) {
                                    if h {
                                        if iszero(eq(keccak256(subject, searchLength), h)) {
                                            mstore(result, t)
                                            result := add(result, 1)
                                            subject := add(subject, 1)
                                            if iszero(lt(subject, subjectSearchEnd)) { break }
                                            continue
                                        }
                                    }
                                    // Copy the `replacement` one word at a time.
                                    for { let o := 0 } 1 {} {
                                        mstore(add(result, o), mload(add(replacement, o)))
                                        o := add(o, 0x20)
                                        if iszero(lt(o, replacementLength)) { break }
                                    }
                                    result := add(result, replacementLength)
                                    subject := add(subject, searchLength)
                                    if searchLength {
                                        if iszero(lt(subject, subjectSearchEnd)) { break }
                                        continue
                                    }
                                }
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                            }
                        }
                        let resultRemainder := result
                        result := add(mload(0x40), 0x20)
                        let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
                        // Copy the rest of the string one word at a time.
                        for {} lt(subject, subjectEnd) {} {
                            mstore(resultRemainder, mload(subject))
                            resultRemainder := add(resultRemainder, 0x20)
                            subject := add(subject, 0x20)
                        }
                        result := sub(result, 0x20)
                        let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
                        mstore(last, 0)
                        mstore(0x40, add(last, 0x20)) // Allocate the memory.
                        mstore(result, k) // Store the length.
                    }
                }
                /// @dev Returns the byte index of the first location of `search` in `subject`,
                /// searching from left to right, starting from `from`.
                /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
                function indexOf(string memory subject, string memory search, uint256 from)
                    internal
                    pure
                    returns (uint256 result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for { let subjectLength := mload(subject) } 1 {} {
                            if iszero(mload(search)) {
                                if iszero(gt(from, subjectLength)) {
                                    result := from
                                    break
                                }
                                result := subjectLength
                                break
                            }
                            let searchLength := mload(search)
                            let subjectStart := add(subject, 0x20)
                            result := not(0) // Initialize to `NOT_FOUND`.
                            subject := add(subjectStart, from)
                            let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
                            let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                            let s := mload(add(search, 0x20))
                            if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
                            if iszero(lt(searchLength, 0x20)) {
                                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                                    if iszero(shr(m, xor(mload(subject), s))) {
                                        if eq(keccak256(subject, searchLength), h) {
                                            result := sub(subject, subjectStart)
                                            break
                                        }
                                    }
                                    subject := add(subject, 1)
                                    if iszero(lt(subject, end)) { break }
                                }
                                break
                            }
                            for {} 1 {} {
                                if iszero(shr(m, xor(mload(subject), s))) {
                                    result := sub(subject, subjectStart)
                                    break
                                }
                                subject := add(subject, 1)
                                if iszero(lt(subject, end)) { break }
                            }
                            break
                        }
                    }
                }
                /// @dev Returns the byte index of the first location of `search` in `subject`,
                /// searching from left to right.
                /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
                function indexOf(string memory subject, string memory search)
                    internal
                    pure
                    returns (uint256 result)
                {
                    result = indexOf(subject, search, 0);
                }
                /// @dev Returns the byte index of the first location of `search` in `subject`,
                /// searching from right to left, starting from `from`.
                /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
                function lastIndexOf(string memory subject, string memory search, uint256 from)
                    internal
                    pure
                    returns (uint256 result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        for {} 1 {} {
                            result := not(0) // Initialize to `NOT_FOUND`.
                            let searchLength := mload(search)
                            if gt(searchLength, mload(subject)) { break }
                            let w := result
                            let fromMax := sub(mload(subject), searchLength)
                            if iszero(gt(fromMax, from)) { from := fromMax }
                            let end := add(add(subject, 0x20), w)
                            subject := add(add(subject, 0x20), from)
                            if iszero(gt(subject, end)) { break }
                            // As this function is not too often used,
                            // we shall simply use keccak256 for smaller bytecode size.
                            for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                                if eq(keccak256(subject, searchLength), h) {
                                    result := sub(subject, add(end, 1))
                                    break
                                }
                                subject := add(subject, w) // `sub(subject, 1)`.
                                if iszero(gt(subject, end)) { break }
                            }
                            break
                        }
                    }
                }
                /// @dev Returns the byte index of the first location of `search` in `subject`,
                /// searching from right to left.
                /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
                function lastIndexOf(string memory subject, string memory search)
                    internal
                    pure
                    returns (uint256 result)
                {
                    result = lastIndexOf(subject, search, uint256(int256(-1)));
                }
                /// @dev Returns true if `search` is found in `subject`, false otherwise.
                function contains(string memory subject, string memory search) internal pure returns (bool) {
                    return indexOf(subject, search) != NOT_FOUND;
                }
                /// @dev Returns whether `subject` starts with `search`.
                function startsWith(string memory subject, string memory search)
                    internal
                    pure
                    returns (bool result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let searchLength := mload(search)
                        // Just using keccak256 directly is actually cheaper.
                        // forgefmt: disable-next-item
                        result := and(
                            iszero(gt(searchLength, mload(subject))),
                            eq(
                                keccak256(add(subject, 0x20), searchLength),
                                keccak256(add(search, 0x20), searchLength)
                            )
                        )
                    }
                }
                /// @dev Returns whether `subject` ends with `search`.
                function endsWith(string memory subject, string memory search)
                    internal
                    pure
                    returns (bool result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let searchLength := mload(search)
                        let subjectLength := mload(subject)
                        // Whether `search` is not longer than `subject`.
                        let withinRange := iszero(gt(searchLength, subjectLength))
                        // Just using keccak256 directly is actually cheaper.
                        // forgefmt: disable-next-item
                        result := and(
                            withinRange,
                            eq(
                                keccak256(
                                    // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                                    add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                                    searchLength
                                ),
                                keccak256(add(search, 0x20), searchLength)
                            )
                        )
                    }
                }
                /// @dev Returns `subject` repeated `times`.
                function repeat(string memory subject, uint256 times)
                    internal
                    pure
                    returns (string memory result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let subjectLength := mload(subject)
                        if iszero(or(iszero(times), iszero(subjectLength))) {
                            subject := add(subject, 0x20)
                            result := mload(0x40)
                            let output := add(result, 0x20)
                            for {} 1 {} {
                                // Copy the `subject` one word at a time.
                                for { let o := 0 } 1 {} {
                                    mstore(add(output, o), mload(add(subject, o)))
                                    o := add(o, 0x20)
                                    if iszero(lt(o, subjectLength)) { break }
                                }
                                output := add(output, subjectLength)
                                times := sub(times, 1)
                                if iszero(times) { break }
                            }
                            mstore(output, 0) // Zeroize the slot after the string.
                            let resultLength := sub(output, add(result, 0x20))
                            mstore(result, resultLength) // Store the length.
                            // Allocate the memory.
                            mstore(0x40, add(result, add(resultLength, 0x20)))
                        }
                    }
                }
                /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
                /// `start` and `end` are byte offsets.
                function slice(string memory subject, uint256 start, uint256 end)
                    internal
                    pure
                    returns (string memory result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let subjectLength := mload(subject)
                        if iszero(gt(subjectLength, end)) { end := subjectLength }
                        if iszero(gt(subjectLength, start)) { start := subjectLength }
                        if lt(start, end) {
                            result := mload(0x40)
                            let resultLength := sub(end, start)
                            mstore(result, resultLength)
                            subject := add(subject, start)
                            let w := not(0x1f)
                            // Copy the `subject` one word at a time, backwards.
                            for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                                mstore(add(result, o), mload(add(subject, o)))
                                o := add(o, w) // `sub(o, 0x20)`.
                                if iszero(o) { break }
                            }
                            // Zeroize the slot after the string.
                            mstore(add(add(result, 0x20), resultLength), 0)
                            // Allocate memory for the length and the bytes,
                            // rounded up to a multiple of 32.
                            mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
                        }
                    }
                }
                /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
                /// `start` is a byte offset.
                function slice(string memory subject, uint256 start)
                    internal
                    pure
                    returns (string memory result)
                {
                    result = slice(subject, start, uint256(int256(-1)));
                }
                /// @dev Returns all the indices of `search` in `subject`.
                /// The indices are byte offsets.
                function indicesOf(string memory subject, string memory search)
                    internal
                    pure
                    returns (uint256[] memory result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let subjectLength := mload(subject)
                        let searchLength := mload(search)
                        if iszero(gt(searchLength, subjectLength)) {
                            subject := add(subject, 0x20)
                            search := add(search, 0x20)
                            result := add(mload(0x40), 0x20)
                            let subjectStart := subject
                            let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                            let h := 0
                            if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                            let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                            let s := mload(search)
                            for {} 1 {} {
                                let t := mload(subject)
                                // Whether the first `searchLength % 32` bytes of
                                // `subject` and `search` matches.
                                if iszero(shr(m, xor(t, s))) {
                                    if h {
                                        if iszero(eq(keccak256(subject, searchLength), h)) {
                                            subject := add(subject, 1)
                                            if iszero(lt(subject, subjectSearchEnd)) { break }
                                            continue
                                        }
                                    }
                                    // Append to `result`.
                                    mstore(result, sub(subject, subjectStart))
                                    result := add(result, 0x20)
                                    // Advance `subject` by `searchLength`.
                                    subject := add(subject, searchLength)
                                    if searchLength {
                                        if iszero(lt(subject, subjectSearchEnd)) { break }
                                        continue
                                    }
                                }
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                            }
                            let resultEnd := result
                            // Assign `result` to the free memory pointer.
                            result := mload(0x40)
                            // Store the length of `result`.
                            mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                            // Allocate memory for result.
                            // We allocate one more word, so this array can be recycled for {split}.
                            mstore(0x40, add(resultEnd, 0x20))
                        }
                    }
                }
                /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
                function split(string memory subject, string memory delimiter)
                    internal
                    pure
                    returns (string[] memory result)
                {
                    uint256[] memory indices = indicesOf(subject, delimiter);
                    /// @solidity memory-safe-assembly
                    assembly {
                        let w := not(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))
                        let prevIndex := 0
                        for {} 1 {} {
                            let index := mload(indexPtr)
                            mstore(indexPtr, 0x60)
                            if iszero(eq(index, prevIndex)) {
                                let element := mload(0x40)
                                let elementLength := sub(index, prevIndex)
                                mstore(element, elementLength)
                                // Copy the `subject` one word at a time, backwards.
                                for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                                    mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                                    o := add(o, w) // `sub(o, 0x20)`.
                                    if iszero(o) { break }
                                }
                                // Zeroize the slot after the string.
                                mstore(add(add(element, 0x20), elementLength), 0)
                                // Allocate memory for the length and the bytes,
                                // rounded up to a multiple of 32.
                                mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                                // Store the `element` into the array.
                                mstore(indexPtr, element)
                            }
                            prevIndex := add(index, mload(delimiter))
                            indexPtr := add(indexPtr, 0x20)
                            if iszero(lt(indexPtr, indicesEnd)) { break }
                        }
                        result := indices
                        if iszero(mload(delimiter)) {
                            result := add(indices, 0x20)
                            mstore(result, sub(mload(indices), 2))
                        }
                    }
                }
                /// @dev Returns a concatenated string of `a` and `b`.
                /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
                function concat(string memory a, string memory b)
                    internal
                    pure
                    returns (string memory result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let w := not(0x1f)
                        result := mload(0x40)
                        let aLength := mload(a)
                        // Copy `a` one word at a time, backwards.
                        for { let o := and(add(aLength, 0x20), w) } 1 {} {
                            mstore(add(result, o), mload(add(a, o)))
                            o := add(o, w) // `sub(o, 0x20)`.
                            if iszero(o) { break }
                        }
                        let bLength := mload(b)
                        let output := add(result, aLength)
                        // Copy `b` one word at a time, backwards.
                        for { let o := and(add(bLength, 0x20), w) } 1 {} {
                            mstore(add(output, o), mload(add(b, o)))
                            o := add(o, w) // `sub(o, 0x20)`.
                            if iszero(o) { break }
                        }
                        let totalLength := add(aLength, bLength)
                        let last := add(add(result, 0x20), totalLength)
                        // Zeroize the slot after the string.
                        mstore(last, 0)
                        // Stores the length.
                        mstore(result, totalLength)
                        // Allocate memory for the length and the bytes,
                        // rounded up to a multiple of 32.
                        mstore(0x40, and(add(last, 0x1f), w))
                    }
                }
                /// @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 length := mload(subject)
                        if length {
                            result := add(mload(0x40), 0x20)
                            subject := add(subject, 1)
                            let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                            let w := not(0)
                            for { let o := length } 1 {} {
                                o := add(o, w)
                                let b := and(0xff, mload(add(subject, o)))
                                mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                                if iszero(o) { break }
                            }
                            result := mload(0x40)
                            mstore(result, length) // Store the length.
                            let last := add(add(result, 0x20), length)
                            mstore(last, 0) // Zeroize the slot after the string.
                            mstore(0x40, add(last, 0x20)) // Allocate the 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)
                        let o := add(result, 0x20)
                        mstore(o, s)
                        mstore(add(o, n), 0)
                        mstore(0x40, add(result, 0x40))
                    }
                }
                /// @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 {
                        let end := add(s, mload(s))
                        result := add(mload(0x40), 0x20)
                        // Store the bytes of the packed offsets and strides into the scratch space.
                        // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                        mstore(0x1f, 0x900094)
                        mstore(0x08, 0xc0000000a6ab)
                        // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                        mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
                        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(result, c)
                                result := add(result, 1)
                                continue
                            }
                            let t := shr(248, mload(c))
                            mstore(result, mload(and(t, 0x1f)))
                            result := add(result, shr(5, t))
                        }
                        let last := result
                        mstore(last, 0) // Zeroize the slot after the string.
                        result := mload(0x40)
                        mstore(result, sub(last, add(result, 0x20))) // Store the length.
                        mstore(0x40, add(last, 0x20)) // Allocate the 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 {
                        let end := add(s, mload(s))
                        result := add(mload(0x40), 0x20)
                        if addDoubleQuotes {
                            mstore8(result, 34)
                            result := add(1, result)
                        }
                        // 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 {} iszero(eq(s, end)) {} {
                            s := add(s, 1)
                            let c := and(mload(s), 0xff)
                            if iszero(lt(c, 0x20)) {
                                if iszero(and(shl(c, 1), e)) {
                                    // Not in `["\\"","\\\\"]`.
                                    mstore8(result, c)
                                    result := add(result, 1)
                                    continue
                                }
                                mstore8(result, 0x5c) // "\\\\".
                                mstore8(add(result, 1), c)
                                result := add(result, 2)
                                continue
                            }
                            if iszero(and(shl(c, 1), 0x3700)) {
                                // Not in `["\\b","\\t","\
            ","\\f","\\d"]`.
                                mstore8(0x1d, mload(shr(4, c))) // Hex value.
                                mstore8(0x1e, mload(and(c, 15))) // Hex value.
                                mstore(result, mload(0x19)) // "\\\\u00XX".
                                result := add(result, 6)
                                continue
                            }
                            mstore8(result, 0x5c) // "\\\\".
                            mstore8(add(result, 1), mload(add(c, 8)))
                            result := add(result, 2)
                        }
                        if addDoubleQuotes {
                            mstore8(result, 34)
                            result := add(1, result)
                        }
                        let last := result
                        mstore(last, 0) // Zeroize the slot after the string.
                        result := mload(0x40)
                        mstore(result, sub(last, add(result, 0x20))) // Store the length.
                        mstore(0x40, add(last, 0x20)) // Allocate the 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 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 {
                        // Grab the free memory pointer.
                        result := mload(0x40)
                        // Allocate 2 words (1 for the length, 1 for the bytes).
                        mstore(0x40, add(result, 0x40))
                        // Zeroize the length slot.
                        mstore(result, 0)
                        // Store the length and bytes.
                        mstore(add(result, 0x1f), packed)
                        // Right pad with zeroes.
                        mstore(add(add(result, 0x20), mload(result)), 0)
                    }
                }
                /// @dev Packs two strings with their lengths into a single word.
                /// Returns `bytes32(0)` if combined length is zero or greater than 30.
                function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let aLength := mload(a)
                        // We don't need to zero right pad the strings,
                        // since this is our own custom non-standard packing scheme.
                        result :=
                            mul(
                                // Load the length and the bytes of `a` and `b`.
                                or(
                                    shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                                    mload(sub(add(b, 0x1e), aLength))
                                ),
                                // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                                // Assumes that the lengths are valid and within the block gas limit.
                                lt(sub(add(aLength, mload(b)), 1), 0x1e)
                            )
                    }
                }
                /// @dev Unpacks strings packed using {packTwo}.
                /// Returns the empty strings if `packed` is `bytes32(0)`.
                /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
                function unpackTwo(bytes32 packed)
                    internal
                    pure
                    returns (string memory resultA, string memory resultB)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Grab the free memory pointer.
                        resultA := mload(0x40)
                        resultB := add(resultA, 0x40)
                        // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
                        mstore(0x40, add(resultB, 0x40))
                        // Zeroize the length slots.
                        mstore(resultA, 0)
                        mstore(resultB, 0)
                        // Store the lengths and bytes.
                        mstore(add(resultA, 0x1f), packed)
                        mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
                        // Right pad with zeroes.
                        mstore(add(add(resultA, 0x20), mload(resultA)), 0)
                        mstore(add(add(resultB, 0x20), mload(resultB)), 0)
                    }
                }
                /// @dev Directly returns `a` without copying.
                function directReturn(string memory a) internal pure {
                    assembly {
                        // Assumes that the string does not start from the scratch space.
                        let retStart := sub(a, 0x20)
                        let retSize := add(mload(a), 0x40)
                        // Right pad with zeroes. Just in case the string is produced
                        // by a method that doesn't zero right pad.
                        mstore(add(retStart, retSize), 0)
                        // Store the return offset.
                        mstore(retStart, 0x20)
                        // End the transaction, returning the string.
                        return(retStart, retSize)
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
            /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
            ///
            /// @dev Note:
            /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
            /// - For ERC20s, this implementation won't check that a token has code,
            ///   responsibility is delegated to the caller.
            library SafeTransferLib {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       CUSTOM ERRORS                        */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The ETH transfer has failed.
                error ETHTransferFailed();
                /// @dev The ERC20 `transferFrom` has failed.
                error TransferFromFailed();
                /// @dev The ERC20 `transfer` has failed.
                error TransferFailed();
                /// @dev The ERC20 `approve` has failed.
                error ApproveFailed();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         CONSTANTS                          */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
                uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
                /// @dev Suggested gas stipend for contract receiving ETH to perform a few
                /// storage reads and writes, but low enough to prevent griefing.
                uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       ETH OPERATIONS                       */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
                //
                // The regular variants:
                // - Forwards all remaining gas to the target.
                // - Reverts if the target reverts.
                // - Reverts if the current contract has insufficient balance.
                //
                // The force variants:
                // - Forwards with an optional gas stipend
                //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
                // - If the target reverts, or if the gas stipend is exhausted,
                //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
                //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
                // - Reverts if the current contract has insufficient balance.
                //
                // The try variants:
                // - Forwards with a mandatory gas stipend.
                // - Instead of reverting, returns whether the transfer succeeded.
                /// @dev Sends `amount` (in wei) ETH to `to`.
                function safeTransferETH(address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
                /// @dev Sends all the ETH in the current contract to `to`.
                function safeTransferAllETH(address to) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Transfer all the ETH and check if it succeeded or not.
                        if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
                /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
                function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if lt(selfbalance(), amount) {
                            mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                        if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, to) // Store the address in scratch space.
                            mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                            mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                            if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                        }
                    }
                }
                /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
                function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, to) // Store the address in scratch space.
                            mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                            mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                            if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                        }
                    }
                }
                /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
                function forceSafeTransferETH(address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if lt(selfbalance(), amount) {
                            mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                        if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, to) // Store the address in scratch space.
                            mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                            mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                            if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                        }
                    }
                }
                /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
                function forceSafeTransferAllETH(address to) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // forgefmt: disable-next-item
                        if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                            mstore(0x00, to) // Store the address in scratch space.
                            mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                            mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                            if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
                        }
                    }
                }
                /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
                function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
                    internal
                    returns (bool success)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
                    }
                }
                /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
                function trySafeTransferAllETH(address to, uint256 gasStipend)
                    internal
                    returns (bool success)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                      ERC20 OPERATIONS                      */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
                /// Reverts upon failure.
                ///
                /// The `from` account must have at least `amount` approved for
                /// the current contract to manage.
                function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x60, amount) // Store the `amount` argument.
                        mstore(0x40, to) // Store the `to` argument.
                        mstore(0x2c, shl(96, from)) // Store the `from` argument.
                        mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
                        // Perform the transfer, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x60, 0) // Restore the zero slot to zero.
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /// @dev Sends all of ERC20 `token` from `from` to `to`.
                /// Reverts upon failure.
                ///
                /// The `from` account must have their entire balance approved for
                /// the current contract to manage.
                function safeTransferAllFrom(address token, address from, address to)
                    internal
                    returns (uint256 amount)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        let m := mload(0x40) // Cache the free memory pointer.
                        mstore(0x40, to) // Store the `to` argument.
                        mstore(0x2c, shl(96, from)) // Store the `from` argument.
                        mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                        // Read the balance, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                                staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
                        amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
                        // Perform the transfer, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x60, 0) // Restore the zero slot to zero.
                        mstore(0x40, m) // Restore the free memory pointer.
                    }
                }
                /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
                /// Reverts upon failure.
                function safeTransfer(address token, address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x14, to) // Store the `to` argument.
                        mstore(0x34, amount) // Store the `amount` argument.
                        mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                        // Perform the transfer, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                    }
                }
                /// @dev Sends all of ERC20 `token` from the current contract to `to`.
                /// Reverts upon failure.
                function safeTransferAll(address token, address to) internal returns (uint256 amount) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
                        mstore(0x20, address()) // Store the address of the current contract.
                        // Read the balance, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                                staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x14, to) // Store the `to` argument.
                        amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
                        mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
                        // Perform the transfer, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                    }
                }
                /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
                /// Reverts upon failure.
                function safeApprove(address token, address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x14, to) // Store the `to` argument.
                        mstore(0x34, amount) // Store the `amount` argument.
                        mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                        // Perform the approval, reverting upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                        mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                    }
                }
                /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
                /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
                /// then retries the approval again (some tokens, e.g. USDT, requires this).
                /// Reverts upon failure.
                function safeApproveWithRetry(address token, address to, uint256 amount) internal {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x14, to) // Store the `to` argument.
                        mstore(0x34, amount) // Store the `amount` argument.
                        mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                        // Perform the approval, retrying upon failure.
                        if iszero(
                            and( // The arguments of `and` are evaluated from right to left.
                                or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                            )
                        ) {
                            mstore(0x34, 0) // Store 0 for the `amount`.
                            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                            pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                            mstore(0x34, amount) // Store back the original `amount`.
                            // Retry the approval, reverting upon failure.
                            if iszero(
                                and(
                                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                                )
                            ) {
                                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
                    }
                }
                /// @dev Returns the amount of ERC20 `token` owned by `account`.
                /// Returns zero if the `token` does not exist.
                function balanceOf(address token, address account) internal view returns (uint256 amount) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x14, account) // Store the `account` argument.
                        mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
                        amount :=
                            mul(
                                mload(0x20),
                                and( // The arguments of `and` are evaluated from right to left.
                                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                                    staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                                )
                            )
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Library for burning gas without reverting.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/GasBurnerLib.sol)
            library GasBurnerLib {
                /// @dev Burns approximately `x` amount of gas.
                /// Intended for Contract Secured Revenue (CSR).
                ///
                /// Recommendation: pass in an admin-controlled dynamic value instead of a hardcoded one.
                /// This is so that you can adjust your contract as needed depending on market conditions,
                /// and to give you and your users a leeway in case the L2 chain change the rules.
                function burn(uint256 x) internal pure {
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore(0x10, or(1, x))
                        let n := mul(gt(x, 120), div(x, 91))
                        // We use keccak256 instead of blake2f precompile for better widespread compatibility.
                        for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
                            mstore(0x10, keccak256(0x10, 0x10)) // Yes.
                        }
                        if iszero(mload(0x10)) { invalid() }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.4;
            /// @notice Simple single owner authorization mixin.
            /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
            ///
            /// @dev Note:
            /// This implementation does NOT auto-initialize the owner to `msg.sender`.
            /// You MUST call the `_initializeOwner` in the constructor / initializer.
            ///
            /// While the ownable portion follows
            /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
            /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
            abstract contract Ownable {
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                       CUSTOM ERRORS                        */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The caller is not authorized to call the function.
                error Unauthorized();
                /// @dev The `newOwner` cannot be the zero address.
                error NewOwnerIsZeroAddress();
                /// @dev The `pendingOwner` does not have a valid handover request.
                error NoHandoverRequest();
                /// @dev Cannot double-initialize.
                error AlreadyInitialized();
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                           EVENTS                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
                /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
                /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
                /// despite it not being as lightweight as a single argument event.
                event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
                /// @dev An ownership handover to `pendingOwner` has been requested.
                event OwnershipHandoverRequested(address indexed pendingOwner);
                /// @dev The ownership handover to `pendingOwner` has been canceled.
                event OwnershipHandoverCanceled(address indexed pendingOwner);
                /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
                uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
                    0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
                /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
                uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
                    0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
                /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
                uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
                    0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                          STORAGE                           */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev The owner slot is given by:
                /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
                /// It is intentionally chosen to be a high value
                /// to avoid collision with lower slots.
                /// The choice of manual storage layout is to enable compatibility
                /// with both regular and upgradeable contracts.
                bytes32 internal constant _OWNER_SLOT =
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
                /// The ownership handover slot of `newOwner` is given by:
                /// ```
                ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
                ///     let handoverSlot := keccak256(0x00, 0x20)
                /// ```
                /// It stores the expiry timestamp of the two-step ownership handover.
                uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                     INTERNAL FUNCTIONS                     */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
                function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
                /// @dev Initializes the owner directly without authorization guard.
                /// This function must be called upon initialization,
                /// regardless of whether the contract is upgradeable or not.
                /// This is to enable generalization to both regular and upgradeable contracts,
                /// and to save gas in case the initial owner is not the caller.
                /// For performance reasons, this function will not check if there
                /// is an existing owner.
                function _initializeOwner(address newOwner) internal virtual {
                    if (_guardInitializeOwner()) {
                        /// @solidity memory-safe-assembly
                        assembly {
                            let ownerSlot := _OWNER_SLOT
                            if sload(ownerSlot) {
                                mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                                revert(0x1c, 0x04)
                            }
                            // Clean the upper 96 bits.
                            newOwner := shr(96, shl(96, newOwner))
                            // Store the new value.
                            sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                            // Emit the {OwnershipTransferred} event.
                            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                        }
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            // Clean the upper 96 bits.
                            newOwner := shr(96, shl(96, newOwner))
                            // Store the new value.
                            sstore(_OWNER_SLOT, newOwner)
                            // Emit the {OwnershipTransferred} event.
                            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
                        }
                    }
                }
                /// @dev Sets the owner directly without authorization guard.
                function _setOwner(address newOwner) internal virtual {
                    if (_guardInitializeOwner()) {
                        /// @solidity memory-safe-assembly
                        assembly {
                            let ownerSlot := _OWNER_SLOT
                            // Clean the upper 96 bits.
                            newOwner := shr(96, shl(96, newOwner))
                            // Emit the {OwnershipTransferred} event.
                            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                            // Store the new value.
                            sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                        }
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            let ownerSlot := _OWNER_SLOT
                            // Clean the upper 96 bits.
                            newOwner := shr(96, shl(96, newOwner))
                            // Emit the {OwnershipTransferred} event.
                            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                            // Store the new value.
                            sstore(ownerSlot, newOwner)
                        }
                    }
                }
                /// @dev Throws if the sender is not the owner.
                function _checkOwner() internal view virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // If the caller is not the stored owner, revert.
                        if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                            mstore(0x00, 0x82b42900) // `Unauthorized()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
                /// @dev Returns how long a two-step ownership handover is valid for in seconds.
                /// Override to return a different value if needed.
                /// Made internal to conserve bytecode. Wrap it in a public function if needed.
                function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
                    return 48 * 3600;
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                  PUBLIC UPDATE FUNCTIONS                   */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Allows the owner to transfer the ownership to `newOwner`.
                function transferOwnership(address newOwner) public payable virtual onlyOwner {
                    /// @solidity memory-safe-assembly
                    assembly {
                        if iszero(shl(96, newOwner)) {
                            mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                            revert(0x1c, 0x04)
                        }
                    }
                    _setOwner(newOwner);
                }
                /// @dev Allows the owner to renounce their ownership.
                function renounceOwnership() public payable virtual onlyOwner {
                    _setOwner(address(0));
                }
                /// @dev Request a two-step ownership handover to the caller.
                /// The request will automatically expire in 48 hours (172800 seconds) by default.
                function requestOwnershipHandover() public payable virtual {
                    unchecked {
                        uint256 expires = block.timestamp + _ownershipHandoverValidFor();
                        /// @solidity memory-safe-assembly
                        assembly {
                            // Compute and set the handover slot to `expires`.
                            mstore(0x0c, _HANDOVER_SLOT_SEED)
                            mstore(0x00, caller())
                            sstore(keccak256(0x0c, 0x20), expires)
                            // Emit the {OwnershipHandoverRequested} event.
                            log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                        }
                    }
                }
                /// @dev Cancels the two-step ownership handover to the caller, if any.
                function cancelOwnershipHandover() public payable virtual {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute and set the handover slot to 0.
                        mstore(0x0c, _HANDOVER_SLOT_SEED)
                        mstore(0x00, caller())
                        sstore(keccak256(0x0c, 0x20), 0)
                        // Emit the {OwnershipHandoverCanceled} event.
                        log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
                    }
                }
                /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
                /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
                function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute and set the handover slot to 0.
                        mstore(0x0c, _HANDOVER_SLOT_SEED)
                        mstore(0x00, pendingOwner)
                        let handoverSlot := keccak256(0x0c, 0x20)
                        // If the handover does not exist, or has expired.
                        if gt(timestamp(), sload(handoverSlot)) {
                            mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                            revert(0x1c, 0x04)
                        }
                        // Set the handover slot to 0.
                        sstore(handoverSlot, 0)
                    }
                    _setOwner(pendingOwner);
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                   PUBLIC READ FUNCTIONS                    */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Returns the owner of the contract.
                function owner() public view virtual returns (address result) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := sload(_OWNER_SLOT)
                    }
                }
                /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
                function ownershipHandoverExpiresAt(address pendingOwner)
                    public
                    view
                    virtual
                    returns (uint256 result)
                {
                    /// @solidity memory-safe-assembly
                    assembly {
                        // Compute the handover slot.
                        mstore(0x0c, _HANDOVER_SLOT_SEED)
                        mstore(0x00, pendingOwner)
                        // Load the handover slot.
                        result := sload(keccak256(0x0c, 0x20))
                    }
                }
                /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
                /*                         MODIFIERS                          */
                /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
                /// @dev Marks a function as only callable by the owner.
                modifier onlyOwner() virtual {
                    _checkOwner();
                    _;
                }
            }
            

            File 4 of 5: WETH9
            // Copyright (C) 2015, 2016, 2017 Dapphub
            
            // This program is free software: you can redistribute it and/or modify
            // it under the terms of the GNU General Public License as published by
            // the Free Software Foundation, either version 3 of the License, or
            // (at your option) any later version.
            
            // This program is distributed in the hope that it will be useful,
            // but WITHOUT ANY WARRANTY; without even the implied warranty of
            // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
            // GNU General Public License for more details.
            
            // You should have received a copy of the GNU General Public License
            // along with this program.  If not, see <http://www.gnu.org/licenses/>.
            
            pragma solidity ^0.4.18;
            
            contract WETH9 {
                string public name     = "Wrapped Ether";
                string public symbol   = "WETH";
                uint8  public decimals = 18;
            
                event  Approval(address indexed src, address indexed guy, uint wad);
                event  Transfer(address indexed src, address indexed dst, uint wad);
                event  Deposit(address indexed dst, uint wad);
                event  Withdrawal(address indexed src, uint wad);
            
                mapping (address => uint)                       public  balanceOf;
                mapping (address => mapping (address => uint))  public  allowance;
            
                function() public payable {
                    deposit();
                }
                function deposit() public payable {
                    balanceOf[msg.sender] += msg.value;
                    Deposit(msg.sender, msg.value);
                }
                function withdraw(uint wad) public {
                    require(balanceOf[msg.sender] >= wad);
                    balanceOf[msg.sender] -= wad;
                    msg.sender.transfer(wad);
                    Withdrawal(msg.sender, wad);
                }
            
                function totalSupply() public view returns (uint) {
                    return this.balance;
                }
            
                function approve(address guy, uint wad) public returns (bool) {
                    allowance[msg.sender][guy] = wad;
                    Approval(msg.sender, guy, wad);
                    return true;
                }
            
                function transfer(address dst, uint wad) public returns (bool) {
                    return transferFrom(msg.sender, dst, wad);
                }
            
                function transferFrom(address src, address dst, uint wad)
                    public
                    returns (bool)
                {
                    require(balanceOf[src] >= wad);
            
                    if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                        require(allowance[src][msg.sender] >= wad);
                        allowance[src][msg.sender] -= wad;
                    }
            
                    balanceOf[src] -= wad;
                    balanceOf[dst] += wad;
            
                    Transfer(src, dst, wad);
            
                    return true;
                }
            }
            
            
            /*
                                GNU GENERAL PUBLIC LICENSE
                                   Version 3, 29 June 2007
            
             Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
             Everyone is permitted to copy and distribute verbatim copies
             of this license document, but changing it is not allowed.
            
                                        Preamble
            
              The GNU General Public License is a free, copyleft license for
            software and other kinds of works.
            
              The licenses for most software and other practical works are designed
            to take away your freedom to share and change the works.  By contrast,
            the GNU General Public License is intended to guarantee your freedom to
            share and change all versions of a program--to make sure it remains free
            software for all its users.  We, the Free Software Foundation, use the
            GNU General Public License for most of our software; it applies also to
            any other work released this way by its authors.  You can apply it to
            your programs, too.
            
              When we speak of free software, we are referring to freedom, not
            price.  Our General Public Licenses are designed to make sure that you
            have the freedom to distribute copies of free software (and charge for
            them if you wish), that you receive source code or can get it if you
            want it, that you can change the software or use pieces of it in new
            free programs, and that you know you can do these things.
            
              To protect your rights, we need to prevent others from denying you
            these rights or asking you to surrender the rights.  Therefore, you have
            certain responsibilities if you distribute copies of the software, or if
            you modify it: responsibilities to respect the freedom of others.
            
              For example, if you distribute copies of such a program, whether
            gratis or for a fee, you must pass on to the recipients the same
            freedoms that you received.  You must make sure that they, too, receive
            or can get the source code.  And you must show them these terms so they
            know their rights.
            
              Developers that use the GNU GPL protect your rights with two steps:
            (1) assert copyright on the software, and (2) offer you this License
            giving you legal permission to copy, distribute and/or modify it.
            
              For the developers' and authors' protection, the GPL clearly explains
            that there is no warranty for this free software.  For both users' and
            authors' sake, the GPL requires that modified versions be marked as
            changed, so that their problems will not be attributed erroneously to
            authors of previous versions.
            
              Some devices are designed to deny users access to install or run
            modified versions of the software inside them, although the manufacturer
            can do so.  This is fundamentally incompatible with the aim of
            protecting users' freedom to change the software.  The systematic
            pattern of such abuse occurs in the area of products for individuals to
            use, which is precisely where it is most unacceptable.  Therefore, we
            have designed this version of the GPL to prohibit the practice for those
            products.  If such problems arise substantially in other domains, we
            stand ready to extend this provision to those domains in future versions
            of the GPL, as needed to protect the freedom of users.
            
              Finally, every program is threatened constantly by software patents.
            States should not allow patents to restrict development and use of
            software on general-purpose computers, but in those that do, we wish to
            avoid the special danger that patents applied to a free program could
            make it effectively proprietary.  To prevent this, the GPL assures that
            patents cannot be used to render the program non-free.
            
              The precise terms and conditions for copying, distribution and
            modification follow.
            
                                   TERMS AND CONDITIONS
            
              0. Definitions.
            
              "This License" refers to version 3 of the GNU General Public License.
            
              "Copyright" also means copyright-like laws that apply to other kinds of
            works, such as semiconductor masks.
            
              "The Program" refers to any copyrightable work licensed under this
            License.  Each licensee is addressed as "you".  "Licensees" and
            "recipients" may be individuals or organizations.
            
              To "modify" a work means to copy from or adapt all or part of the work
            in a fashion requiring copyright permission, other than the making of an
            exact copy.  The resulting work is called a "modified version" of the
            earlier work or a work "based on" the earlier work.
            
              A "covered work" means either the unmodified Program or a work based
            on the Program.
            
              To "propagate" a work means to do anything with it that, without
            permission, would make you directly or secondarily liable for
            infringement under applicable copyright law, except executing it on a
            computer or modifying a private copy.  Propagation includes copying,
            distribution (with or without modification), making available to the
            public, and in some countries other activities as well.
            
              To "convey" a work means any kind of propagation that enables other
            parties to make or receive copies.  Mere interaction with a user through
            a computer network, with no transfer of a copy, is not conveying.
            
              An interactive user interface displays "Appropriate Legal Notices"
            to the extent that it includes a convenient and prominently visible
            feature that (1) displays an appropriate copyright notice, and (2)
            tells the user that there is no warranty for the work (except to the
            extent that warranties are provided), that licensees may convey the
            work under this License, and how to view a copy of this License.  If
            the interface presents a list of user commands or options, such as a
            menu, a prominent item in the list meets this criterion.
            
              1. Source Code.
            
              The "source code" for a work means the preferred form of the work
            for making modifications to it.  "Object code" means any non-source
            form of a work.
            
              A "Standard Interface" means an interface that either is an official
            standard defined by a recognized standards body, or, in the case of
            interfaces specified for a particular programming language, one that
            is widely used among developers working in that language.
            
              The "System Libraries" of an executable work include anything, other
            than the work as a whole, that (a) is included in the normal form of
            packaging a Major Component, but which is not part of that Major
            Component, and (b) serves only to enable use of the work with that
            Major Component, or to implement a Standard Interface for which an
            implementation is available to the public in source code form.  A
            "Major Component", in this context, means a major essential component
            (kernel, window system, and so on) of the specific operating system
            (if any) on which the executable work runs, or a compiler used to
            produce the work, or an object code interpreter used to run it.
            
              The "Corresponding Source" for a work in object code form means all
            the source code needed to generate, install, and (for an executable
            work) run the object code and to modify the work, including scripts to
            control those activities.  However, it does not include the work's
            System Libraries, or general-purpose tools or generally available free
            programs which are used unmodified in performing those activities but
            which are not part of the work.  For example, Corresponding Source
            includes interface definition files associated with source files for
            the work, and the source code for shared libraries and dynamically
            linked subprograms that the work is specifically designed to require,
            such as by intimate data communication or control flow between those
            subprograms and other parts of the work.
            
              The Corresponding Source need not include anything that users
            can regenerate automatically from other parts of the Corresponding
            Source.
            
              The Corresponding Source for a work in source code form is that
            same work.
            
              2. Basic Permissions.
            
              All rights granted under this License are granted for the term of
            copyright on the Program, and are irrevocable provided the stated
            conditions are met.  This License explicitly affirms your unlimited
            permission to run the unmodified Program.  The output from running a
            covered work is covered by this License only if the output, given its
            content, constitutes a covered work.  This License acknowledges your
            rights of fair use or other equivalent, as provided by copyright law.
            
              You may make, run and propagate covered works that you do not
            convey, without conditions so long as your license otherwise remains
            in force.  You may convey covered works to others for the sole purpose
            of having them make modifications exclusively for you, or provide you
            with facilities for running those works, provided that you comply with
            the terms of this License in conveying all material for which you do
            not control copyright.  Those thus making or running the covered works
            for you must do so exclusively on your behalf, under your direction
            and control, on terms that prohibit them from making any copies of
            your copyrighted material outside their relationship with you.
            
              Conveying under any other circumstances is permitted solely under
            the conditions stated below.  Sublicensing is not allowed; section 10
            makes it unnecessary.
            
              3. Protecting Users' Legal Rights From Anti-Circumvention Law.
            
              No covered work shall be deemed part of an effective technological
            measure under any applicable law fulfilling obligations under article
            11 of the WIPO copyright treaty adopted on 20 December 1996, or
            similar laws prohibiting or restricting circumvention of such
            measures.
            
              When you convey a covered work, you waive any legal power to forbid
            circumvention of technological measures to the extent such circumvention
            is effected by exercising rights under this License with respect to
            the covered work, and you disclaim any intention to limit operation or
            modification of the work as a means of enforcing, against the work's
            users, your or third parties' legal rights to forbid circumvention of
            technological measures.
            
              4. Conveying Verbatim Copies.
            
              You may convey verbatim copies of the Program's source code as you
            receive it, in any medium, provided that you conspicuously and
            appropriately publish on each copy an appropriate copyright notice;
            keep intact all notices stating that this License and any
            non-permissive terms added in accord with section 7 apply to the code;
            keep intact all notices of the absence of any warranty; and give all
            recipients a copy of this License along with the Program.
            
              You may charge any price or no price for each copy that you convey,
            and you may offer support or warranty protection for a fee.
            
              5. Conveying Modified Source Versions.
            
              You may convey a work based on the Program, or the modifications to
            produce it from the Program, in the form of source code under the
            terms of section 4, provided that you also meet all of these conditions:
            
                a) The work must carry prominent notices stating that you modified
                it, and giving a relevant date.
            
                b) The work must carry prominent notices stating that it is
                released under this License and any conditions added under section
                7.  This requirement modifies the requirement in section 4 to
                "keep intact all notices".
            
                c) You must license the entire work, as a whole, under this
                License to anyone who comes into possession of a copy.  This
                License will therefore apply, along with any applicable section 7
                additional terms, to the whole of the work, and all its parts,
                regardless of how they are packaged.  This License gives no
                permission to license the work in any other way, but it does not
                invalidate such permission if you have separately received it.
            
                d) If the work has interactive user interfaces, each must display
                Appropriate Legal Notices; however, if the Program has interactive
                interfaces that do not display Appropriate Legal Notices, your
                work need not make them do so.
            
              A compilation of a covered work with other separate and independent
            works, which are not by their nature extensions of the covered work,
            and which are not combined with it such as to form a larger program,
            in or on a volume of a storage or distribution medium, is called an
            "aggregate" if the compilation and its resulting copyright are not
            used to limit the access or legal rights of the compilation's users
            beyond what the individual works permit.  Inclusion of a covered work
            in an aggregate does not cause this License to apply to the other
            parts of the aggregate.
            
              6. Conveying Non-Source Forms.
            
              You may convey a covered work in object code form under the terms
            of sections 4 and 5, provided that you also convey the
            machine-readable Corresponding Source under the terms of this License,
            in one of these ways:
            
                a) Convey the object code in, or embodied in, a physical product
                (including a physical distribution medium), accompanied by the
                Corresponding Source fixed on a durable physical medium
                customarily used for software interchange.
            
                b) Convey the object code in, or embodied in, a physical product
                (including a physical distribution medium), accompanied by a
                written offer, valid for at least three years and valid for as
                long as you offer spare parts or customer support for that product
                model, to give anyone who possesses the object code either (1) a
                copy of the Corresponding Source for all the software in the
                product that is covered by this License, on a durable physical
                medium customarily used for software interchange, for a price no
                more than your reasonable cost of physically performing this
                conveying of source, or (2) access to copy the
                Corresponding Source from a network server at no charge.
            
                c) Convey individual copies of the object code with a copy of the
                written offer to provide the Corresponding Source.  This
                alternative is allowed only occasionally and noncommercially, and
                only if you received the object code with such an offer, in accord
                with subsection 6b.
            
                d) Convey the object code by offering access from a designated
                place (gratis or for a charge), and offer equivalent access to the
                Corresponding Source in the same way through the same place at no
                further charge.  You need not require recipients to copy the
                Corresponding Source along with the object code.  If the place to
                copy the object code is a network server, the Corresponding Source
                may be on a different server (operated by you or a third party)
                that supports equivalent copying facilities, provided you maintain
                clear directions next to the object code saying where to find the
                Corresponding Source.  Regardless of what server hosts the
                Corresponding Source, you remain obligated to ensure that it is
                available for as long as needed to satisfy these requirements.
            
                e) Convey the object code using peer-to-peer transmission, provided
                you inform other peers where the object code and Corresponding
                Source of the work are being offered to the general public at no
                charge under subsection 6d.
            
              A separable portion of the object code, whose source code is excluded
            from the Corresponding Source as a System Library, need not be
            included in conveying the object code work.
            
              A "User Product" is either (1) a "consumer product", which means any
            tangible personal property which is normally used for personal, family,
            or household purposes, or (2) anything designed or sold for incorporation
            into a dwelling.  In determining whether a product is a consumer product,
            doubtful cases shall be resolved in favor of coverage.  For a particular
            product received by a particular user, "normally used" refers to a
            typical or common use of that class of product, regardless of the status
            of the particular user or of the way in which the particular user
            actually uses, or expects or is expected to use, the product.  A product
            is a consumer product regardless of whether the product has substantial
            commercial, industrial or non-consumer uses, unless such uses represent
            the only significant mode of use of the product.
            
              "Installation Information" for a User Product means any methods,
            procedures, authorization keys, or other information required to install
            and execute modified versions of a covered work in that User Product from
            a modified version of its Corresponding Source.  The information must
            suffice to ensure that the continued functioning of the modified object
            code is in no case prevented or interfered with solely because
            modification has been made.
            
              If you convey an object code work under this section in, or with, or
            specifically for use in, a User Product, and the conveying occurs as
            part of a transaction in which the right of possession and use of the
            User Product is transferred to the recipient in perpetuity or for a
            fixed term (regardless of how the transaction is characterized), the
            Corresponding Source conveyed under this section must be accompanied
            by the Installation Information.  But this requirement does not apply
            if neither you nor any third party retains the ability to install
            modified object code on the User Product (for example, the work has
            been installed in ROM).
            
              The requirement to provide Installation Information does not include a
            requirement to continue to provide support service, warranty, or updates
            for a work that has been modified or installed by the recipient, or for
            the User Product in which it has been modified or installed.  Access to a
            network may be denied when the modification itself materially and
            adversely affects the operation of the network or violates the rules and
            protocols for communication across the network.
            
              Corresponding Source conveyed, and Installation Information provided,
            in accord with this section must be in a format that is publicly
            documented (and with an implementation available to the public in
            source code form), and must require no special password or key for
            unpacking, reading or copying.
            
              7. Additional Terms.
            
              "Additional permissions" are terms that supplement the terms of this
            License by making exceptions from one or more of its conditions.
            Additional permissions that are applicable to the entire Program shall
            be treated as though they were included in this License, to the extent
            that they are valid under applicable law.  If additional permissions
            apply only to part of the Program, that part may be used separately
            under those permissions, but the entire Program remains governed by
            this License without regard to the additional permissions.
            
              When you convey a copy of a covered work, you may at your option
            remove any additional permissions from that copy, or from any part of
            it.  (Additional permissions may be written to require their own
            removal in certain cases when you modify the work.)  You may place
            additional permissions on material, added by you to a covered work,
            for which you have or can give appropriate copyright permission.
            
              Notwithstanding any other provision of this License, for material you
            add to a covered work, you may (if authorized by the copyright holders of
            that material) supplement the terms of this License with terms:
            
                a) Disclaiming warranty or limiting liability differently from the
                terms of sections 15 and 16 of this License; or
            
                b) Requiring preservation of specified reasonable legal notices or
                author attributions in that material or in the Appropriate Legal
                Notices displayed by works containing it; or
            
                c) Prohibiting misrepresentation of the origin of that material, or
                requiring that modified versions of such material be marked in
                reasonable ways as different from the original version; or
            
                d) Limiting the use for publicity purposes of names of licensors or
                authors of the material; or
            
                e) Declining to grant rights under trademark law for use of some
                trade names, trademarks, or service marks; or
            
                f) Requiring indemnification of licensors and authors of that
                material by anyone who conveys the material (or modified versions of
                it) with contractual assumptions of liability to the recipient, for
                any liability that these contractual assumptions directly impose on
                those licensors and authors.
            
              All other non-permissive additional terms are considered "further
            restrictions" within the meaning of section 10.  If the Program as you
            received it, or any part of it, contains a notice stating that it is
            governed by this License along with a term that is a further
            restriction, you may remove that term.  If a license document contains
            a further restriction but permits relicensing or conveying under this
            License, you may add to a covered work material governed by the terms
            of that license document, provided that the further restriction does
            not survive such relicensing or conveying.
            
              If you add terms to a covered work in accord with this section, you
            must place, in the relevant source files, a statement of the
            additional terms that apply to those files, or a notice indicating
            where to find the applicable terms.
            
              Additional terms, permissive or non-permissive, may be stated in the
            form of a separately written license, or stated as exceptions;
            the above requirements apply either way.
            
              8. Termination.
            
              You may not propagate or modify a covered work except as expressly
            provided under this License.  Any attempt otherwise to propagate or
            modify it is void, and will automatically terminate your rights under
            this License (including any patent licenses granted under the third
            paragraph of section 11).
            
              However, if you cease all violation of this License, then your
            license from a particular copyright holder is reinstated (a)
            provisionally, unless and until the copyright holder explicitly and
            finally terminates your license, and (b) permanently, if the copyright
            holder fails to notify you of the violation by some reasonable means
            prior to 60 days after the cessation.
            
              Moreover, your license from a particular copyright holder is
            reinstated permanently if the copyright holder notifies you of the
            violation by some reasonable means, this is the first time you have
            received notice of violation of this License (for any work) from that
            copyright holder, and you cure the violation prior to 30 days after
            your receipt of the notice.
            
              Termination of your rights under this section does not terminate the
            licenses of parties who have received copies or rights from you under
            this License.  If your rights have been terminated and not permanently
            reinstated, you do not qualify to receive new licenses for the same
            material under section 10.
            
              9. Acceptance Not Required for Having Copies.
            
              You are not required to accept this License in order to receive or
            run a copy of the Program.  Ancillary propagation of a covered work
            occurring solely as a consequence of using peer-to-peer transmission
            to receive a copy likewise does not require acceptance.  However,
            nothing other than this License grants you permission to propagate or
            modify any covered work.  These actions infringe copyright if you do
            not accept this License.  Therefore, by modifying or propagating a
            covered work, you indicate your acceptance of this License to do so.
            
              10. Automatic Licensing of Downstream Recipients.
            
              Each time you convey a covered work, the recipient automatically
            receives a license from the original licensors, to run, modify and
            propagate that work, subject to this License.  You are not responsible
            for enforcing compliance by third parties with this License.
            
              An "entity transaction" is a transaction transferring control of an
            organization, or substantially all assets of one, or subdividing an
            organization, or merging organizations.  If propagation of a covered
            work results from an entity transaction, each party to that
            transaction who receives a copy of the work also receives whatever
            licenses to the work the party's predecessor in interest had or could
            give under the previous paragraph, plus a right to possession of the
            Corresponding Source of the work from the predecessor in interest, if
            the predecessor has it or can get it with reasonable efforts.
            
              You may not impose any further restrictions on the exercise of the
            rights granted or affirmed under this License.  For example, you may
            not impose a license fee, royalty, or other charge for exercise of
            rights granted under this License, and you may not initiate litigation
            (including a cross-claim or counterclaim in a lawsuit) alleging that
            any patent claim is infringed by making, using, selling, offering for
            sale, or importing the Program or any portion of it.
            
              11. Patents.
            
              A "contributor" is a copyright holder who authorizes use under this
            License of the Program or a work on which the Program is based.  The
            work thus licensed is called the contributor's "contributor version".
            
              A contributor's "essential patent claims" are all patent claims
            owned or controlled by the contributor, whether already acquired or
            hereafter acquired, that would be infringed by some manner, permitted
            by this License, of making, using, or selling its contributor version,
            but do not include claims that would be infringed only as a
            consequence of further modification of the contributor version.  For
            purposes of this definition, "control" includes the right to grant
            patent sublicenses in a manner consistent with the requirements of
            this License.
            
              Each contributor grants you a non-exclusive, worldwide, royalty-free
            patent license under the contributor's essential patent claims, to
            make, use, sell, offer for sale, import and otherwise run, modify and
            propagate the contents of its contributor version.
            
              In the following three paragraphs, a "patent license" is any express
            agreement or commitment, however denominated, not to enforce a patent
            (such as an express permission to practice a patent or covenant not to
            sue for patent infringement).  To "grant" such a patent license to a
            party means to make such an agreement or commitment not to enforce a
            patent against the party.
            
              If you convey a covered work, knowingly relying on a patent license,
            and the Corresponding Source of the work is not available for anyone
            to copy, free of charge and under the terms of this License, through a
            publicly available network server or other readily accessible means,
            then you must either (1) cause the Corresponding Source to be so
            available, or (2) arrange to deprive yourself of the benefit of the
            patent license for this particular work, or (3) arrange, in a manner
            consistent with the requirements of this License, to extend the patent
            license to downstream recipients.  "Knowingly relying" means you have
            actual knowledge that, but for the patent license, your conveying the
            covered work in a country, or your recipient's use of the covered work
            in a country, would infringe one or more identifiable patents in that
            country that you have reason to believe are valid.
            
              If, pursuant to or in connection with a single transaction or
            arrangement, you convey, or propagate by procuring conveyance of, a
            covered work, and grant a patent license to some of the parties
            receiving the covered work authorizing them to use, propagate, modify
            or convey a specific copy of the covered work, then the patent license
            you grant is automatically extended to all recipients of the covered
            work and works based on it.
            
              A patent license is "discriminatory" if it does not include within
            the scope of its coverage, prohibits the exercise of, or is
            conditioned on the non-exercise of one or more of the rights that are
            specifically granted under this License.  You may not convey a covered
            work if you are a party to an arrangement with a third party that is
            in the business of distributing software, under which you make payment
            to the third party based on the extent of your activity of conveying
            the work, and under which the third party grants, to any of the
            parties who would receive the covered work from you, a discriminatory
            patent license (a) in connection with copies of the covered work
            conveyed by you (or copies made from those copies), or (b) primarily
            for and in connection with specific products or compilations that
            contain the covered work, unless you entered into that arrangement,
            or that patent license was granted, prior to 28 March 2007.
            
              Nothing in this License shall be construed as excluding or limiting
            any implied license or other defenses to infringement that may
            otherwise be available to you under applicable patent law.
            
              12. No Surrender of Others' Freedom.
            
              If conditions are imposed on you (whether by court order, agreement or
            otherwise) that contradict the conditions of this License, they do not
            excuse you from the conditions of this License.  If you cannot convey a
            covered work so as to satisfy simultaneously your obligations under this
            License and any other pertinent obligations, then as a consequence you may
            not convey it at all.  For example, if you agree to terms that obligate you
            to collect a royalty for further conveying from those to whom you convey
            the Program, the only way you could satisfy both those terms and this
            License would be to refrain entirely from conveying the Program.
            
              13. Use with the GNU Affero General Public License.
            
              Notwithstanding any other provision of this License, you have
            permission to link or combine any covered work with a work licensed
            under version 3 of the GNU Affero General Public License into a single
            combined work, and to convey the resulting work.  The terms of this
            License will continue to apply to the part which is the covered work,
            but the special requirements of the GNU Affero General Public License,
            section 13, concerning interaction through a network will apply to the
            combination as such.
            
              14. Revised Versions of this License.
            
              The Free Software Foundation may publish revised and/or new versions of
            the GNU General Public License from time to time.  Such new versions will
            be similar in spirit to the present version, but may differ in detail to
            address new problems or concerns.
            
              Each version is given a distinguishing version number.  If the
            Program specifies that a certain numbered version of the GNU General
            Public License "or any later version" applies to it, you have the
            option of following the terms and conditions either of that numbered
            version or of any later version published by the Free Software
            Foundation.  If the Program does not specify a version number of the
            GNU General Public License, you may choose any version ever published
            by the Free Software Foundation.
            
              If the Program specifies that a proxy can decide which future
            versions of the GNU General Public License can be used, that proxy's
            public statement of acceptance of a version permanently authorizes you
            to choose that version for the Program.
            
              Later license versions may give you additional or different
            permissions.  However, no additional obligations are imposed on any
            author or copyright holder as a result of your choosing to follow a
            later version.
            
              15. Disclaimer of Warranty.
            
              THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
            APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
            HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
            OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
            THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
            PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
            IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
            ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
            
              16. Limitation of Liability.
            
              IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
            WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
            THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
            GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
            USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
            DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
            PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
            EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
            SUCH DAMAGES.
            
              17. Interpretation of Sections 15 and 16.
            
              If the disclaimer of warranty and limitation of liability provided
            above cannot be given local legal effect according to their terms,
            reviewing courts shall apply local law that most closely approximates
            an absolute waiver of all civil liability in connection with the
            Program, unless a warranty or assumption of liability accompanies a
            copy of the Program in return for a fee.
            
                                 END OF TERMS AND CONDITIONS
            
                        How to Apply These Terms to Your New Programs
            
              If you develop a new program, and you want it to be of the greatest
            possible use to the public, the best way to achieve this is to make it
            free software which everyone can redistribute and change under these terms.
            
              To do so, attach the following notices to the program.  It is safest
            to attach them to the start of each source file to most effectively
            state the exclusion of warranty; and each file should have at least
            the "copyright" line and a pointer to where the full notice is found.
            
                <one line to give the program's name and a brief idea of what it does.>
                Copyright (C) <year>  <name of author>
            
                This program is free software: you can redistribute it and/or modify
                it under the terms of the GNU General Public License as published by
                the Free Software Foundation, either version 3 of the License, or
                (at your option) any later version.
            
                This program is distributed in the hope that it will be useful,
                but WITHOUT ANY WARRANTY; without even the implied warranty of
                MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                GNU General Public License for more details.
            
                You should have received a copy of the GNU General Public License
                along with this program.  If not, see <http://www.gnu.org/licenses/>.
            
            Also add information on how to contact you by electronic and paper mail.
            
              If the program does terminal interaction, make it output a short
            notice like this when it starts in an interactive mode:
            
                <program>  Copyright (C) <year>  <name of author>
                This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
                This is free software, and you are welcome to redistribute it
                under certain conditions; type `show c' for details.
            
            The hypothetical commands `show w' and `show c' should show the appropriate
            parts of the General Public License.  Of course, your program's commands
            might be different; for a GUI interface, you would use an "about box".
            
              You should also get your employer (if you work as a programmer) or school,
            if any, to sign a "copyright disclaimer" for the program, if necessary.
            For more information on this, and how to apply and follow the GNU GPL, see
            <http://www.gnu.org/licenses/>.
            
              The GNU General Public License does not permit incorporating your program
            into proprietary programs.  If your program is a subroutine library, you
            may consider it more useful to permit linking proprietary applications with
            the library.  If this is what you want to do, use the GNU Lesser General
            Public License instead of this License.  But first, please read
            <http://www.gnu.org/philosophy/why-not-lgpl.html>.
            
            */

            File 5 of 5: UniswapV2Router02
            pragma solidity =0.6.6;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            interface IUniswapV2Router01 {
                function factory() external pure returns (address);
                function WETH() external pure returns (address);
            
                function addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external returns (uint amountA, uint amountB, uint liquidity);
                function addLiquidityETH(
                    address token,
                    uint amountTokenDesired,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
                function removeLiquidity(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external returns (uint amountA, uint amountB);
                function removeLiquidityETH(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external returns (uint amountToken, uint amountETH);
                function removeLiquidityWithPermit(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountA, uint amountB);
                function removeLiquidityETHWithPermit(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountToken, uint amountETH);
                function swapExactTokensForTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external returns (uint[] memory amounts);
                function swapTokensForExactTokens(
                    uint amountOut,
                    uint amountInMax,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external returns (uint[] memory amounts);
                function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    payable
                    returns (uint[] memory amounts);
                function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                    external
                    returns (uint[] memory amounts);
                function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    returns (uint[] memory amounts);
                function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                    external
                    payable
                    returns (uint[] memory amounts);
            
                function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
                function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
                function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
            }
            
            interface IUniswapV2Router02 is IUniswapV2Router01 {
                function removeLiquidityETHSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external returns (uint amountETH);
                function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountETH);
            
                function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external;
                function swapExactETHForTokensSupportingFeeOnTransferTokens(
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external payable;
                function swapExactTokensForETHSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external;
            }
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            interface IWETH {
                function deposit() external payable;
                function transfer(address to, uint value) external returns (bool);
                function withdraw(uint) external;
            }
            
            contract UniswapV2Router02 is IUniswapV2Router02 {
                using SafeMath for uint;
            
                address public immutable override factory;
                address public immutable override WETH;
            
                modifier ensure(uint deadline) {
                    require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
                    _;
                }
            
                constructor(address _factory, address _WETH) public {
                    factory = _factory;
                    WETH = _WETH;
                }
            
                receive() external payable {
                    assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
                }
            
                // **** ADD LIQUIDITY ****
                function _addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin
                ) internal virtual returns (uint amountA, uint amountB) {
                    // create the pair if it doesn't exist yet
                    if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
                        IUniswapV2Factory(factory).createPair(tokenA, tokenB);
                    }
                    (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
                    if (reserveA == 0 && reserveB == 0) {
                        (amountA, amountB) = (amountADesired, amountBDesired);
                    } else {
                        uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
                        if (amountBOptimal <= amountBDesired) {
                            require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                            (amountA, amountB) = (amountADesired, amountBOptimal);
                        } else {
                            uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                            assert(amountAOptimal <= amountADesired);
                            require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                            (amountA, amountB) = (amountAOptimal, amountBDesired);
                        }
                    }
                }
                function addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
                    (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
                    TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
                    liquidity = IUniswapV2Pair(pair).mint(to);
                }
                function addLiquidityETH(
                    address token,
                    uint amountTokenDesired,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
                    (amountToken, amountETH) = _addLiquidity(
                        token,
                        WETH,
                        amountTokenDesired,
                        msg.value,
                        amountTokenMin,
                        amountETHMin
                    );
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
                    IWETH(WETH).deposit{value: amountETH}();
                    assert(IWETH(WETH).transfer(pair, amountETH));
                    liquidity = IUniswapV2Pair(pair).mint(to);
                    // refund dust eth, if any
                    if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
                }
            
                // **** REMOVE LIQUIDITY ****
                function removeLiquidity(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
                    (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
                    (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
                    (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
                    require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                    require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                }
                function removeLiquidityETH(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
                    (amountToken, amountETH) = removeLiquidity(
                        token,
                        WETH,
                        liquidity,
                        amountTokenMin,
                        amountETHMin,
                        address(this),
                        deadline
                    );
                    TransferHelper.safeTransfer(token, to, amountToken);
                    IWETH(WETH).withdraw(amountETH);
                    TransferHelper.safeTransferETH(to, amountETH);
                }
                function removeLiquidityWithPermit(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountA, uint amountB) {
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
                }
                function removeLiquidityETHWithPermit(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountToken, uint amountETH) {
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
                }
            
                // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
                function removeLiquidityETHSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountETH) {
                    (, amountETH) = removeLiquidity(
                        token,
                        WETH,
                        liquidity,
                        amountTokenMin,
                        amountETHMin,
                        address(this),
                        deadline
                    );
                    TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
                    IWETH(WETH).withdraw(amountETH);
                    TransferHelper.safeTransferETH(to, amountETH);
                }
                function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountETH) {
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
                        token, liquidity, amountTokenMin, amountETHMin, to, deadline
                    );
                }
            
                // **** SWAP ****
                // requires the initial amount to have already been sent to the first pair
                function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
                    for (uint i; i < path.length - 1; i++) {
                        (address input, address output) = (path[i], path[i + 1]);
                        (address token0,) = UniswapV2Library.sortTokens(input, output);
                        uint amountOut = amounts[i + 1];
                        (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
                        address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
                        IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
                            amount0Out, amount1Out, to, new bytes(0)
                        );
                    }
                }
                function swapExactTokensForTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
                    amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, to);
                }
                function swapTokensForExactTokens(
                    uint amountOut,
                    uint amountInMax,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, to);
                }
                function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    IWETH(WETH).deposit{value: amounts[0]}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
                    _swap(amounts, path, to);
                }
                function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, address(this));
                    IWETH(WETH).withdraw(amounts[amounts.length - 1]);
                    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
                }
                function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, address(this));
                    IWETH(WETH).withdraw(amounts[amounts.length - 1]);
                    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
                }
                function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    IWETH(WETH).deposit{value: amounts[0]}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
                    _swap(amounts, path, to);
                    // refund dust eth, if any
                    if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
                }
            
                // **** SWAP (supporting fee-on-transfer tokens) ****
                // requires the initial amount to have already been sent to the first pair
                function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
                    for (uint i; i < path.length - 1; i++) {
                        (address input, address output) = (path[i], path[i + 1]);
                        (address token0,) = UniswapV2Library.sortTokens(input, output);
                        IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
                        uint amountInput;
                        uint amountOutput;
                        { // scope to avoid stack too deep errors
                        (uint reserve0, uint reserve1,) = pair.getReserves();
                        (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                        amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
                        amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
                        }
                        (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
                        address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
                        pair.swap(amount0Out, amount1Out, to, new bytes(0));
                    }
                }
                function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) {
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
                    );
                    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
                    _swapSupportingFeeOnTransferTokens(path, to);
                    require(
                        IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
                        'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
                    );
                }
                function swapExactETHForTokensSupportingFeeOnTransferTokens(
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                )
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    uint amountIn = msg.value;
                    IWETH(WETH).deposit{value: amountIn}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
                    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
                    _swapSupportingFeeOnTransferTokens(path, to);
                    require(
                        IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
                        'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
                    );
                }
                function swapExactTokensForETHSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                )
                    external
                    virtual
                    override
                    ensure(deadline)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
                    );
                    _swapSupportingFeeOnTransferTokens(path, address(this));
                    uint amountOut = IERC20(WETH).balanceOf(address(this));
                    require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    IWETH(WETH).withdraw(amountOut);
                    TransferHelper.safeTransferETH(to, amountOut);
                }
            
                // **** LIBRARY FUNCTIONS ****
                function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
                    return UniswapV2Library.quote(amountA, reserveA, reserveB);
                }
            
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
                    public
                    pure
                    virtual
                    override
                    returns (uint amountOut)
                {
                    return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
                }
            
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
                    public
                    pure
                    virtual
                    override
                    returns (uint amountIn)
                {
                    return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
                }
            
                function getAmountsOut(uint amountIn, address[] memory path)
                    public
                    view
                    virtual
                    override
                    returns (uint[] memory amounts)
                {
                    return UniswapV2Library.getAmountsOut(factory, amountIn, path);
                }
            
                function getAmountsIn(uint amountOut, address[] memory path)
                    public
                    view
                    virtual
                    override
                    returns (uint[] memory amounts)
                {
                    return UniswapV2Library.getAmountsIn(factory, amountOut, path);
                }
            }
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            library UniswapV2Library {
                using SafeMath for uint;
            
                // returns sorted token addresses, used to handle return values from pairs sorted in this order
                function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
                    require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
                    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
                    require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
                }
            
                // calculates the CREATE2 address for a pair without making any external calls
                function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
                    (address token0, address token1) = sortTokens(tokenA, tokenB);
                    pair = address(uint(keccak256(abi.encodePacked(
                            hex'ff',
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                        ))));
                }
            
                // fetches and sorts the reserves for a pair
                function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
                    (address token0,) = sortTokens(tokenA, tokenB);
                    (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
                    (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                }
            
                // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
                function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
                    require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
                    require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    amountB = amountA.mul(reserveB) / reserveA;
                }
            
                // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
                    require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
                    require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    uint amountInWithFee = amountIn.mul(997);
                    uint numerator = amountInWithFee.mul(reserveOut);
                    uint denominator = reserveIn.mul(1000).add(amountInWithFee);
                    amountOut = numerator / denominator;
                }
            
                // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
                    require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
                    require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    uint numerator = reserveIn.mul(amountOut).mul(1000);
                    uint denominator = reserveOut.sub(amountOut).mul(997);
                    amountIn = (numerator / denominator).add(1);
                }
            
                // performs chained getAmountOut calculations on any number of pairs
                function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
                    require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
                    amounts = new uint[](path.length);
                    amounts[0] = amountIn;
                    for (uint i; i < path.length - 1; i++) {
                        (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
                        amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
                    }
                }
            
                // performs chained getAmountIn calculations on any number of pairs
                function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
                    require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
                    amounts = new uint[](path.length);
                    amounts[amounts.length - 1] = amountOut;
                    for (uint i = path.length - 1; i > 0; i--) {
                        (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
                        amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
                    }
                }
            }
            
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
                function safeApprove(address token, address to, uint value) internal {
                    // bytes4(keccak256(bytes('approve(address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
                }
            
                function safeTransfer(address token, address to, uint value) internal {
                    // bytes4(keccak256(bytes('transfer(address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
                }
            
                function safeTransferFrom(address token, address from, address to, uint value) internal {
                    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
                }
            
                function safeTransferETH(address to, uint value) internal {
                    (bool success,) = to.call{value:value}(new bytes(0));
                    require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
                }
            }