ETH Price: $3,373.89 (-3.24%)

Token

Asterix Vesting Vaults (AVV)
 

Overview

Max Total Supply

24 AVV

Holders

24

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A

Other Info

Balance
1 AVV
0xac97ef01338b92f137511eec25c56f0b7cded725
Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

Contract Name:
VestingVaultManager

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 8 : VestingVaultManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {VaultManager, ERC721} from "../vault/VaultManager.sol";

/// @notice Vesting vault manager class.
contract VestingVaultManager is VaultManager {
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         CONSTANTS                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev The role flag for admin.
    uint256 public constant ADMIN_ROLE = _ROLE_0;

    /// @dev Initial vesting mode.
    uint8 internal constant _VESTING_MODE_NONE = 0;

    /// @dev Either in cliff, or in regular vesting.
    uint8 internal constant _VESTING_MODE_REGULAR = 1;

    /// @dev In early vesting.
    uint8 internal constant _VESTING_MODE_EARLY = 2;

    /// @dev For safety, in case of a typo.
    uint32 internal constant _VESTING_DURATION_MAX = 365 days;

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

    /// @dev One non-transferable vault per account.
    error Soulbound();

    /// @dev The value is locked.
    error Locked();

    /// @dev The operation is not allowed for the current vesting mode.
    error InvalidVestingMode();

    /// @dev Cannot start early vesting when vesting has already started.
    error VestingAlreadyStarted();

    /// @dev The vesting duration must be greater than zero and
    /// less than or equal to `_VESTING_DURATION_MAX`.
    error InvalidVestingDuration();

    /// @dev Withdrawn too much.
    error OverWithdrawn();

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

    /// @dev Address of the vault's proxy.
    address public vaultProxy;

    /// @dev Whether the cliff duration is locked.
    bool public cliffDurationLocked;

    /// @dev Whether the vesting duration is locked.
    bool public vestingDurationLocked;

    /// @dev Whether the early vesting duration is locked.
    bool public earlyVestingDurationLocked;

    /// @dev Whether the vault's proxy is locked.
    bool public vaultProxyLocked;

    /// @dev The vesting mode.
    uint8 public vestingMode;

    /// @dev The start of the vesting period.
    uint32 public vestingStart;

    /// @dev The end of the vesting period.
    uint32 public vestingEnd;

    /// @dev The cliff duration.
    uint32 public cliffDuration;

    /// @dev The regular vesting duration.
    uint32 public vestingDuration;

    /// @dev The early vesting duration.
    uint32 public earlyVestingDuration;

    /// @dev Mapping of `holder` to their initially locked Asterix ERC20 amount.
    mapping(address => uint256) public initialLockedAsterixERC20Amount;

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

    constructor() {
        // Use an average of 30 days in a month.
        cliffDuration = 1 days * 30 * 6; // 6 months.
        vestingDuration = 1 days * 30 * 10; // 10 months.
        earlyVestingDuration = 1 days * 30 * 8; // 8 months;
        _initializeVaultManager(
            msg.sender == 0x0000000000FFe8B47B3e2130213B802212439497 ? tx.origin : msg.sender
        );
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      VAULT OPERATIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Returns the amount of Asterix ERC20 that `holder` can currently withdraw.
    function withdrawableAsterixERC20(address holder)
        public
        view
        virtual
        returns (uint256 result)
    {
        address vault = vaultOf(holder);
        if (vault == address(0)) return 0;
        // `vaultBalance > requiredBalance ? vaultBalance - requiredBalance : 0`.
        return _zeroFloorSub(_balanceOf(ASTERIX, vault), requiredAsterixERC20Balance(holder));
    }

    /// @dev Returns the required balance in the vault of `holder`.
    function requiredAsterixERC20Balance(address holder)
        public
        view
        virtual
        returns (uint256 result)
    {
        // If the cliff has not started, the `holder` is still allowed to withdraw
        // all of their Asterix balance in the vault, in case of an over deposit by mistake.
        if (vestingMode != _VESTING_MODE_NONE) {
            result = initialLockedAsterixERC20Amount[holder]; // Within uint96.
            uint256 end = vestingEnd;
            uint256 start = vestingStart;
            if (block.timestamp >= start) {
                unchecked {
                    // `((end > timestamp ? end - time : 0) * result) / (end - start)`.
                    result = _rawDiv(_zeroFloorSub(end, block.timestamp) * result, end - start);
                }
            }
        }
    }

    /// @dev Override to return the vault proxy for use in `_createVault`.
    function _vaultProxy() internal view virtual override(VaultManager) returns (address) {
        return vaultProxy;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      ADMIN OPERATIONS                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Locks the vault proxy.
    function lockVaultProxy() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        vaultProxyLocked = true;
    }

    /// @dev Sets the vault proxy.
    function setVaultProxy(address proxy) public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (vaultProxyLocked) revert Locked();
        vaultProxy = proxy;
    }

    /// @dev Locks the cliff duration.
    function lockCliffDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        cliffDurationLocked = true;
    }

    /// @dev Set the cliff duration.
    function setCliffDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (cliffDurationLocked) revert Locked();
        if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration();
        cliffDuration = value;
    }

    /// @dev Locks the vesting duration.
    function lockVestingDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        vestingDurationLocked = true;
    }

    /// @dev Sets the vesting duration.
    function setVestingDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (vestingDurationLocked) revert Locked();
        if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration();
        vestingDuration = value;
    }

    /// @dev Locks the early vesting duration.
    function lockEarlyVestingDuration() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        earlyVestingDurationLocked = true;
    }

    /// @dev Sets the early vesting duration.
    function setEarlyVestingDuration(uint32 value) public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (earlyVestingDurationLocked) revert Locked();
        if (value == 0 || value > _VESTING_DURATION_MAX) revert InvalidVestingDuration();
        earlyVestingDuration = value;
    }

    /// @dev Sets the cliff, which will lead to the regular vesting.
    function startCliff() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (vestingMode != _VESTING_MODE_NONE) revert InvalidVestingMode();
        uint32 start = uint32(block.timestamp) + cliffDuration;
        vestingMode = _VESTING_MODE_REGULAR;
        vestingStart = start;
        vestingEnd = start + vestingDuration;
        cliffDurationLocked = true;
        vestingDurationLocked = true;
    }

    /// @dev Starts the early vesting period.
    function startEarlyVesting() public virtual onlyOwnerOrRoles(ADMIN_ROLE) {
        if (vestingMode != _VESTING_MODE_REGULAR) revert InvalidVestingMode();
        if (block.timestamp >= vestingStart) revert VestingAlreadyStarted();
        vestingMode = _VESTING_MODE_EARLY;
        vestingStart = uint32(block.timestamp);
        vestingEnd = uint32(block.timestamp) + earlyVestingDuration;
        earlyVestingDurationLocked = true;
    }

    /// @dev Directly resets the vesting state for testing purposes.
    function directResetVestingState(uint8 mode, uint32 start, uint32 end)
        public
        onlyOwnerOrRoles(ADMIN_ROLE)
    {
        _requireOnlyTestnet();
        require(end >= start + 1, "`end` must be >= `start + 1`.");
        vestingMode = mode;
        vestingStart = start;
        vestingEnd = end;
        cliffDurationLocked = false;
        vestingDurationLocked = false;
        earlyVestingDurationLocked = false;
    }

    /// @dev Directly resets the vesting state for testing purposes.
    function directResetVestingState() public onlyOwnerOrRoles(ADMIN_ROLE) {
        _requireOnlyTestnet();
        vestingMode = _VESTING_MODE_NONE;
        vestingStart = 0;
        vestingEnd = 0;
        cliffDurationLocked = false;
        vestingDurationLocked = false;
        earlyVestingDurationLocked = false;
    }

    /// @dev Directly resets the vesting state for testing purposes.
    function directSetInitialLockedAsterixERC20Amount(address holder, uint256 amount)
        public
        onlyOwnerOrRoles(ADMIN_ROLE)
    {
        _requireOnlyTestnet();
        require(amount < 2 ** 96, "`amount` too big.");
        initialLockedAsterixERC20Amount[holder] = amount;
    }

    /// @dev Reverts if the chain is not Sepolia / Holesky testnet.
    function _requireOnlyTestnet() private view {
        require(
            block.chainid == 17000 || block.chainid == 11155111,
            "Only on Sepolia / Holesky testnet."
        );
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         OVERRIDES                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Updates the initial locked amount for `holder`.
    function _afterDepositAsterixFor(address holder) internal virtual override(VaultManager) {
        uint256 currentBalance = _balanceOf(ASTERIX, vaultOf(holder));
        // Before vesting starts, any deposit via the VestingVaultManager will update
        // `initialLockedAsterixERC20Amount` to the Asterix ERC20 balance of the vault.
        if (
            vestingMode == _VESTING_MODE_NONE || block.timestamp < vestingStart
                || (currentBalance > initialLockedAsterixERC20Amount[holder] && msg.sender == owner())
        ) {
            initialLockedAsterixERC20Amount[holder] = currentBalance;
        }
    }

    /// @dev Checks that there is enough ERC20 locked in the caller's vault.
    /// This override ensures that during the cliff and vesting phases,
    /// the `holder` cannot overWithdraw.
    function _afterWithdrawAsterix() internal virtual override(VaultManager) {
        address vault = vaultOf(msg.sender);
        if (vestingMode == _VESTING_MODE_NONE) {
            // Before vesting starts, any withdrawal via the VestingVaultManager will update
            // `initialLockedAsterixERC20Amount` to the Asterix ERC20 balance of the vault.
            initialLockedAsterixERC20Amount[msg.sender] = _balanceOf(ASTERIX, vault);
        } else {
            uint256 requiredBalance = requiredAsterixERC20Balance(msg.sender);
            if (_balanceOf(ASTERIX, vault) < requiredBalance) revert OverWithdrawn();
        }
    }

    /// @dev Override for account-bound tokens.
    function setSingleTransferEnabled(bool) public virtual override(VaultManager) {
        if (true) revert Soulbound();
    }

    /// @dev Override for account-bound tokens.
    function transferFrom(address, address, uint256) public payable virtual override(ERC721) {
        if (true) revert Soulbound();
    }

    /// @dev Override for account-bound tokens.
    function approve(address, uint256) public payable virtual override(ERC721) {
        if (true) revert Soulbound();
    }

    /// @dev Override for account-bound tokens.
    function getApproved(uint256) public view virtual override(ERC721) returns (address) {
        return address(0);
    }

    /// @dev Override for account-bound tokens.
    function setApprovalForAll(address, bool) public virtual override(ERC721) {
        if (true) revert Soulbound();
    }

    /// @dev Override for account-bound tokens.
    function isApprovedForAll(address, address)
        public
        view
        virtual
        override(ERC721)
        returns (bool)
    {
        return false;
    }
}

File 2 of 8 : VaultManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {ERC721} from "solady/tokens/ERC721.sol";
import {Ownable, OwnableRoles} from "solady/auth/OwnableRoles.sol";
import {LibERC6551} from "solady/accounts/LibERC6551.sol";
import {LibString} from "solady/utils/LibString.sol";
import {ReentrancyGuard} from "soledge/utils/ReentrancyGuard.sol";

