ETH Price: $2,852.38 (-2.94%)
 

Overview

Max Total Supply

10,000,000 TEST

Holders

3

Transfers

-
0

Market

Price

$0.00 @ 0.000000 ETH

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
Token

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 1000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;    

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ShadowWhitelist} from "./ShadowWhitelist.sol";

contract Token is ERC20, Ownable {
    address private _deployer;
    address public shadow;
    address public immutable uniswapV3Factory = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
    address public immutable positionManager = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
    address public immutable swapRouter = 0x2626664c2603336E57B271c5C0b26F421741e481;
    address public immutable quoter = 0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a;
    bool immutable fullAccess;
    bool immutable featured;
    address public poolAddress;
    uint256 public maxWalletPercentage;
    address payable public feeDistributor;
    event TransferAttempt(address indexed from, address indexed to, address indexed origin, address sender, uint256 amount);
    event DebugStep(string message,string info, address from, address to, uint256 amount);

    ShadowWhitelist public whitelist;
    uint256 public whitelistEndTime;
    bool public whitelistActive;
    mapping(address => uint256) public whitelistedAddresses;
    mapping(bytes32 => bool) public transactionHashed;

    address[] public whitelistTimestamps;
    mapping(uint256 => mapping(address => bool)) private _firstTxProcessed;

    modifier onlyDeployer() {
        require(msg.sender == _deployer, "Only Deployer");
        _;
    }

    constructor(
        string memory name_,
        string memory symbol_,
        uint256 maxSupply_,
        address deployer_,
        uint256 _maxWalletPercentage,
        address _whitelist,
        bool featured_
    ) ERC20(name_, symbol_) Ownable(deployer_) {
        _deployer = deployer_;
        shadow = msg.sender;
        maxWalletPercentage = _maxWalletPercentage;
        whitelist = ShadowWhitelist(_whitelist);
        _mint(msg.sender, maxSupply_);
        whitelistActive = false;
        featured = featured_;
    }

    function enableWhitelist() external {
        require(msg.sender == shadow, "Only Shadow");
        require(!whitelistActive, "Whitelist already active");
        whitelistActive = true;
        whitelistEndTime = block.timestamp + 10 minutes;
    }

    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        bytes32 txHash = keccak256(abi.encodePacked(from, block.timestamp));
        if (whitelistActive && block.timestamp <= whitelistEndTime) {
            if (transactionHashed[txHash]) {
                return super.transferFrom(from, to, amount);
            } 
            if (whitelistedAddresses[to] > 0) {
                uint256 maxPercentage = whitelistedAddresses[to];
                uint256 maxWalletAmount = (totalSupply() * maxPercentage) / 10000; 
                require(amount <= maxWalletAmount, "Transfer amount exceeds max wallet percentage");
                transactionHashed[txHash] = true;
                emit TransferAttempt(from, to, tx.origin, msg.sender, amount);
                return super.transferFrom(from, to, amount);
            } else {
                return false;
            }
        } else {
        emit TransferAttempt(from, to, tx.origin, msg.sender, amount);
        return super.transferFrom(from, to, amount);
        }
    }

    function isFeatured() public view returns (bool) {
        return featured;
    }

    function deployer() public view returns (address) {
        return _deployer;
    }

    function returnUserPercentage(address user) public view returns (uint256) {
        return whitelistedAddresses[user];
    }

    function setPoolAddress(address _poolAddress) external {
        require(msg.sender == shadow, "Only Shadow");
        poolAddress = _poolAddress;
    }

    function mint(address to, uint256 amount) external onlyOwner {
        if (!fullAccess) {
            revert("Token: minting is disabled");
        }
        _mint(to, amount);
    }

    function burn(uint256 amount) external onlyOwner {
        require(poolAddress != address(0), "Pool not set");
        uint256 lpBalance = balanceOf(poolAddress);
        require(lpBalance >= amount, "Insufficient LP balance");
        _burn(poolAddress, amount);
    }

    function transferOwnership(address /* newOwner */) public virtual override {
        revert("Ownership transfer is disabled");
    }

    function getWhitelistedPercentages() external returns (address[] memory, uint256[] memory) {
        (address[] memory addresses, uint256[] memory percentages) = whitelist.getWhitelistedAddresses();
        
        for (uint256 i = 0; i < whitelistTimestamps.length; i++) {
            whitelistedAddresses[whitelistTimestamps[i]] = 0;
        }
        whitelistTimestamps = new address[](0);
        
        for (uint256 i = 0; i < addresses.length; i++) {
            whitelistedAddresses[addresses[i]] = percentages[i];
            whitelistTimestamps.push(addresses[i]);
        }
        
        return (addresses, percentages);
    }

    function shadow_airdrop(address[] calldata recipients, uint256[] calldata amounts) external {
        require(msg.sender == shadow, "Only Shadow can airdrop");
        require(recipients.length == amounts.length, "Arrays length mismatch");
        require(recipients.length > 0, "Empty arrays");

        uint256 totalAmount = 0;
        for (uint256 i = 0; i < amounts.length; i++) {
            require(amounts[i] > 0, "Amount must be positive");
            require(recipients[i] != address(0), "Invalid recipient address");
            totalAmount += amounts[i];
        }

        require(balanceOf(msg.sender) >= totalAmount, "Insufficient balance for airdrop");

        for (uint256 i = 0; i < recipients.length; i++) {
            _transfer(msg.sender, recipients[i], amounts[i]);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol";
import {SafeERC20} from "../utils/SafeERC20.sol";
import {IERC4626} from "../../../interfaces/IERC4626.sol";
import {Math} from "../../../utils/math/Math.sol";

/**
 * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
 * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
 * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
 * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
 * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
 * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
 * expensive than it is profitable. More details about the underlying math can be found
 * xref:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626 is ERC20, IERC4626 {
    using Math for uint256;

    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
     */
    constructor(IERC20 asset_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
        (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
            abi.encodeCall(IERC20Metadata.decimals, ())
        );
        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        return _underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}.
     *
     * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
     * In this case, the shares will be minted without requiring any assets to be deposited.
     */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
        _mint(receiver, shares);

        emit Deposit(caller, receiver, assets, shares);
    }

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer(_asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";

contract ShadowStacking is ERC4626 {
    enum LockDuration {
        DAYS_30,
        DAYS_60,
        DAYS_90,
        DAYS_365  
    }

    mapping(address => uint256) public unlockTime;
    mapping(address => LockDuration) public userLockDuration;
    bool private _depositWithLockCalled;
    address[] private stackersArray;
    mapping(address => bool) private isStacker;

    uint256 public constant SECONDS_30_DAYS = 30 days;
    uint256 public constant SECONDS_60_DAYS = 60 days;
    uint256 public constant SECONDS_90_DAYS = 90 days;
    uint256 public constant SECONDS_365_DAYS = 365 days;

    constructor(IERC20 _asset) ERC4626(_asset) ERC20("Shadow Stacking", "sSHADOW") {}

    modifier onlyDepositWithLock() {
        require(_depositWithLockCalled, "Use depositWithLock instead");
        _;
        _depositWithLockCalled = false;
    }

    function getLockDurationInSeconds(LockDuration duration) public pure returns (uint256) {
        if (duration == LockDuration.DAYS_30) return SECONDS_30_DAYS;
        if (duration == LockDuration.DAYS_60) return SECONDS_60_DAYS;
        if (duration == LockDuration.DAYS_90) return SECONDS_90_DAYS;
        if (duration == LockDuration.DAYS_365) return SECONDS_365_DAYS;
        return 0;
    }

    function deposit(uint256 assets, address receiver) public virtual override onlyDepositWithLock returns (uint256) {
        if (!isStacker[receiver]) {
            stackersArray.push(receiver);
            isStacker[receiver] = true;
        }
        return super.deposit(assets, receiver);
    }

    function depositWithLock(uint256 assets, address receiver, LockDuration lockDuration) public returns (uint256) {
        _depositWithLockCalled = true;
        uint256 shares = deposit(assets, receiver);
        userLockDuration[receiver] = lockDuration;
        unlockTime[receiver] = block.timestamp + getLockDurationInSeconds(lockDuration);
        return shares;
    }

    function withdraw(uint256 assets, address receiver, address owner) public override returns (uint256) {
        require(block.timestamp >= unlockTime[owner], "Tokens are still locked");
        return super.withdraw(assets, receiver, owner);
    }

    function redeem(uint256 shares, address receiver, address owner) public override returns (uint256) {
        require(block.timestamp >= unlockTime[owner], "Tokens are still locked");
        return super.redeem(shares, receiver, owner);
    }

    function getRemainingLockTime(address user) public view returns (uint256) {
        if (block.timestamp >= unlockTime[user]) return 0;
        return unlockTime[user] - block.timestamp;
    }

    function getUserLockDuration(address user) public view returns (LockDuration) {
        return userLockDuration[user];
    }

    function getUnlockTimestamp(address user) public view returns (uint256) {
        return unlockTime[user];
    }

    function getAllStackers() external view returns (
        address[] memory stackers,
        uint256[] memory balances,
        uint256[] memory lockTimes,
        LockDuration[] memory durations
    ) {
        stackers = stackersArray;
        balances = new uint256[](stackersArray.length);
        lockTimes = new uint256[](stackersArray.length);
        durations = new LockDuration[](stackersArray.length);

        for(uint i = 0; i < stackersArray.length; i++) {
            address stacker = stackersArray[i];
            balances[i] = this.balanceOf(stacker);
            lockTimes[i] = unlockTime[stacker];
            durations[i] = userLockDuration[stacker];
        }

        return (stackers, balances, lockTimes, durations);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./ShadowStacking.sol";

contract ShadowWhitelist is Ownable {
    bool public whitelistEnabled;
    ShadowStacking public shadowStacking;

    // Supply thresholds
    uint256 constant SUPPLY_TIER_1 = 0.000000000000001 ether;
    uint256 constant SUPPLY_TIER_2 = 0.000000000000002 ether;
    uint256 constant SUPPLY_TIER_3 = 0.00000000000001 ether;

    // Percentage values (in basis points - 1% = 100)
    uint256 constant PERCENTAGE_TIER_1 = 20;  // 0.2%
    uint256 constant PERCENTAGE_TIER_2 = 50;  // 0.5%
    uint256 constant PERCENTAGE_TIER_3 = 100; // 1%

    constructor(address owner_) Ownable(owner_) {}

    function setShadowStacking(address _shadowStacking) external onlyOwner {
        require(_shadowStacking != address(0), "Invalid ShadowStacking address");
        shadowStacking = ShadowStacking(_shadowStacking);
    }

    function getWhitelistedAddresses() external view returns (address[] memory, uint256[] memory) {
        (address[] memory stackers, uint256[] memory balances, ,) = shadowStacking.getAllStackers();
        uint256[] memory percentages = new uint256[](stackers.length);
        address[] memory stackerAddresses = new address[](stackers.length);
        uint256[] memory stackerPercentages = new uint256[](stackers.length);
        uint256 validCount = 0;
        for (uint256 i = 0; i < stackers.length; i++) {
            percentages[i] = getPercentageForSupply(balances[i]);
            if (percentages[i] > 0) {
                stackerAddresses[validCount] = stackers[i];
                stackerPercentages[validCount] = percentages[i];
                validCount++;
            }
        }
        return (stackerAddresses, stackerPercentages);
    }

    function getPercentageForSupply(uint256 supply) public pure returns (uint256) {
        if (supply >= SUPPLY_TIER_3) return PERCENTAGE_TIER_3;
        if (supply >= SUPPLY_TIER_2) return PERCENTAGE_TIER_2;
        if (supply >= SUPPLY_TIER_1) return PERCENTAGE_TIER_1;
        return 0;
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint256","name":"maxSupply_","type":"uint256"},{"internalType":"address","name":"deployer_","type":"address"},{"internalType":"uint256","name":"_maxWalletPercentage","type":"uint256"},{"internalType":"address","name":"_whitelist","type":"address"},{"internalType":"bool","name":"featured_","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"message","type":"string"},{"indexed":false,"internalType":"string","name":"info","type":"string"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DebugStep","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"origin","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferAttempt","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWhitelistedPercentages","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isFeatured","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWalletPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"returnUserPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolAddress","type":"address"}],"name":"setPoolAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shadow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"shadow_airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"transactionHashed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapV3Factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelist","outputs":[{"internalType":"contract ShadowWhitelist","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"whitelistTimestamps","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedAddresses","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

6101406040523461054357611f718038038061001a81610548565b92833981019060e0818303126105435780516001600160401b038111610543578261004691830161056d565b602082015190926001600160401b0382116105435761006691830161056d565b90604081015191610079606083016105d8565b60808301519160c061008d60a086016105d8565b940151958615158703610543578051906001600160401b0382116104405760035490600182811c92168015610539575b60208310146104205781601f8493116104c9575b50602090601f831160011461046157600092610456575b50508160011b916000199060031b1c1916176003555b8051906001600160401b0382116104405760045490600182811c92168015610436575b60208310146104205781601f8493116103b0575b50602090601f83116001146103485760009261033d575b50508160011b916000199060031b1c1916176004555b6001600160a01b0316801561032757600580546001600160a01b03198116831790915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a37333128a8fc17869897dce68ed026d694621f6fdfd6080527303a520b32c04bf3beef7beb72e919cf822ed34f160a052732626664c2603336e57b271c5c0b26f421741e48160c052733d4e44eb1374240ce5f1b871ab261cd16335b76a60e05260018060a01b031960065416176006553360018060a01b0319600754161760075560095560018060a01b031660018060a01b0319600b541617600b553315610311576002548181018091116102fb57600255600033815280602052604081208281540190556040519182527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a360ff19600d5416600d556101205260405161198490816105ed823960805181610e4a015260a05181610a6f015260c0518161047e015260e0518161043a015261010051816110420152610120518161025c0152f35b634e487b7160e01b600052601160045260246000fd5b63ec442f0560e01b600052600060045260246000fd5b631e4fbdf760e01b600052600060045260246000fd5b01519050388061014c565b600460009081528281209350601f198516905b818110610398575090846001959493921061037f575b505050811b01600455610162565b015160001960f88460031b161c19169055388080610371565b9293602060018192878601518155019501930161035b565b60046000529091507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f840160051c81019160208510610416575b90601f859493920160051c01905b8181106104075750610135565b600081558493506001016103fa565b90915081906103ec565b634e487b7160e01b600052602260045260246000fd5b91607f1691610121565b634e487b7160e01b600052604160045260246000fd5b0151905038806100e8565b600360009081528281209350601f198516905b8181106104b15750908460019594939210610498575b505050811b016003556100fe565b015160001960f88460031b161c1916905538808061048a565b92936020600181928786015181550195019301610474565b60036000529091507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f840160051c8101916020851061052f575b90601f859493920160051c01905b81811061052057506100d1565b60008155849350600101610513565b9091508190610505565b91607f16916100bd565b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761044057604052565b81601f82011215610543578051906001600160401b0382116104405761059c601f8301601f1916602001610548565b92828452602083830101116105435760005b8281106105c357505060206000918301015290565b806020809284010151828287010152016105ae565b51906001600160a01b03821682036105435756fe608080604052600436101561001357600080fd5b60003560e01c90816302ce5813146113cf5750806306c933d814610b3b57806306fdde0314611310578063095ea7b31461122c5780630d43e8ad146112055780631755ff21146111de57806318160ddd146111c057806323b872dd14611187578063313ce5671461116b57806337b2152a1461113a57806340c10f191461101b57806342966c6814610e8c578063599ca39714610e6e5780635b54918214610e2a578063612fc62f14610b75578063636cd86c14610b3b57806370a0823114610afb578063715018a614610a93578063791b98bc14610a4f57806384ad9879146106505780638da5cb5b1461062957806393e59dc11461060257806395d89b41146104fa578063a9059cbb146104c9578063ac600a3c146104a2578063c31c9c071461045e578063c6bbd5a71461041a578063cdfb2b4e14610372578063d5f394881461034b578063dd62ed3e146102f3578063e9e15b4f1461029f578063ebdfd72214610281578063ee4892e614610244578063f1d03349146102035763f2fde38b146101a057600080fd5b346101fe5760203660031901126101fe576101b96113ef565b50606460405162461bcd60e51b815260206004820152601e60248201527f4f776e657273686970207472616e736665722069732064697361626c656400006044820152fd5b600080fd5b346101fe5760203660031901126101fe576004356010548110156101fe576001600160a01b03610234602092611495565b90549060031b1c16604051908152f35b346101fe5760003660031901126101fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101fe5760003660031901126101fe576020600c54604051908152f35b346101fe5760203660031901126101fe576001600160a01b036102c06113ef565b6102cf82600754163314611734565b1673ffffffffffffffffffffffffffffffffffffffff196008541617600855600080f35b346101fe5760403660031901126101fe5761030c6113ef565b6001600160a01b0361031c611405565b911660005260016020526001600160a01b03604060002091166000526020526020604060002054604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360065416604051908152f35b346101fe5760003660031901126101fe576103996001600160a01b03600754163314611734565b600d5460ff81166103d65760019060ff191617600d5561025842018042116103c057600c55005b634e487b7160e01b600052601160045260246000fd5b606460405162461bcd60e51b815260206004820152601860248201527f57686974656c69737420616c72656164792061637469766500000000000000006044820152fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101fe5760003660031901126101fe5760206001600160a01b0360075416604051908152f35b346101fe5760403660031901126101fe576104ef6104e56113ef565b6024359033611886565b602060405160018152f35b346101fe5760003660031901126101fe5760405160006004548060011c906001811680156105f8575b6020831081146105e4578285529081156105c05750600114610560575b61055c83610550818503826114c6565b6040519182918261141b565b0390f35b91905060046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b916000905b8082106105a657509091508101602001610550610540565b91926001816020925483858801015201910190929161058e565b60ff191660208086019190915291151560051b840190910191506105509050610540565b602484634e487b7160e01b81526022600452fd5b91607f1691610523565b346101fe5760003660031901126101fe5760206001600160a01b03600b5416604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360055416604051908152f35b346101fe5760003660031901126101fe57600460006001600160a01b03600b5416604051928380927f6d0280270000000000000000000000000000000000000000000000000000000082525afa8015610a43576000918291610919575b5060005b6010548110156106ec57806001600160a01b036106cf600193611495565b90549060031b1c16600052600e60205260006040812055016106b1565b506020916040516106fd84826114c6565b60008152600036813780519067ffffffffffffffff82116107e0576801000000000000000082116107e0578490601054836010558084106108b7575b5001601060005260005b82811061087c5750505060005b81518110156107f6576107638184611720565b516001600160a01b036107768385611720565b5116600052600e85526040600020556001600160a01b036107978284611720565b51169060105491680100000000000000008310156107e0576107c0836001809501601055611495565b6001600160a01b03829392549160031b92831b921b191617905501610750565b634e487b7160e01b600052604160045260246000fd5b509060405192839260408401604085528151809152826060860192019060005b81811061085c575050508381038285015281808451928381520193019160005b82811061084557505050500390f35b835185528695509381019392810192600101610836565b82516001600160a01b031684528796509284019291840191600101610816565b81516001600160a01b03167f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae67282015590850190600101610743565b7f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae67201837f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672015b81811061090a5750610739565b600081558793506001016108fd565b90503d8083833e61092a81836114c6565b8101604082820312610a3f57815167ffffffffffffffff8111610a0f5782019181601f84011215610a0f57825161096081611708565b9361096e60405195866114c6565b81855260208086019260051b82010190848211610a3b57602001915b818310610a175750505060208101519067ffffffffffffffff8211610a13570181601f82011215610a0f578051906109c182611708565b946109cf60405196876114c6565b82865260208087019360051b830101938411610a0c5750602001905b8282106109fc5750505090826106ad565b81518152602091820191016109eb565b80fd5b8380fd5b8480fd5b82516001600160a01b0381168103610a375781526020928301920161098a565b8780fd5b8680fd5b8280fd5b6040513d6000823e3d90fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101fe5760003660031901126101fe57610aac611844565b60006001600160a01b0360055473ffffffffffffffffffffffffffffffffffffffff198116600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101fe5760203660031901126101fe576020610b33610b196113ef565b6001600160a01b0316600052600060205260406000205490565b604051908152f35b346101fe5760203660031901126101fe576001600160a01b03610b5c6113ef565b16600052600e6020526020604060002054604051908152f35b346101fe5760403660031901126101fe5760043567ffffffffffffffff81116101fe57610ba6903690600401611464565b9060243567ffffffffffffffff81116101fe57610bc7903690600401611464565b906001600160a01b03600754163303610de657818403610da2578315610d5e576000805b838110610c89575033600052600060205260406000205410610c455760005b848110610c1357005b80610c3f610c2c610c2760019489896116d7565b6116e7565b610c378387876116d7565b359033611886565b01610c0a565b606460405162461bcd60e51b815260206004820152602060248201527f496e73756666696369656e742062616c616e636520666f722061697264726f706044820152fd5b90610c958285856116d7565b3515610d1a576001600160a01b03610cb1610c278489896116d7565b1615610cd657610ccf600191610cc88487876116d7565b35906116fb565b9101610beb565b606460405162461bcd60e51b815260206004820152601960248201527f496e76616c696420726563697069656e742061646472657373000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f416d6f756e74206d75737420626520706f7369746976650000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152600c60248201527f456d7074792061727261797300000000000000000000000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601660248201527f417272617973206c656e677468206d69736d61746368000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920536861646f772063616e2061697264726f700000000000000000006044820152fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101fe5760003660031901126101fe576020600954604051908152f35b346101fe5760203660031901126101fe57600435610ea8611844565b6001600160a01b0360085416908115610fd75780610eda836001600160a01b0316600052600060205260406000205490565b10610f935760009082610f2f57917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084610f1985966002546116fb565b6002555b8060025403600255604051908152a380f35b828252816020526040822054818110610f7b5760208285937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9386978752868452036040862055610f1d565b60649363391434e360e21b8452600452602452604452fd5b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e74204c502062616c616e63650000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152600c60248201527f506f6f6c206e6f742073657400000000000000000000000000000000000000006044820152fd5b346101fe5760403660031901126101fe576110346113ef565b60243590611040611844565b7f0000000000000000000000000000000000000000000000000000000000000000156110f6576001600160a01b031680156110c757816110846020936002546116fb565b6002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600093849284845283825260408420818154019055604051908152a380f35b7fec442f0500000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b606460405162461bcd60e51b815260206004820152601a60248201527f546f6b656e3a206d696e74696e672069732064697361626c65640000000000006044820152fd5b346101fe5760203660031901126101fe57600435600052600f602052602060ff604060002054166040519015158152f35b346101fe5760003660031901126101fe57602060405160128152f35b346101fe5760603660031901126101fe5760206111b66111a56113ef565b6111ad611405565b604435916114e8565b6040519015158152f35b346101fe5760003660031901126101fe576020600254604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360085416604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b03600a5416604051908152f35b346101fe5760403660031901126101fe576112456113ef565b6024359033156112e1576001600160a01b03169081156112b257336000526001602052604060002082600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b7f94280d6200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b7fe602df0500000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b346101fe5760003660031901126101fe5760405160006003548060011c906001811680156113c5575b6020831081146105e4578285529081156105c057506001146113655761055c83610550818503826114c6565b91905060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b916000905b8082106113ab57509091508101602001610550610540565b919260018160209254838588010152019101909291611393565b91607f1691611339565b346101fe5760003660031901126101fe5760209060ff600d541615158152f35b600435906001600160a01b03821682036101fe57565b602435906001600160a01b03821682036101fe57565b91909160208152825180602083015260005b81811061144e575060409293506000838284010152601f8019910116010190565b806020809287010151604082860101520161142d565b9181601f840112156101fe5782359167ffffffffffffffff83116101fe576020808501948460051b0101116101fe57565b6010548110156114b057601060005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176107e057604052565b919060405192602084016001600160a01b038216946bffffffffffffffffffffffff198360601b168252426034820152603481526115276054826114c6565b5190209360ff600d5416806116cb575b1561167e5784600052600f60205260ff60406000205416611673576001600160a01b0383166000818152600e6020526040902054156116685780600052600e6020526040600020546002548181029181830414901517156103c057612710900485116115fe576000958652600f6020908152604096879020805460ff1916600117905586513381529081018690526115fb963293917fbd271ef139c83b0b92924563a89bd3bd04d2552d58cb760d45cd26e2a570057e91819081015b0390a461177f565b90565b608460405162461bcd60e51b815260206004820152602d60248201527f5472616e7366657220616d6f756e742065786365656473206d61782077616c6c60448201527f65742070657263656e74616765000000000000000000000000000000000000006064820152fd5b505050505050600090565b506115fb935061177f565b60408051338152602081018690526115fb965032926001600160a01b0386169290917fbd271ef139c83b0b92924563a89bd3bd04d2552d58cb760d45cd26e2a570057e91819081016115f3565b50600c54421115611537565b91908110156114b05760051b0190565b356001600160a01b03811681036101fe5790565b919082018092116103c057565b67ffffffffffffffff81116107e05760051b60200190565b80518210156114b05760209160051b010190565b1561173b57565b606460405162461bcd60e51b815260206004820152600b60248201527f4f6e6c7920536861646f770000000000000000000000000000000000000000006044820152fd5b91906001600160a01b0383169283600052600160205260406000206001600160a01b03331660005260205260406000205460001981036117ca575b506117c59350611886565b600190565b83811061180e5784156112e15733156112b2576117c594600052600160205260406000206001600160a01b03331660005260205283604060002091039055386117ba565b83907ffb8f41b2000000000000000000000000000000000000000000000000000000006000523360045260245260445260646000fd5b6001600160a01b0360055416330361185857565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6001600160a01b031690811561191f576001600160a01b03169182156110c75760008281528060205260408120548281106119055791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b60649363391434e360e21b83949352600452602452604452fd5b7f96c6fd1e00000000000000000000000000000000000000000000000000000000600052600060045260246000fdfea26469706673582212202756e13ded8d9c27025b1132e35979fa7340e6100bda9b449feba6a0a188d81164736f6c634300081b003300000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000084595161401484a000000000000000000000000000000a45c15f5a909569ba7e48ff1aef66271127c035f000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000004f6e833d7469ef46d15a1041c373a513e35e78830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5445535420627920536861646f7700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045445535400000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816302ce5813146113cf5750806306c933d814610b3b57806306fdde0314611310578063095ea7b31461122c5780630d43e8ad146112055780631755ff21146111de57806318160ddd146111c057806323b872dd14611187578063313ce5671461116b57806337b2152a1461113a57806340c10f191461101b57806342966c6814610e8c578063599ca39714610e6e5780635b54918214610e2a578063612fc62f14610b75578063636cd86c14610b3b57806370a0823114610afb578063715018a614610a93578063791b98bc14610a4f57806384ad9879146106505780638da5cb5b1461062957806393e59dc11461060257806395d89b41146104fa578063a9059cbb146104c9578063ac600a3c146104a2578063c31c9c071461045e578063c6bbd5a71461041a578063cdfb2b4e14610372578063d5f394881461034b578063dd62ed3e146102f3578063e9e15b4f1461029f578063ebdfd72214610281578063ee4892e614610244578063f1d03349146102035763f2fde38b146101a057600080fd5b346101fe5760203660031901126101fe576101b96113ef565b50606460405162461bcd60e51b815260206004820152601e60248201527f4f776e657273686970207472616e736665722069732064697361626c656400006044820152fd5b600080fd5b346101fe5760203660031901126101fe576004356010548110156101fe576001600160a01b03610234602092611495565b90549060031b1c16604051908152f35b346101fe5760003660031901126101fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101fe5760003660031901126101fe576020600c54604051908152f35b346101fe5760203660031901126101fe576001600160a01b036102c06113ef565b6102cf82600754163314611734565b1673ffffffffffffffffffffffffffffffffffffffff196008541617600855600080f35b346101fe5760403660031901126101fe5761030c6113ef565b6001600160a01b0361031c611405565b911660005260016020526001600160a01b03604060002091166000526020526020604060002054604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360065416604051908152f35b346101fe5760003660031901126101fe576103996001600160a01b03600754163314611734565b600d5460ff81166103d65760019060ff191617600d5561025842018042116103c057600c55005b634e487b7160e01b600052601160045260246000fd5b606460405162461bcd60e51b815260206004820152601860248201527f57686974656c69737420616c72656164792061637469766500000000000000006044820152fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000003d4e44eb1374240ce5f1b871ab261cd16335b76a168152f35b346101fe5760003660031901126101fe5760206040516001600160a01b037f0000000000000000000000002626664c2603336e57b271c5c0b26f421741e481168152f35b346101fe5760003660031901126101fe5760206001600160a01b0360075416604051908152f35b346101fe5760403660031901126101fe576104ef6104e56113ef565b6024359033611886565b602060405160018152f35b346101fe5760003660031901126101fe5760405160006004548060011c906001811680156105f8575b6020831081146105e4578285529081156105c05750600114610560575b61055c83610550818503826114c6565b6040519182918261141b565b0390f35b91905060046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b916000905b8082106105a657509091508101602001610550610540565b91926001816020925483858801015201910190929161058e565b60ff191660208086019190915291151560051b840190910191506105509050610540565b602484634e487b7160e01b81526022600452fd5b91607f1691610523565b346101fe5760003660031901126101fe5760206001600160a01b03600b5416604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360055416604051908152f35b346101fe5760003660031901126101fe57600460006001600160a01b03600b5416604051928380927f6d0280270000000000000000000000000000000000000000000000000000000082525afa8015610a43576000918291610919575b5060005b6010548110156106ec57806001600160a01b036106cf600193611495565b90549060031b1c16600052600e60205260006040812055016106b1565b506020916040516106fd84826114c6565b60008152600036813780519067ffffffffffffffff82116107e0576801000000000000000082116107e0578490601054836010558084106108b7575b5001601060005260005b82811061087c5750505060005b81518110156107f6576107638184611720565b516001600160a01b036107768385611720565b5116600052600e85526040600020556001600160a01b036107978284611720565b51169060105491680100000000000000008310156107e0576107c0836001809501601055611495565b6001600160a01b03829392549160031b92831b921b191617905501610750565b634e487b7160e01b600052604160045260246000fd5b509060405192839260408401604085528151809152826060860192019060005b81811061085c575050508381038285015281808451928381520193019160005b82811061084557505050500390f35b835185528695509381019392810192600101610836565b82516001600160a01b031684528796509284019291840191600101610816565b81516001600160a01b03167f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae67282015590850190600101610743565b7f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae67201837f1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672015b81811061090a5750610739565b600081558793506001016108fd565b90503d8083833e61092a81836114c6565b8101604082820312610a3f57815167ffffffffffffffff8111610a0f5782019181601f84011215610a0f57825161096081611708565b9361096e60405195866114c6565b81855260208086019260051b82010190848211610a3b57602001915b818310610a175750505060208101519067ffffffffffffffff8211610a13570181601f82011215610a0f578051906109c182611708565b946109cf60405196876114c6565b82865260208087019360051b830101938411610a0c5750602001905b8282106109fc5750505090826106ad565b81518152602091820191016109eb565b80fd5b8380fd5b8480fd5b82516001600160a01b0381168103610a375781526020928301920161098a565b8780fd5b8680fd5b8280fd5b6040513d6000823e3d90fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f00000000000000000000000003a520b32c04bf3beef7beb72e919cf822ed34f1168152f35b346101fe5760003660031901126101fe57610aac611844565b60006001600160a01b0360055473ffffffffffffffffffffffffffffffffffffffff198116600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346101fe5760203660031901126101fe576020610b33610b196113ef565b6001600160a01b0316600052600060205260406000205490565b604051908152f35b346101fe5760203660031901126101fe576001600160a01b03610b5c6113ef565b16600052600e6020526020604060002054604051908152f35b346101fe5760403660031901126101fe5760043567ffffffffffffffff81116101fe57610ba6903690600401611464565b9060243567ffffffffffffffff81116101fe57610bc7903690600401611464565b906001600160a01b03600754163303610de657818403610da2578315610d5e576000805b838110610c89575033600052600060205260406000205410610c455760005b848110610c1357005b80610c3f610c2c610c2760019489896116d7565b6116e7565b610c378387876116d7565b359033611886565b01610c0a565b606460405162461bcd60e51b815260206004820152602060248201527f496e73756666696369656e742062616c616e636520666f722061697264726f706044820152fd5b90610c958285856116d7565b3515610d1a576001600160a01b03610cb1610c278489896116d7565b1615610cd657610ccf600191610cc88487876116d7565b35906116fb565b9101610beb565b606460405162461bcd60e51b815260206004820152601960248201527f496e76616c696420726563697069656e742061646472657373000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f416d6f756e74206d75737420626520706f7369746976650000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152600c60248201527f456d7074792061727261797300000000000000000000000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601660248201527f417272617973206c656e677468206d69736d61746368000000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152601760248201527f4f6e6c7920536861646f772063616e2061697264726f700000000000000000006044820152fd5b346101fe5760003660031901126101fe5760206040516001600160a01b037f00000000000000000000000033128a8fc17869897dce68ed026d694621f6fdfd168152f35b346101fe5760003660031901126101fe576020600954604051908152f35b346101fe5760203660031901126101fe57600435610ea8611844565b6001600160a01b0360085416908115610fd75780610eda836001600160a01b0316600052600060205260406000205490565b10610f935760009082610f2f57917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084610f1985966002546116fb565b6002555b8060025403600255604051908152a380f35b828252816020526040822054818110610f7b5760208285937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9386978752868452036040862055610f1d565b60649363391434e360e21b8452600452602452604452fd5b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e74204c502062616c616e63650000000000000000006044820152fd5b606460405162461bcd60e51b815260206004820152600c60248201527f506f6f6c206e6f742073657400000000000000000000000000000000000000006044820152fd5b346101fe5760403660031901126101fe576110346113ef565b60243590611040611844565b7f0000000000000000000000000000000000000000000000000000000000000000156110f6576001600160a01b031680156110c757816110846020936002546116fb565b6002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600093849284845283825260408420818154019055604051908152a380f35b7fec442f0500000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b606460405162461bcd60e51b815260206004820152601a60248201527f546f6b656e3a206d696e74696e672069732064697361626c65640000000000006044820152fd5b346101fe5760203660031901126101fe57600435600052600f602052602060ff604060002054166040519015158152f35b346101fe5760003660031901126101fe57602060405160128152f35b346101fe5760603660031901126101fe5760206111b66111a56113ef565b6111ad611405565b604435916114e8565b6040519015158152f35b346101fe5760003660031901126101fe576020600254604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b0360085416604051908152f35b346101fe5760003660031901126101fe5760206001600160a01b03600a5416604051908152f35b346101fe5760403660031901126101fe576112456113ef565b6024359033156112e1576001600160a01b03169081156112b257336000526001602052604060002082600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b7f94280d6200000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b7fe602df0500000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b346101fe5760003660031901126101fe5760405160006003548060011c906001811680156113c5575b6020831081146105e4578285529081156105c057506001146113655761055c83610550818503826114c6565b91905060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b916000905b8082106113ab57509091508101602001610550610540565b919260018160209254838588010152019101909291611393565b91607f1691611339565b346101fe5760003660031901126101fe5760209060ff600d541615158152f35b600435906001600160a01b03821682036101fe57565b602435906001600160a01b03821682036101fe57565b91909160208152825180602083015260005b81811061144e575060409293506000838284010152601f8019910116010190565b806020809287010151604082860101520161142d565b9181601f840112156101fe5782359167ffffffffffffffff83116101fe576020808501948460051b0101116101fe57565b6010548110156114b057601060005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176107e057604052565b919060405192602084016001600160a01b038216946bffffffffffffffffffffffff198360601b168252426034820152603481526115276054826114c6565b5190209360ff600d5416806116cb575b1561167e5784600052600f60205260ff60406000205416611673576001600160a01b0383166000818152600e6020526040902054156116685780600052600e6020526040600020546002548181029181830414901517156103c057612710900485116115fe576000958652600f6020908152604096879020805460ff1916600117905586513381529081018690526115fb963293917fbd271ef139c83b0b92924563a89bd3bd04d2552d58cb760d45cd26e2a570057e91819081015b0390a461177f565b90565b608460405162461bcd60e51b815260206004820152602d60248201527f5472616e7366657220616d6f756e742065786365656473206d61782077616c6c60448201527f65742070657263656e74616765000000000000000000000000000000000000006064820152fd5b505050505050600090565b506115fb935061177f565b60408051338152602081018690526115fb965032926001600160a01b0386169290917fbd271ef139c83b0b92924563a89bd3bd04d2552d58cb760d45cd26e2a570057e91819081016115f3565b50600c54421115611537565b91908110156114b05760051b0190565b356001600160a01b03811681036101fe5790565b919082018092116103c057565b67ffffffffffffffff81116107e05760051b60200190565b80518210156114b05760209160051b010190565b1561173b57565b606460405162461bcd60e51b815260206004820152600b60248201527f4f6e6c7920536861646f770000000000000000000000000000000000000000006044820152fd5b91906001600160a01b0383169283600052600160205260406000206001600160a01b03331660005260205260406000205460001981036117ca575b506117c59350611886565b600190565b83811061180e5784156112e15733156112b2576117c594600052600160205260406000206001600160a01b03331660005260205283604060002091039055386117ba565b83907ffb8f41b2000000000000000000000000000000000000000000000000000000006000523360045260245260445260646000fd5b6001600160a01b0360055416330361185857565b7f118cdaa7000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b6001600160a01b031690811561191f576001600160a01b03169182156110c75760008281528060205260408120548281106119055791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b60649363391434e360e21b83949352600452602452604452fd5b7f96c6fd1e00000000000000000000000000000000000000000000000000000000600052600060045260246000fdfea26469706673582212202756e13ded8d9c27025b1132e35979fa7340e6100bda9b449feba6a0a188d81164736f6c634300081b0033

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

00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000084595161401484a000000000000000000000000000000a45c15f5a909569ba7e48ff1aef66271127c035f000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000004f6e833d7469ef46d15a1041c373a513e35e78830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e5445535420627920536861646f7700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045445535400000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : name_ (string): TEST by Shadow
Arg [1] : symbol_ (string): TEST
Arg [2] : maxSupply_ (uint256): 10000000000000000000000000
Arg [3] : deployer_ (address): 0xa45c15F5a909569ba7E48Ff1aeF66271127c035F
Arg [4] : _maxWalletPercentage (uint256): 10
Arg [5] : _whitelist (address): 0x4f6E833d7469ef46D15A1041c373A513e35E7883
Arg [6] : featured_ (bool): False

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [2] : 000000000000000000000000000000000000000000084595161401484a000000
Arg [3] : 000000000000000000000000a45c15f5a909569ba7e48ff1aef66271127c035f
Arg [4] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [5] : 0000000000000000000000004f6e833d7469ef46d15a1041c373a513e35e7883
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [8] : 5445535420627920536861646f77000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [10] : 5445535400000000000000000000000000000000000000000000000000000000


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.