/// @notice ERC6551 vault manager class.
abstract contract VaultManager is ERC721, OwnableRoles, ReentrancyGuard {
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         CONSTANTS                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Address of the Asterix base ERC20.
    address public constant ASTERIX = 0x0000000000ca73A6df4C58b84C5B4b847FE8Ff39;

    /// @dev Address of the Asterix mirror ERC721.
    address public constant ASTERIX_MIRROR = 0x0000000000c26FAbFe894D13233d5ec73F61cc72;

    /// @dev The role flag for a metadata setter.
    uint256 public constant METADATA_SETTER_ROLE = _ROLE_31;

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant _WAD = 10 ** 18;

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

    /// @dev An account can only have one vault at a time.
    error OnlyOneVaultPerAccount();

    /// @dev The vault owner has not enabled it for a single transfer.
    error SingleTransferNotEnabled();

    /// @dev Cannot double initialize the vault manager.
    error VaultManagerAlreadyInitialized();

    /// @dev A vault has not been created yet for the caller.
    error VaultHasNotBeenCreated();

    /// @dev The vault proxy is the zero address.
    error VaultProxyIsZeroAddress();

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                           EVENTS                           */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Emitted when a vault is created.
    event VaultCreated(address indexed owner, uint256 indexed tokenId, address indexed vault);

    /// @dev The vault has been enabled / disabled for transfer.
    event SingleTransferEnabledSet(uint256 indexed tokenId, bool enabled);

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

    /// @dev For ERC721 tokenURI.
    address internal _tokenURIRenderer;

    /// @dev ERC721 name.
    string internal _name;

    /// @dev ERC721 symbol.
    string internal _symbol;

    /// @dev For ERC721 tokenURI.
    string internal _baseURI;

    /// @dev The current number of vaults in existence.
    /// The token IDs of vaults are auto-incremented, starting from 1.
    uint64 public totalVaults;

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                        INITIALIZER                         */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev This MUST be called in the subclass' constructor.
    function _initializeVaultManager(address deployer) internal virtual {
        _initializeOwner(deployer);
        _grantRoles(deployer, METADATA_SETTER_ROLE);
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                          METADATA                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev ERC721 name.
    function name() public view override(ERC721) returns (string memory) {
        return _name;
    }

    /// @dev ERC721 symbol.
    function symbol() public view override(ERC721) returns (string memory) {
        return _symbol;
    }

    /// @dev ERC721 tokenURI.
    /// Note: This function directly returns the string via assembly.
    function tokenURI(uint256 id) public view override(ERC721) returns (string memory result) {
        if (!_exists(id)) revert TokenDoesNotExist();

        address renderer = _tokenURIRenderer;
        // If `renderer` is non-zero, use it instead of `_baseURI`.
        if (renderer != address(0)) {
            /// @solidity memory-safe-assembly
            assembly {
                result := mload(0x40)
                mstore(0x00, 0xc87b56dd) // `tokenURI(uint256)`.
                mstore(0x20, id)
                if iszero(staticcall(gas(), renderer, 0x1c, 0x24, 0x00, 0x00)) {
                    returndatacopy(result, 0x00, returndatasize())
                    revert(result, returndatasize())
                }
                returndatacopy(0x00, 0x00, 0x20) // Copy the offset of the string in returndata.
                returndatacopy(result, mload(0x00), 0x20) // Copy the length of the string.
                returndatacopy(add(result, 0x20), add(mload(0x00), 0x20), mload(result)) // Copy the string.
                let end := add(add(result, 0x20), mload(result))
                mstore(end, 0) // Zeroize the word after the string.
                mstore(0x40, add(end, 0x20)) // Allocate memory.
            }
        } else if (bytes(_baseURI).length != 0) {
            result = LibString.replace(_baseURI, "{id}", LibString.toString(id));
        }
    }

    /// @dev For ERC721 trackers.
    function totalSupply() public view virtual returns (uint256) {
        return totalVaults;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                 METADATA SETTER FUNCTIONS                  */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Sets the base URI for the ERC721 token URI.
    function setBaseURI(string calldata uri) public onlyOwnerOrRoles(METADATA_SETTER_ROLE) {
        _baseURI = uri;
    }

    /// @dev Sets the token URI renderer contract.
    function setTokenURIRenderer(address renderer) public onlyOwnerOrRoles(METADATA_SETTER_ROLE) {
        _tokenURIRenderer = renderer;
    }

    /// @dev Sets the ERC721 name and symbol.
    function setNameAndSymbol(string calldata name_, string calldata symbol_)
        public
        onlyOwnerOrRoles(METADATA_SETTER_ROLE)
    {
        _name = name_;
        _symbol = symbol_;
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                       CONFIGURABLES                        */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Hook to override, which is called after any Asterix deposit.
    /// This is used to check if there's an over withdrawal of Asterix tokens,
    /// for used in vesting.
    function _afterDepositAsterixFor(address owner) internal virtual {}

    /// @dev Hook to override, which is called after any Asterix withdraw.
    /// This is used to check if there's an over withdrawal of Asterix tokens,
    /// for used in vesting.
    function _afterWithdrawAsterix() internal virtual {}

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                  ASTERIX VAULT OPERATIONS                  */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Creates a vault for `holder`.
    function createVaultFor(address holder) public virtual nonReentrant returns (address vault) {
        vault = _createVault(holder);
    }

    /// @dev Allows the caller to create a vault.
    function createVault() public virtual returns (address vault) {
        vault = createVaultFor(msg.sender);
    }

    /// @dev Deposits `amount` of the caller's Asterix ERC20 tokens into the vault of `holder`.
    /// A vault will be lazily created for `holder` if it does not exists.
    function depositAsterixERC20For(address holder, uint256 amount)
        public
        virtual
        nonReentrant
        returns (address vault)
    {
        vault = _createVault(holder);
        _depositBaseERC20ToVault(vault, ASTERIX, msg.sender, amount);
        _afterDepositAsterixFor(holder);
    }

    /// @dev Deposits `amount` of the caller's Asterix ERC20 tokens into the caller's vault.
    /// A vault will be lazily created for `holder` if it does not exists.
    function depositAsterixERC20(uint256 amount) public virtual returns (address vault) {
        vault = depositAsterixERC20For(msg.sender, amount);
    }

    /// @dev Deposits `tokenIds` of the caller's Asterix ERC721 tokens into the vault of `holder`.
    /// A vault will be lazily created for `holder` if it does not exists.
    function depositAsterixMirrorERC721For(address holder, uint256[] calldata tokenIds)
        public
        virtual
        nonReentrant
        returns (address vault)
    {
        vault = _createVault(holder);
        for (uint256 i; i < tokenIds.length; ++i) {
            _depositMirrorERC721ToVault(vault, ASTERIX_MIRROR, msg.sender, _get(tokenIds, i));
        }
        _afterDepositAsterixFor(holder);
    }

    /// @dev Deposits `tokenIds` of the caller's Asterix ERC721 tokens into the caller's vault.
    /// A vault will be lazily created for the caller if it does not exists.
    function depositAsterixMirrorERC721(uint256[] calldata tokenIds)
        public
        virtual
        returns (address vault)
    {
        vault = depositAsterixMirrorERC721For(msg.sender, tokenIds);
    }

    /// @dev Withdraws `amount` Asterix ERC20 tokens from the caller's vault to `to`.
    function withdrawAsterixERC20To(address to, uint256 amount)
        public
        virtual
        nonReentrant
        returns (address vault)
    {
        vault = vaultOf(msg.sender); // Must use `msg.sender`.
        if (vault == address(0)) revert VaultHasNotBeenCreated();
        _withdrawBaseERC20FromVault(vault, ASTERIX, to, amount);
        _afterWithdrawAsterix();
    }

    /// @dev Withdraws `amount` Asterix ERC20 tokens from the caller's vault to the caller.
    function withdrawAsterixERC20(uint256 amount) public virtual returns (address vault) {
        vault = withdrawAsterixERC20To(msg.sender, amount);
    }

    /// @dev Withdraws `tokenIds` Asterix ERC721 tokens from the caller's vault to `to`.
    function withdrawAsterixMirrorERC721To(address to, uint256[] calldata tokenIds)
        public
        virtual
        nonReentrant
        returns (address vault)
    {
        vault = vaultOf(msg.sender); // Must use `msg.sender`.
        if (vault == address(0)) revert VaultHasNotBeenCreated();
        for (uint256 i; i < tokenIds.length; ++i) {
            _withdrawMirrorERC721FromVault(vault, ASTERIX_MIRROR, to, _get(tokenIds, i));
        }
        _afterWithdrawAsterix();
    }

    /// @dev Withdraws `tokenIds` Asterix ERC721 tokens from the caller's vault to the caller.
    function withdrawAsterixMirrorERC721(uint256[] calldata tokenIds)
        public
        virtual
        returns (address vault)
    {
        vault = withdrawAsterixMirrorERC721To(msg.sender, tokenIds);
    }

    /// @dev Returns the maximum amount of Asterix ERC20 that can be withdrawn
    /// by the `holder` without burning any ERC721s in their vault.
    /// If the `holder` does not have a vault, the returned value is zero.
    function maxAsterixERC20WithdrawableWithoutBurningERC721s(address holder)
        public
        view
        virtual
        returns (uint256)
    {
        address vault = vaultOf(holder);
        if (vault == address(0)) return 0;
        unchecked {
            return _balanceOf(ASTERIX, vault) - _balanceOf(ASTERIX_MIRROR, vault) * _WAD;
        }
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                  GENERAL VAULT OPERATIONS                  */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Override to return a valid vault proxy address.
    function _vaultProxy() internal view virtual returns (address) {}

    /// @dev Creates a vault for the `owner`.
    /// If a vault has already been created, returns the current vault held by the `owner`.
    function _createVault(address owner) internal virtual returns (address vault) {
        vault = address(uint160(_getAux(owner)));
        if (vault != address(0)) return vault;
        unchecked {
            address proxy = _vaultProxy();
            if (proxy == address(0)) revert VaultProxyIsZeroAddress();
            uint256 tokenId = ++totalVaults; // Overflow of uint64 is impractical.
            _mintAndSetExtraDataUnchecked(owner, tokenId, 0);
            // This calls `createAccount` on the canonical ERC6551 registry,
            // which deploys a ERC6551 minimal bytecode proxy with
            // `implementation`, `salt`, `chainId`, `tokenContract` baked into the bytecode.
            vault = LibERC6551.createAccount(
                proxy, // `implementation`, which is a proxy. So we have a proxy to a proxy.
                bytes32(0), // `salt`. Always zero, for simplicity.
                block.chainid, // `chainId`.
                address(this), // `tokenContract`.
                tokenId // `tokenId`.
            );
            emit VaultCreated(owner, tokenId, vault);
            // Use Solady's ERC721 data hitchhiking functions to store the vault's address.
            _setAux(owner, uint160(vault));
        }
    }

    /// @dev Calls `setSkipNFT(bool)` on `baseERC20` for the `vault`.
    /// This function assumes `vault` has been created, and is a valid vault.
    function _setSkipNFT(address vault, address baseERC20, bool status) internal virtual {
        // `setSkipNFT(bool)`.
        _execute(vault, baseERC20, 0x2a6a935d, _toUint(status), 0, 0);
    }

    /// @dev Transfers `amount` of `baseERC20` from `from` to the `vault`.
    /// Requires that at least `amount` of `baseERC20` has been approved by `from`
    /// to be managed by this contract.
    /// This function assumes that `baseERC20` exists, and is a valid ERC20.
    /// This function assumes `vault` has been created, and is a valid vault.
    function _depositBaseERC20ToVault(
        address vault,
        address baseERC20,
        address from,
        uint256 amount
    ) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, vault) // 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(call(gas(), baseERC20, 0, 0x1c, 0x64, 0x00, 0x20)) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Transfers `amount` of `baseERC20` from the `vault` to `to`.
    /// This function assumes `vault` has been created, and is a valid vault.
    ///
    /// Note: If the vault has 1 or more NFT tokens, we should revert if
    /// withdrawing will lead to the burning of any NFT token.
    function _withdrawBaseERC20FromVault(
        address vault,
        address baseERC20,
        address to,
        uint256 amount
    ) internal virtual {
        // `transfer(address,uint256)`.
        _execute(vault, baseERC20, 0xa9059cbb, uint160(to), uint160(amount), 0);
    }

    /// @dev Transfers `tokenId` of `mirrorERC721` from `from` to the `vault`.
    /// Requires that either `tokenId` has been approved by `from` to be managed
    /// by this contract, or this contract has been approved as an operator for `from`.
    /// This function assumes that `mirrorERC721` exists, and is a valid ERC721.
    /// This function assumes `vault` has been created, and is a valid vault.
    function _depositMirrorERC721ToVault(
        address vault,
        address mirrorERC721,
        address from,
        uint256 tokenId
    ) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, tokenId) // Store the `tokenId` argument.
            mstore(0x40, vault) // 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(call(gas(), mirrorERC721, 0, 0x1c, 0x64, 0x00, 0x20)) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Transfers `tokenId` of `mirrorERC721` from the `vault` to `to`.
    /// This function assumes `vault` has been created, and is a valid vault.
    function _withdrawMirrorERC721FromVault(
        address vault,
        address mirrorERC721,
        address to,
        uint256 tokenId
    ) internal virtual {
        // `transferFrom(address,address,uint256)`.
        _execute(vault, mirrorERC721, 0x23b872dd, uint160(vault), uint160(to), tokenId);
    }

    /// @dev Helper function to call `execute` on the `vault`.
    /// This function assumes `vault` has been created, and is a valid vault.
    function _execute(
        address vault,
        address target,
        uint256 fnSelector,
        uint256 arg0,
        uint256 arg1,
        uint256 arg2
    ) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x14), target)
            mstore(m, 0x51945447000000000000000000000000) // `execute(address,uint256,bytes,uint8)`.
            mstore(add(m, 0x34), 0) // `value`.
            mstore(add(m, 0x54), 0x80) // Offset of `data`.
            mstore(add(m, 0x74), 0) // `operation`.
            mstore(add(m, 0x98), fnSelector)
            mstore(add(m, 0xb8), arg0)
            mstore(add(m, 0xd8), arg1)
            mstore(add(m, 0xf8), arg2)
            mstore(add(m, 0x94), 0x64) // `data.length`.
            if iszero(call(gas(), vault, 0, add(m, 0x10), 0x124, codesize(), 0x00)) {
                returndatacopy(m, 0x00, returndatasize())
                revert(m, returndatasize())
            }
        }
    }

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

    /// @dev Returns the vault of the `owner`.
    /// Returns the zero address if owner does not have a vault.
    function vaultOf(address owner) public view virtual returns (address) {
        return address(uint160(_getAux(owner)));
    }

    /// @dev Returns the owner of the `vault`.
    /// Returns the zero address if the vault is not a valid vault.
    function vaultOwner(address vault) public view virtual returns (address result) {
        // The `tokenId` is from the ERC6551's proxy bytecode.
        uint256 tokenId = LibERC6551.tokenId(vault);
        address possibleOwner = _ownerOf(tokenId);
        // We must always validate against the vault's address stored in this contract.
        if (vaultOf(possibleOwner) == vault) result = possibleOwner;
    }

    /// @dev Returns the token ID of the `vault.
    /// Returns zero if the vault is not a valid vault.
    function vaultTokenId(address vault) public view virtual returns (uint256 result) {
        // The `tokenId` is from the ERC6551's proxy bytecode.
        uint256 tokenId = LibERC6551.tokenId(vault);
        address possibleOwner = _ownerOf(tokenId);
        // We must always validate against the vault's address stored in this contract.
        if (vaultOf(possibleOwner) == vault) result = tokenId;
    }

    /// @dev Returns the vault at `tokenId`.
    /// Returns the zero address if the vault has not been created.
    function vaultAt(uint256 tokenId) public view virtual returns (address) {
        return address(uint160(_getAux(_ownerOf(tokenId))));
    }

    /// @dev Returns if the `tokenId` has been enabled for a single transfer.
    function singleTransferEnabled(uint256 tokenId) public view virtual returns (bool) {
        return _getExtraData(tokenId) != 0;
    }

    /// @dev Enable a single transfer for the caller's vault.
    function setSingleTransferEnabled(bool enabled) public virtual nonReentrant {
        address vault = vaultOf(msg.sender);
        if (vault == address(0)) revert VaultHasNotBeenCreated();
        // `vault` is from this contract's storage,
        // so it's safe to get the `tokenId` from its immutable ERC6551 bytecode.
        uint256 tokenId = LibERC6551.tokenId(vault);
        // Use Solady's ERC721 data hitchhiking to store the enabled flag.
        _setExtraData(tokenId, uint96(_toUint(enabled)));
        emit SingleTransferEnabledSet(tokenId, enabled);
    }

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                         OVERRIDES                          */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

    /// @dev Hook override to ensure that an account can at most hold
    /// one vault at any time.
    ///
    /// Note: This override does not support burning logic.
    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        virtual
        override(ERC721)
    {
        if (_getAux(to) != 0) revert OnlyOneVaultPerAccount();
        if (from != address(0)) {
            if (_getExtraData(tokenId) == 0) revert SingleTransferNotEnabled();
            _setExtraData(tokenId, 0); // Reset the single token transfer to zero.
            _setAux(to, _getAux(from)); // Set `to.vault` to `from.vault`.
            _setAux(from, 0); // Set `from.vault` to `address(0)`.
        }
    }

    /// @dev Use the ownable to prevent double initialization.
    function _guardInitializeOwner() internal pure virtual override(Ownable) returns (bool) {
        return true;
    }

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

    /// @dev Returns `b ? 1 : 0`.
    function _toUint(bool b) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(iszero(b))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function _zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `y == 0 ? 0 : x / y`.
    function _rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `a[i]`, without bounds checks.
    function _get(uint256[] calldata a, uint256 i) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := calldataload(add(a.offset, shl(5, i)))
        }
    }
}

File 3 of 8 : ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 4 of 8 : OwnableRoles.sol
// 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;
}

File 5 of 8 : LibERC6551.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for interacting with ERC6551 accounts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/LibERC6551.sol)
/// @author ERC6551 team (https://github.com/erc6551/reference/blob/main/src/lib/ERC6551AccountLib.sol)
library LibERC6551 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Failed to create a ERC6551 account via the registry.
    error AccountCreationFailed();

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

    /// @dev The canonical ERC6551 registry address for EVM chains.
    address internal constant REGISTRY = 0x000000006551c19487814612e58FE06813775758;

    /// @dev The canonical ERC6551 registry bytecode for EVM chains.
    /// Useful for forge tests:
    /// `vm.etch(REGISTRY, REGISTRY_BYTECODE)`.
    bytes internal constant REGISTRY_BYTECODE =
        hex"608060405234801561001057600080fd5b50600436106100365760003560e01c8063246a00211461003b5780638a54c52f1461006a575b600080fd5b61004e6100493660046101b7565b61007d565b6040516001600160a01b03909116815260200160405180910390f35b61004e6100783660046101b7565b6100e1565b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b60015284601552605560002060601b60601c60005260206000f35b600060806024608c376e5af43d82803e903d91602b57fd5bf3606c5285605d52733d60ad80600a3d3981f3363d3d373d3d3d363d7360495260ff60005360b76055206035523060601b600152846015526055600020803b61018b578560b760556000f580610157576320188a596000526004601cfd5b80606c52508284887f79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf887226060606ca46020606cf35b8060601b60601c60005260206000f35b80356001600160a01b03811681146101b257600080fd5b919050565b600080600080600060a086880312156101cf57600080fd5b6101d88661019b565b945060208601359350604086013592506101f46060870161019b565b94979396509194608001359291505056fea2646970667358221220ea2fe53af507453c64dd7c1db05549fa47a298dfb825d6d11e1689856135f16764736f6c63430008110033";

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                ACCOUNT BYTECODE OPERATIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the initialization code of the ERC6551 account.
    function initCode(
        address implementation_,
        bytes32 salt_,
        uint256 chainId_,
        address tokenContract_,
        uint256 tokenId_
    ) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40) // Grab the free memory pointer..
            // Layout the variables and bytecode backwards.
            mstore(add(result, 0xb7), tokenId_)
            mstore(add(result, 0x97), shr(96, shl(96, tokenContract_)))
            mstore(add(result, 0x77), chainId_)
            mstore(add(result, 0x57), salt_)
            mstore(add(result, 0x37), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(result, 0x28), implementation_)
            mstore(add(result, 0x14), 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73)
            mstore(result, 0xb7) // Store the length.
            mstore(0x40, add(result, 0xd7)) // Allocate the memory.
        }
    }

    /// @dev Returns the initialization code hash of the ERC6551 account.
    function initCodeHash(
        address implementation_,
        bytes32 salt_,
        uint256 chainId_,
        address tokenContract_,
        uint256 tokenId_
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40) // Grab the free memory pointer.
            // Layout the variables and bytecode backwards.
            mstore(add(result, 0xa3), tokenId_)
            mstore(add(result, 0x83), shr(96, shl(96, tokenContract_)))
            mstore(add(result, 0x63), chainId_)
            mstore(add(result, 0x43), salt_)
            mstore(add(result, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(result, 0x14), implementation_)
            mstore(result, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73)
            result := keccak256(add(result, 0x0c), 0xb7)
        }
    }

    /// @dev Creates an account via the ERC6551 registry.
    function createAccount(
        address implementation_,
        bytes32 salt_,
        uint256 chainId_,
        address tokenContract_,
        uint256 tokenId_
    ) internal returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x14), implementation_)
            mstore(add(m, 0x34), salt_)
            mstore(add(m, 0x54), chainId_)
            mstore(add(m, 0x74), shr(96, shl(96, tokenContract_)))
            mstore(add(m, 0x94), tokenId_)
            // `createAccount(address,bytes32,uint256,address,uint256)`.
            mstore(m, 0x8a54c52f000000000000000000000000)
            if iszero(
                and(
                    gt(returndatasize(), 0x1f),
                    call(gas(), REGISTRY, 0, add(m, 0x10), 0xa4, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x20188a59) // `AccountCreationFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Returns the address of the ERC6551 account.
    function account(
        address implementation_,
        bytes32 salt_,
        uint256 chainId_,
        address tokenContract_,
        uint256 tokenId_
    ) internal pure returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40) // Grab the free memory pointer.
            // Layout the variables and bytecode backwards.
            mstore(add(result, 0xa3), tokenId_)
            mstore(add(result, 0x83), shr(96, shl(96, tokenContract_)))
            mstore(add(result, 0x63), chainId_)
            mstore(add(result, 0x43), salt_)
            mstore(add(result, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(result, 0x14), implementation_)
            mstore(result, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73)
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(add(result, 0x0c), 0xb7))
            mstore(0x01, shl(96, REGISTRY))
            mstore(0x15, salt_)
            result := keccak256(0x00, 0x55)
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns if `a` is an ERC6551 account with `expectedImplementation`.
    function isERC6551Account(address a, address expectedImplementation)
        internal
        view
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer..
            extcodecopy(a, add(m, 0x20), 0x0a, 0xa3)
            let implementation_ := shr(96, mload(add(m, 0x20)))
            if mul(
                extcodesize(implementation_),
                gt(eq(extcodesize(a), 0xad), shl(96, xor(expectedImplementation, implementation_)))
            ) {
                // Layout the variables and bytecode backwards.
                mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
                mstore(add(m, 0x14), implementation_)
                mstore(m, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73)
                // Compute and store the bytecode hash.
                mstore8(0x00, 0xff) // Write the prefix.
                mstore(0x35, keccak256(add(m, 0x0c), 0xb7))
                mstore(0x01, shl(96, REGISTRY))
                mstore(0x15, mload(add(m, 0x43)))
                result := iszero(shl(96, xor(a, keccak256(0x00, 0x55))))
                mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
            }
        }
    }

    /// @dev Returns the implementation of the ERC6551 account `a`.
    function implementation(address a) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(a, 0x00, 0x0a, 0x14)
            result := shr(96, mload(0x00))
        }
    }

    /// @dev Returns the static variables of the ERC6551 account `a`.
    function context(address a)
        internal
        view
        returns (bytes32 salt_, uint256 chainId_, address tokenContract_, uint256 tokenId_)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            extcodecopy(a, 0x00, 0x2d, 0x80)
            salt_ := mload(0x00)
            chainId_ := mload(0x20)
            tokenContract_ := mload(0x40)
            tokenId_ := mload(0x60)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the salt of the ERC6551 account `a`.
    function salt(address a) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(a, 0x00, 0x2d, 0x20)
            result := mload(0x00)
        }
    }

    /// @dev Returns the chain ID of the ERC6551 account `a`.
    function chainId(address a) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(a, 0x00, 0x4d, 0x20)
            result := mload(0x00)
        }
    }

    /// @dev Returns the token contract of the ERC6551 account `a`.
    function tokenContract(address a) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(a, 0x00, 0x6d, 0x20)
            result := mload(0x00)
        }
    }

    /// @dev Returns the token ID of the ERC6551 account `a`.
    function tokenId(address a) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            extcodecopy(a, 0x00, 0x8d, 0x20)
            result := mload(0x00)
        }
    }
}

File 6 of 8 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         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","\n","\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)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, add(mload(a), 0x40)), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, add(mload(a), 0x60))
        }
    }
}

File 7 of 8 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Reentrancy guard mixin.
/// @author Soledge (https://github.com/vectorized/soledge/blob/main/src/utils/ReentrancyGuard.sol)
///
/// Note: This implementation utilizes the `TSTORE` and `TLOAD` opcodes.
/// Please ensure that the chain you are deploying on supports them.
abstract contract ReentrancyGuard {
    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                       CUSTOM ERRORS                        */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

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

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

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

    /*«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-«-*/
    /*                      REENTRANCY GUARD                      */
    /*-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»-»*/

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

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if tload(_REENTRANCY_GUARD_SLOT) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

File 8 of 8 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

Settings
{
  "remappings": [
    "forge-std/=test/utils/forge-std/",
    "murky/=lib/murky/",
    "dn404/=lib/dn404/src/",
    "solady/=lib/solady/src/",
    "ds-test/=lib/murky/lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/murky/lib/forge-std/src/",
    "murky/=lib/murky/",
    "openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/",
    "soledge/=lib/soledge/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"InvalidVestingDuration","type":"error"},{"inputs":[],"name":"InvalidVestingMode","type":"error"},{"inputs":[],"name":"Locked","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OnlyOneVaultPerAccount","type":"error"},{"inputs":[],"name":"OverWithdrawn","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"SingleTransferNotEnabled","type":"error"},{"inputs":[],"name":"Soulbound","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VaultHasNotBeenCreated","type":"error"},{"inputs":[],"name":"VaultManagerAlreadyInitialized","type":"error"},{"inputs":[],"name":"VaultProxyIsZeroAddress","type":"error"},{"inputs":[],"name":"VestingAlreadyStarted","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"SingleTransferEnabledSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultCreated","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASTERIX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASTERIX_MIRROR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"METADATA_SETTER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cliffDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cliffDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"createVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"createVaultFor","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAsterixERC20","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAsterixERC20For","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"depositAsterixMirrorERC721","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"depositAsterixMirrorERC721For","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"directResetVestingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"mode","type":"uint8"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"}],"name":"directResetVestingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"directSetInitialLockedAsterixERC20Amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"earlyVestingDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"earlyVestingDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialLockedAsterixERC20Amount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockCliffDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockEarlyVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockVaultProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"maxAsterixERC20WithdrawableWithoutBurningERC721s","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"requiredAsterixERC20Balance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setCliffDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setEarlyVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"name":"setNameAndSymbol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"setSingleTransferEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"renderer","type":"address"}],"name":"setTokenURIRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxy","type":"address"}],"name":"setVaultProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setVestingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"singleTransferEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startCliff","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startEarlyVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalVaults","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"vaultAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"vaultOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultOwner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultProxyLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"vaultTokenId","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingDurationLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingEnd","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingMode","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAsterixERC20","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAsterixERC20To","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"withdrawAsterixMirrorERC721","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"withdrawAsterixMirrorERC721To","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"withdrawableAsterixERC20","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"}]

608060405234801561000f575f80fd5b5060058054600160481b600160a81b03191674013c6800018b820000ed4e0000000000000000000017905561005f336effe8b47b3e2130213b802212439497146100595733610064565b32610064565b61013d565b61006d8161007e565b61007b8163800000006100d6565b50565b638b78c6d81980541561009857630dc149f05f526004601cfd5b6001600160a01b03909116801560ff1b8117909155805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6100e2828260016100e6565b5050565b638b78c6d8600c52825f526020600c20805483811783610107575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b612b348061014a5f395ff3fe608060405260043610610458575f3560e01c806370a0823111610241578063aeb84f2411610134578063d85349f7116100b3578063f8e2567911610078578063f8e2567914610d3c578063fbcd215914610d5b578063fee81cf414610d7a578063ff28736114610dab578063ff87a5f514610dbf575f80fd5b8063d85349f714610cb3578063dc91c88f14610cd6578063e985e9c514610cf5578063f04e283e14610d16578063f2fde38b14610d29575f80fd5b8063b88d4fde116100f9578063b88d4fde14610c24578063c597578814610c37578063c87b56dd14610c56578063ccc7397414610c75578063cf70698a14610c94575f80fd5b8063aeb84f2414610b8d578063b19c3ca314610bac578063b1f047dd14610bc6578063b50fd68114610bf1578063b69e064314610c10575f80fd5b80638da5cb5b116101c0578063983525bf11610185578063983525bf14610acc578063a22cb46514610aeb578063a6caeb4b14610b05578063a7e76d4814610b42578063ae9e38ae14610b62575f80fd5b80638da5cb5b14610a3d578063905e344114610a5557806394a7c3ef14610a7757806395d89b4114610a995780639636d88114610aad575f80fd5b806384a1931f1161020657806384a1931f1461097f578063861145cd146109a2578063893a5372146109c15780638c13a453146109e75780638d65402314610a06575f80fd5b806370a0823114610902578063711039a614610921578063715018a61461094057806375b238fc1461094857806377b04c8f1461095c575f80fd5b806329a4927a116103595780634c5cd306116102d857806355f804b31161029d57806355f804b3146108715780635a446215146108905780635d12928b146108af5780635e3318f8146108c35780636352211e146108e3575f80fd5b80634c5cd306146107ed57806350a534c714610801578063514e62fc1461082057806353ff9fcb1461085557806354d1f13d14610869575f80fd5b8063397bfe551161031e578063397bfe5514610769578063398e6cb71461078857806342842e0e146107a757806347f7d14a146107ba5780634a4ee7b1146107da575f80fd5b806329a4927a146106c35780632de94807146106e2578063302ad53a14610713578063354b7f941461072a57806336fe3cb81461074a575f80fd5b806318160ddd116103e55780631cd64df4116103aa5780631cd64df41461064357806320cae9081461067857806323b872dd1461068c578063254800d41461069a57806325692962146106bb575f80fd5b806318160ddd146105c3578063183a4f6e146105ea57806318da2d98146105fd5780631a966d70146106115780631c10893f14610630575f80fd5b806308c0c05f1161042b57806308c0c05f14610524578063095ea7b31461053a57806309d2851a1461054d57806310a96a011461056c5780631514617e1461058b575f80fd5b806301ffc9a71461045c57806306fdde03146104ad5780630709df45146104ce578063081812fc14610505575b5f80fd5b348015610467575f80fd5b50610498610476366004612583565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b8575f80fd5b506104c1610dde565b6040516104a491906125aa565b3480156104d9575f80fd5b506104ed6104e83660046125f5565b610e6e565b6040516001600160a01b0390911681526020016104a4565b348015610510575f80fd5b506104ed61051f36600461260e565b505f90565b34801561052f575f80fd5b50610538610e7e565b005b610538610548366004612625565b610eb5565b348015610558575f80fd5b50610538610567366004612660565b610ed2565b348015610577575f80fd5b506104ed6105863660046126c0565b610f6f565b348015610596575f80fd5b506005546105ae90600160681b900463ffffffff1681565b60405163ffffffff90911681526020016104a4565b3480156105ce575f80fd5b506004546001600160401b03165b6040519081526020016104a4565b6105386105f836600461260e565b61101b565b348015610608575f80fd5b50610538611028565b34801561061c575f80fd5b506104ed61062b3660046125f5565b611049565b61053861063e366004612625565b61108f565b34801561064e575f80fd5b5061049861065d366004612625565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610683575f80fd5b506105386110a1565b61053861054836600461270e565b3480156106a5575f80fd5b506005546105ae90610100900463ffffffff1681565b6105386110c7565b3480156106ce575f80fd5b506104ed6106dd3660046126c0565b611113565b3480156106ed575f80fd5b506105dc6106fc3660046125f5565b638b78c6d8600c9081525f91909152602090205490565b34801561071e575f80fd5b506105dc638000000081565b348015610735575f80fd5b5060045461049890600160e81b900460ff1681565b348015610755575f80fd5b506105dc6107643660046125f5565b611186565b348015610774575f80fd5b506105386107833660046125f5565b6111cb565b348015610793575f80fd5b506104ed6107a236600461260e565b611233565b6105386107b536600461270e565b61123e565b3480156107c5575f80fd5b5060045461049890600160e01b900460ff1681565b6105386107e8366004612625565b61126a565b3480156107f8575f80fd5b5061053861127c565b34801561080c575f80fd5b506105dc61081b3660046125f5565b61129d565b34801561082b575f80fd5b5061049861083a366004612625565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610860575f80fd5b50610538611304565b6105386113cb565b34801561087c575f80fd5b5061053861088b366004612784565b611404565b34801561089b575f80fd5b506105386108aa3660046127c2565b611425565b3480156108ba575f80fd5b506104ed611456565b3480156108ce575f80fd5b5060045461049890600160f01b900460ff1681565b3480156108ee575f80fd5b506104ed6108fd36600461260e565b611465565b34801561090d575f80fd5b506105dc61091c3660046125f5565b611488565b34801561092c575f80fd5b5061053861093b366004612660565b6114c0565b610538611557565b348015610953575f80fd5b506105dc600181565b348015610967575f80fd5b506005546105ae90600160881b900463ffffffff1681565b34801561098a575f80fd5b506005546105ae90600160281b900463ffffffff1681565b3480156109ad575f80fd5b506105dc6109bc3660046125f5565b61156a565b3480156109cc575f80fd5b506004546104ed90600160401b90046001600160a01b031681565b3480156109f2575f80fd5b506104ed610a0136600461260e565b6115c2565b348015610a11575f80fd5b50600454610a25906001600160401b031681565b6040516001600160401b0390911681526020016104a4565b348015610a48575f80fd5b50638b78c6d819546104ed565b348015610a60575f80fd5b506104ed6eca73a6df4c58b84c5b4b847fe8ff3981565b348015610a82575f80fd5b506104ed6ec26fabfe894d13233d5ec73f61cc7281565b348015610aa4575f80fd5b506104c16115d4565b348015610ab8575f80fd5b50610538610ac7366004612660565b6115e3565b348015610ad7575f80fd5b506104ed610ae6366004612625565b61167a565b348015610af6575f80fd5b50610538610548366004612837565b348015610b10575f80fd5b50610498610b1f36600461260e565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b348015610b4d575f80fd5b5060045461049890600160f81b900460ff1681565b348015610b6d575f80fd5b50600554610b7b9060ff1681565b60405160ff90911681526020016104a4565b348015610b98575f80fd5b506104ed610ba7366004612868565b61170b565b348015610bb7575f80fd5b5061053861054836600461289a565b348015610bd1575f80fd5b506105dc610be03660046125f5565b60066020525f908152604090205481565b348015610bfc575f80fd5b506104ed610c0b36600461260e565b611717565b348015610c1b575f80fd5b50610538611722565b610538610c323660046128b3565b611745565b348015610c42575f80fd5b506104ed610c513660046125f5565b61179f565b348015610c61575f80fd5b506104c1610c7036600461260e565b6117e0565b348015610c80575f80fd5b50610538610c8f366004612625565b61195b565b348015610c9f575f80fd5b506104ed610cae366004612868565b6119d6565b348015610cbe575f80fd5b506005546105ae90600160481b900463ffffffff1681565b348015610ce1575f80fd5b50610538610cf036600461291c565b6119e2565b348015610d00575f80fd5b50610498610d0f366004612963565b5f92915050565b610538610d243660046125f5565b611aab565b610538610d373660046125f5565b611ae5565b348015610d47575f80fd5b506105dc610d563660046125f5565b611b0b565b348015610d66575f80fd5b506104ed610d75366004612625565b611b66565b348015610d85575f80fd5b506105dc610d943660046125f5565b63389a75e1600c9081525f91909152602090205490565b348015610db6575f80fd5b50610538611bbf565b348015610dca575f80fd5b50610538610dd93660046125f5565b611c92565b606060018054610ded9061298b565b80601f0160208091040260200160405190810160405280929190818152602001828054610e199061298b565b8015610e645780601f10610e3b57610100808354040283529160200191610e64565b820191905f5260205f20905b815481529060010190602001808311610e4757829003601f168201915b5050505050905090565b5f610e7882611cc2565b92915050565b6001610e8981611ce2565b610e91611d13565b506005805468ffffffffffffffffff191690556004805462ffffff60e01b19169055565b60405163a4420a9560e01b815260040160405180910390fd5b5050565b6001610edd81611ce2565b600454600160e01b900460ff1615610f08576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff82161580610f2457506301e1338063ffffffff8316115b15610f4257604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160481b026cffffffff00000000000000000019909216919091179055565b5f68929eee149b4bd212685c15610f8d5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610fa233610e6e565b90506001600160a01b038116610fcb5760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610fff57610ff7826ec26fabfe894d13233d5ec73f61cc7287600585901b880135611d7c565b600101610fcd565b50611008611da0565b5f68929eee149b4bd212685d9392505050565b6110253382611e27565b50565b600161103381611ce2565b506004805460ff60f01b1916600160f01b179055565b5f68929eee149b4bd212685c156110675763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107c82611e32565b90505f68929eee149b4bd212685d919050565b611097611f16565b610ece8282611f30565b60016110ac81611ce2565b506004805460ff60e81b1916600160e81b179055565b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c156111315763ab143c065f526004601cfd5b3068929eee149b4bd212685d61114684611e32565b90505f5b8281101561117c57611174826ec26fabfe894d13233d5ec73f61cc7233600585901b880135611f3c565b60010161114a565b5061100884611f7d565b5f8061119183612024565b90505f61119d82612033565b9050836001600160a01b03166111b282610e6e565b6001600160a01b0316036111c4578192505b5050919050565b60016111d681611ce2565b600454600160f81b900460ff1615611201576040516303cb96db60e21b815260040160405180910390fd5b50600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610e78338361167a565b611249838383610eb5565b813b156110c2576110c283838360405180602001604052805f81525061205a565b611272611f16565b610ece8282611e27565b600161128781611ce2565b506004805460ff60e01b1916600160e01b179055565b5f806112a883610e6e565b90506001600160a01b0381166112c057505f92915050565b670de0b6b3a76400006112e26ec26fabfe894d13233d5ec73f61cc72836120db565b026112fc6eca73a6df4c58b84c5b4b847fe8ff39836120db565b039392505050565b600161130f81611ce2565b60055460ff161561133357604051636752fae160e11b815260040160405180910390fd5b6005545f9061134f90600160481b900463ffffffff16426129bd565b6005805463ffffffff8084166101000264ffffffffff19909216919091176001179182905591925061138a91600160681b90910416826129bd565b6005805468ffffffff00000000001916600160281b63ffffffff939093169290920291909117905550506004805461ffff60e01b191661010160e01b179055565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b638000000061141281611ce2565b600361141f838583612a45565b50505050565b638000000061143381611ce2565b6001611440858783612a45565b50600261144e838583612a45565b505050505050565b5f61146033611049565b905090565b5f61146f82612033565b9050806114835763ceea21b65f526004601cfd5b919050565b5f8161149b57638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b60016114cb81611ce2565b600454600160f01b900460ff16156114f6576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061151257506301e1338063ffffffff8316115b1561153057604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160881b0263ffffffff60881b19909216919091179055565b61155f611f16565b6115685f612105565b565b5f8061157583610e6e565b90506001600160a01b03811661158d57505f92915050565b6115bb6115a96eca73a6df4c58b84c5b4b847fe8ff39836120db565b6115b285611b0b565b80821191030290565b9392505050565b5f610e786115cf83612033565b611cc2565b606060028054610ded9061298b565b60016115ee81611ce2565b600454600160e81b900460ff1615611619576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061163557506301e1338063ffffffff8316115b1561165357604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160681b0263ffffffff60681b19909216919091179055565b5f68929eee149b4bd212685c156116985763ab143c065f526004601cfd5b3068929eee149b4bd212685d6116ad33610e6e565b90506001600160a01b0381166116d65760405163425ef92760e01b815260040160405180910390fd5b6116f1816eca73a6df4c58b84c5b4b847fe8ff39858561214b565b6116f9611da0565b5f68929eee149b4bd212685d92915050565b5f6115bb338484610f6f565b5f610e783383611b66565b600161172d81611ce2565b50600480546001600160f81b0316600160f81b179055565b611750858585610eb5565b833b156117985761179885858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061205a92505050565b5050505050565b5f806117aa83612024565b90505f6117b682612033565b9050836001600160a01b03166117cb82610e6e565b6001600160a01b0316036111c4579392505050565b6060611809825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6118265760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561188a57604051915063c87b56dd5f52826020525f806024601c845afa61185c573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f81526020810160405250611955565b600380546118979061298b565b159050611955576115bb600380546118ae9061298b565b80601f01602080910402602001604051908101604052809291908181526020018280546118da9061298b565b80156119255780601f106118fc57610100808354040283529160200191611925565b820191905f5260205f20905b81548152906001019060200180831161190857829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b8152506119508661216f565b6121b1565b50919050565b600161196681611ce2565b61196e611d13565b600160601b82106119ba5760405162461bcd60e51b81526020600482015260116024820152703030b6b7bab73a30103a37b7903134b39760791b60448201526064015b60405180910390fd5b506001600160a01b039091165f90815260066020526040902055565b5f6115bb338484611113565b60016119ed81611ce2565b6119f5611d13565b611a008360016129bd565b63ffffffff168263ffffffff161015611a5b5760405162461bcd60e51b815260206004820152601d60248201527f60656e6460206d757374206265203e3d20607374617274202b2031602e00000060448201526064016119b1565b506005805460ff9490941664ffffffffff199094169390931761010063ffffffff938416021768ffffffff00000000001916600160281b91909216021790556004805462ffffff60e01b19169055565b611ab3611f16565b63389a75e1600c52805f526020600c208054421115611ad957636f5e88185f526004601cfd5b5f905561102581612105565b611aed611f16565b8060601b611b0257637448fbae5f526004601cfd5b61102581612105565b6005545f9060ff161561148357506001600160a01b0381165f9081526006602052604090205460055463ffffffff600160281b82048116916101009004164281116111c4578082034280841190840302840204949350505050565b5f68929eee149b4bd212685c15611b845763ab143c065f526004601cfd5b3068929eee149b4bd212685d611b9983611e32565b9050611bb6816eca73a6df4c58b84c5b4b847fe8ff393385611f3c565b6116f983611f7d565b6001611bca81611ce2565b60055460ff16600114611bf057604051636752fae160e11b815260040160405180910390fd5b600554610100900463ffffffff164210611c1d576040516372de7acd60e01b815260040160405180910390fd5b6005805463ffffffff428181166101000264ffffffffff199093169290921760021792839055611c5592600160881b900416906129bd565b6005805463ffffffff92909216600160281b0268ffffffff000000000019909216919091179055506004805460ff60f01b1916600160f01b179055565b6380000000611ca081611ce2565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b638b78c6d81954331461102557638b78c6d8600c52335f52806020600c205416611025576382b429005f526004601cfd5b466142681480611d2557504662aa36a7145b6115685760405162461bcd60e51b815260206004820152602260248201527f4f6e6c79206f6e205365706f6c6961202f20486f6c65736b7920746573746e656044820152613a1760f11b60648201526084016119b1565b61141f84846323b872dd876001600160a01b0316866001600160a01b0316866122d1565b5f611daa33610e6e565b60055490915060ff16611de257611dd06eca73a6df4c58b84c5b4b847fe8ff39826120db565b335f9081526006602052604090205550565b5f611dec33611b0b565b905080611e086eca73a6df4c58b84c5b4b847fe8ff39846120db565b1015610ece57604051631909a90560e01b815260040160405180910390fd5b610ece82825f612338565b5f611e3c82611cc2565b90506001600160a01b03811615611e5257919050565b600454600160401b90046001600160a01b031680611e8357604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff199091168117909155611eb384825f61238f565b611ec0825f463085612421565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a46111c484846001600160a01b0316612493565b638b78c6d819543314611568576382b429005f526004601cfd5b610ece82826001612338565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af1611f70573d5f823e3d81fd5b5f60605260405250505050565b5f611f9f6eca73a6df4c58b84c5b4b847fe8ff39611f9a84610e6e565b6120db565b60055490915060ff161580611fc05750600554610100900463ffffffff1642105b8061200357506001600160a01b0382165f90815260066020526040902054811180156120035750638b78c6d819546001600160a01b0316336001600160a01b0316145b15610ece576001600160a01b03919091165f90815260066020526040902055565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156120a1578060c08401826020870160045afa505b60208360a48301601c86015f8a5af16120c2573d156120c2573d5f843e3d83fd5b508060e01b82511461144e5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b61141f848463a9059cbb856001600160a01b0316856001600160a01b03165f6122d1565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480612188575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311612281576001838203015f602085106121f157508388205b601f851660200360031b89515b8b51818118831c6122685783156122385783888e201461223857808a5260019c8d019c90990198848d10612232575061227c565b506121fe565b5f5b8b8101518b82015260200187811061223a57509b87019b98860198871561226857848d10612232575061227c565b895260019b8c019b90980197838c106121fe575b505050505b84935060206040510194508781038585030192505b808810156122b1578751845260209788019790930192612296565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af161232f573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783612359575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b61239a5f84846124bf565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff811685026123f35767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116612486576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b6124c882611cc2565b6001600160e01b0316156124ef5760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b038316156110c2575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c900361253c5760405163768a0e2b60e01b815260040160405180910390fd5b5f818152673ec412a9852d173d60c11b601c52602090208101810180546001600160a01b031981161890556125798261257485611cc2565b612493565b6110c2835f612493565b5f60208284031215612593575f80fd5b81356001600160e01b0319811681146115bb575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114611483575f80fd5b5f60208284031215612605575f80fd5b6115bb826125df565b5f6020828403121561261e575f80fd5b5035919050565b5f8060408385031215612636575f80fd5b61263f836125df565b946020939093013593505050565b803563ffffffff81168114611483575f80fd5b5f60208284031215612670575f80fd5b6115bb8261264d565b5f8083601f840112612689575f80fd5b5081356001600160401b0381111561269f575f80fd5b6020830191508360208260051b85010111156126b9575f80fd5b9250929050565b5f805f604084860312156126d2575f80fd5b6126db846125df565b925060208401356001600160401b038111156126f5575f80fd5b61270186828701612679565b9497909650939450505050565b5f805f60608486031215612720575f80fd5b612729846125df565b9250612737602085016125df565b9150604084013590509250925092565b5f8083601f840112612757575f80fd5b5081356001600160401b0381111561276d575f80fd5b6020830191508360208285010111156126b9575f80fd5b5f8060208385031215612795575f80fd5b82356001600160401b038111156127aa575f80fd5b6127b685828601612747565b90969095509350505050565b5f805f80604085870312156127d5575f80fd5b84356001600160401b03808211156127eb575f80fd5b6127f788838901612747565b9096509450602087013591508082111561280f575f80fd5b5061281c87828801612747565b95989497509550505050565b80358015158114611483575f80fd5b5f8060408385031215612848575f80fd5b612851836125df565b915061285f60208401612828565b90509250929050565b5f8060208385031215612879575f80fd5b82356001600160401b0381111561288e575f80fd5b6127b685828601612679565b5f602082840312156128aa575f80fd5b6115bb82612828565b5f805f805f608086880312156128c7575f80fd5b6128d0866125df565b94506128de602087016125df565b93506040860135925060608601356001600160401b038111156128ff575f80fd5b61290b88828901612747565b969995985093965092949392505050565b5f805f6060848603121561292e575f80fd5b833560ff8116811461293e575f80fd5b925061294c6020850161264d565b915061295a6040850161264d565b90509250925092565b5f8060408385031215612974575f80fd5b61297d836125df565b915061285f602084016125df565b600181811c9082168061299f57607f821691505b60208210810361195557634e487b7160e01b5f52602260045260245ffd5b63ffffffff8181168382160190808211156129e657634e487b7160e01b5f52601160045260245ffd5b5092915050565b634e487b7160e01b5f52604160045260245ffd5b601f8211156110c257805f5260205f20601f840160051c81016020851015612a265750805b601f840160051c820191505b81811015611798575f8155600101612a32565b6001600160401b03831115612a5c57612a5c6129ed565b612a7083612a6a835461298b565b83612a01565b5f601f841160018114612aa1575f8515612a8a5750838201355b5f19600387901b1c1916600186901b178355611798565b5f83815260208120601f198716915b82811015612ad05786850135825560209485019460019092019101612ab0565b5086821015612aec575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea264697066735822122077798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e07566464736f6c63430008190033

Deployed Bytecode

0x608060405260043610610458575f3560e01c806370a0823111610241578063aeb84f2411610134578063d85349f7116100b3578063f8e2567911610078578063f8e2567914610d3c578063fbcd215914610d5b578063fee81cf414610d7a578063ff28736114610dab578063ff87a5f514610dbf575f80fd5b8063d85349f714610cb3578063dc91c88f14610cd6578063e985e9c514610cf5578063f04e283e14610d16578063f2fde38b14610d29575f80fd5b8063b88d4fde116100f9578063b88d4fde14610c24578063c597578814610c37578063c87b56dd14610c56578063ccc7397414610c75578063cf70698a14610c94575f80fd5b8063aeb84f2414610b8d578063b19c3ca314610bac578063b1f047dd14610bc6578063b50fd68114610bf1578063b69e064314610c10575f80fd5b80638da5cb5b116101c0578063983525bf11610185578063983525bf14610acc578063a22cb46514610aeb578063a6caeb4b14610b05578063a7e76d4814610b42578063ae9e38ae14610b62575f80fd5b80638da5cb5b14610a3d578063905e344114610a5557806394a7c3ef14610a7757806395d89b4114610a995780639636d88114610aad575f80fd5b806384a1931f1161020657806384a1931f1461097f578063861145cd146109a2578063893a5372146109c15780638c13a453146109e75780638d65402314610a06575f80fd5b806370a0823114610902578063711039a614610921578063715018a61461094057806375b238fc1461094857806377b04c8f1461095c575f80fd5b806329a4927a116103595780634c5cd306116102d857806355f804b31161029d57806355f804b3146108715780635a446215146108905780635d12928b146108af5780635e3318f8146108c35780636352211e146108e3575f80fd5b80634c5cd306146107ed57806350a534c714610801578063514e62fc1461082057806353ff9fcb1461085557806354d1f13d14610869575f80fd5b8063397bfe551161031e578063397bfe5514610769578063398e6cb71461078857806342842e0e146107a757806347f7d14a146107ba5780634a4ee7b1146107da575f80fd5b806329a4927a146106c35780632de94807146106e2578063302ad53a14610713578063354b7f941461072a57806336fe3cb81461074a575f80fd5b806318160ddd116103e55780631cd64df4116103aa5780631cd64df41461064357806320cae9081461067857806323b872dd1461068c578063254800d41461069a57806325692962146106bb575f80fd5b806318160ddd146105c3578063183a4f6e146105ea57806318da2d98146105fd5780631a966d70146106115780631c10893f14610630575f80fd5b806308c0c05f1161042b57806308c0c05f14610524578063095ea7b31461053a57806309d2851a1461054d57806310a96a011461056c5780631514617e1461058b575f80fd5b806301ffc9a71461045c57806306fdde03146104ad5780630709df45146104ce578063081812fc14610505575b5f80fd5b348015610467575f80fd5b50610498610476366004612583565b6301ffc9a760e09190911c9081146380ac58cd821417635b5e139f9091141790565b60405190151581526020015b60405180910390f35b3480156104b8575f80fd5b506104c1610dde565b6040516104a491906125aa565b3480156104d9575f80fd5b506104ed6104e83660046125f5565b610e6e565b6040516001600160a01b0390911681526020016104a4565b348015610510575f80fd5b506104ed61051f36600461260e565b505f90565b34801561052f575f80fd5b50610538610e7e565b005b610538610548366004612625565b610eb5565b348015610558575f80fd5b50610538610567366004612660565b610ed2565b348015610577575f80fd5b506104ed6105863660046126c0565b610f6f565b348015610596575f80fd5b506005546105ae90600160681b900463ffffffff1681565b60405163ffffffff90911681526020016104a4565b3480156105ce575f80fd5b506004546001600160401b03165b6040519081526020016104a4565b6105386105f836600461260e565b61101b565b348015610608575f80fd5b50610538611028565b34801561061c575f80fd5b506104ed61062b3660046125f5565b611049565b61053861063e366004612625565b61108f565b34801561064e575f80fd5b5061049861065d366004612625565b638b78c6d8600c9081525f9290925260209091205481161490565b348015610683575f80fd5b506105386110a1565b61053861054836600461270e565b3480156106a5575f80fd5b506005546105ae90610100900463ffffffff1681565b6105386110c7565b3480156106ce575f80fd5b506104ed6106dd3660046126c0565b611113565b3480156106ed575f80fd5b506105dc6106fc3660046125f5565b638b78c6d8600c9081525f91909152602090205490565b34801561071e575f80fd5b506105dc638000000081565b348015610735575f80fd5b5060045461049890600160e81b900460ff1681565b348015610755575f80fd5b506105dc6107643660046125f5565b611186565b348015610774575f80fd5b506105386107833660046125f5565b6111cb565b348015610793575f80fd5b506104ed6107a236600461260e565b611233565b6105386107b536600461270e565b61123e565b3480156107c5575f80fd5b5060045461049890600160e01b900460ff1681565b6105386107e8366004612625565b61126a565b3480156107f8575f80fd5b5061053861127c565b34801561080c575f80fd5b506105dc61081b3660046125f5565b61129d565b34801561082b575f80fd5b5061049861083a366004612625565b638b78c6d8600c9081525f9290925260209091205416151590565b348015610860575f80fd5b50610538611304565b6105386113cb565b34801561087c575f80fd5b5061053861088b366004612784565b611404565b34801561089b575f80fd5b506105386108aa3660046127c2565b611425565b3480156108ba575f80fd5b506104ed611456565b3480156108ce575f80fd5b5060045461049890600160f01b900460ff1681565b3480156108ee575f80fd5b506104ed6108fd36600461260e565b611465565b34801561090d575f80fd5b506105dc61091c3660046125f5565b611488565b34801561092c575f80fd5b5061053861093b366004612660565b6114c0565b610538611557565b348015610953575f80fd5b506105dc600181565b348015610967575f80fd5b506005546105ae90600160881b900463ffffffff1681565b34801561098a575f80fd5b506005546105ae90600160281b900463ffffffff1681565b3480156109ad575f80fd5b506105dc6109bc3660046125f5565b61156a565b3480156109cc575f80fd5b506004546104ed90600160401b90046001600160a01b031681565b3480156109f2575f80fd5b506104ed610a0136600461260e565b6115c2565b348015610a11575f80fd5b50600454610a25906001600160401b031681565b6040516001600160401b0390911681526020016104a4565b348015610a48575f80fd5b50638b78c6d819546104ed565b348015610a60575f80fd5b506104ed6eca73a6df4c58b84c5b4b847fe8ff3981565b348015610a82575f80fd5b506104ed6ec26fabfe894d13233d5ec73f61cc7281565b348015610aa4575f80fd5b506104c16115d4565b348015610ab8575f80fd5b50610538610ac7366004612660565b6115e3565b348015610ad7575f80fd5b506104ed610ae6366004612625565b61167a565b348015610af6575f80fd5b50610538610548366004612837565b348015610b10575f80fd5b50610498610b1f36600461260e565b5f818152673ec412a9852d173d60c11b601c52602090208101015460a01c151590565b348015610b4d575f80fd5b5060045461049890600160f81b900460ff1681565b348015610b6d575f80fd5b50600554610b7b9060ff1681565b60405160ff90911681526020016104a4565b348015610b98575f80fd5b506104ed610ba7366004612868565b61170b565b348015610bb7575f80fd5b5061053861054836600461289a565b348015610bd1575f80fd5b506105dc610be03660046125f5565b60066020525f908152604090205481565b348015610bfc575f80fd5b506104ed610c0b36600461260e565b611717565b348015610c1b575f80fd5b50610538611722565b610538610c323660046128b3565b611745565b348015610c42575f80fd5b506104ed610c513660046125f5565b61179f565b348015610c61575f80fd5b506104c1610c7036600461260e565b6117e0565b348015610c80575f80fd5b50610538610c8f366004612625565b61195b565b348015610c9f575f80fd5b506104ed610cae366004612868565b6119d6565b348015610cbe575f80fd5b506005546105ae90600160481b900463ffffffff1681565b348015610ce1575f80fd5b50610538610cf036600461291c565b6119e2565b348015610d00575f80fd5b50610498610d0f366004612963565b5f92915050565b610538610d243660046125f5565b611aab565b610538610d373660046125f5565b611ae5565b348015610d47575f80fd5b506105dc610d563660046125f5565b611b0b565b348015610d66575f80fd5b506104ed610d75366004612625565b611b66565b348015610d85575f80fd5b506105dc610d943660046125f5565b63389a75e1600c9081525f91909152602090205490565b348015610db6575f80fd5b50610538611bbf565b348015610dca575f80fd5b50610538610dd93660046125f5565b611c92565b606060018054610ded9061298b565b80601f0160208091040260200160405190810160405280929190818152602001828054610e199061298b565b8015610e645780601f10610e3b57610100808354040283529160200191610e64565b820191905f5260205f20905b815481529060010190602001808311610e4757829003601f168201915b5050505050905090565b5f610e7882611cc2565b92915050565b6001610e8981611ce2565b610e91611d13565b506005805468ffffffffffffffffff191690556004805462ffffff60e01b19169055565b60405163a4420a9560e01b815260040160405180910390fd5b5050565b6001610edd81611ce2565b600454600160e01b900460ff1615610f08576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff82161580610f2457506301e1338063ffffffff8316115b15610f4257604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160481b026cffffffff00000000000000000019909216919091179055565b5f68929eee149b4bd212685c15610f8d5763ab143c065f526004601cfd5b3068929eee149b4bd212685d610fa233610e6e565b90506001600160a01b038116610fcb5760405163425ef92760e01b815260040160405180910390fd5b5f5b82811015610fff57610ff7826ec26fabfe894d13233d5ec73f61cc7287600585901b880135611d7c565b600101610fcd565b50611008611da0565b5f68929eee149b4bd212685d9392505050565b6110253382611e27565b50565b600161103381611ce2565b506004805460ff60f01b1916600160f01b179055565b5f68929eee149b4bd212685c156110675763ab143c065f526004601cfd5b3068929eee149b4bd212685d61107c82611e32565b90505f68929eee149b4bd212685d919050565b611097611f16565b610ece8282611f30565b60016110ac81611ce2565b506004805460ff60e81b1916600160e81b179055565b505050565b5f6202a3006001600160401b03164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a250565b5f68929eee149b4bd212685c156111315763ab143c065f526004601cfd5b3068929eee149b4bd212685d61114684611e32565b90505f5b8281101561117c57611174826ec26fabfe894d13233d5ec73f61cc7233600585901b880135611f3c565b60010161114a565b5061100884611f7d565b5f8061119183612024565b90505f61119d82612033565b9050836001600160a01b03166111b282610e6e565b6001600160a01b0316036111c4578192505b5050919050565b60016111d681611ce2565b600454600160f81b900460ff1615611201576040516303cb96db60e21b815260040160405180910390fd5b50600480546001600160a01b03909216600160401b0268010000000000000000600160e01b0319909216919091179055565b5f610e78338361167a565b611249838383610eb5565b813b156110c2576110c283838360405180602001604052805f81525061205a565b611272611f16565b610ece8282611e27565b600161128781611ce2565b506004805460ff60e01b1916600160e01b179055565b5f806112a883610e6e565b90506001600160a01b0381166112c057505f92915050565b670de0b6b3a76400006112e26ec26fabfe894d13233d5ec73f61cc72836120db565b026112fc6eca73a6df4c58b84c5b4b847fe8ff39836120db565b039392505050565b600161130f81611ce2565b60055460ff161561133357604051636752fae160e11b815260040160405180910390fd5b6005545f9061134f90600160481b900463ffffffff16426129bd565b6005805463ffffffff8084166101000264ffffffffff19909216919091176001179182905591925061138a91600160681b90910416826129bd565b6005805468ffffffff00000000001916600160281b63ffffffff939093169290920291909117905550506004805461ffff60e01b191661010160e01b179055565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2565b638000000061141281611ce2565b600361141f838583612a45565b50505050565b638000000061143381611ce2565b6001611440858783612a45565b50600261144e838583612a45565b505050505050565b5f61146033611049565b905090565b5f61146f82612033565b9050806114835763ceea21b65f526004601cfd5b919050565b5f8161149b57638f4eb6045f526004601cfd5b673ec412a9852d173d60c11b601c52815f5263ffffffff601c600c2054169050919050565b60016114cb81611ce2565b600454600160f01b900460ff16156114f6576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061151257506301e1338063ffffffff8316115b1561153057604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160881b0263ffffffff60881b19909216919091179055565b61155f611f16565b6115685f612105565b565b5f8061157583610e6e565b90506001600160a01b03811661158d57505f92915050565b6115bb6115a96eca73a6df4c58b84c5b4b847fe8ff39836120db565b6115b285611b0b565b80821191030290565b9392505050565b5f610e786115cf83612033565b611cc2565b606060028054610ded9061298b565b60016115ee81611ce2565b600454600160e81b900460ff1615611619576040516303cb96db60e21b815260040160405180910390fd5b63ffffffff8216158061163557506301e1338063ffffffff8316115b1561165357604051632b39f6cb60e01b815260040160405180910390fd5b506005805463ffffffff909216600160681b0263ffffffff60681b19909216919091179055565b5f68929eee149b4bd212685c156116985763ab143c065f526004601cfd5b3068929eee149b4bd212685d6116ad33610e6e565b90506001600160a01b0381166116d65760405163425ef92760e01b815260040160405180910390fd5b6116f1816eca73a6df4c58b84c5b4b847fe8ff39858561214b565b6116f9611da0565b5f68929eee149b4bd212685d92915050565b5f6115bb338484610f6f565b5f610e783383611b66565b600161172d81611ce2565b50600480546001600160f81b0316600160f81b179055565b611750858585610eb5565b833b156117985761179885858585858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061205a92505050565b5050505050565b5f806117aa83612024565b90505f6117b682612033565b9050836001600160a01b03166117cb82610e6e565b6001600160a01b0316036111c4579392505050565b6060611809825f818152673ec412a9852d173d60c11b601c52602090208101015460601b151590565b6118265760405163677510db60e11b815260040160405180910390fd5b5f546001600160a01b0316801561188a57604051915063c87b56dd5f52826020525f806024601c845afa61185c573d5f833e3d82fd5b60205f803e60205f51833e815160205f5101602084013e815160208301015f81526020810160405250611955565b600380546118979061298b565b159050611955576115bb600380546118ae9061298b565b80601f01602080910402602001604051908101604052809291908181526020018280546118da9061298b565b80156119255780601f106118fc57610100808354040283529160200191611925565b820191905f5260205f20905b81548152906001019060200180831161190857829003601f168201915b5050505050604051806040016040528060048152602001637b69647d60e01b8152506119508661216f565b6121b1565b50919050565b600161196681611ce2565b61196e611d13565b600160601b82106119ba5760405162461bcd60e51b81526020600482015260116024820152703030b6b7bab73a30103a37b7903134b39760791b60448201526064015b60405180910390fd5b506001600160a01b039091165f90815260066020526040902055565b5f6115bb338484611113565b60016119ed81611ce2565b6119f5611d13565b611a008360016129bd565b63ffffffff168263ffffffff161015611a5b5760405162461bcd60e51b815260206004820152601d60248201527f60656e6460206d757374206265203e3d20607374617274202b2031602e00000060448201526064016119b1565b506005805460ff9490941664ffffffffff199094169390931761010063ffffffff938416021768ffffffff00000000001916600160281b91909216021790556004805462ffffff60e01b19169055565b611ab3611f16565b63389a75e1600c52805f526020600c208054421115611ad957636f5e88185f526004601cfd5b5f905561102581612105565b611aed611f16565b8060601b611b0257637448fbae5f526004601cfd5b61102581612105565b6005545f9060ff161561148357506001600160a01b0381165f9081526006602052604090205460055463ffffffff600160281b82048116916101009004164281116111c4578082034280841190840302840204949350505050565b5f68929eee149b4bd212685c15611b845763ab143c065f526004601cfd5b3068929eee149b4bd212685d611b9983611e32565b9050611bb6816eca73a6df4c58b84c5b4b847fe8ff393385611f3c565b6116f983611f7d565b6001611bca81611ce2565b60055460ff16600114611bf057604051636752fae160e11b815260040160405180910390fd5b600554610100900463ffffffff164210611c1d576040516372de7acd60e01b815260040160405180910390fd5b6005805463ffffffff428181166101000264ffffffffff199093169290921760021792839055611c5592600160881b900416906129bd565b6005805463ffffffff92909216600160281b0268ffffffff000000000019909216919091179055506004805460ff60f01b1916600160f01b179055565b6380000000611ca081611ce2565b505f80546001600160a01b0319166001600160a01b0392909216919091179055565b673ec412a9852d173d60c11b601c9081525f91909152600c205460201c90565b638b78c6d81954331461102557638b78c6d8600c52335f52806020600c205416611025576382b429005f526004601cfd5b466142681480611d2557504662aa36a7145b6115685760405162461bcd60e51b815260206004820152602260248201527f4f6e6c79206f6e205365706f6c6961202f20486f6c65736b7920746573746e656044820152613a1760f11b60648201526084016119b1565b61141f84846323b872dd876001600160a01b0316866001600160a01b0316866122d1565b5f611daa33610e6e565b60055490915060ff16611de257611dd06eca73a6df4c58b84c5b4b847fe8ff39826120db565b335f9081526006602052604090205550565b5f611dec33611b0b565b905080611e086eca73a6df4c58b84c5b4b847fe8ff39846120db565b1015610ece57604051631909a90560e01b815260040160405180910390fd5b610ece82825f612338565b5f611e3c82611cc2565b90506001600160a01b03811615611e5257919050565b600454600160401b90046001600160a01b031680611e8357604051630873153160e11b815260040160405180910390fd5b600480546001600160401b038082166001011667ffffffffffffffff199091168117909155611eb384825f61238f565b611ec0825f463085612421565b9250826001600160a01b031681856001600160a01b03167f019b61d9140f8784f4eed1e66569335630452af2d974ceb13d3e706c0786729660405160405180910390a46111c484846001600160a01b0316612493565b638b78c6d819543314611568576382b429005f526004601cfd5b610ece82826001612338565b60405181606052846040528260601b602c526323b872dd60601b600c5260205f6064601c5f885af1611f70573d5f823e3d81fd5b5f60605260405250505050565b5f611f9f6eca73a6df4c58b84c5b4b847fe8ff39611f9a84610e6e565b6120db565b60055490915060ff161580611fc05750600554610100900463ffffffff1642105b8061200357506001600160a01b0382165f90815260066020526040902054811180156120035750638b78c6d819546001600160a01b0316336001600160a01b0316145b15610ece576001600160a01b03919091165f90815260066020526040902055565b5f6020608d5f843c50505f5190565b5f818152673ec412a9852d173d60c11b601c5260209020810101546001600160a01b031690565b60405163150b7a028082523360208301528560601b60601c604083015283606083015260808083015282518060a084015280156120a1578060c08401826020870160045afa505b60208360a48301601c86015f8a5af16120c2573d156120c2573d5f843e3d83fd5b508060e01b82511461144e5763d1a57ed65f526004601cfd5b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3811560ff1b8217905550565b61141f848463a9059cbb856001600160a01b0316856001600160a01b03165f6122d1565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480612188575050819003601f19909101908152919050565b60608351835183516020870196506020860195506020850194506020604051019350828701838311612281576001838203015f602085106121f157508388205b601f851660200360031b89515b8b51818118831c6122685783156122385783888e201461223857808a5260019c8d019c90990198848d10612232575061227c565b506121fe565b5f5b8b8101518b82015260200187811061223a57509b87019b98860198871561226857848d10612232575061227c565b895260019b8c019b90980197838c106121fe575b505050505b84935060206040510194508781038585030192505b808810156122b1578751845260209788019790930192612296565b50505f818401908152602001604052601f19909201918252509392505050565b604051856014820152635194544760601b81525f6034820152608060548201525f60748201528460988201528360b88201528260d88201528160f8820152606460948201525f38610124601084015f8b5af161232f573d5f823e3d81fd5b50505050505050565b638b78c6d8600c52825f526020600c20805483811783612359575080841681185b80835580600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe265f80a3505050505050565b61239a5f84846124bf565b8260601b60601c9250815f52673ec412a9852d173d60c11b601c52828160a01b1760205f208301830155825f52601c600c20600181540163ffffffff811685026123f35767ea553b3401336cea851560021b526004601cfd5b905581835f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8138a4505050565b5f6040518660148201528560348201528460548201528360601b60601c6074820152826094820152638a54c52f60601b815260205f60a4601084015f6f6551c19487814612e58fe068137757585af1601f3d1116612486576320188a595f526004601cfd5b50505f5195945050505050565b673ec412a9852d173d60c11b601c52815f52601c600c2080548060201c831860201b8118825550505050565b6124c882611cc2565b6001600160e01b0316156124ef5760405163759be05b60e11b815260040160405180910390fd5b6001600160a01b038316156110c2575f818152673ec412a9852d173d60c11b601c5260208120820182015460a01c900361253c5760405163768a0e2b60e01b815260040160405180910390fd5b5f818152673ec412a9852d173d60c11b601c52602090208101810180546001600160a01b031981161890556125798261257485611cc2565b612493565b6110c2835f612493565b5f60208284031215612593575f80fd5b81356001600160e01b0319811681146115bb575f80fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114611483575f80fd5b5f60208284031215612605575f80fd5b6115bb826125df565b5f6020828403121561261e575f80fd5b5035919050565b5f8060408385031215612636575f80fd5b61263f836125df565b946020939093013593505050565b803563ffffffff81168114611483575f80fd5b5f60208284031215612670575f80fd5b6115bb8261264d565b5f8083601f840112612689575f80fd5b5081356001600160401b0381111561269f575f80fd5b6020830191508360208260051b85010111156126b9575f80fd5b9250929050565b5f805f604084860312156126d2575f80fd5b6126db846125df565b925060208401356001600160401b038111156126f5575f80fd5b61270186828701612679565b9497909650939450505050565b5f805f60608486031215612720575f80fd5b612729846125df565b9250612737602085016125df565b9150604084013590509250925092565b5f8083601f840112612757575f80fd5b5081356001600160401b0381111561276d575f80fd5b6020830191508360208285010111156126b9575f80fd5b5f8060208385031215612795575f80fd5b82356001600160401b038111156127aa575f80fd5b6127b685828601612747565b90969095509350505050565b5f805f80604085870312156127d5575f80fd5b84356001600160401b03808211156127eb575f80fd5b6127f788838901612747565b9096509450602087013591508082111561280f575f80fd5b5061281c87828801612747565b95989497509550505050565b80358015158114611483575f80fd5b5f8060408385031215612848575f80fd5b612851836125df565b915061285f60208401612828565b90509250929050565b5f8060208385031215612879575f80fd5b82356001600160401b0381111561288e575f80fd5b6127b685828601612679565b5f602082840312156128aa575f80fd5b6115bb82612828565b5f805f805f608086880312156128c7575f80fd5b6128d0866125df565b94506128de602087016125df565b93506040860135925060608601356001600160401b038111156128ff575f80fd5b61290b88828901612747565b969995985093965092949392505050565b5f805f6060848603121561292e575f80fd5b833560ff8116811461293e575f80fd5b925061294c6020850161264d565b915061295a6040850161264d565b90509250925092565b5f8060408385031215612974575f80fd5b61297d836125df565b915061285f602084016125df565b600181811c9082168061299f57607f821691505b60208210810361195557634e487b7160e01b5f52602260045260245ffd5b63ffffffff8181168382160190808211156129e657634e487b7160e01b5f52601160045260245ffd5b5092915050565b634e487b7160e01b5f52604160045260245ffd5b601f8211156110c257805f5260205f20601f840160051c81016020851015612a265750805b601f840160051c820191505b81811015611798575f8155600101612a32565b6001600160401b03831115612a5c57612a5c6129ed565b612a7083612a6a835461298b565b83612a01565b5f601f841160018114612aa1575f8515612a8a5750838201355b5f19600387901b1c1916600186901b178355611798565b5f83815260208120601f198716915b82811015612ad05786850135825560209485019460019092019101612ab0565b5086821015612aec575f1960f88860031b161c19848701351681555b505060018560011b018355505050505056fea264697066735822122077798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e07566464736f6c63430008190033

Deployed Bytecode Sourcemap

163:12963:7:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14864:380:3;;;;;;;;;;-1:-1:-1;14864:380:3;;;;;:::i;:::-;15177:10;15042:3;15038:21;;;;15171:17;;;15196:10;15190:17;;15168:40;15216:10;15210:17;;;15165:63;;14864:380;;;;470:14:8;;463:22;445:41;;433:2;418:18;14864:380:3;;;;;;;;4010:98:6;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;20241:126::-;;;;;;;;;;-1:-1:-1;20241:126:6;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;1453:32:8;;;1435:51;;1423:2;1408:18;20241:126:6;1289:203:8;12609:119:7;;;;;;;;;;-1:-1:-1;12609:119:7;;;;;:::i;:::-;-1:-1:-1;12685:7:7;;12609:119;9265:321;;;;;;;;;;;;;:::i;:::-;;12435:120;;;;;;:::i;:::-;;:::i;6443:262::-;;;;;;;;;;-1:-1:-1;6443:262:7;;;;;:::i;:::-;;:::i;10759:489:6:-;;;;;;;;;;-1:-1:-1;10759:489:6;;;;;:::i;:::-;;:::i;2833:29:7:-;;;;;;;;;;-1:-1:-1;2833:29:7;;;;-1:-1:-1;;;2833:29:7;;;;;;;;;3360:10:8;3348:23;;;3330:42;;3318:2;3303:18;2833:29:7;3186:192:8;5772:96:6;;;;;;;;;;-1:-1:-1;5850:11:6;;-1:-1:-1;;;;;5850:11:6;5772:96;;;3529:25:8;;;3517:2;3502:18;5772:96:6;3383:177:8;10127:109:2;;;;;;:::i;:::-;;:::i;7239:130:7:-;;;;;;;;;;;;;:::i;7842:137:6:-;;;;;;;;;;-1:-1:-1;7842:137:6;;;;;:::i;:::-;;:::i;9588:123:2:-;;;;;;:::i;:::-;;:::i;11178:139::-;;;;;;;;;;-1:-1:-1;11178:139:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;11257:4;10798:18;;;;10898:4;10882:21;;;10876:28;11280:21;;:30;;11178:139;6752:120:7;;;;;;;;;;;;;:::i;12247:134::-;;;;;;:::i;2615:26::-;;;;;;;;;;-1:-1:-1;2615:26:7;;;;;;;;;;;9021:617:1;;;:::i;9138:415:6:-;;;;;;;;;;-1:-1:-1;9138:415:6;;;;;:::i;:::-;;:::i;10567:353:2:-;;;;;;;;;;-1:-1:-1;10567:353:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;10627:13;10798:18;;;;10898:4;10882:21;;10876:28;;10567:353;1064:55:6;;;;;;;;;;;;14166:7:2;1064:55:6;;2278:33:7;;;;;;;;;;-1:-1:-1;2278:33:7;;;;-1:-1:-1;;;2278:33:7;;;;;;21010:407:6;;;;;;;;;;-1:-1:-1;21010:407:6;;;;;:::i;:::-;;:::i;6075:164:7:-;;;;;;;;;;-1:-1:-1;6075:164:7;;;;;:::i;:::-;;:::i;10512:152:6:-;;;;;;;;;;-1:-1:-1;10512:152:6;;;;;:::i;:::-;;:::i;13705:198:3:-;;;;;;:::i;:::-;;:::i;2187:31:7:-;;;;;;;;;;-1:-1:-1;2187:31:7;;;;-1:-1:-1;;;2187:31:7;;;;;;9856:125:2;;;;;;:::i;:::-;;:::i;6284:116:7:-;;;;;;;;;;;;;:::i;11786:359:6:-;;;;;;;;;;-1:-1:-1;11786:359:6;;;;;:::i;:::-;;:::i;10982:134:2:-;;;;;;;;;;-1:-1:-1;10982:134:2;;;;;:::i;:::-;10769:15;10763:4;10756:29;;;11060:4;10798:18;;;;10898:4;10882:21;;;10876:28;11083:21;:26;;;10982:134;7779:408:7;;;;;;;;;;;;;:::i;9720:456:1:-;;;:::i;6199:118:6:-;;;;;;;;;;-1:-1:-1;6199:118:6;;;;;:::i;:::-;;:::i;6564:197::-;;;;;;;;;;-1:-1:-1;6564:197:6;;;;;:::i;:::-;;:::i;8035:113::-;;;;;;;;;;;;;:::i;2377:38:7:-;;;;;;;;;;-1:-1:-1;2377:38:7;;;;-1:-1:-1;;;2377:38:7;;;;;;6957:332:3;;;;;;;;;;-1:-1:-1;6957:332:3;;;;;:::i;:::-;;:::i;7433:533::-;;;;;;;;;;-1:-1:-1;7433:533:3;;;;;:::i;:::-;;:::i;7421:283:7:-;;;;;;;;;;-1:-1:-1;7421:283:7;;;;;:::i;:::-;;:::i;8762:100:1:-;;;:::i;520:44:7:-;;;;;;;;;;;;12635:6:2;520:44:7;;2910:34;;;;;;;;;;-1:-1:-1;2910:34:7;;;;-1:-1:-1;;;2910:34:7;;;;;;2692:24;;;;;;;;;;-1:-1:-1;2692:24:7;;;;-1:-1:-1;;;2692:24:7;;;;;;4115:399;;;;;;;;;;-1:-1:-1;4115:399:7;;;;;:::i;:::-;;:::i;2104:25::-;;;;;;;;;;-1:-1:-1;2104:25:7;;;;-1:-1:-1;;;2104:25:7;;-1:-1:-1;;;;;2104:25:7;;;21536:140:6;;;;;;;;;;-1:-1:-1;21536:140:6;;;;;:::i;:::-;;:::i;3183:25::-;;;;;;;;;;-1:-1:-1;3183:25:6;;;;-1:-1:-1;;;;;3183:25:6;;;;;;-1:-1:-1;;;;;5555:31:8;;;5537:50;;5525:2;5510:18;3183:25:6;5393:200:8;11408:182:1;;;;;;;;;;-1:-1:-1;;;11556:18:1;11408:182;;790:76:6;;;;;;;;;;;;824:42;790:76;;924:83;;;;;;;;;;;;965:42;924:83;;4142:102;;;;;;;;;;;;;:::i;6918:268:7:-;;;;;;;;;;-1:-1:-1;6918:268:7;;;;;:::i;:::-;;:::i;10032:382:6:-;;;;;;;;;;-1:-1:-1;10032:382:6;;;;;:::i;:::-;;:::i;12782:119:7:-;;;;;;;;;;-1:-1:-1;12782:119:7;;;;;:::i;21760:134:6:-;;;;;;;;;;-1:-1:-1;21760:134:6;;;;;:::i;:::-;21837:4;18323:16:3;;;-1:-1:-1;;;18359:4:3;18352:38;18460:4;18444:21;;18436:30;;18428:39;18422:46;18417:3;18413:56;21860:27:6;;;21760:134;2472:28:7;;;;;;;;;;-1:-1:-1;2472:28:7;;;;-1:-1:-1;;;2472:28:7;;;;;;2538:24;;;;;;;;;;-1:-1:-1;2538:24:7;;;;;;;;;;;6194:4:8;6182:17;;;6164:36;;6152:2;6137:18;2538:24:7;6022:184:8;11349:209:6;;;;;;;;;;-1:-1:-1;11349:209:6;;;;;:::i;:::-;;:::i;12070:123:7:-;;;;;;;;;;-1:-1:-1;12070:123:7;;;;;:::i;3032:66::-;;;;;;;;;;-1:-1:-1;3032:66:7;;;;;:::i;:::-;;;;;;;;;;;;;;8807:151:6;;;;;;;;;;-1:-1:-1;8807:151:6;;;;;:::i;:::-;;:::i;5924:110:7:-;;;;;;;;;;;;;:::i;14406:249:3:-;;;;;;:::i;:::-;;:::i;20488:411:6:-;;;;;;;;;;-1:-1:-1;20488:411:6;;;;;:::i;:::-;;:::i;4350:1382::-;;;;;;;;;;-1:-1:-1;4350:1382:6;;;;;:::i;:::-;;:::i;9661:290:7:-;;;;;;;;;;-1:-1:-1;9661:290:7;;;;;:::i;:::-;;:::i;9732:208:6:-;;;;;;;;;;-1:-1:-1;9732:208:6;;;;;:::i;:::-;;:::i;2756:27:7:-;;;;;;;;;;-1:-1:-1;2756:27:7;;;;-1:-1:-1;;;2756:27:7;;;;;;8753:437;;;;;;;;;;-1:-1:-1;8753:437:7;;;;;:::i;:::-;;:::i;12955:169::-;;;;;;;;;;-1:-1:-1;12955:169:7;;;;;:::i;:::-;13085:4;12955:169;;;;;10363:708:1;;;;;;:::i;:::-;;:::i;8348:349::-;;;;;;:::i;:::-;;:::i;4588:825:7:-;;;;;;;;;;-1:-1:-1;4588:825:7;;;;;:::i;:::-;;:::i;8325:308:6:-;;;;;;;;;;-1:-1:-1;8325:308:6;;;;;:::i;:::-;;:::i;11693:435:1:-;;;;;;;;;;-1:-1:-1;11693:435:1;;;;;:::i;:::-;11963:19;11957:4;11950:33;;;11812:14;11996:26;;;;12106:4;12090:21;;12084:28;;11693:435;8239:439:7;;;;;;;;;;;;;:::i;6374:138:6:-;;;;;;;;;;-1:-1:-1;6374:138:6;;;;;:::i;:::-;;:::i;4010:98::-;4064:13;4096:5;4089:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4010:98;:::o;20241:126::-;20302:7;20344:14;20352:5;20344:7;:14::i;:::-;20321:39;20241:126;-1:-1:-1;;20241:126:6:o;9265:321:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;9346:21:7::1;:19;:21::i;:::-;-1:-1:-1::0;9377:11:7::1;:32:::0;;-1:-1:-1;;9445:14:7;;;9469:19:::1;:27:::0;;-1:-1:-1;;;;9545:34:7;;;9265:321::o;12435:120::-;12537:11;;-1:-1:-1;;;12537:11:7;;;;;;;;;;;12520:28;12435:120;;:::o;6443:262::-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;6541:19:7::1;::::0;-1:-1:-1;;;6541:19:7;::::1;;;6537:40;;;6569:8;;-1:-1:-1::0;;;6569:8:7::1;;;;;;;;;;;6537:40;6591:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;6605:29;::::0;::::1;;6591:43;6587:80;;;6643:24;;-1:-1:-1::0;;;6643:24:7::1;;;;;;;;;;;6587:80;-1:-1:-1::0;6677:13:7::1;:21:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;6677:21:7::1;-1:-1:-1::0;;6677:21:7;;::::1;::::0;;;::::1;::::0;;6443:262::o;10759:489:6:-;10908:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;10945:19:6::1;10953:10;10945:7;:19::i;:::-;10937:27:::0;-1:-1:-1;;;;;;11004:19:6;::::1;11000:56;;11032:24;;-1:-1:-1::0;;;11032:24:6::1;;;;;;;;;;;11000:56;11071:9;11066:143;11082:19:::0;;::::1;11066:143;;;11122:76;11153:5:::0;965:42:::1;11176:2:::0;24939:1;24935:9;;;24921:24;;24908:38;11122:30:::1;:76::i;:::-;11103:3;;11066:143;;;;11218:23;:21;:23::i;:::-;2021:1:5::0;1997:22;1990:33;10759:489:6;;;;;:::o;10127:109:2:-;10198:31;10211:10;10223:5;10198:12;:31::i;:::-;10127:109;:::o;7239:130:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;7329:26:7::1;:33:::0;;-1:-1:-1;;;;7329:33:7::1;-1:-1:-1::0;;;7329:33:7::1;::::0;;7239:130::o;7842:137:6:-;7919:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;7952:20:6::1;7965:6;7952:12;:20::i;:::-;7944:28;;2021:1:5::0;1997:22;1990:33;7842:137:6;;;:::o;9588:123:2:-;12517:13:1;:11;:13::i;:::-;9680:24:2::1;9692:4;9698:5;9680:11;:24::i;6752:120:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6837:21:7::1;:28:::0;;-1:-1:-1;;;;6837:28:7::1;-1:-1:-1::0;;;6837:28:7::1;::::0;;6752:120::o;12346:28::-;12247:134;;;:::o;9021:617:1:-;9114:15;7972:9;-1:-1:-1;;;;;9132:46:1;:15;:46;9114:64;;9346:19;9340:4;9333:33;9396:8;9390:4;9383:22;9452:7;9445:4;9439;9429:21;9422:38;9599:8;9552:45;9549:1;9546;9541:67;9248:374;9021:617::o;9138:415:6:-;9291:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;9328:20:6::1;9341:6;9328:12;:20::i;:::-;9320:28;;9363:9;9358:148;9374:19:::0;;::::1;9358:148;;;9414:81;9442:5:::0;965:42:::1;9465:10;24939:1:::0;24935:9;;;24921:24;;24908:38;9414:27:::1;:81::i;:::-;9395:3;;9358:148;;;;9515:31;9539:6;9515:23;:31::i;21010:407::-:0;21076:14;21165:15;21183:25;21202:5;21183:18;:25::i;:::-;21165:43;;21218:21;21242:17;21251:7;21242:8;:17::i;:::-;21218:41;;21387:5;-1:-1:-1;;;;;21361:31:6;:22;21369:13;21361:7;:22::i;:::-;-1:-1:-1;;;;;21361:31:6;;21357:53;;21403:7;21394:16;;21357:53;21092:325;;21010:407;;;:::o;6075:164:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;6171:16:7::1;::::0;-1:-1:-1;;;6171:16:7;::::1;;;6167:37;;;6196:8;;-1:-1:-1::0;;;6196:8:7::1;;;;;;;;;;;6167:37;-1:-1:-1::0;6214:10:7::1;:18:::0;;-1:-1:-1;;;;;6214:18:7;;::::1;-1:-1:-1::0;;;6214:18:7::1;-1:-1:-1::0;;;;;;6214:18:7;;::::1;::::0;;;::::1;::::0;;6075:164::o;10512:152:6:-;10582:13;10615:42;10638:10;10650:6;10615:22;:42::i;13705:198:3:-;13802:26;13815:4;13821:2;13825;13802:12;:26::i;:::-;38367:14;;13838:58;;;13856:40;13879:4;13885:2;13889;13856:40;;;;;;;;;;;;:22;:40::i;9856:125:2:-;12517:13:1;:11;:13::i;:::-;9949:25:2::1;9962:4;9968:5;9949:12;:25::i;6284:116:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6367:19:7::1;:26:::0;;-1:-1:-1;;;;6367:26:7::1;-1:-1:-1::0;;;6367:26:7::1;::::0;;6284:116::o;11786:359:6:-;11921:7;11944:13;11960:15;11968:6;11960:7;:15::i;:::-;11944:31;-1:-1:-1;;;;;;11989:19:6;;11985:33;;-1:-1:-1;12017:1:6;;11786:359;-1:-1:-1;;11786:359:6:o;11985:33::-;1207:8;12088:33;965:42;12115:5;12088:10;:33::i;:::-;:40;12059:26;824:42;12079:5;12059:10;:26::i;:::-;:69;;11786:359;-1:-1:-1;;;11786:359:6:o;7779:408:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7859:11:7::1;::::0;:33:::1;:11;:33:::0;7855:66:::1;;7901:20;;-1:-1:-1::0;;;7901:20:7::1;;;;;;;;;;;7855:66;7972:13;::::0;7931:12:::1;::::0;7946:39:::1;::::0;-1:-1:-1;;;7972:13:7;::::1;;;7953:15;7946:39;:::i;:::-;7995:11;:35:::0;;8040:20:::1;::::0;;::::1;7995:35;8040:20;-1:-1:-1::0;;8040:20:7;;;;;;;760:1:::1;8040:20:::0;;;;;7931:54;;-1:-1:-1;8083:23:7::1;::::0;-1:-1:-1;;;8091:15:7;;::::1;;7931:54:::0;8083:23:::1;:::i;:::-;8070:10;:36:::0;;-1:-1:-1;;8070:36:7::1;-1:-1:-1::0;;;8070:36:7::1;::::0;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;8116:19:7::1;:26:::0;;-1:-1:-1;;;;8152:28:7;-1:-1:-1;;;8152:28:7;;;7779:408::o;9720:456:1:-;9922:19;9916:4;9909:33;9968:8;9962:4;9955:22;10020:1;10013:4;10007;9997:21;9990:32;10151:8;10105:44;10102:1;10099;10094:66;9720:456::o;6199:118:6:-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;6296:8:6::1;:14;6307:3:::0;;6296:8;:14:::1;:::i;:::-;;6199:118:::0;;;:::o;6564:197::-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;6714:5:6::1;:13;6722:5:::0;;6714;:13:::1;:::i;:::-;-1:-1:-1::0;6737:7:6::1;:17;6747:7:::0;;6737;:17:::1;:::i;:::-;;6564:197:::0;;;;;:::o;8035:113::-;8082:13;8115:26;8130:10;8115:14;:26::i;:::-;8107:34;;8035:113;:::o;6957:332:3:-;7015:14;7050:12;7059:2;7050:8;:12::i;:::-;7041:21;;7148:6;7138:135;;7187:10;7181:4;7174:24;7254:4;7248;7241:18;7138:135;6957:332;;;:::o;7433:533::-;7496:14;7656:5;7646:143;;7694:10;7688:4;7681:24;7770:4;7764;7757:18;7646:143;-1:-1:-1;;;7809:4:3;7802:38;7866:5;7860:4;7853:19;7929:20;7921:4;7915;7905:21;7899:28;7895:55;7885:65;;7433:533;;;:::o;7421:283:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7526:26:7::1;::::0;-1:-1:-1;;;7526:26:7;::::1;;;7522:47;;;7561:8;;-1:-1:-1::0;;;7561:8:7::1;;;;;;;;;;;7522:47;7583:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;7597:29;::::0;::::1;;7583:43;7579:80;;;7635:24;;-1:-1:-1::0;;;7635:24:7::1;;;;;;;;;;;7579:80;-1:-1:-1::0;7669:20:7::1;:28:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;7669:28:7::1;-1:-1:-1::0;;;;7669:28:7;;::::1;::::0;;;::::1;::::0;;7421:283::o;8762:100:1:-;12517:13;:11;:13::i;:::-;8834:21:::1;8852:1;8834:9;:21::i;:::-;8762:100::o:0;4115:399:7:-;4226:14;4256:13;4272:15;4280:6;4272:7;:15::i;:::-;4256:31;-1:-1:-1;;;;;;4301:19:7;;4297:33;;-1:-1:-1;4329:1:7;;4115:399;-1:-1:-1;;4115:399:7:o;4297:33::-;4429:78;4443:26;824:42:6;4463:5:7;4443:10;:26::i;:::-;4471:35;4499:6;4471:27;:35::i;:::-;24414:8:6;;;24424:9;;24410:24;;24250:200;4429:78:7;4422:85;4115:399;-1:-1:-1;;;4115:399:7:o;21536:140:6:-;21599:7;21641:26;21649:17;21658:7;21649:8;:17::i;:::-;21641:7;:26::i;4142:102::-;4198:13;4230:7;4223:14;;;;;:::i;6918:268:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;7018:21:7::1;::::0;-1:-1:-1;;;7018:21:7;::::1;;;7014:42;;;7048:8;;-1:-1:-1::0;;;7048:8:7::1;;;;;;;;;;;7014:42;7070:10;::::0;::::1;::::0;;:43:::1;;-1:-1:-1::0;946:8:7::1;7084:29;::::0;::::1;;7070:43;7066:80;;;7122:24;;-1:-1:-1::0;;;7122:24:7::1;;;;;;;;;;;7066:80;-1:-1:-1::0;7156:15:7::1;:23:::0;;::::1;::::0;;::::1;-1:-1:-1::0;;;7156:23:7::1;-1:-1:-1::0;;;;7156:23:7;;::::1;::::0;;;::::1;::::0;;6918:268::o;10032:382:6:-;10161:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;10198:19:6::1;10206:10;10198:7;:19::i;:::-;10190:27:::0;-1:-1:-1;;;;;;10257:19:6;::::1;10253:56;;10285:24;;-1:-1:-1::0;;;10285:24:6::1;;;;;;;;;;;10253:56;10319:55;10347:5;824:42;10363:2;10367:6;10319:27;:55::i;:::-;10384:23;:21;:23::i;:::-;2021:1:5::0;1997:22;1990:33;10032:382:6;;;;:::o;11349:209::-;11463:13;11500:51;11530:10;11542:8;;11500:29;:51::i;8807:151::-;8876:13;8909:42;8932:10;8944:6;8909:22;:42::i;5924:110:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6004:16:7::1;:23:::0;;-1:-1:-1;;;;;6004:23:7::1;-1:-1:-1::0;;;6004:23:7::1;::::0;;5924:110::o;14406:249:3:-;14552:26;14565:4;14571:2;14575;14552:12;:26::i;:::-;38367:14;;14588:60;;;14606:42;14629:4;14635:2;14639;14643:4;;14606:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;14606:22:3;;-1:-1:-1;;;14606:42:3:i;:::-;14406:249;;;;;:::o;20488:411:6:-;20552:14;20641:15;20659:25;20678:5;20659:18;:25::i;:::-;20641:43;;20694:21;20718:17;20727:7;20718:8;:17::i;:::-;20694:41;;20863:5;-1:-1:-1;;;;;20837:31:6;:22;20845:13;20837:7;:22::i;:::-;-1:-1:-1;;;;;20837:31:6;;20833:59;;20879:13;20488:411;-1:-1:-1;;;20488:411:6:o;4350:1382::-;4418:20;4455:11;4463:2;15636:11:3;15725:16;;;-1:-1:-1;;;15761:4:3;15754:38;15875:4;15859:21;;15851:30;;15843:39;15837:46;15833:2;15829:55;15822:63;15815:71;;15576:326;4455:11:6;4450:44;;4475:19;;-1:-1:-1;;;4475:19:6;;;;;;;;;;;4450:44;4505:16;4524:17;-1:-1:-1;;;;;4524:17:6;4623:22;;4619:1107;;4751:4;4745:11;4735:21;;4786:10;4780:4;4773:24;4851:2;4845:4;4838:16;4927:4;4921;4915;4909;4899:8;4892:5;4881:51;4871:202;;4985:16;4979:4;4971:6;4956:46;5038:16;5030:6;5023:32;4871:202;5117:4;5111;5105;5090:32;5223:4;5216;5210:11;5202:6;5187:41;5343:6;5337:13;5330:4;5323;5317:11;5313:22;5306:4;5298:6;5294:17;5279:72;5428:6;5422:13;5415:4;5407:6;5403:17;5399:37;5465:1;5460:3;5453:14;5544:4;5539:3;5535:14;5529:4;5522:28;;4619:1107;;;5610:8;5604:22;;;;;:::i;:::-;:27;;-1:-1:-1;5600:126:6;;5656:59;5674:8;5656:59;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5656:59:6;;;5692:22;5711:2;5692:18;:22::i;:::-;5656:17;:59::i;5600:126::-;4440:1292;4350:1382;;;:::o;9661:290:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;9809:21:7::1;:19;:21::i;:::-;-1:-1:-1::0;;;9848:6:7::1;:16;9840:46;;;::::0;-1:-1:-1;;;9840:46:7;;11169:2:8;9840:46:7::1;::::0;::::1;11151:21:8::0;11208:2;11188:18;;;11181:30;-1:-1:-1;;;11227:18:8;;;11220:47;11284:18;;9840:46:7::1;;;;;;;;;-1:-1:-1::0;;;;;;9896:39:7;;::::1;;::::0;;;:31:::1;:39;::::0;;;;:48;9661:290::o;9732:208:6:-;9845:13;9882:51;9912:10;9924:8;;9882:29;:51::i;8753:437:7:-;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;8890:21:7::1;:19;:21::i;:::-;8936:9;:5:::0;8944:1:::1;8936:9;:::i;:::-;8929:16;;:3;:16;;;;8921:58;;;::::0;-1:-1:-1;;;8921:58:7;;11515:2:8;8921:58:7::1;::::0;::::1;11497:21:8::0;11554:2;11534:18;;;11527:30;11593:31;11573:18;;;11566:59;11642:18;;8921:58:7::1;11313:353:8::0;8921:58:7::1;-1:-1:-1::0;8989:11:7::1;:18:::0;;::::1;::::0;;;::::1;-1:-1:-1::0;;9017:20:7;;;;;;;8989:18:::1;9017:20;::::0;;::::1;;;-1:-1:-1::0;;9047:16:7::1;-1:-1:-1::0;;;9047:16:7;;;::::1;;;::::0;;9073:19:::1;:27:::0;;-1:-1:-1;;;;9149:34:7;;;8753:437::o;10363:708:1:-;12517:13;:11;:13::i;:::-;10597:19:::1;10591:4;10584:33;10643:12;10637:4;10630:26;10705:4;10699;10689:21;10811:12;10805:19;10792:11;10789:36;10786:157;;;10857:10;10851:4;10844:24;10924:4;10918;10911:18;10786:157;11020:1;10999:23:::0;;11041::::1;11051:12:::0;11041:9:::1;:23::i;8348:349::-:0;12517:13;:11;:13::i;:::-;8520:8:::1;8516:2;8512:17;8502:150;;8562:10;8556:4;8549:24;8633:4;8627;8620:18;8502:150;8671:19;8681:8;8671:9;:19::i;4588:825:7:-:0;4912:11;;4702:14;;4912:33;:11;:33;4908:499;;-1:-1:-1;;;;;;4970:39:7;;;;;;:31;:39;;;;;;5055:10;;;-1:-1:-1;;;5055:10:7;;;;;;5095:12;;;5125:15;:24;-1:-1:-1;5121:276:7;;5352:11;;;5325:15;24414:8:6;;;24424:9;;;24410:24;5306:44:7;;24653:9:6;5289:75:7;4588:825;-1:-1:-1;;;;4588:825:7:o;8325:308:6:-;8458:13;1706:22:5;1700:29;1697:143;;;1761:10;1755:4;1748:24;1821:4;1815;1808:18;1697:143;1884:9;1860:22;1853:41;8495:20:6::1;8508:6;8495:12;:20::i;:::-;8487:28;;8525:60;8550:5;824:42;8566:10;8578:6;8525:24;:60::i;:::-;8595:31;8619:6;8595:23;:31::i;8239:439:7:-:0;12635:6:2;11991:25;12010:5;11991:18;:25::i;:::-;8326:11:7::1;::::0;:36:::1;:11;760:1;8326:36;8322:69;;8371:20;;-1:-1:-1::0;;;8371:20:7::1;;;;;;;;;;;8322:69;8424:12;::::0;::::1;::::0;::::1;;;8405:15;:31;8401:67;;8445:23;;-1:-1:-1::0;;;8445:23:7::1;;;;;;;;;;;8401:67;8478:11;:33:::0;;8521:38:::1;8543:15;8521:38:::0;;::::1;8478:33;8521:38;-1:-1:-1::0;;8521:38:7;;;;;;;845:1:::1;8521:38:::0;;;;;8582:46:::1;::::0;-1:-1:-1;;;8608:20:7;::::1;;::::0;8582:46:::1;:::i;:::-;8569:10;:59:::0;;::::1;::::0;;;::::1;-1:-1:-1::0;;;8569:59:7::1;-1:-1:-1::0;;8569:59:7;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;8638:26:7::1;:33:::0;;-1:-1:-1;;;;8638:33:7::1;-1:-1:-1::0;;;8638:33:7::1;::::0;;8239:439::o;6374:138:6:-;14166:7:2;11991:25;12010:5;11991:18;:25::i;:::-;-1:-1:-1;6477:17:6::1;:28:::0;;-1:-1:-1;;;;;;6477:28:6::1;-1:-1:-1::0;;;;;6477:28:6;;;::::1;::::0;;;::::1;::::0;;6374:138::o;17011:301:3:-;-1:-1:-1;;;17173:4:3;17166:38;;;17074:14;17217:19;;;;17283:4;17273:21;17267:28;17263:2;17259:37;;17011:301::o;5219:802:2:-;-1:-1:-1;;5507:27:2;5497:8;5494:41;5484:521;;5610:15;5604:4;5597:29;5656:8;5650:4;5643:22;5859:5;5851:4;5845;5835:21;5829:28;5825:40;5815:176;;5902:10;5896:4;5889:24;5968:4;5962;5955:18;10025:193:7;10100:13;10117:5;10100:22;:51;;;;10126:13;10143:8;10126:25;10100:51;10079:132;;;;-1:-1:-1;;;10079:132:7;;11873:2:8;10079:132:7;;;11855:21:8;11912:2;11892:18;;;11885:30;11951:34;11931:18;;;11924:62;-1:-1:-1;;;12002:18:8;;;11995:32;12044:19;;10079:132:7;11671:398:8;17773:309:6;17996:79;18005:5;18012:12;18026:10;18046:5;-1:-1:-1;;;;;17996:79:6;18062:2;-1:-1:-1;;;;;17996:79:6;18067:7;17996:8;:79::i;11379:637:7:-;11462:13;11478:19;11486:10;11478:7;:19::i;:::-;11511:11;;11462:35;;-1:-1:-1;11511:33:7;:11;11507:503;;11791:26;824:42:6;11811:5:7;11791:10;:26::i;:::-;11777:10;11745:43;;;;:31;:43;;;;;:72;10127:109:2;:::o;11507:503:7:-;11848:23;11874:39;11902:10;11874:27;:39::i;:::-;11848:65;;11960:15;11931:26;824:42:6;11951:5:7;11931:10;:26::i;:::-;:44;11927:72;;;11984:15;;-1:-1:-1;;;11984:15:7;;;;;;;;;;;4304:117:2;4382:32;4395:4;4401:5;4408;4382:12;:32::i;12689:1254:6:-;12752:13;12801:14;12809:5;12801:7;:14::i;:::-;12777:40;-1:-1:-1;;;;;;12831:19:6;;;12827:37;;12689:1254;;;:::o;12827:37::-;5597:10:7;;-1:-1:-1;;;5597:10:7;;-1:-1:-1;;;;;5597:10:7;;12941:57:6;;12973:25;;-1:-1:-1;;;12973:25:6;;;;;;;;;;;12941:57;13032:11;13030:13;;-1:-1:-1;;;;;13030:13:6;;;;;;-1:-1:-1;;13030:13:6;;;;;;;;13095:48;13125:5;13030:13;13012:15;13095:29;:48::i;:::-;13402:334;13444:5;13545:1;13605:13;13658:4;13701:7;13402:24;:334::i;:::-;13394:342;;13784:5;-1:-1:-1;;;;;13755:35:6;13775:7;13768:5;-1:-1:-1;;;;;13755:35:6;;;;;;;;;;;13896:30;13904:5;13919;-1:-1:-1;;;;;13896:30:6;:7;:30::i;7292:355:1:-;-1:-1:-1;;7498:18:1;7488:8;7485:32;7475:156;;7550:10;7544:4;7537:24;7612:4;7606;7599:18;4053:115:2;4130:31;4143:4;4149:5;4156:4;4130:12;:31::i;16623:989:6:-;16874:4;16868:11;16939:7;16933:4;16926:21;17006:5;17000:4;16993:19;17074:4;17070:2;17066:13;17060:4;17053:27;-1:-1:-1;;;17130:4:6;17123:48;17346:4;17340;17334;17328;17325:1;17311:12;17304:5;17299:52;17289:181;;17395:16;17389:4;17386:1;17371:41;17439:16;17436:1;17429:27;17289:181;17496:1;17490:4;17483:15;17552:4;17545:15;-1:-1:-1;;;;16623:989:6:o;10553:629:7:-;10652:22;10677:36;824:42:6;10697:15:7;10705:6;10697:7;:15::i;:::-;10677:10;:36::i;:::-;10914:11;;10652:61;;-1:-1:-1;10914:33:7;:11;:33;;:67;;-1:-1:-1;10969:12:7;;;;;;;10951:15;:30;10914:67;:170;;;-1:-1:-1;;;;;;11019:39:7;;;;;;:31;:39;;;;;;11002:56;;:81;;;;-1:-1:-1;;;11556:18:1;-1:-1:-1;;;;;11062:21:7;:10;-1:-1:-1;;;;;11062:21:7;;11002:81;10897:279;;;-1:-1:-1;;;;;11109:39:7;;;;;;;;:31;:39;;;;;:56;10553:629::o;10486:225:0:-;10537:14;10656:4;10650;10644;10641:1;10629:32;-1:-1:-1;;10690:4:0;10684:11;;10486:225::o;16037:323:3:-;16098:14;16190:16;;;-1:-1:-1;;;16226:4:3;16219:38;16334:4;16318:21;;16310:30;;16302:39;16296:46;-1:-1:-1;;;;;16280:64:3;;16037:323::o;38588:1370::-;38825:4;38819:11;38875:10;38908:24;38905:1;38898:35;38967:8;38960:4;38957:1;38953:12;38946:30;39075:4;39071:2;39067:13;39063:2;39059:22;39052:4;39049:1;39045:12;39038:44;39116:2;39109:4;39106:1;39102:12;39095:24;39153:4;39146;39143:1;39139:12;39132:26;39186:4;39180:11;39225:1;39218:4;39215:1;39211:12;39204:23;39243:1;39240:71;;;39306:1;39299:4;39296:1;39292:12;39289:1;39282:4;39276;39272:15;39269:1;39262:5;39251:57;39247:62;39240:71;39427:4;39424:1;39417:4;39414:1;39410:12;39403:4;39400:1;39396:12;39393:1;39389:2;39382:5;39377:55;39367:313;;39455:16;39452:214;;;39583:16;39577:4;39574:1;39559:41;39631:16;39628:1;39621:27;39452:214;39367:313;39776:24;39771:3;39767:34;39763:1;39757:8;39754:48;39744:198;;39835:10;39829:4;39822:24;39923:4;39917;39910:18;19383:740:6;19458:14;19563:7;19557:4;19550:21;-1:-1:-1;;;19624:4:6;19617:48;20062:4;20056;20050;20044;20037:5;20030;20019:48;19957:4;19939:16;19936:26;19847:242;19820:4;19814:11;19729:378;19703:404;;19383:740;;;;:::o;6145:1089:1:-;-1:-1:-1;;6579:16:1;;-1:-1:-1;;;;;6427:26:1;;;;;;6539:38;6536:1;;6528:78;6703:16;;6698:3;6694:26;6681:40;;6663:59;;10127:109:2;:::o;15928:282:6:-;16132:71;16141:5;16148:9;16159:10;16179:2;-1:-1:-1;;;;;16132:71:6;16192:6;-1:-1:-1;;;;;16132:71:6;16201:1;16132:8;:71::i;1946:1641:4:-;2002:17;2447:4;2440;2434:11;2430:22;2423:29;;2546:4;2541:3;2537:14;2531:4;2524:28;2627:1;2622:3;2615:14;2728:3;2758:1;2754:6;2967:5;2949:402;3005:11;;;;3186:2;3200;3190:13;;3182:22;3005:11;3169:36;3292:2;3282:13;;3312:25;2949:402;3312:25;-1:-1:-1;;3379:13:4;;;-1:-1:-1;;3492:14:4;;;3552:19;;;3492:14;1946:1641;-1:-1:-1;1946:1641:4:o;19186:3338::-;19322:20;19451:7;19445:14;19498:6;19492:13;19549:11;19543:18;19599:4;19590:7;19586:18;19575:29;;19639:4;19631:6;19627:17;19617:27;;19689:4;19676:11;19672:22;19657:37;;19734:4;19727;19721:11;19717:22;19707:32;;19784:13;19775:7;19771:27;19838:13;19824:12;19821:31;19811:1966;;19931:1;19916:12;19904:10;19900:29;19896:37;19959:1;20004:4;19990:12;19987:22;19977:74;;-1:-1:-1;20018:31:4;;;19977:74;20112:4;20098:12;20094:23;20088:4;20084:34;20081:1;20077:42;20151:6;20145:13;20175:1588;20224:7;20218:14;20402:1;20399;20395:9;20392:1;20388:17;20378:1166;;20436:1;20433:433;;;20515:1;20500:12;20491:7;20481:32;20478:39;20468:372;;20553:17;;;20625:1;20671:15;;;;20613:14;;;;20729:29;;;20719:50;;20762:5;;;20719:50;20802:8;20175:1588;;20468:372;20976:1;20961:250;21044:19;;;21038:26;21022:14;;;21015:50;21106:4;21099:12;21150:24;;;20961:250;21140:45;-1:-1:-1;21312:26:4;;;;21246:30;;;;21363:159;;;;21431:16;21422:7;21419:29;21409:50;;21452:5;;;21363:159;21565:17;;21625:1;21659:15;;;;21613:14;;;;21705:29;;;20175:1588;21695:50;20175:1588;20179:2;;;;19811:1966;21814:6;21791:29;;21860:4;21853;21847:11;21843:22;21833:32;;21937:7;21925:10;21921:24;21912:6;21895:15;21891:28;21887:59;21878:68;;22022:213;22041:10;22032:7;22029:23;22022:213;;;22098:14;;22074:39;;22170:4;22203:18;;;;22149:26;;;;22022:213;;;-1:-1:-1;;22304:17:4;22300:25;;;22376:15;;;22270:4;22417:15;22411:4;22404:29;-1:-1:-1;;22258:17:4;;;22470;;;-1:-1:-1;22258:17:4;19186:3338;-1:-1:-1;;;19186:3338:4:o;18229:1017:6:-;18502:4;18496:11;18541:6;18534:4;18531:1;18527:12;18520:28;-1:-1:-1;;;18568:1:6;18561:45;18683:1;18676:4;18673:1;18669:12;18662:23;18731:4;18724;18721:1;18717:12;18710:26;18791:1;18784:4;18781:1;18777:12;18770:23;18843:10;18836:4;18833:1;18829:12;18822:32;18888:4;18881;18878:1;18874:12;18867:26;18927:4;18920;18917:1;18913:12;18906:26;18966:4;18959;18956:1;18952:12;18945:26;19005:4;18998;18995:1;18991:12;18984:26;19106:4;19094:10;19087:5;19080:4;19077:1;19073:12;19070:1;19063:5;19056;19051:60;19041:189;;19155:16;19149:4;19146:1;19131:41;19199:16;19196:1;19189:27;19041:189;;18229:1017;;;;;;:::o;2952:967:2:-;3118:15;3112:4;3105:29;3160:4;3154;3147:18;3210:4;3204;3194:21;3288:8;3282:15;3395:5;3386:7;3383:18;3639:2;3629:62;;-1:-1:-1;3669:19:2;;;3656:33;;3629:62;3763:7;3753:8;3746:25;3895:7;3887:4;3881:11;3877:2;3873:20;3841:30;3838:1;3835;3830:73;;;;2952:967;;;:::o;21573:1353:3:-;21677:40;21706:1;21710:2;21714;21677:20;:40::i;:::-;21855:2;21851;21847:11;21843:2;21839:20;21833:26;;21938:2;21932:4;21925:16;-1:-1:-1;;;21961:4:3;21954:38;22073:2;22065:5;22060:3;22056:15;22053:23;22044:4;22038;22028:21;22024:2;22020:30;22016:2;22012:39;22005:72;22172:2;22166:4;22159:16;22227:4;22221;22211:21;22298:1;22284:11;22278:18;22274:26;22450:20;22431:17;22427:44;22423:2;22419:53;22409:268;;22601:18;22595:2;22588:10;22585:1;22581:18;22574:46;22654:4;22648;22641:18;22409:268;22694:38;;22858:2;22854;22851:1;22824:25;22851:1;22806:10;22801:60;12247:134:7;;;:::o;4854:1046:0:-;5041:14;5148:4;5142:11;5187:15;5180:4;5177:1;5173:12;5166:37;5237:5;5230:4;5227:1;5223:12;5216:27;5277:8;5270:4;5267:1;5263:12;5256:30;5336:14;5332:2;5328:23;5324:2;5320:32;5313:4;5310:1;5306:12;5299:54;5387:8;5380:4;5377:1;5373:12;5366:30;-1:-1:-1;;;5489:1:0;5482:45;5691:4;5685;5679;5672;5669:1;5665:12;5662:1;5652:8;5645:5;5640:56;5613:4;5595:16;5592:26;5567:147;5540:310;;5760:10;5754:4;5747:24;5831:4;5825;5818:18;5540:310;-1:-1:-1;;5879:4:0;5873:11;;4854:1046;-1:-1:-1;;;;;4854:1046:0:o;17563:407:3:-;-1:-1:-1;;;17710:4:3;17703:38;17767:5;17761:4;17754:19;17821:4;17815;17805:21;17859:11;17853:18;17943:6;17939:2;17935:15;17928:5;17924:27;17920:2;17916:36;17908:6;17904:49;17891:11;17884:70;;;17563:407;;:::o;22976:554:6:-;23125:11;23133:2;23125:7;:11::i;:::-;-1:-1:-1;;;;;23125:16:6;;23121:53;;23150:24;;-1:-1:-1;;;23150:24:6;;;;;;;;;;;23121:53;-1:-1:-1;;;;;23188:18:6;;;23184:340;;18232:13:3;18323:16;;;-1:-1:-1;;;18359:4:3;18352:38;18460:4;18444:21;;18436:30;;18428:39;;18422:46;18417:3;18413:56;23226:27:6;;23222:66;;23262:26;;-1:-1:-1;;;23262:26:6;;;;;;;;;;;23222:66;23325:1;18831:16:3;;;-1:-1:-1;;;18867:4:3;18860:38;18964:4;18948:21;;18940:30;;18932:39;;18998:20;;-1:-1:-1;;;;;;19065:38:3;;19053:51;19031:74;;23385:26:6;23393:2;23397:13;23405:4;23397:7;:13::i;:::-;23385:7;:26::i;:::-;23460:16;23468:4;23474:1;23460:7;:16::i;14:286:8:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:8;;209:43;;199:71;;266:1;263;256:12;497:418;646:2;635:9;628:21;609:4;678:6;672:13;721:6;716:2;705:9;701:18;694:34;780:6;775:2;767:6;763:15;758:2;747:9;743:18;737:50;836:1;831:2;822:6;811:9;807:22;803:31;796:42;906:2;899;895:7;890:2;882:6;878:15;874:29;863:9;859:45;855:54;847:62;;;497:418;;;;:::o;920:173::-;988:20;;-1:-1:-1;;;;;1037:31:8;;1027:42;;1017:70;;1083:1;1080;1073:12;1098:186;1157:6;1210:2;1198:9;1189:7;1185:23;1181:32;1178:52;;;1226:1;1223;1216:12;1178:52;1249:29;1268:9;1249:29;:::i;1497:180::-;1556:6;1609:2;1597:9;1588:7;1584:23;1580:32;1577:52;;;1625:1;1622;1615:12;1577:52;-1:-1:-1;1648:23:8;;1497:180;-1:-1:-1;1497:180:8:o;1682:254::-;1750:6;1758;1811:2;1799:9;1790:7;1786:23;1782:32;1779:52;;;1827:1;1824;1817:12;1779:52;1850:29;1869:9;1850:29;:::i;:::-;1840:39;1926:2;1911:18;;;;1898:32;;-1:-1:-1;;;1682:254:8:o;1941:163::-;2008:20;;2068:10;2057:22;;2047:33;;2037:61;;2094:1;2091;2084:12;2109:184;2167:6;2220:2;2208:9;2199:7;2195:23;2191:32;2188:52;;;2236:1;2233;2226:12;2188:52;2259:28;2277:9;2259:28;:::i;2298:367::-;2361:8;2371:6;2425:3;2418:4;2410:6;2406:17;2402:27;2392:55;;2443:1;2440;2433:12;2392:55;-1:-1:-1;2466:20:8;;-1:-1:-1;;;;;2498:30:8;;2495:50;;;2541:1;2538;2531:12;2495:50;2578:4;2570:6;2566:17;2554:29;;2638:3;2631:4;2621:6;2618:1;2614:14;2606:6;2602:27;2598:38;2595:47;2592:67;;;2655:1;2652;2645:12;2592:67;2298:367;;;;;:::o;2670:511::-;2765:6;2773;2781;2834:2;2822:9;2813:7;2809:23;2805:32;2802:52;;;2850:1;2847;2840:12;2802:52;2873:29;2892:9;2873:29;:::i;:::-;2863:39;;2953:2;2942:9;2938:18;2925:32;-1:-1:-1;;;;;2972:6:8;2969:30;2966:50;;;3012:1;3009;3002:12;2966:50;3051:70;3113:7;3104:6;3093:9;3089:22;3051:70;:::i;:::-;2670:511;;3140:8;;-1:-1:-1;3025:96:8;;-1:-1:-1;;;;2670:511:8:o;3565:328::-;3642:6;3650;3658;3711:2;3699:9;3690:7;3686:23;3682:32;3679:52;;;3727:1;3724;3717:12;3679:52;3750:29;3769:9;3750:29;:::i;:::-;3740:39;;3798:38;3832:2;3821:9;3817:18;3798:38;:::i;:::-;3788:48;;3883:2;3872:9;3868:18;3855:32;3845:42;;3565:328;;;;;:::o;3898:348::-;3950:8;3960:6;4014:3;4007:4;3999:6;3995:17;3991:27;3981:55;;4032:1;4029;4022:12;3981:55;-1:-1:-1;4055:20:8;;-1:-1:-1;;;;;4087:30:8;;4084:50;;;4130:1;4127;4120:12;4084:50;4167:4;4159:6;4155:17;4143:29;;4219:3;4212:4;4203:6;4195;4191:19;4187:30;4184:39;4181:59;;;4236:1;4233;4226:12;4251:411;4322:6;4330;4383:2;4371:9;4362:7;4358:23;4354:32;4351:52;;;4399:1;4396;4389:12;4351:52;4439:9;4426:23;-1:-1:-1;;;;;4464:6:8;4461:30;4458:50;;;4504:1;4501;4494:12;4458:50;4543:59;4594:7;4585:6;4574:9;4570:22;4543:59;:::i;:::-;4621:8;;4517:85;;-1:-1:-1;4251:411:8;-1:-1:-1;;;;4251:411:8:o;4667:721::-;4759:6;4767;4775;4783;4836:2;4824:9;4815:7;4811:23;4807:32;4804:52;;;4852:1;4849;4842:12;4804:52;4892:9;4879:23;-1:-1:-1;;;;;4962:2:8;4954:6;4951:14;4948:34;;;4978:1;4975;4968:12;4948:34;5017:59;5068:7;5059:6;5048:9;5044:22;5017:59;:::i;:::-;5095:8;;-1:-1:-1;4991:85:8;-1:-1:-1;5183:2:8;5168:18;;5155:32;;-1:-1:-1;5199:16:8;;;5196:36;;;5228:1;5225;5218:12;5196:36;;5267:61;5320:7;5309:8;5298:9;5294:24;5267:61;:::i;:::-;4667:721;;;;-1:-1:-1;5347:8:8;-1:-1:-1;;;;4667:721:8:o;5598:160::-;5663:20;;5719:13;;5712:21;5702:32;;5692:60;;5748:1;5745;5738:12;5763:254;5828:6;5836;5889:2;5877:9;5868:7;5864:23;5860:32;5857:52;;;5905:1;5902;5895:12;5857:52;5928:29;5947:9;5928:29;:::i;:::-;5918:39;;5976:35;6007:2;5996:9;5992:18;5976:35;:::i;:::-;5966:45;;5763:254;;;;;:::o;6211:437::-;6297:6;6305;6358:2;6346:9;6337:7;6333:23;6329:32;6326:52;;;6374:1;6371;6364:12;6326:52;6414:9;6401:23;-1:-1:-1;;;;;6439:6:8;6436:30;6433:50;;;6479:1;6476;6469:12;6433:50;6518:70;6580:7;6571:6;6560:9;6556:22;6518:70;:::i;6653:180::-;6709:6;6762:2;6750:9;6741:7;6737:23;6733:32;6730:52;;;6778:1;6775;6768:12;6730:52;6801:26;6817:9;6801:26;:::i;6838:627::-;6935:6;6943;6951;6959;6967;7020:3;7008:9;6999:7;6995:23;6991:33;6988:53;;;7037:1;7034;7027:12;6988:53;7060:29;7079:9;7060:29;:::i;:::-;7050:39;;7108:38;7142:2;7131:9;7127:18;7108:38;:::i;:::-;7098:48;;7193:2;7182:9;7178:18;7165:32;7155:42;;7248:2;7237:9;7233:18;7220:32;-1:-1:-1;;;;;7267:6:8;7264:30;7261:50;;;7307:1;7304;7297:12;7261:50;7346:59;7397:7;7388:6;7377:9;7373:22;7346:59;:::i;:::-;6838:627;;;;-1:-1:-1;6838:627:8;;-1:-1:-1;7424:8:8;;7320:85;6838:627;-1:-1:-1;;;6838:627:8:o;7470:413::-;7543:6;7551;7559;7612:2;7600:9;7591:7;7587:23;7583:32;7580:52;;;7628:1;7625;7618:12;7580:52;7667:9;7654:23;7717:4;7710:5;7706:16;7699:5;7696:27;7686:55;;7737:1;7734;7727:12;7686:55;7760:5;-1:-1:-1;7784:37:8;7817:2;7802:18;;7784:37;:::i;:::-;7774:47;;7840:37;7873:2;7862:9;7858:18;7840:37;:::i;:::-;7830:47;;7470:413;;;;;:::o;7888:260::-;7956:6;7964;8017:2;8005:9;7996:7;7992:23;7988:32;7985:52;;;8033:1;8030;8023:12;7985:52;8056:29;8075:9;8056:29;:::i;:::-;8046:39;;8104:38;8138:2;8127:9;8123:18;8104:38;:::i;8153:380::-;8232:1;8228:12;;;;8275;;;8296:61;;8350:4;8342:6;8338:17;8328:27;;8296:61;8403:2;8395:6;8392:14;8372:18;8369:38;8366:161;;8449:10;8444:3;8440:20;8437:1;8430:31;8484:4;8481:1;8474:15;8512:4;8509:1;8502:15;8538:269;8605:10;8635;;;8647;;;8631:27;;8670:11;;;8667:134;;;8723:10;8718:3;8714:20;8711:1;8704:31;8758:4;8755:1;8748:15;8786:4;8783:1;8776:15;8667:134;;8538:269;;;;:::o;8812:127::-;8873:10;8868:3;8864:20;8861:1;8854:31;8904:4;8901:1;8894:15;8928:4;8925:1;8918:15;9070:518;9172:2;9167:3;9164:11;9161:421;;;9208:5;9205:1;9198:16;9252:4;9249:1;9239:18;9322:2;9310:10;9306:19;9303:1;9299:27;9293:4;9289:38;9358:4;9346:10;9343:20;9340:47;;;-1:-1:-1;9381:4:8;9340:47;9436:2;9431:3;9427:12;9424:1;9420:20;9414:4;9410:31;9400:41;;9491:81;9509:2;9502:5;9499:13;9491:81;;;9568:1;9554:16;;9535:1;9524:13;9491:81;;9764:1198;-1:-1:-1;;;;;9883:3:8;9880:27;9877:53;;;9910:18;;:::i;:::-;9939:94;10029:3;9989:38;10021:4;10015:11;9989:38;:::i;:::-;9983:4;9939:94;:::i;:::-;10059:1;10084:2;10079:3;10076:11;10101:1;10096:608;;;;10748:1;10765:3;10762:93;;;-1:-1:-1;10821:19:8;;;10808:33;10762:93;-1:-1:-1;;9721:1:8;9717:11;;;9713:24;9709:29;9699:40;9745:1;9741:11;;;9696:57;10868:78;;10069:887;;10096:608;9017:1;9010:14;;;9054:4;9041:18;;-1:-1:-1;;10132:17:8;;;10247:229;10261:7;10258:1;10255:14;10247:229;;;10350:19;;;10337:33;10322:49;;10457:4;10442:20;;;;10410:1;10398:14;;;;10277:12;10247:229;;;10251:3;10504;10495:7;10492:16;10489:159;;;10628:1;10624:6;10618:3;10612;10609:1;10605:11;10601:21;10597:34;10593:39;10580:9;10575:3;10571:19;10558:33;10554:79;10546:6;10539:95;10489:159;;;10691:1;10685:3;10682:1;10678:11;10674:19;10668:4;10661:33;10069:887;;9764:1198;;;:::o

Swarm Source

ipfs://77798a98c56e954f07284ba2e0b0f9e9ae12504a117f14e1589d59f78e075664
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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