false
true
0

Contract Address Details

0x1Ad5E34a1a7a3Eb44b2D20ED91871d41B614318f

Contract Name
SwitchEngine
Creator
0x860ca5–8ae9d0 at 0xdf0f69–d91ced
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
680 Transactions
Transfers
0 Transfers
Gas Used
0
Last Balance Update
26156455
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
SwitchEngine




Optimization enabled
true
Compiler version
v0.8.24+commit.e11b9ed9




Optimization runs
200
EVM Version
shanghai




Verified at
2025-06-13T01:14:48.842368Z

Constructor Arguments

0x00000000000000000000000030f3aa4de2152a588695e3f38669a712826c7696

Arg [0] (address) : 0x30f3aa4de2152a588695e3f38669a712826c7696

              

contracts/SwitchEngine.sol

// SPDX-License-Identifier: MIT


pragma solidity 0.8.24;


import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./tokens/Token.sol";
import "./tokens/TaxToken.sol";
import "./tokens/SwitchEngineRefund.sol";

/*------------------------------------------------------------------
 *    :::: EXTERNAL_CONTRACT_INTERFACE ::::
 *-----------------------------------------------------------------*/


interface IHoldingPattern {
    function addToken(address token) external returns(bool);
}

interface ISupercharge {
    function addTaxToken(address token) external returns(bool);
    function getConfig(uint8 configType) external returns(uint256);
}

interface IIntarsiaConfigProvider {
    function setConfig(uint8 configType, address value) external returns(bool);
    function u_TaxStartTime() external view returns (uint32);
    function a_IntarsiaSafe() external view returns (address);
    function a_HoldingPattern() external view returns (address);
    function a_Supercharge() external view returns (address);
    function g_AddressArray(uint8 configType) external view returns (address[] memory returnedArray);
    function g_PowerRates() external view returns (uint64[] memory);
}

/*-------------------------------------------------------
 *    :::: CONTRACT_SWITCH_ENGINE ::::
 *------------------------------------------------------*/


/**
 * @title Switch Engine
 * @dev This contract provides an immutable and comprehensive token-switching mechanism.
 *
 * @notice The Switch Engine implements a robust system for switching tokens, ensuring strict validation 
 *         checks for balances, allowances, and transaction rules prior to executing token transfers. 
 *         Key features include:
 *         - Tax calculation logic for applicable tax tokens (standard ERC20 contracts).
 *         - Event logging for all significant actions.
 *         - Automatic refunds for tax tokens received during a switch, provided refund supplies are available.
 *
 *         Critical functions are restricted to the owner to mitigate centralization risks 
 *         and prevent manipulation of the protocol. The following functions have specific restrictions:
 *         - `createToken`: Disabled after contract ownership is renounced or when `b_isCreatingTokensActive` 
 *           is set to false. Token creation poses no additional risk as tokens are exclusively delivered 
 *           to this contract and/or the Holding Pattern.
 *         - `setCreateTokensFalse`: Can only be invoked to disable the creation of new tokens.
 *         - `modifyStartDate`: Includes a timelock that restricts its use after the switching process has begun.
 *         - `addHoldingPatterns`: Also includes a timelock to restrict its use after switching has commenced.
 *
 *         The contract utilizes the SAFE ERC20 library to ensure secure handling of bridged tokens.
 */

contract SwitchEngine is  
    Ownable, 
    ReentrancyGuard
{
    
   /*----------------------------
    *    :::: SAFE_ERC20 ::::
    *---------------------------*/

    using SafeERC20 for IERC20;
    
   /*----------------------------
    *    :::: ENUMERATION ::::
    *---------------------------*/

    enum ArrayType {
        StandardToken,
        PowerToken,
        MicroToken,
        TaxToken,
        AllTokens
    }
    
   /*----------------------------
    *    :::: STRUCTS ::::
    *---------------------------*/
    
	struct TokenInfo {
	    bool exists;
	    uint16 taxRate;
	    uint64 powerRate;
	}
	
    struct TokenCreationParams {
        string name;
        string symbol;
        uint16 taxRate;
	    uint64 powerRate;
        uint256 switchEngineSupply;
        uint256 holdingPatternSupply;
    }
    
   /*----------------------------
    *    :::: MAPPINGS ::::
    *---------------------------*/
    
    mapping(address => TokenInfo) private tokenInfo;
    mapping(address => bool) private m_isHoldingPattern;
    
   /*----------------------------
    *    :::: CONSTANTS ::::
    *---------------------------*/
    
    uint256 private constant MIN_AMOUNT = 1e6;
    uint256 private constant MAX_AMOUNT = 10e30;
    uint256 private constant MIN_POWER_RATE = 1;
    uint256 private constant MAX_POWER_RATE = 1e18;
    uint256 private constant MIN_TAX_RATE = 50;
    uint256 private constant MAX_TAX_RATE = 500;
    uint256 private constant MAX_SUPPLY = 1e33;
    
   /*----------------------------
    *    :::: IMMUTABLES ::::
    *---------------------------*/
    
    SwitchEngineRefund public immutable s_Refund_Token_Address;
    IIntarsiaConfigProvider public immutable i_configProvider;
    
    address public immutable a_Intarsia_Safe_Contract;
    address public immutable a_Supercharge_Contract;
    address public immutable a_Holding_Pattern;
    address public immutable a_Central_Token;
    uint32 public immutable u_Tax_Start_Time;
    
   /*----------------------------
    *    :::: ARRAYS ::::
    *---------------------------*/
    
    address[] public aa_listStandardTokens;
    address[] public aa_listMicroTokens;
    address[] public aa_listPowerTokens;
    address[] public aa_listTaxTokens;
    
   /*----------------------------
    *    :::: VARIABLES ::::
    *---------------------------*/
    
    bool public b_isCreatingTokensActive;
    uint256 public u_Switch_Start_Time;
    uint256 public u_Total_Token_Count;
    

/*-------------------------------------------------------
 *    :::: EVENTS_AND_ERRORS ::::
 *------------------------------------------------------*/


    event SwitchEngineInitialized(address indexed initializer,address switchEngineRefund, uint256 blockTimestamp);
    event AddHoldingPatternsEvent(address indexed sender, address holdingPattern);
    event ModifyStartDateEvent(address indexed sender, uint256 newStartDate);
    event ModifyCreateTokensEvent(address indexed sender, bool isCreateTokensActive);
    event SwitchTokensEvent(address indexed sender, uint256 taxReceiveRate, uint256 powerSendRate, uint256 powerReceiveRate, uint256 sendAmount, uint256 receiveAmount, uint256 refundAmount);
    event TokenCreated(address indexed tokenAddress, string name, string symbol, uint256 switchEngineSupply, uint256 holdingPatternSupply);
    
    
    error AddressZero_Error();
    error AllowanceLow_Error();
    error Amount_Error();
    error BalanceLow_Error();
    error CreateTokenAmount_Error();
    error CreatingTokensNotActive_Error();
    error FailedToAddToHP_Error();
    error FailedToAddToSC_Error();
    error InvalidHoldingPattern_Error();
    error InvalidToken_Error();
    error InvalidType_Error();
    error NameOrSymbol_Error();
    error PowerRate_Error();
    error RateOutOfBounds_Error();
    error ReceiveAmount_Error();
    error ReceiveAmountHigh_Error();
    error ReceiveAmountLow_Error();
    error RefundFailed_Error();
    error SendAmount_Error();
    error SetContract_Error();
    error SwitchingNotStarted_Error();
    error SwitchingStarted_Error();
    error Timestamp_Error();
    error TokenAmountCheck_Error();
    


/*-------------------------------------------------------
 *    :::: MODIFIERS ::::
 *------------------------------------------------------*/
 
    
   /** 
    * @notice Ensures the function is called before the switching period starts.
    */
    modifier switchingNotStarted() { 
        if (block.timestamp >= u_Switch_Start_Time) revert SwitchingStarted_Error();
        _;
        
    }

   /** 
    * @notice Ensures the function is called after the switching period has started.
    */
    modifier switchingStarted() {
        if (block.timestamp < u_Switch_Start_Time) revert SwitchingNotStarted_Error();
        _;
    }

    
/*-------------------------------------------------------
 *    :::: SWITCH_ENGINE_CONSTRUCTOR ::::
 *------------------------------------------------------*/
 
 
   /**
    * @dev Constructor for initializing the Switch Engine contract with specified parameters.
    *      Initializes the refund token contract and sets up the initial token configurations.
    */
    constructor(address _configProviderAddress) Ownable(_msgSender()) { 
    	
        i_configProvider = IIntarsiaConfigProvider(_configProviderAddress);
        
        address[] memory initialTokens_ = i_configProvider.g_AddressArray(11);
        uint64[] memory initialPowerRates_ = i_configProvider.g_PowerRates();
        
        if (initialTokens_.length != initialPowerRates_.length) revert Amount_Error();
        
        a_Holding_Pattern = i_configProvider.a_HoldingPattern();
        a_Supercharge_Contract = i_configProvider.a_Supercharge();
        a_Intarsia_Safe_Contract = i_configProvider.a_IntarsiaSafe();
        a_Central_Token = initialTokens_[0];
        u_Tax_Start_Time = i_configProvider.u_TaxStartTime();
        u_Switch_Start_Time = block.timestamp + 315360000; // DEFAULT
        
        SwitchEngineRefund.SerTokenConfig memory config = SwitchEngineRefund.SerTokenConfig({name: "Switch Engine Refund", symbol: "SER", switchEngineContract: address(this)});
        s_Refund_Token_Address = new SwitchEngineRefund(config);
        
        for (uint8 i; i < initialTokens_.length;) {
            address token = initialTokens_[i];
            uint64 powerRate = initialPowerRates_[i];
            tokenInfo[token] = TokenInfo(true, 0, powerRate);
            if (powerRate == 100) {aa_listStandardTokens.push(token);} 
            else if (powerRate > 100) {aa_listPowerTokens.push(token);} 
            else {aa_listMicroTokens.push(token);}
            unchecked { ++u_Total_Token_Count; }
            unchecked { ++i; }
        }
        
        m_isHoldingPattern[a_Holding_Pattern] = true;
        b_isCreatingTokensActive = true;
        
        // Set SwitchEngine within the Intarsia Config Contract
        if (!i_configProvider.setConfig(6, address(this))) revert SetContract_Error();
        if (!i_configProvider.setConfig(8, address(s_Refund_Token_Address))) revert SetContract_Error();
        
    }


/*-------------------------------------------------------
 *    :::: ONLY_OWNER_FUNCTION ::::
 *------------------------------------------------------*/


   /**
    * @dev Creates a new token with the specified parameters.
    * @notice This function can only be called by the contract owner. It checks that token creation is active,
    *         validates the provided parameters, and then creates either a standard token or a tax token based on
    *         the specified tax rate. The new token's address is stored, and it is added to the appropriate lists
    *         based on its characteristics.
    * @param params The parameters for creating the token, encapsulated in a `TokenCreationParams` struct.
    * Emits a {TokenCreated} event upon successful creation of the token.
    */
    function createToken(TokenCreationParams calldata params) external onlyOwner {
        
        if (!b_isCreatingTokensActive) revert CreatingTokensNotActive_Error();

        address holdingPattern_ = a_Holding_Pattern;

        if (bytes(params.name).length == 0 || bytes(params.name).length > 32 || bytes(params.symbol).length == 0 || bytes(params.symbol).length > 10) revert NameOrSymbol_Error();
        if (holdingPattern_ == address(0) || !m_isHoldingPattern[holdingPattern_]) revert InvalidHoldingPattern_Error();
        if (params.switchEngineSupply > MAX_SUPPLY || params.holdingPatternSupply > MAX_SUPPLY) revert CreateTokenAmount_Error();
        unchecked {if (params.switchEngineSupply + params.holdingPatternSupply == 0) revert CreateTokenAmount_Error();}
        
        bool isNonTaxToken = params.taxRate == 0 ? true : false;
        address tokenAddress;
        
        if (isNonTaxToken) { 
            if (params.powerRate > MAX_POWER_RATE || params.powerRate < MIN_POWER_RATE) revert RateOutOfBounds_Error();
            Token.TokenConfig memory config = Token.TokenConfig({
                name: params.name,
                symbol: params.symbol,
                holdingPatternSupply: params.holdingPatternSupply,
                switchEngineSupply: params.switchEngineSupply,
                creatorAddress: _msgSender(),
                holdingPatternContract: holdingPattern_,
                switchEngineContract: address(this)
            });
            tokenAddress = address(new Token(config));
        } else { 
            if (params.taxRate > MAX_TAX_RATE || params.taxRate < MIN_TAX_RATE || params.powerRate != 100) revert RateOutOfBounds_Error();
            TaxToken.TaxTokenConfig memory config = TaxToken.TaxTokenConfig({
                name: params.name,
                symbol: params.symbol,
                holdingPatternSupply: params.holdingPatternSupply,
                switchEngineSupply: params.switchEngineSupply,
                creatorAddress: _msgSender(),
                holdingPatternContract: holdingPattern_,
                switchEngineContract: address(this),
                superchargeContract: a_Supercharge_Contract,
                taxStartTime: u_Tax_Start_Time,
                taxRate: params.taxRate
            });
            tokenAddress = address(new TaxToken(config));
        }

        tokenInfo[tokenAddress] = TokenInfo(true, params.taxRate, params.powerRate);
        
        if (!isNonTaxToken) {
        	if (!ISupercharge(a_Supercharge_Contract).addTaxToken(tokenAddress)) revert FailedToAddToSC_Error();
            aa_listTaxTokens.push(tokenAddress);
        } else if (params.powerRate == 100) {
            aa_listStandardTokens.push(tokenAddress);
        } else if (params.powerRate > 100) {
            aa_listPowerTokens.push(tokenAddress);
        } else if (params.powerRate < 100) {
            aa_listMicroTokens.push(tokenAddress);
        } else {
            revert RateOutOfBounds_Error();
        }
        
        if (!IHoldingPattern(holdingPattern_).addToken(tokenAddress)) revert FailedToAddToHP_Error();
        
        unchecked { ++u_Total_Token_Count; }

        emit TokenCreated(
            tokenAddress,
            params.name,
            params.symbol,
            params.switchEngineSupply,
            params.holdingPatternSupply
        );
    }

 
   /**
    * @notice Disables the ability to create new tokens.
    * @dev Sets the b_isCreatingTokensActive flag to false. Can only be called by the contract owner and deployer.
    */
    function setCreateTokensFalse() external onlyOwner {
        b_isCreatingTokensActive = false;
        emit ModifyCreateTokensEvent(_msgSender(), b_isCreatingTokensActive);
    }
    

/*-------------------------------------------------------
 *    :::: ONLY_OWNER_DISABLED_AFTER_SWITCHING_STARTS ::::
 *------------------------------------------------------*/
 
    
   /** 
    * @notice Callable only before switching initiates.
    * @dev Modifies the start date of the switching process.
    * @param newStartDate The new timestamp to set as the start date.
    */
    function modifyStartDate(uint256 newStartDate) external onlyOwner switchingNotStarted {
        if (newStartDate <= block.timestamp) revert Timestamp_Error();
        u_Switch_Start_Time = newStartDate;
        emit ModifyStartDateEvent(_msgSender(),newStartDate);
    }

    
/*-------------------------------------------------------
 *    :::: EXTERNAL_GETTER_FUNCTIONS ::::
 *------------------------------------------------------*/
 
 
   /**
    * @dev Returns the array count for the specific type.
    */
    function getArrayCount(ArrayType arrayType) external view returns (uint256) {
        if (arrayType == ArrayType.StandardToken) return aa_listStandardTokens.length;
        if (arrayType == ArrayType.PowerToken) return aa_listPowerTokens.length;
        if (arrayType == ArrayType.MicroToken) return aa_listMicroTokens.length;
        if (arrayType == ArrayType.TaxToken) return aa_listTaxTokens.length;
        if (arrayType == ArrayType.AllTokens) return u_Total_Token_Count;
        revert InvalidType_Error();
    }


   /**
    * @dev Calculates the amounts to be received and the tax amount based on 
    *      the specified send amount and power rates.
    */
    function getSwitchAmounts(uint16 taxReceiveRate, uint64 powerSendRate, uint64 powerReceiveRate, uint256 sendAmount) external pure returns (uint256, uint256, uint256) { 
        uint256 amountReceived = _calcReceiveAmount(sendAmount, powerSendRate, powerReceiveRate);
        return(
            (powerSendRate < powerReceiveRate) ? ((MIN_AMOUNT * powerReceiveRate) / powerSendRate) : MIN_AMOUNT,
            amountReceived,
            taxReceiveRate != 0 ? _calcTax(amountReceived, taxReceiveRate) : 0
        );
    }
    

   /**
    * @dev Retrieves information about a specific token.
    */
    function getTokenInfo(address token) external view returns (uint16, uint64, bool) {
        TokenInfo memory tokenInfo_ = tokenInfo[token];
        if (!tokenInfo_.exists) revert InvalidToken_Error();
        return (tokenInfo_.taxRate, tokenInfo_.powerRate, tokenInfo_.exists);
    }


/*-------------------------------------------------------
 *    :::: EXTERNAL_WRITE_FUNCTION ::::
 *------------------------------------------------------*/
 

   /**
    * @notice Allows users to switch tokens by sending a specified amount of one token 
    *         and receiving another token in return.
    * @dev This function calculates the rates for sending and receiving tokens, checks 
    *      the token amounts, and processes the transfers. It also handles any applicable 
    *      tax refunds and emits a SwitchTokensEvent upon successful execution.
    *
    * @param sendToken The ERC20 token that the user is sending.
    * @param receiveToken The ERC20 token that the user will receive.
    * @param sendAmount The amount of the sendToken that the user wants to send.
    * 
    * Requirements:
    * - Switching must have started and the function must not be reentrant.
    * - The sender must have a valid amount of sendToken to switch.
    * - The calculated receive amount must be valid based on the rates.
    *
    */
    function switchTokens(
        IERC20 sendToken, 
        IERC20 receiveToken, 
        uint256 sendAmount
    ) external 
        switchingStarted 
        nonReentrant 
    { 
    	
        address sender_ = _msgSender();
        address sendTokenAddress_ = address(sendToken);
        address receiveTokenAddress_ = address(receiveToken);
        
        (uint64 powerSendRate, uint16 taxReceiveRate, uint64 powerReceiveRate) = _getRates(sender_, sendTokenAddress_, receiveTokenAddress_);
        (uint256 receiveAmount) = _calcReceiveAmount(sendAmount, powerSendRate, powerReceiveRate);
        
        if (!_checkTokenAmount(sender_, sendToken, receiveToken, sendAmount, receiveAmount)) revert TokenAmountCheck_Error();
        
        sendToken.safeTransferFrom(sender_, address(this), sendAmount);
        receiveToken.safeTransfer(sender_, receiveAmount);
        
        uint256 refundAmount_;
        
        if (taxReceiveRate != 0 && block.timestamp >= u_Tax_Start_Time) { 
            refundAmount_ = _calcTax(receiveAmount, taxReceiveRate);
            if (!s_Refund_Token_Address.refundTokens(sender_, refundAmount_)) revert RefundFailed_Error();
        }
        
        emit SwitchTokensEvent(sender_, taxReceiveRate, powerSendRate, powerReceiveRate, sendAmount, receiveAmount - refundAmount_, refundAmount_);
    }
    
    
/*-------------------------------------------------------
 *    :::: PRIVATE_FUNCTIONS ::::
 *------------------------------------------------------*/
 
    
   /**
    * @notice Retrieves the rates for sending and receiving tokens.
    * @dev Reverts with AddressZero_Error if any address is zero.
    *      Reverts with InvalidToken_Error if the tokens do not exist or are the same.
    * @param _sender The address of the sender.
    * @param _sendAddress The address of the token being sent.
    * @param _receiveAddress The address of the token being received.
    * @return powerRate The power rate of the token being sent.
    * @return taxRate The tax rate of the token being received.
    * @return receivePowerRate The power rate of the token being received.
    */
    function _getRates(address _sender, address _sendAddress, address _receiveAddress) private view returns (uint64, uint16, uint64) {
        if (_sender == address(0) || _sendAddress == address(0) || _receiveAddress == address(0)) revert AddressZero_Error();
        TokenInfo memory sendInfo_ = tokenInfo[_sendAddress];
        TokenInfo memory receiveInfo_ = tokenInfo[_receiveAddress];
        if (!sendInfo_.exists || !receiveInfo_.exists || _sendAddress == _receiveAddress) revert InvalidToken_Error();
        return (sendInfo_.powerRate, receiveInfo_.taxRate, receiveInfo_.powerRate);
    }
    
    
   /**
    * @notice Calculates the amount received after applying the power rates using integer arithmetic, truncating remainders to maintain whole token units, with a minimum amount check to ensure meaningful outputs.
    * @dev Reverts with Amount_Error if the minimum amount check fails. (e.g., "3e6 * 1e18 / 2e18 = 1e6, truncating 1.5e6")
    * @param _sendAmount The amount of tokens being sent.
    * @param _powerSendRate The power rate of the token being sent.
    * @param _powerReceiveRate The power rate of the token being received.
    * @return The amount of tokens received after applying the rates.
    */ 
    function _calcReceiveAmount(uint256 _sendAmount, uint256 _powerSendRate, uint256 _powerReceiveRate) private pure returns(uint256) {
        if (!_checkMinAmount(_sendAmount, _powerSendRate, _powerReceiveRate)) revert ReceiveAmount_Error();
        return ((_sendAmount * _powerSendRate) / _powerReceiveRate);
    }
    
    
   /** 
    * @notice Taxes only apply to tax tokens that are approved addresses for this protocol. 
    *         Taxes are refunded to the user in `Switch Engine Refund` SER tokens.
    * @dev Private function to check if token received has a tax and calculate the tax for refund
    * @param _receiveAmount amount of tokens.
    * @param _taxReceiveRate tax rate.
    */
    function _calcTax(uint256 _receiveAmount, uint256 _taxReceiveRate) private pure returns(uint256) {
        return ((_receiveAmount * _taxReceiveRate * 1e26) / 1e30);
    }
    
    
   /**
    * @notice Checks if the send amount meets the minimum requirements based on power send and receive rates.
    * @param _sendAmount The amount of tokens being sent.
    * @param _powerSendRate The rate at which tokens are sent, expressed in a fixed-point format.
    * @param _powerReceiveRate The rate at which tokens are received, expressed in a fixed-point format.
    * @return bool True if the send amount is valid, false otherwise.
    */
    function _checkMinAmount(uint256 _sendAmount, uint256 _powerSendRate, uint256 _powerReceiveRate) private pure returns(bool) {
        uint256 minAmountRequired_ = MIN_AMOUNT;
        uint256 maxAmountRequired_ = MAX_AMOUNT;
        uint256 maxPowerRateRequired_ = MAX_POWER_RATE;
        if (_sendAmount < minAmountRequired_ || _sendAmount > maxAmountRequired_) revert SendAmount_Error();
        if (_powerSendRate == 0 || _powerSendRate > maxPowerRateRequired_ || _powerReceiveRate == 0 || _powerReceiveRate > maxPowerRateRequired_) revert PowerRate_Error();
        uint256 minAmount = (_powerSendRate < _powerReceiveRate) ? ((minAmountRequired_ * _powerReceiveRate) / _powerSendRate) : minAmountRequired_;
        return (minAmount >= minAmountRequired_ && _sendAmount >= minAmount);
    }
    

   /**
    * @notice Validates the token amounts for the transaction.
    * @param _sender The address of the sender.
    * @param _sendToken The token being sent.
    * @param _receiveToken The token being received.
    * @param _sendAmount The amount of tokens being sent.
    * @param _receiveAmount The amount of tokens expected to be received.
    * @return bool True if the token amounts are valid, false otherwise.
    */
    function _checkTokenAmount(address _sender, IERC20 _sendToken, IERC20 _receiveToken, uint256 _sendAmount, uint256 _receiveAmount) private view returns (bool) {
        if (_receiveAmount < MIN_AMOUNT) revert ReceiveAmountLow_Error();
        if (_receiveAmount > MAX_AMOUNT) revert ReceiveAmountHigh_Error();
        if (_sendToken.balanceOf(_sender) < _sendAmount) revert BalanceLow_Error();
        if (_sendToken.allowance(_sender, address(this)) < _sendAmount) revert AllowanceLow_Error();
        if (_receiveToken.balanceOf(address(this)) < _receiveAmount) revert BalanceLow_Error();
        return true;
    }
    


}

        

@openzeppelin/contracts/utils/Context.sol

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

@openzeppelin/contracts/utils/ReentrancyGuard.sol

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

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 ERC-20 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);
}
          

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

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

pragma solidity ^0.8.20;

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

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 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 {
    /**
     * @dev An operation with an ERC-20 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 Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(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.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    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.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    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.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    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 Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            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 silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
          

@openzeppelin/contracts/access/Ownable.sol

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

@openzeppelin/contracts/interfaces/IERC1363.sol

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

pragma solidity ^0.8.20;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
          

@openzeppelin/contracts/interfaces/IERC165.sol

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

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
          

@openzeppelin/contracts/interfaces/IERC20.sol

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

pragma solidity ^0.8.20;

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

@openzeppelin/contracts/interfaces/draft-IERC6093.sol

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

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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);
}
          

@openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.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 ERC-20
 * applications.
 */
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}.
     *
     * Both 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}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * 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:
     *
     * ```solidity
     * 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);
            }
        }
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
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);
}
          

@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol

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

pragma solidity ^0.8.20;

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

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys a `value` amount of tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 value) public virtual {
        _burn(_msgSender(), value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, deducting from
     * the caller's allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `value`.
     */
    function burnFrom(address account, uint256 value) public virtual {
        _spendAllowance(account, _msgSender(), value);
        _burn(account, value);
    }
}
          

@openzeppelin/contracts/utils/introspection/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
          

contracts/tokens/SwitchEngineRefund.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


/**
 * @title SwitchEngineRefund Token for the Intarsia Switch Engine Tax Refunds
 * @dev This contract is an ERC20 token that can be minted by the Switch Engine.
 * @notice The owner of the contract is set to the address of the switch engine contract upon deployment.
 */
contract SwitchEngineRefund is 
    ERC20, 
    Ownable 
{
	
    struct SerTokenConfig {
        address switchEngineContract;
        string name;
        string symbol;
    }
    
    address public immutable switchEngineContract;
    
    event TokensRefunded(address indexed recipient, uint256 amount);
    
    error OwnerTransferDisabled();
    error OwnerRenounceDisabled();
    
    constructor(
        SerTokenConfig memory config
    ) Ownable(config.switchEngineContract) ERC20(config.name, config.symbol) {
        switchEngineContract = config.switchEngineContract;
    }
    
    function refundTokens(address recipient, uint256 amount) external onlyOwner returns (bool) {
        _mint(recipient, amount);
        emit TokensRefunded(recipient, amount);
        return true;
    }

    // Override to make ownership immutable
    function transferOwnership(address) public onlyOwner override view {
        revert OwnerTransferDisabled();
    }

    // Override renounceOwnership
    function renounceOwnership() public onlyOwner override view {
        revert OwnerRenounceDisabled();
    }
    
}
          

contracts/tokens/TaxToken.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

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


/**
 * @title Taxed Token for the Intarsia Switch Engine
 * @notice This contract implements a standard ERC20 token with a fee on transfers and burnable functionality, created by the SwitchEngine contract.
 *         Transfers of amounts less than 1e-15 will incur a specified tax amount:
 *         - If the amount sent is between 1e-16 and 9.99e-16, the tax is 5e-17.
 *         - If the amount sent is between 1e-17 and 9.9e-17, the tax is 5e-18.
 *         - If the amount sent is between 1e-18 and 9e-18, the tax is 1e-18.
 * @dev This contract represents a `tax` token that is minted to the SwitchEngine and a designated Holding Pattern contract upon deployment.
 *      It includes immutable metadata for the creator, SwitchEngine, and Holding Pattern addresses, as well as the total initial supply.
 */
contract TaxToken is 
    ERC20, 
    ERC20Burnable 
{
    struct TaxTokenConfig {
        string name;
        string symbol;
        uint256 holdingPatternSupply;
        uint256 switchEngineSupply;
        address creatorAddress;
        address holdingPatternContract;
        address switchEngineContract;
        address superchargeContract;
        uint32 taxStartTime;
        uint16 taxRate;
    }
    
	mapping(address => bool) private excludeSenderFromFee;
	mapping(address => bool) private excludeRecipientFromFee;

    uint16 public immutable TAX_RATE;
    uint32 public immutable TAX_START_TIME;
    uint256 public immutable totalInitialSupply;
    address public immutable superchargeContract;
    address public immutable creatorAddress;
    address public immutable switchEngineContract;
    address public immutable holdingPatternContract;
    
    event TaxTokenInitialized(address indexed creator, address holdingPattern, address switchEngine, uint256 totalSupply);
    event TransferWithTax(address indexed from, address to, uint16 taxRate, uint256 sentAmount, uint256 taxPaid, uint256 remainder);
    
    constructor(
        TaxTokenConfig memory config
    ) ERC20(config.name, config.symbol) { 
    	
        creatorAddress = config.creatorAddress;
        switchEngineContract = config.switchEngineContract;
        holdingPatternContract = config.holdingPatternContract;
        superchargeContract = config.superchargeContract;
        unchecked { totalInitialSupply = config.holdingPatternSupply + config.switchEngineSupply; }
        
        TAX_START_TIME = config.taxStartTime;
        TAX_RATE = config.taxRate;
        
        // EXCLUDE_TAX_FROM
        excludeSenderFromFee[config.holdingPatternContract] = true;
        excludeSenderFromFee[config.superchargeContract] = true;
        excludeRecipientFromFee[config.holdingPatternContract] = true;
        excludeRecipientFromFee[config.switchEngineContract] = true;
        
        if (config.holdingPatternSupply != 0) _mint(config.holdingPatternContract, config.holdingPatternSupply);
        if (config.switchEngineSupply != 0) _mint(config.switchEngineContract, config.switchEngineSupply);
        
        emit TaxTokenInitialized(creatorAddress,holdingPatternContract,switchEngineContract,totalInitialSupply);
    }
    
    function _update(address sender, address recipient, uint256 amount) internal override {
        uint16 taxRate = TAX_RATE;
        if (
            amount != 0 && 
            block.timestamp >= TAX_START_TIME && 
            !excludeSenderFromFee[sender] && 
            !excludeRecipientFromFee[recipient]
        ) {
            uint256 remainder = _taxRemainder(amount, taxRate);
            uint256 tax = amount - remainder;
            super._update(sender, recipient, remainder);
            super._update(sender, superchargeContract, tax);
            emit TransferWithTax(sender, recipient, taxRate, amount, tax, remainder);
            return;
        }
        super._update(sender, recipient, amount);
    }
    
    function _taxRemainder(uint256 _amount, uint256 _taxRate) private pure returns (uint256) {
        if (_amount > 1000) return (_amount - ((_amount * _taxRate * 1e26) / 1e30));
        if (_amount > 100) return _amount - 50;
        if (_amount > 10) return _amount - 5;
        if (_amount > 0) return _amount - 1;
        return 0;
    }
    
}
          

contracts/tokens/Token.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

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


/**
 * @title Token For The Intarsia Switch Engine
 * @notice A standard ERC20 token with burnable functionality, created by the SwitchEngine contract.
 * @dev This contract represents a token that is minted to the SwitchEngine and a designated Holding Pattern contract upon deployment.
 *      It includes immutable metadata for the creator, SwitchEngine, and Holding Pattern addresses, as well as the total initial supply.
 */
contract Token is 
    ERC20, 
    ERC20Burnable 
{
    struct TokenConfig {
        string name;
        string symbol;
        uint256 holdingPatternSupply;
        uint256 switchEngineSupply;
        address creatorAddress;
        address holdingPatternContract;
        address switchEngineContract;
    }

    address public immutable creatorAddress;
    address public immutable switchEngineContract;
    address public immutable holdingPatternContract;
    uint256 public immutable totalInitialSupply;
    
    event TokenInitialized(address indexed creator, address holdingPattern, address switchEngine, uint256 totalSupply);
    
    constructor(
        TokenConfig memory config
    ) ERC20(config.name, config.symbol) { 
        creatorAddress = config.creatorAddress;
        switchEngineContract = config.switchEngineContract;
        holdingPatternContract = config.holdingPatternContract;
        unchecked { totalInitialSupply = config.holdingPatternSupply + config.switchEngineSupply; }
        if (config.holdingPatternSupply != 0) _mint(config.holdingPatternContract, config.holdingPatternSupply);
        if (config.switchEngineSupply != 0) _mint(config.switchEngineContract, config.switchEngineSupply);
        emit TokenInitialized(creatorAddress,holdingPatternContract,switchEngineContract,totalInitialSupply);
    }
}
          

Compiler Settings

{"outputSelection":{},"optimizer":{"runs":200,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"shanghai"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_configProviderAddress","internalType":"address"}]},{"type":"error","name":"AddressZero_Error","inputs":[]},{"type":"error","name":"AllowanceLow_Error","inputs":[]},{"type":"error","name":"Amount_Error","inputs":[]},{"type":"error","name":"BalanceLow_Error","inputs":[]},{"type":"error","name":"CreateTokenAmount_Error","inputs":[]},{"type":"error","name":"CreatingTokensNotActive_Error","inputs":[]},{"type":"error","name":"FailedToAddToHP_Error","inputs":[]},{"type":"error","name":"FailedToAddToSC_Error","inputs":[]},{"type":"error","name":"InvalidHoldingPattern_Error","inputs":[]},{"type":"error","name":"InvalidToken_Error","inputs":[]},{"type":"error","name":"InvalidType_Error","inputs":[]},{"type":"error","name":"NameOrSymbol_Error","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"PowerRate_Error","inputs":[]},{"type":"error","name":"RateOutOfBounds_Error","inputs":[]},{"type":"error","name":"ReceiveAmountHigh_Error","inputs":[]},{"type":"error","name":"ReceiveAmountLow_Error","inputs":[]},{"type":"error","name":"ReceiveAmount_Error","inputs":[]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"RefundFailed_Error","inputs":[]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"error","name":"SendAmount_Error","inputs":[]},{"type":"error","name":"SetContract_Error","inputs":[]},{"type":"error","name":"SwitchingNotStarted_Error","inputs":[]},{"type":"error","name":"SwitchingStarted_Error","inputs":[]},{"type":"error","name":"Timestamp_Error","inputs":[]},{"type":"error","name":"TokenAmountCheck_Error","inputs":[]},{"type":"event","name":"AddHoldingPatternsEvent","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"address","name":"holdingPattern","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ModifyCreateTokensEvent","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"bool","name":"isCreateTokensActive","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"ModifyStartDateEvent","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"newStartDate","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SwitchEngineInitialized","inputs":[{"type":"address","name":"initializer","internalType":"address","indexed":true},{"type":"address","name":"switchEngineRefund","internalType":"address","indexed":false},{"type":"uint256","name":"blockTimestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SwitchTokensEvent","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"taxReceiveRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"powerSendRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"powerReceiveRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"sendAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"receiveAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"refundAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TokenCreated","inputs":[{"type":"address","name":"tokenAddress","internalType":"address","indexed":true},{"type":"string","name":"name","internalType":"string","indexed":false},{"type":"string","name":"symbol","internalType":"string","indexed":false},{"type":"uint256","name":"switchEngineSupply","internalType":"uint256","indexed":false},{"type":"uint256","name":"holdingPatternSupply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"a_Central_Token","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"a_Holding_Pattern","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"a_Intarsia_Safe_Contract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"a_Supercharge_Contract","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"aa_listMicroTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"aa_listPowerTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"aa_listStandardTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"aa_listTaxTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"b_isCreatingTokensActive","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createToken","inputs":[{"type":"tuple","name":"params","internalType":"struct SwitchEngine.TokenCreationParams","components":[{"type":"string","name":"name","internalType":"string"},{"type":"string","name":"symbol","internalType":"string"},{"type":"uint16","name":"taxRate","internalType":"uint16"},{"type":"uint64","name":"powerRate","internalType":"uint64"},{"type":"uint256","name":"switchEngineSupply","internalType":"uint256"},{"type":"uint256","name":"holdingPatternSupply","internalType":"uint256"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getArrayCount","inputs":[{"type":"uint8","name":"arrayType","internalType":"enum SwitchEngine.ArrayType"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getSwitchAmounts","inputs":[{"type":"uint16","name":"taxReceiveRate","internalType":"uint16"},{"type":"uint64","name":"powerSendRate","internalType":"uint64"},{"type":"uint64","name":"powerReceiveRate","internalType":"uint64"},{"type":"uint256","name":"sendAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"},{"type":"uint64","name":"","internalType":"uint64"},{"type":"bool","name":"","internalType":"bool"}],"name":"getTokenInfo","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIntarsiaConfigProvider"}],"name":"i_configProvider","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"modifyStartDate","inputs":[{"type":"uint256","name":"newStartDate","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract SwitchEngineRefund"}],"name":"s_Refund_Token_Address","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCreateTokensFalse","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"switchTokens","inputs":[{"type":"address","name":"sendToken","internalType":"contract IERC20"},{"type":"address","name":"receiveToken","internalType":"contract IERC20"},{"type":"uint256","name":"sendAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"u_Switch_Start_Time","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"u_Tax_Start_Time","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"u_Total_Token_Count","inputs":[]}]
              

Contract Creation Code

0x61016060405234801562000011575f80fd5b5060405162005c4738038062005c47833981016040819052620000349162000813565b33806200005a57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b62000065816200079a565b50600180556001600160a01b03811660a0819052604051631bf2c52160e11b8152600b60048201525f91906337e58a42906024015f60405180830381865afa158015620000b4573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052620000dd9190810190620008a2565b90505f60a0516001600160a01b031663f5db7cd76040518163ffffffff1660e01b81526004015f60405180830381865afa1580156200011e573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405262000147919081019062000945565b905080518251146200016c5760405163901e555360e01b815260040160405180910390fd5b60a0516001600160a01b03166366214bb46040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ab573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001d1919062000813565b6001600160a01b0316610100816001600160a01b03168152505060a0516001600160a01b0316631baf45896040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200022a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000250919062000813565b6001600160a01b031660e0816001600160a01b03168152505060a0516001600160a01b031663f0854e026040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002a8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002ce919062000813565b6001600160a01b031660c052815182905f90620002ef57620002ef620009ec565b60200260200101516001600160a01b0316610120816001600160a01b03168152505060a0516001600160a01b031663447279246040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000350573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000376919062000a00565b63ffffffff166101405262000390426312cc030062000a25565b6009556040805160608101825230815281518083018352601481527f53776974636820456e67696e6520526566756e640000000000000000000000006020828101919091528083019190915282518084018452600381526229a2a960e91b9181019190915281830152905181906200040890620007e9565b62000414919062000a90565b604051809103905ff0801580156200042e573d5f803e3d5ffd5b506001600160a01b03166080525f5b83518160ff16101562000636575f848260ff1681518110620004635762000463620009ec565b602002602001015190505f848360ff1681518110620004865762000486620009ec565b60209081029190910181015160408051606081018252600181525f8185018181526001600160401b038086168486018181526001600160a01b038b168552600290985294909220925183549151965162ffffff1990921690151562ffff0019161761010061ffff90971696909602959095176301000000600160581b031916630100000095909116949094029390931790925591506064036200057357600480546001810182555f919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b03841617905562000621565b6064816001600160401b03161115620005d657600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b03841617905562000621565b600580546001810182555f919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0384161790555b5050600a80546001908101909155016200043d565b50610100516001600160a01b039081165f90815260036020526040908190208054600160ff19918216811790925560088054909116909117905560a0519051630a3fb8ff60e11b81526006600482015230602482015291169063147f71fe906044016020604051808303815f875af1158015620006b5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620006db919062000ae7565b620006f957604051636c4d3c6760e11b815260040160405180910390fd5b60a051608051604051630a3fb8ff60e11b8152600860048201526001600160a01b03918216602482015291169063147f71fe906044016020604051808303815f875af11580156200074c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000772919062000ae7565b6200079057604051636c4d3c6760e11b815260040160405180910390fd5b5050505062000b08565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610cf18062004f5683390190565b80516001600160a01b03811681146200080e575f80fd5b919050565b5f6020828403121562000824575f80fd5b6200082f82620007f7565b9392505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171562000875576200087562000836565b604052919050565b5f6001600160401b0382111562000898576200089862000836565b5060051b60200190565b5f6020808385031215620008b4575f80fd5b82516001600160401b03811115620008ca575f80fd5b8301601f81018513620008db575f80fd5b8051620008f2620008ec826200087d565b6200084a565b81815260059190911b8201830190838101908783111562000911575f80fd5b928401925b828410156200093a576200092a84620007f7565b8252928401929084019062000916565b979650505050505050565b5f602080838503121562000957575f80fd5b82516001600160401b03808211156200096e575f80fd5b818501915085601f83011262000982575f80fd5b815162000993620008ec826200087d565b81815260059190911b83018401908481019088831115620009b2575f80fd5b938501935b82851015620009e05784518481168114620009d0575f80fd5b82529385019390850190620009b7565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6020828403121562000a11575f80fd5b815163ffffffff811681146200082f575f80fd5b8082018082111562000a4557634e487b7160e01b5f52601160045260245ffd5b92915050565b5f81518084525f5b8181101562000a715760208185018101518683018201520162000a53565b505f602082860101526020601f19601f83011685010191505092915050565b602080825282516001600160a01b031682820152820151606060408301525f9062000abf608084018262000a4b565b90506040840151601f1984830301606085015262000ade828262000a4b565b95945050505050565b5f6020828403121562000af8575f80fd5b815180151581146200082f575f80fd5b60805160a05160c05160e0516101005161012051610140516143d262000b845f395f81816101e2015281816106f70152610e1f01525f61042901525f8181610401015261097001525f818161029201528181610df00152610f8401525f6103c201525f61018601525f8181610322015261075f01526143d25ff3fe608060405234801562000010575f80fd5b506004361062000170575f3560e01c80638da5cb5b11620000cf578063b77991bf1162000083578063b77991bf14620003bc578063c4f1ef8f14620003e4578063cab6353814620003fb578063d61bde7d1462000423578063e8283a6c146200044b578063f2fde38b1462000455575f80fd5b80638da5cb5b146200030b57806393232b99146200031c5780639fc80f071462000344578063a8426c26146200035b578063ad3ad830146200038e578063b6455b8814620003a5575f80fd5b80634b86391411620001275780634b86391414620002735780635274908d146200028c57806353cc749614620002b45780636c2c5d8e14620002cb578063715018a614620002ea5780637bbacb9d14620002f4575f80fd5b806306a61f081462000174578063097b49691462000180578063100462a214620001c55780631b1fd2b914620001dc5780631f69565f146200021a578063357f62ac146200025c575b5f80fd5b6200017e6200046c565b005b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6200017e620001d636600462001a91565b620004b8565b620002047f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001620001bc565b620002316200022b36600462001abe565b62000543565b6040805161ffff90941684526001600160401b039092166020840152151590820152606001620001bc565b620001a86200026d36600462001a91565b620005d2565b6200027d600a5481565b604051908152602001620001bc565b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b620001a8620002c536600462001a91565b620005fb565b600854620002d99060ff1681565b6040519015158152602001620001bc565b6200017e6200060b565b6200017e6200030536600462001ae3565b62000622565b5f546001600160a01b0316620001a8565b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b620001a86200035536600462001a91565b62000882565b620003726200036c36600462001b54565b62000892565b60408051938452602084019290925290820152606001620001bc565b6200017e6200039f36600462001ba3565b62000940565b6200027d620003b636600462001bdc565b620012d8565b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b620001a8620003f536600462001a91565b620013aa565b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b620001a87f000000000000000000000000000000000000000000000000000000000000000081565b6200027d60095481565b6200017e6200046636600462001abe565b620013ba565b6200047662001401565b6008805460ff19169055604080515f8152905133917ffd21bfe5b51c39faafd72a5a1911aa6049e46550ae104de3f8468516fd37e843919081900360200190a2565b620004c262001401565b6009544210620004e5576040516336192f1560e11b815260040160405180910390fd5b4281116200050657604051633f380c4560e21b815260040160405180910390fd5b600981905560405181815233907f22e5783ee3349ec9ee0402cd256d97f48db9c16914cecc0a544dbb93ed85862e9060200160405180910390a250565b6001600160a01b0381165f9081526002602090815260408083208151606081018352905460ff81161515808352610100820461ffff1694830194909452630100000090046001600160401b0316918101919091528291829190620005ba5760405163627c56b360e11b815260040160405180910390fd5b60208101516040820151915190969195509350915050565b60048181548110620005e2575f80fd5b5f918252602090912001546001600160a01b0316905081565b60058181548110620005e2575f80fd5b6200061562001401565b620006205f6200142f565b565b600954421015620006465760405163fe36753f60e01b815260040160405180910390fd5b620006506200147e565b3383835f808062000663868686620014a9565b9250925092505f6200068988856001600160401b0316846001600160401b0316620015ee565b90506200069a878b8b8b856200163b565b620006b85760405163185d483560e01b815260040160405180910390fd5b620006cf6001600160a01b038b1688308b62001849565b620006e56001600160a01b038a168883620018b8565b5f61ffff8416158015906200072057507f000000000000000000000000000000000000000000000000000000000000000063ffffffff164210155b15620007ec5762000736828561ffff16620018eb565b60405163549c6bbb60e01b81526001600160a01b038a81166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063549c6bbb906044016020604051808303815f875af1158015620007a8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620007ce919062001bfc565b620007ec57604051637ed84a4560e01b815260040160405180910390fd5b6001600160a01b0388167fb9599ccd59c01bd240490d5b5a14bb2740edcf8dc95df77f901498613df565d18587868d62000827878962001c31565b6040805161ffff9690961686526001600160401b03948516602087015292909316918401919091526060830152608082015260a0810184905260c00160405180910390a250505050505050506200087d60018055565b505050565b60078181548110620005e2575f80fd5b5f805f80620008b585886001600160401b0316886001600160401b0316620015ee565b9050856001600160401b0316876001600160401b031610620008db57620f42406200090b565b866001600160401b0316866001600160401b0316620f4240620008ff919062001c47565b6200090b919062001c61565b818961ffff165f036200091f575f6200092f565b6200092f838b61ffff16620018eb565b935093509350509450945094915050565b6200094a62001401565b60085460ff166200096e5760405163376f849160e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006200099b828062001c81565b15905080620009b757506020620009b3838062001c81565b9050115b80620009d15750620009cd602083018362001c81565b1590505b80620009ed5750600a620009e9602084018462001c81565b9050115b1562000a0c57604051630381d06560e01b815260040160405180910390fd5b6001600160a01b038116158062000a3b57506001600160a01b0381165f9081526003602052604090205460ff16155b1562000a5a5760405163ab1955f960e01b815260040160405180910390fd5b6d314dc6448d9338c15b0a000000008260800135118062000a8c57506d314dc6448d9338c15b0a000000008260a00135115b1562000aab5760405163755cdad760e11b815260040160405180910390fd5b8160a001358260800135015f0362000ad65760405163755cdad760e11b815260040160405180910390fd5b5f62000ae9606084016040850162001ccd565b61ffff161562000afa575f62000afd565b60015b90505f811562000c8e57670de0b6b3a764000062000b22608086016060870162001ce9565b6001600160401b0316118062000b525750600162000b47608086016060870162001ce9565b6001600160401b0316105b1562000b715760405163b9ad180760e01b815260040160405180910390fd5b6040805160e081019091525f908062000b8b878062001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019062000bd59088018862001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060a0870135602082015260808701356040820152606001336001600160a01b03168152602001856001600160a01b03168152602001306001600160a01b031681525090508060405162000c5e9062001a75565b62000c6a919062001d4a565b604051809103905ff08015801562000c84573d5f803e3d5ffd5b5091505062000ea1565b6101f462000ca3606086016040870162001ccd565b61ffff16118062000cc95750603262000cc3606086016040870162001ccd565b61ffff16105b8062000cf0575062000ce2608085016060860162001ce9565b6001600160401b0316606414155b1562000d0f5760405163b9ad180760e01b815260040160405180910390fd5b6040805161014081019091525f908062000d2a878062001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019062000d749088018862001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060a0870135602082015260808701356040820152606001336001600160a01b03168152602001856001600160a01b03168152602001306001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000000000000063ffffffff16815260200186604001602081019062000e5e919062001ccd565b61ffff1681525090508060405162000e769062001a83565b62000e82919062001dd6565b604051809103905ff08015801562000e9c573d5f803e3d5ffd5b509150505b604051806060016040528060011515815260200185604001602081019062000eca919062001ccd565b61ffff16815260200162000ee5608087016060880162001ce9565b6001600160401b039081169091526001600160a01b0383165f908152600260209081526040918290208451815492860151959093015162ffffff1990921692151562ffff0019169290921761010061ffff90951694909402939093176affffffffffffffff000000191663010000009390921692909202179055816200105f576040516323dedf9b60e21b81526001600160a01b0382811660048301527f00000000000000000000000000000000000000000000000000000000000000001690638f7b7e6c906024016020604051808303815f875af115801562000fcb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000ff1919062001bfc565b6200100f57604051632235f4b360e21b815260040160405180910390fd5b600780546001810182555f919091527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b0319166001600160a01b038316179055620011d3565b62001071608085016060860162001ce9565b6001600160401b0316606403620010d257600480546001810182555f919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b038316179055620011d3565b6064620010e6608086016060870162001ce9565b6001600160401b031611156200114657600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b038316179055620011d3565b60646200115a608086016060870162001ce9565b6001600160401b03161015620011ba57600580546001810182555f919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b038316179055620011d3565b60405163b9ad180760e01b815260040160405180910390fd5b60405163d48bfca760e01b81526001600160a01b03828116600483015284169063d48bfca7906024016020604051808303815f875af115801562001219573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200123f919062001bfc565b6200125d5760405163188d1cb360e01b815260040160405180910390fd5b600a805460010190556001600160a01b0381167f67cc2637dc8147ee999178ad9002b9fcccdde83ae7a8664c59f49ed2cceb2e626200129d868062001c81565b620012ac602089018962001c81565b89608001358a60a00135604051620012ca9695949392919062001ee5565b60405180910390a250505050565b5f80826004811115620012ef57620012ef62001f25565b03620012fd57505060045490565b600182600481111562001314576200131462001f25565b036200132257505060065490565b600282600481111562001339576200133962001f25565b036200134757505060055490565b60038260048111156200135e576200135e62001f25565b036200136c57505060075490565b600482600481111562001383576200138362001f25565b0362001391575050600a5490565b60405163151d20b160e21b815260040160405180910390fd5b60068181548110620005e2575f80fd5b620013c462001401565b6001600160a01b038116620013f357604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b620013fe816200142f565b50565b5f546001600160a01b03163314620006205760405163118cdaa760e01b8152336004820152602401620013ea565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600260015403620014a257604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b5f80806001600160a01b0386161580620014ca57506001600160a01b038516155b80620014dd57506001600160a01b038416155b15620014fc57604051631863246760e01b815260040160405180910390fd5b6001600160a01b038086165f908152600260208181526040808420815160608082018452915460ff8082161515835261ffff6101008084048216858901526001600160401b036301000000948590048116868901529a8f168a5297875297859020855194850186525490811615158452958604909616938201939093529390920490931690820152815115806200159257508051155b80620015af5750856001600160a01b0316876001600160a01b0316145b15620015ce5760405163627c56b360e11b815260040160405180910390fd5b816040015181602001518260400151945094509450505093509350939050565b5f620015fc84848462001932565b6200161a57604051635646d13b60e11b815260040160405180910390fd5b8162001627848662001c47565b62001633919062001c61565b949350505050565b5f620f424082101562001661576040516382a3aeb760e01b815260040160405180910390fd5b6c7e37be2022c0914b26800000008211156200169057604051630980859f60e31b815260040160405180910390fd5b6040516370a0823160e01b81526001600160a01b0387811660048301528491908716906370a0823190602401602060405180830381865afa158015620016d8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620016fe919062001f39565b10156200171e57604051636502ac4160e11b815260040160405180910390fd5b604051636eb1769f60e11b81526001600160a01b03878116600483015230602483015284919087169063dd62ed3e90604401602060405180830381865afa1580156200176c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062001792919062001f39565b1015620017b25760405163e96042e960e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015282906001600160a01b038616906370a0823190602401602060405180830381865afa158015620017f7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200181d919062001f39565b10156200183d57604051636502ac4160e11b815260040160405180910390fd5b50600195945050505050565b6040516001600160a01b038481166024830152838116604483015260648201839052620018b29186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505062001a04565b50505050565b6040516001600160a01b038381166024830152604482018390526200087d91859182169063a9059cbb906064016200187f565b5f6c0c9f2c9cd04674edea4000000062001906838562001c47565b6200191d906a52b7d2dcc80cd2e400000062001c47565b62001929919062001c61565b90505b92915050565b5f620f42406c7e37be2022c0914b2680000000670de0b6b3a7640000828710806200195c57508187115b156200197b57604051633afbeabf60e21b815260040160405180910390fd5b8515806200198857508086115b8062001992575084155b806200199d57508085115b15620019bc5760405163330512eb60e21b815260040160405180910390fd5b5f858710620019cc5783620019e5565b86620019d9878662001c47565b620019e5919062001c61565b9050838110158015620019f85750808810155b98975050505050505050565b5f8060205f8451602086015f885af18062001a24576040513d5f823e3d81fd5b50505f513d9150811562001a3d57806001141562001a4a565b6001600160a01b0384163b155b15620018b257604051635274afe760e01b81526001600160a01b0385166004820152602401620013ea565b610ef88062001f5283390190565b6115538062002e4a83390190565b5f6020828403121562001aa2575f80fd5b5035919050565b6001600160a01b0381168114620013fe575f80fd5b5f6020828403121562001acf575f80fd5b813562001adc8162001aa9565b9392505050565b5f805f6060848603121562001af6575f80fd5b833562001b038162001aa9565b9250602084013562001b158162001aa9565b929592945050506040919091013590565b803561ffff8116811462001b38575f80fd5b919050565b80356001600160401b038116811462001b38575f80fd5b5f805f806080858703121562001b68575f80fd5b62001b738562001b26565b935062001b836020860162001b3d565b925062001b936040860162001b3d565b9396929550929360600135925050565b5f6020828403121562001bb4575f80fd5b81356001600160401b0381111562001bca575f80fd5b820160c0818503121562001adc575f80fd5b5f6020828403121562001bed575f80fd5b81356005811062001adc575f80fd5b5f6020828403121562001c0d575f80fd5b8151801515811462001adc575f80fd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156200192c576200192c62001c1d565b80820281158282048414176200192c576200192c62001c1d565b5f8262001c7c57634e487b7160e01b5f52601260045260245ffd5b500490565b5f808335601e1984360301811262001c97575f80fd5b8301803591506001600160401b0382111562001cb1575f80fd5b60200191503681900382131562001cc6575f80fd5b9250929050565b5f6020828403121562001cde575f80fd5b620019298262001b26565b5f6020828403121562001cfa575f80fd5b620019298262001b3d565b5f81518084525f5b8181101562001d2b5760208185018101518683018201520162001d0d565b505f602082860101526020601f19601f83011685010191505092915050565b602081525f825160e0602084015262001d6861010084018262001d05565b90506020840151601f1984830301604085015262001d87828262001d05565b9150506040840151606084015260608401516080840152608084015160018060a01b0380821660a08601528060a08701511660c08601528060c08701511660e086015250508091505092915050565b602081525f825161014080602085015262001df661016085018362001d05565b91506020850151601f1985840301604086015262001e15838262001d05565b9250506040850151606085015260608501516080850152608085015162001e4760a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010062001e8f818701836001600160a01b03169052565b860151905061012062001ea98682018363ffffffff169052565b9095015161ffff1693019290925250919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f62001efa60808301888a62001ebd565b828103602084015262001f0f81878962001ebd565b6040840195909552505060600152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f6020828403121562001f4a575f80fd5b505191905056fe61010060405234801562000011575f80fd5b5060405162000ef838038062000ef88339810160408190526200003491620003bf565b8051602082015160036200004983826200052b565b5060046200005882826200052b565b5050506080808201516001600160a01b0390811690915260c080830151821660a09081528301519091169052606081015160408201805190910160e0525115620000b657620000b68160a0015182604001516200013a60201b60201c565b606081015115620000db57620000db8160c0015182606001516200013a60201b60201c565b60805160c05160a05160e051604080516001600160a01b03948516815292841660208401528201529116907f1a572700faea7ceef5021bf84a4e501a19a2a57c03571fbffedea0ead9b717f29060600160405180910390a2506200061d565b6001600160a01b038216620001695760405163ec442f0560e01b81525f60048201526024015b60405180910390fd5b620001765f83836200017a565b5050565b6001600160a01b038316620001a8578060025f8282546200019c9190620005f7565b909155506200021a9050565b6001600160a01b0383165f9081526020819052604090205481811015620001fc5760405163391434e360e21b81526001600160a01b0385166004820152602481018290526044810183905260640162000160565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216620002385760028054829003905562000256565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516200029c91815260200190565b60405180910390a3505050565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b0381118282101715620002e257620002e2620002a9565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620003135762000313620002a9565b604052919050565b5f82601f8301126200032b575f80fd5b81516001600160401b03811115620003475762000347620002a9565b60206200035d601f8301601f19168201620002e8565b828152858284870101111562000371575f80fd5b5f5b838110156200039057858101830151828201840152820162000373565b505f928101909101919091529392505050565b80516001600160a01b0381168114620003ba575f80fd5b919050565b5f60208284031215620003d0575f80fd5b81516001600160401b0380821115620003e7575f80fd5b9083019060e08286031215620003fb575f80fd5b62000405620002bd565b82518281111562000414575f80fd5b62000422878286016200031b565b82525060208301518281111562000437575f80fd5b62000445878286016200031b565b60208301525060408301516040820152606083015160608201526200046d60808401620003a3565b60808201526200048060a08401620003a3565b60a08201526200049360c08401620003a3565b60c082015295945050505050565b600181811c90821680620004b657607f821691505b602082108103620004d557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200052657805f5260205f20601f840160051c81016020851015620005025750805b601f840160051c820191505b8181101562000523575f81556001016200050e565b50505b505050565b81516001600160401b03811115620005475762000547620002a9565b6200055f81620005588454620004a1565b84620004db565b602080601f83116001811462000595575f84156200057d5750858301515b5f19600386901b1c1916600185901b178555620005ef565b5f85815260208120601f198616915b82811015620005c557888601518255948401946001909101908401620005a4565b5085821015620005e357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b808201808211156200061757634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516108a7620006515f395f61022c01525f61016e01525f6101ad01525f61029e01526108a75ff3fe608060405234801561000f575f80fd5b50600436106100f0575f3560e01c806342966c68116100935780639fd4da40116100635780639fd4da4014610227578063a9059cbb1461024e578063dd62ed3e14610261578063e927fc5c14610299575f80fd5b806342966c68146101cf57806370a08231146101e457806379cc67901461020c57806395d89b411461021f575f80fd5b806323b872dd116100ce57806323b872dd14610147578063313ce5671461015a578063353916e114610169578063419dd9a5146101a8575f80fd5b806306fdde03146100f4578063095ea7b31461011257806318160ddd14610135575b5f80fd5b6100fc6102c0565b60405161010991906106ea565b60405180910390f35b610125610120366004610751565b610350565b6040519015158152602001610109565b6002545b604051908152602001610109565b610125610155366004610779565b610369565b60405160128152602001610109565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610109565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6101e26101dd3660046107b2565b61038c565b005b6101396101f23660046107c9565b6001600160a01b03165f9081526020819052604090205490565b6101e261021a366004610751565b610399565b6100fc6103b2565b6101397f000000000000000000000000000000000000000000000000000000000000000081565b61012561025c366004610751565b6103c1565b61013961026f3660046107e9565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6060600380546102cf9061081a565b80601f01602080910402602001604051908101604052809291908181526020018280546102fb9061081a565b80156103465780601f1061031d57610100808354040283529160200191610346565b820191905f5260205f20905b81548152906001019060200180831161032957829003601f168201915b5050505050905090565b5f3361035d8185856103ce565b60019150505b92915050565b5f336103768582856103e0565b610381858585610461565b506001949350505050565b61039633826104be565b50565b6103a48233836103e0565b6103ae82826104be565b5050565b6060600480546102cf9061081a565b5f3361035d818585610461565b6103db83838360016104f2565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f1981101561045b578181101561044d57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61045b84848484035f6104f2565b50505050565b6001600160a01b03831661048a57604051634b637e8f60e11b81525f6004820152602401610444565b6001600160a01b0382166104b35760405163ec442f0560e01b81525f6004820152602401610444565b6103db8383836105c4565b6001600160a01b0382166104e757604051634b637e8f60e11b81525f6004820152602401610444565b6103ae825f836105c4565b6001600160a01b03841661051b5760405163e602df0560e01b81525f6004820152602401610444565b6001600160a01b03831661054457604051634a1406b160e11b81525f6004820152602401610444565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561045b57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516105b691815260200190565b60405180910390a350505050565b6001600160a01b0383166105ee578060025f8282546105e39190610852565b9091555061065e9050565b6001600160a01b0383165f90815260208190526040902054818110156106405760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610444565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661067a57600280548290039055610698565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516106dd91815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b81811015610716578581018301518582016040015282016106fa565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461074c575f80fd5b919050565b5f8060408385031215610762575f80fd5b61076b83610736565b946020939093013593505050565b5f805f6060848603121561078b575f80fd5b61079484610736565b92506107a260208501610736565b9150604084013590509250925092565b5f602082840312156107c2575f80fd5b5035919050565b5f602082840312156107d9575f80fd5b6107e282610736565b9392505050565b5f80604083850312156107fa575f80fd5b61080383610736565b915061081160208401610736565b90509250929050565b600181811c9082168061082e57607f821691505b60208210810361084c57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561036357634e487b7160e01b5f52601160045260245ffdfea264697066735822122001730799c3516b6ea3798778721a8f1832f3c6320f9464ec7dda16aacae654f664736f6c6343000818003361016060405234801562000011575f80fd5b506040516200155338038062001553833981016040819052620000349162000644565b805160208201516003620000498382620007f2565b506004620000588282620007f2565b5050506080818101516001600160a01b0390811661010090815260c08085018051841661012090815260a0808801805187166101405260e0808a018051891690915260608a01516040808c018051909201909752968a015163ffffffff169092529188015161ffff16909652805185165f908152600560209081528482208054600160ff19918216811790925598518816835285832080548a16821790559251871682526006905283812080548816831790559151909416815220805490931690911790915551156200013f576200013f8160a001518260400151620001c660201b60201c565b6060810151156200016457620001648160c001518260600151620001c660201b60201c565b61010051610140516101205160c051604080516001600160a01b03948516815292841660208401528201529116907f8149dd5a43c422d56e6156bb0211a2029f1a9fbf919ddbeba47a86d7655d88659060600160405180910390a25062000938565b6001600160a01b038216620001f55760405163ec442f0560e01b81525f60048201526024015b60405180910390fd5b620002025f838362000206565b5050565b608051811580159062000221575060a05163ffffffff164210155b80156200024657506001600160a01b0384165f9081526005602052604090205460ff16155b80156200026b57506001600160a01b0383165f9081526006602052604090205460ff16155b156200031d575f620002828361ffff841662000330565b90505f620002918285620008d2565b9050620002a0868684620003d8565b620002b58660e05183620003d860201b60201c565b604080516001600160a01b03878116825261ffff861660208301529181018690526060810183905260808101849052908716907fb675d44afccfb8f0f10932b6cf03058fbae5859482eedcd7fa3195d0c02c42669060a00160405180910390a2505050505050565b6200032a848484620003d8565b50505050565b5f6103e88311156200038d576c0c9f2c9cd04674edea40000000620003568385620008e8565b6200036d906a52b7d2dcc80cd2e4000000620008e8565b62000379919062000902565b620003859084620008d2565b9050620003d2565b6064831115620003a45762000385603284620008d2565b600a831115620003bb5762000385600584620008d2565b8215620003cf5762000385600184620008d2565b505f5b92915050565b6001600160a01b03831662000406578060025f828254620003fa919062000922565b90915550620004789050565b6001600160a01b0383165f90815260208190526040902054818110156200045a5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401620001ec565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b0382166200049657600280548290039055620004b4565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620004fa91815260200190565b60405180910390a3505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171562000541576200054162000507565b60405290565b604051601f8201601f191681016001600160401b038111828210171562000572576200057262000507565b604052919050565b5f82601f8301126200058a575f80fd5b81516001600160401b03811115620005a657620005a662000507565b6020620005bc601f8301601f1916820162000547565b8281528582848701011115620005d0575f80fd5b5f5b83811015620005ef578581018301518282018401528201620005d2565b505f928101909101919091529392505050565b80516001600160a01b038116811462000619575f80fd5b919050565b805163ffffffff8116811462000619575f80fd5b805161ffff8116811462000619575f80fd5b5f6020828403121562000655575f80fd5b81516001600160401b03808211156200066c575f80fd5b90830190610140828603121562000681575f80fd5b6200068b6200051b565b8251828111156200069a575f80fd5b620006a8878286016200057a565b825250602083015182811115620006bd575f80fd5b620006cb878286016200057a565b6020830152506040830151604082015260608301516060820152620006f36080840162000602565b60808201526200070660a0840162000602565b60a08201526200071960c0840162000602565b60c08201526200072c60e0840162000602565b60e08201526101009150620007438284016200061e565b8282015261012091506200075982840162000632565b91810191909152949350505050565b600181811c908216806200077d57607f821691505b6020821081036200079c57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620007ed57805f5260205f20601f840160051c81016020851015620007c95750805b601f840160051c820191505b81811015620007ea575f8155600101620007d5565b50505b505050565b81516001600160401b038111156200080e576200080e62000507565b62000826816200081f845462000768565b84620007a2565b602080601f8311600181146200085c575f8415620008445750858301515b5f19600386901b1c1916600185901b178555620008b6565b5f85815260208120601f198616915b828110156200088c578886015182559484019460019091019084016200086b565b5085821015620008aa57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115620003d257620003d2620008be565b8082028115828204841417620003d257620003d2620008be565b5f826200091d57634e487b7160e01b5f52601260045260245ffd5b500490565b80820180821115620003d257620003d2620008be565b60805160a05160c05160e051610100516101205161014051610bb46200099f5f395f61018f01525f6101ce01525f61035c01525f8181610287015261075401525f6102ea01525f81816102ae01526106af01525f818161024501526106840152610bb45ff3fe608060405234801561000f575f80fd5b5060043610610111575f3560e01c806379cc67901161009e5780639e8ac22a1161006e5780639e8ac22a146102a95780639fd4da40146102e5578063a9059cbb1461030c578063dd62ed3e1461031f578063e927fc5c14610357575f80fd5b806379cc67901461022d57806383f170be1461024057806395d89b411461027a5780639ba1483a14610282575f80fd5b8063313ce567116100e4578063313ce5671461017b578063353916e11461018a578063419dd9a5146101c957806342966c68146101f057806370a0823114610205575f80fd5b806306fdde0314610115578063095ea7b31461013357806318160ddd1461015657806323b872dd14610168575b5f80fd5b61011d61037e565b60405161012a91906109a6565b60405180910390f35b610146610141366004610a0d565b61040e565b604051901515815260200161012a565b6002545b60405190815260200161012a565b610146610176366004610a35565b610427565b6040516012815260200161012a565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161012a565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6102036101fe366004610a6e565b61044a565b005b61015a610213366004610a85565b6001600160a01b03165f9081526020819052604090205490565b61020361023b366004610a0d565b610457565b6102677f000000000000000000000000000000000000000000000000000000000000000081565b60405161ffff909116815260200161012a565b61011d610470565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6102d07f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff909116815260200161012a565b61015a7f000000000000000000000000000000000000000000000000000000000000000081565b61014661031a366004610a0d565b61047f565b61015a61032d366004610aa5565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b60606003805461038d90610ad6565b80601f01602080910402602001604051908101604052809291908181526020018280546103b990610ad6565b80156104045780601f106103db57610100808354040283529160200191610404565b820191905f5260205f20905b8154815290600101906020018083116103e757829003601f168201915b5050505050905090565b5f3361041b81858561048c565b60019150505b92915050565b5f3361043485828561049e565b61043f85858561051f565b506001949350505050565b610454338261057c565b50565b61046282338361049e565b61046c828261057c565b5050565b60606004805461038d90610ad6565b5f3361041b81858561051f565b61049983838360016105b0565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610519578181101561050b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61051984848484035f6105b0565b50505050565b6001600160a01b03831661054857604051634b637e8f60e11b81525f6004820152602401610502565b6001600160a01b0382166105715760405163ec442f0560e01b81525f6004820152602401610502565b610499838383610682565b6001600160a01b0382166105a557604051634b637e8f60e11b81525f6004820152602401610502565b61046c825f83610682565b6001600160a01b0384166105d95760405163e602df0560e01b81525f6004820152602401610502565b6001600160a01b03831661060257604051634a1406b160e11b81525f6004820152602401610502565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561051957826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161067491815260200190565b60405180910390a350505050565b7f000000000000000000000000000000000000000000000000000000000000000081158015906106d857507f000000000000000000000000000000000000000000000000000000000000000063ffffffff164210155b80156106fc57506001600160a01b0384165f9081526005602052604090205460ff16155b801561072057506001600160a01b0383165f9081526006602052604090205460ff16155b156107e1575f610734838361ffff166107ec565b90505f6107418285610b22565b905061074e868684610880565b610779867f000000000000000000000000000000000000000000000000000000000000000083610880565b604080516001600160a01b03878116825261ffff861660208301529181018690526060810183905260808101849052908716907fb675d44afccfb8f0f10932b6cf03058fbae5859482eedcd7fa3195d0c02c42669060a00160405180910390a2505050505050565b610519848484610880565b5f6103e883111561083f576c0c9f2c9cd04674edea4000000061080f8385610b35565b610824906a52b7d2dcc80cd2e4000000610b35565b61082e9190610b4c565b6108389084610b22565b9050610421565b606483111561085357610838603284610b22565b600a83111561086757610838600584610b22565b821561087857610838600184610b22565b505f92915050565b6001600160a01b0383166108aa578060025f82825461089f9190610b6b565b9091555061091a9050565b6001600160a01b0383165f90815260208190526040902054818110156108fc5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610502565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661093657600280548290039055610954565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161099991815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b818110156109d2578581018301518582016040015282016109b6565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610a08575f80fd5b919050565b5f8060408385031215610a1e575f80fd5b610a27836109f2565b946020939093013593505050565b5f805f60608486031215610a47575f80fd5b610a50846109f2565b9250610a5e602085016109f2565b9150604084013590509250925092565b5f60208284031215610a7e575f80fd5b5035919050565b5f60208284031215610a95575f80fd5b610a9e826109f2565b9392505050565b5f8060408385031215610ab6575f80fd5b610abf836109f2565b9150610acd602084016109f2565b90509250929050565b600181811c90821680610aea57607f821691505b602082108103610b0857634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561042157610421610b0e565b808202811582820484141761042157610421610b0e565b5f82610b6657634e487b7160e01b5f52601260045260245ffd5b500490565b8082018082111561042157610421610b0e56fea26469706673582212205dc7922f423974ceda3fe19a409b724b9ceba6ab0a09bb17cfe59e189f9babe864736f6c63430008180033a26469706673582212200da3b93799cf0848d6aa08d0cf361fa097daa47f30e2396cae98720535acecdb64736f6c6343000818003360a060405234801562000010575f80fd5b5060405162000cf138038062000cf18339810160408190526200003391620001f7565b80516020820151604083015160036200004d838262000335565b5060046200005c828262000335565b5050506001600160a01b0381166200008d57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200009881620000ac565b50516001600160a01b031660805262000401565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715620001365762000136620000fd565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620001675762000167620000fd565b604052919050565b5f82601f8301126200017f575f80fd5b81516001600160401b038111156200019b576200019b620000fd565b6020620001b1601f8301601f191682016200013c565b8281528582848701011115620001c5575f80fd5b5f5b83811015620001e4578581018301518282018401528201620001c7565b505f928101909101919091529392505050565b5f6020828403121562000208575f80fd5b81516001600160401b03808211156200021f575f80fd5b908301906060828603121562000233575f80fd5b6200023d62000111565b82516001600160a01b038116811462000254575f80fd5b815260208301518281111562000268575f80fd5b62000276878286016200016f565b6020830152506040830151828111156200028e575f80fd5b6200029c878286016200016f565b60408301525095945050505050565b600181811c90821680620002c057607f821691505b602082108103620002df57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200033057805f5260205f20601f840160051c810160208510156200030c5750805b601f840160051c820191505b818110156200032d575f815560010162000318565b50505b505050565b81516001600160401b03811115620003515762000351620000fd565b6200036981620003628454620002ab565b84620002e5565b602080601f8311600181146200039f575f8415620003875750858301515b5f19600386901b1c1916600185901b178555620003f9565b5f85815260208120601f198616915b82811015620003cf57888601518255948401946001909101908401620003ae565b5085821015620003ed57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b6080516108d76200041a5f395f61016301526108d75ff3fe608060405234801561000f575f80fd5b50600436106100e5575f3560e01c806370a082311161008857806395d89b411161006357806395d89b41146101f3578063a9059cbb146101fb578063dd62ed3e1461020e578063f2fde38b14610246575f80fd5b806370a08231146101b0578063715018a6146101d85780638da5cb5b146101e2575f80fd5b806323b872dd116100c357806323b872dd1461013c578063313ce5671461014f578063419dd9a51461015e578063549c6bbb1461019d575f80fd5b806306fdde03146100e9578063095ea7b31461010757806318160ddd1461012a575b5f80fd5b6100f1610259565b6040516100fe9190610731565b60405180910390f35b61011a610115366004610798565b6102e9565b60405190151581526020016100fe565b6002545b6040519081526020016100fe565b61011a61014a3660046107c0565b610302565b604051601281526020016100fe565b6101857f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100fe565b61011a6101ab366004610798565b610325565b61012e6101be3660046107f9565b6001600160a01b03165f9081526020819052604090205490565b6101e0610384565b005b6005546001600160a01b0316610185565b6100f16103a5565b61011a610209366004610798565b6103b4565b61012e61021c366004610819565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101e06102543660046107f9565b6103c1565b6060600380546102689061084a565b80601f01602080910402602001604051908101604052809291908181526020018280546102949061084a565b80156102df5780601f106102b6576101008083540402835291602001916102df565b820191905f5260205f20905b8154815290600101906020018083116102c257829003601f168201915b5050505050905090565b5f336102f68185856103e2565b60019150505b92915050565b5f3361030f8582856103f4565b61031a858585610475565b506001949350505050565b5f61032e6104d2565b6103388383610501565b826001600160a01b03167f4a38df4d86b2d93d9427931480e831f8dbede6c4d0fdc4490340fddc0c33627a8360405161037391815260200190565b60405180910390a250600192915050565b61038c6104d2565b604051630364a9b360e51b815260040160405180910390fd5b6060600480546102689061084a565b5f336102f6818585610475565b6103c96104d2565b60405163dbd6d1ef60e01b815260040160405180910390fd5b6103ef8383836001610539565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f1981101561046f578181101561046157604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61046f84848484035f610539565b50505050565b6001600160a01b03831661049e57604051634b637e8f60e11b81525f6004820152602401610458565b6001600160a01b0382166104c75760405163ec442f0560e01b81525f6004820152602401610458565b6103ef83838361060b565b6005546001600160a01b031633146104ff5760405163118cdaa760e01b8152336004820152602401610458565b565b6001600160a01b03821661052a5760405163ec442f0560e01b81525f6004820152602401610458565b6105355f838361060b565b5050565b6001600160a01b0384166105625760405163e602df0560e01b81525f6004820152602401610458565b6001600160a01b03831661058b57604051634a1406b160e11b81525f6004820152602401610458565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561046f57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516105fd91815260200190565b60405180910390a350505050565b6001600160a01b038316610635578060025f82825461062a9190610882565b909155506106a59050565b6001600160a01b0383165f90815260208190526040902054818110156106875760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610458565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b0382166106c1576002805482900390556106df565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161072491815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b8181101561075d57858101830151858201604001528201610741565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610793575f80fd5b919050565b5f80604083850312156107a9575f80fd5b6107b28361077d565b946020939093013593505050565b5f805f606084860312156107d2575f80fd5b6107db8461077d565b92506107e96020850161077d565b9150604084013590509250925092565b5f60208284031215610809575f80fd5b6108128261077d565b9392505050565b5f806040838503121561082a575f80fd5b6108338361077d565b91506108416020840161077d565b90509250929050565b600181811c9082168061085e57607f821691505b60208210810361087c57634e487b7160e01b5f52602260045260245ffd5b50919050565b808201808211156102fc57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220948f7601fd54c2aae35a955142ddbf6d9ef9523b00647b56290fa932f39f132264736f6c6343000818003300000000000000000000000030f3aa4de2152a588695e3f38669a712826c7696

Deployed ByteCode

0x608060405234801562000010575f80fd5b506004361062000170575f3560e01c80638da5cb5b11620000cf578063b77991bf1162000083578063b77991bf14620003bc578063c4f1ef8f14620003e4578063cab6353814620003fb578063d61bde7d1462000423578063e8283a6c146200044b578063f2fde38b1462000455575f80fd5b80638da5cb5b146200030b57806393232b99146200031c5780639fc80f071462000344578063a8426c26146200035b578063ad3ad830146200038e578063b6455b8814620003a5575f80fd5b80634b86391411620001275780634b86391414620002735780635274908d146200028c57806353cc749614620002b45780636c2c5d8e14620002cb578063715018a614620002ea5780637bbacb9d14620002f4575f80fd5b806306a61f081462000174578063097b49691462000180578063100462a214620001c55780631b1fd2b914620001dc5780631f69565f146200021a578063357f62ac146200025c575b5f80fd5b6200017e6200046c565b005b620001a87f00000000000000000000000030f3aa4de2152a588695e3f38669a712826c769681565b6040516001600160a01b0390911681526020015b60405180910390f35b6200017e620001d636600462001a91565b620004b8565b620002047f000000000000000000000000000000000000000000000000000000006955b90081565b60405163ffffffff9091168152602001620001bc565b620002316200022b36600462001abe565b62000543565b6040805161ffff90941684526001600160401b039092166020840152151590820152606001620001bc565b620001a86200026d36600462001a91565b620005d2565b6200027d600a5481565b604051908152602001620001bc565b620001a87f0000000000000000000000006a00b68e23f3fd531ef26d72035ae73eadb97f2c81565b620001a8620002c536600462001a91565b620005fb565b600854620002d99060ff1681565b6040519015158152602001620001bc565b6200017e6200060b565b6200017e6200030536600462001ae3565b62000622565b5f546001600160a01b0316620001a8565b620001a87f00000000000000000000000030a455aa6670deb4eb8c38304ca00bd7b82d6c4981565b620001a86200035536600462001a91565b62000882565b620003726200036c36600462001b54565b62000892565b60408051938452602084019290925290820152606001620001bc565b6200017e6200039f36600462001ba3565b62000940565b6200027d620003b636600462001bdc565b620012d8565b620001a87f000000000000000000000000d1cc6fc563af0bf1d020730cc78a13decb7cb37181565b620001a8620003f536600462001a91565b620013aa565b620001a87f0000000000000000000000006fd0a7d1a3dbc6cc1d010b96567a72b4d27d412181565b620001a87f0000000000000000000000006ecdac0592f072d26c15ae00cffbf2e1f06742f581565b6200027d60095481565b6200017e6200046636600462001abe565b620013ba565b6200047662001401565b6008805460ff19169055604080515f8152905133917ffd21bfe5b51c39faafd72a5a1911aa6049e46550ae104de3f8468516fd37e843919081900360200190a2565b620004c262001401565b6009544210620004e5576040516336192f1560e11b815260040160405180910390fd5b4281116200050657604051633f380c4560e21b815260040160405180910390fd5b600981905560405181815233907f22e5783ee3349ec9ee0402cd256d97f48db9c16914cecc0a544dbb93ed85862e9060200160405180910390a250565b6001600160a01b0381165f9081526002602090815260408083208151606081018352905460ff81161515808352610100820461ffff1694830194909452630100000090046001600160401b0316918101919091528291829190620005ba5760405163627c56b360e11b815260040160405180910390fd5b60208101516040820151915190969195509350915050565b60048181548110620005e2575f80fd5b5f918252602090912001546001600160a01b0316905081565b60058181548110620005e2575f80fd5b6200061562001401565b620006205f6200142f565b565b600954421015620006465760405163fe36753f60e01b815260040160405180910390fd5b620006506200147e565b3383835f808062000663868686620014a9565b9250925092505f6200068988856001600160401b0316846001600160401b0316620015ee565b90506200069a878b8b8b856200163b565b620006b85760405163185d483560e01b815260040160405180910390fd5b620006cf6001600160a01b038b1688308b62001849565b620006e56001600160a01b038a168883620018b8565b5f61ffff8416158015906200072057507f000000000000000000000000000000000000000000000000000000006955b90063ffffffff164210155b15620007ec5762000736828561ffff16620018eb565b60405163549c6bbb60e01b81526001600160a01b038a81166004830152602482018390529192507f00000000000000000000000030a455aa6670deb4eb8c38304ca00bd7b82d6c499091169063549c6bbb906044016020604051808303815f875af1158015620007a8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620007ce919062001bfc565b620007ec57604051637ed84a4560e01b815260040160405180910390fd5b6001600160a01b0388167fb9599ccd59c01bd240490d5b5a14bb2740edcf8dc95df77f901498613df565d18587868d62000827878962001c31565b6040805161ffff9690961686526001600160401b03948516602087015292909316918401919091526060830152608082015260a0810184905260c00160405180910390a250505050505050506200087d60018055565b505050565b60078181548110620005e2575f80fd5b5f805f80620008b585886001600160401b0316886001600160401b0316620015ee565b9050856001600160401b0316876001600160401b031610620008db57620f42406200090b565b866001600160401b0316866001600160401b0316620f4240620008ff919062001c47565b6200090b919062001c61565b818961ffff165f036200091f575f6200092f565b6200092f838b61ffff16620018eb565b935093509350509450945094915050565b6200094a62001401565b60085460ff166200096e5760405163376f849160e01b815260040160405180910390fd5b7f0000000000000000000000006fd0a7d1a3dbc6cc1d010b96567a72b4d27d41216200099b828062001c81565b15905080620009b757506020620009b3838062001c81565b9050115b80620009d15750620009cd602083018362001c81565b1590505b80620009ed5750600a620009e9602084018462001c81565b9050115b1562000a0c57604051630381d06560e01b815260040160405180910390fd5b6001600160a01b038116158062000a3b57506001600160a01b0381165f9081526003602052604090205460ff16155b1562000a5a5760405163ab1955f960e01b815260040160405180910390fd5b6d314dc6448d9338c15b0a000000008260800135118062000a8c57506d314dc6448d9338c15b0a000000008260a00135115b1562000aab5760405163755cdad760e11b815260040160405180910390fd5b8160a001358260800135015f0362000ad65760405163755cdad760e11b815260040160405180910390fd5b5f62000ae9606084016040850162001ccd565b61ffff161562000afa575f62000afd565b60015b90505f811562000c8e57670de0b6b3a764000062000b22608086016060870162001ce9565b6001600160401b0316118062000b525750600162000b47608086016060870162001ce9565b6001600160401b0316105b1562000b715760405163b9ad180760e01b815260040160405180910390fd5b6040805160e081019091525f908062000b8b878062001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019062000bd59088018862001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060a0870135602082015260808701356040820152606001336001600160a01b03168152602001856001600160a01b03168152602001306001600160a01b031681525090508060405162000c5e9062001a75565b62000c6a919062001d4a565b604051809103905ff08015801562000c84573d5f803e3d5ffd5b5091505062000ea1565b6101f462000ca3606086016040870162001ccd565b61ffff16118062000cc95750603262000cc3606086016040870162001ccd565b61ffff16105b8062000cf0575062000ce2608085016060860162001ce9565b6001600160401b0316606414155b1562000d0f5760405163b9ad180760e01b815260040160405180910390fd5b6040805161014081019091525f908062000d2a878062001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060209081019062000d749088018862001c81565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050509082525060a0870135602082015260808701356040820152606001336001600160a01b03168152602001856001600160a01b03168152602001306001600160a01b031681526020017f0000000000000000000000006a00b68e23f3fd531ef26d72035ae73eadb97f2c6001600160a01b031681526020017f000000000000000000000000000000000000000000000000000000006955b90063ffffffff16815260200186604001602081019062000e5e919062001ccd565b61ffff1681525090508060405162000e769062001a83565b62000e82919062001dd6565b604051809103905ff08015801562000e9c573d5f803e3d5ffd5b509150505b604051806060016040528060011515815260200185604001602081019062000eca919062001ccd565b61ffff16815260200162000ee5608087016060880162001ce9565b6001600160401b039081169091526001600160a01b0383165f908152600260209081526040918290208451815492860151959093015162ffffff1990921692151562ffff0019169290921761010061ffff90951694909402939093176affffffffffffffff000000191663010000009390921692909202179055816200105f576040516323dedf9b60e21b81526001600160a01b0382811660048301527f0000000000000000000000006a00b68e23f3fd531ef26d72035ae73eadb97f2c1690638f7b7e6c906024016020604051808303815f875af115801562000fcb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000ff1919062001bfc565b6200100f57604051632235f4b360e21b815260040160405180910390fd5b600780546001810182555f919091527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b0319166001600160a01b038316179055620011d3565b62001071608085016060860162001ce9565b6001600160401b0316606403620010d257600480546001810182555f919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b0180546001600160a01b0319166001600160a01b038316179055620011d3565b6064620010e6608086016060870162001ce9565b6001600160401b031611156200114657600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b038316179055620011d3565b60646200115a608086016060870162001ce9565b6001600160401b03161015620011ba57600580546001810182555f919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b038316179055620011d3565b60405163b9ad180760e01b815260040160405180910390fd5b60405163d48bfca760e01b81526001600160a01b03828116600483015284169063d48bfca7906024016020604051808303815f875af115801562001219573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200123f919062001bfc565b6200125d5760405163188d1cb360e01b815260040160405180910390fd5b600a805460010190556001600160a01b0381167f67cc2637dc8147ee999178ad9002b9fcccdde83ae7a8664c59f49ed2cceb2e626200129d868062001c81565b620012ac602089018962001c81565b89608001358a60a00135604051620012ca9695949392919062001ee5565b60405180910390a250505050565b5f80826004811115620012ef57620012ef62001f25565b03620012fd57505060045490565b600182600481111562001314576200131462001f25565b036200132257505060065490565b600282600481111562001339576200133962001f25565b036200134757505060055490565b60038260048111156200135e576200135e62001f25565b036200136c57505060075490565b600482600481111562001383576200138362001f25565b0362001391575050600a5490565b60405163151d20b160e21b815260040160405180910390fd5b60068181548110620005e2575f80fd5b620013c462001401565b6001600160a01b038116620013f357604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b620013fe816200142f565b50565b5f546001600160a01b03163314620006205760405163118cdaa760e01b8152336004820152602401620013ea565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600260015403620014a257604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b5f80806001600160a01b0386161580620014ca57506001600160a01b038516155b80620014dd57506001600160a01b038416155b15620014fc57604051631863246760e01b815260040160405180910390fd5b6001600160a01b038086165f908152600260208181526040808420815160608082018452915460ff8082161515835261ffff6101008084048216858901526001600160401b036301000000948590048116868901529a8f168a5297875297859020855194850186525490811615158452958604909616938201939093529390920490931690820152815115806200159257508051155b80620015af5750856001600160a01b0316876001600160a01b0316145b15620015ce5760405163627c56b360e11b815260040160405180910390fd5b816040015181602001518260400151945094509450505093509350939050565b5f620015fc84848462001932565b6200161a57604051635646d13b60e11b815260040160405180910390fd5b8162001627848662001c47565b62001633919062001c61565b949350505050565b5f620f424082101562001661576040516382a3aeb760e01b815260040160405180910390fd5b6c7e37be2022c0914b26800000008211156200169057604051630980859f60e31b815260040160405180910390fd5b6040516370a0823160e01b81526001600160a01b0387811660048301528491908716906370a0823190602401602060405180830381865afa158015620016d8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620016fe919062001f39565b10156200171e57604051636502ac4160e11b815260040160405180910390fd5b604051636eb1769f60e11b81526001600160a01b03878116600483015230602483015284919087169063dd62ed3e90604401602060405180830381865afa1580156200176c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062001792919062001f39565b1015620017b25760405163e96042e960e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015282906001600160a01b038616906370a0823190602401602060405180830381865afa158015620017f7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200181d919062001f39565b10156200183d57604051636502ac4160e11b815260040160405180910390fd5b50600195945050505050565b6040516001600160a01b038481166024830152838116604483015260648201839052620018b29186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505062001a04565b50505050565b6040516001600160a01b038381166024830152604482018390526200087d91859182169063a9059cbb906064016200187f565b5f6c0c9f2c9cd04674edea4000000062001906838562001c47565b6200191d906a52b7d2dcc80cd2e400000062001c47565b62001929919062001c61565b90505b92915050565b5f620f42406c7e37be2022c0914b2680000000670de0b6b3a7640000828710806200195c57508187115b156200197b57604051633afbeabf60e21b815260040160405180910390fd5b8515806200198857508086115b8062001992575084155b806200199d57508085115b15620019bc5760405163330512eb60e21b815260040160405180910390fd5b5f858710620019cc5783620019e5565b86620019d9878662001c47565b620019e5919062001c61565b9050838110158015620019f85750808810155b98975050505050505050565b5f8060205f8451602086015f885af18062001a24576040513d5f823e3d81fd5b50505f513d9150811562001a3d57806001141562001a4a565b6001600160a01b0384163b155b15620018b257604051635274afe760e01b81526001600160a01b0385166004820152602401620013ea565b610ef88062001f5283390190565b6115538062002e4a83390190565b5f6020828403121562001aa2575f80fd5b5035919050565b6001600160a01b0381168114620013fe575f80fd5b5f6020828403121562001acf575f80fd5b813562001adc8162001aa9565b9392505050565b5f805f6060848603121562001af6575f80fd5b833562001b038162001aa9565b9250602084013562001b158162001aa9565b929592945050506040919091013590565b803561ffff8116811462001b38575f80fd5b919050565b80356001600160401b038116811462001b38575f80fd5b5f805f806080858703121562001b68575f80fd5b62001b738562001b26565b935062001b836020860162001b3d565b925062001b936040860162001b3d565b9396929550929360600135925050565b5f6020828403121562001bb4575f80fd5b81356001600160401b0381111562001bca575f80fd5b820160c0818503121562001adc575f80fd5b5f6020828403121562001bed575f80fd5b81356005811062001adc575f80fd5b5f6020828403121562001c0d575f80fd5b8151801515811462001adc575f80fd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156200192c576200192c62001c1d565b80820281158282048414176200192c576200192c62001c1d565b5f8262001c7c57634e487b7160e01b5f52601260045260245ffd5b500490565b5f808335601e1984360301811262001c97575f80fd5b8301803591506001600160401b0382111562001cb1575f80fd5b60200191503681900382131562001cc6575f80fd5b9250929050565b5f6020828403121562001cde575f80fd5b620019298262001b26565b5f6020828403121562001cfa575f80fd5b620019298262001b3d565b5f81518084525f5b8181101562001d2b5760208185018101518683018201520162001d0d565b505f602082860101526020601f19601f83011685010191505092915050565b602081525f825160e0602084015262001d6861010084018262001d05565b90506020840151601f1984830301604085015262001d87828262001d05565b9150506040840151606084015260608401516080840152608084015160018060a01b0380821660a08601528060a08701511660c08601528060c08701511660e086015250508091505092915050565b602081525f825161014080602085015262001df661016085018362001d05565b91506020850151601f1985840301604086015262001e15838262001d05565b9250506040850151606085015260608501516080850152608085015162001e4760a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010062001e8f818701836001600160a01b03169052565b860151905061012062001ea98682018363ffffffff169052565b9095015161ffff1693019290925250919050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f62001efa60808301888a62001ebd565b828103602084015262001f0f81878962001ebd565b6040840195909552505060600152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f6020828403121562001f4a575f80fd5b505191905056fe61010060405234801562000011575f80fd5b5060405162000ef838038062000ef88339810160408190526200003491620003bf565b8051602082015160036200004983826200052b565b5060046200005882826200052b565b5050506080808201516001600160a01b0390811690915260c080830151821660a09081528301519091169052606081015160408201805190910160e0525115620000b657620000b68160a0015182604001516200013a60201b60201c565b606081015115620000db57620000db8160c0015182606001516200013a60201b60201c565b60805160c05160a05160e051604080516001600160a01b03948516815292841660208401528201529116907f1a572700faea7ceef5021bf84a4e501a19a2a57c03571fbffedea0ead9b717f29060600160405180910390a2506200061d565b6001600160a01b038216620001695760405163ec442f0560e01b81525f60048201526024015b60405180910390fd5b620001765f83836200017a565b5050565b6001600160a01b038316620001a8578060025f8282546200019c9190620005f7565b909155506200021a9050565b6001600160a01b0383165f9081526020819052604090205481811015620001fc5760405163391434e360e21b81526001600160a01b0385166004820152602481018290526044810183905260640162000160565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b038216620002385760028054829003905562000256565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516200029c91815260200190565b60405180910390a3505050565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b0381118282101715620002e257620002e2620002a9565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620003135762000313620002a9565b604052919050565b5f82601f8301126200032b575f80fd5b81516001600160401b03811115620003475762000347620002a9565b60206200035d601f8301601f19168201620002e8565b828152858284870101111562000371575f80fd5b5f5b838110156200039057858101830151828201840152820162000373565b505f928101909101919091529392505050565b80516001600160a01b0381168114620003ba575f80fd5b919050565b5f60208284031215620003d0575f80fd5b81516001600160401b0380821115620003e7575f80fd5b9083019060e08286031215620003fb575f80fd5b62000405620002bd565b82518281111562000414575f80fd5b62000422878286016200031b565b82525060208301518281111562000437575f80fd5b62000445878286016200031b565b60208301525060408301516040820152606083015160608201526200046d60808401620003a3565b60808201526200048060a08401620003a3565b60a08201526200049360c08401620003a3565b60c082015295945050505050565b600181811c90821680620004b657607f821691505b602082108103620004d557634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200052657805f5260205f20601f840160051c81016020851015620005025750805b601f840160051c820191505b8181101562000523575f81556001016200050e565b50505b505050565b81516001600160401b03811115620005475762000547620002a9565b6200055f81620005588454620004a1565b84620004db565b602080601f83116001811462000595575f84156200057d5750858301515b5f19600386901b1c1916600185901b178555620005ef565b5f85815260208120601f198616915b82811015620005c557888601518255948401946001909101908401620005a4565b5085821015620005e357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b808201808211156200061757634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516108a7620006515f395f61022c01525f61016e01525f6101ad01525f61029e01526108a75ff3fe608060405234801561000f575f80fd5b50600436106100f0575f3560e01c806342966c68116100935780639fd4da40116100635780639fd4da4014610227578063a9059cbb1461024e578063dd62ed3e14610261578063e927fc5c14610299575f80fd5b806342966c68146101cf57806370a08231146101e457806379cc67901461020c57806395d89b411461021f575f80fd5b806323b872dd116100ce57806323b872dd14610147578063313ce5671461015a578063353916e114610169578063419dd9a5146101a8575f80fd5b806306fdde03146100f4578063095ea7b31461011257806318160ddd14610135575b5f80fd5b6100fc6102c0565b60405161010991906106ea565b60405180910390f35b610125610120366004610751565b610350565b6040519015158152602001610109565b6002545b604051908152602001610109565b610125610155366004610779565b610369565b60405160128152602001610109565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610109565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6101e26101dd3660046107b2565b61038c565b005b6101396101f23660046107c9565b6001600160a01b03165f9081526020819052604090205490565b6101e261021a366004610751565b610399565b6100fc6103b2565b6101397f000000000000000000000000000000000000000000000000000000000000000081565b61012561025c366004610751565b6103c1565b61013961026f3660046107e9565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101907f000000000000000000000000000000000000000000000000000000000000000081565b6060600380546102cf9061081a565b80601f01602080910402602001604051908101604052809291908181526020018280546102fb9061081a565b80156103465780601f1061031d57610100808354040283529160200191610346565b820191905f5260205f20905b81548152906001019060200180831161032957829003601f168201915b5050505050905090565b5f3361035d8185856103ce565b60019150505b92915050565b5f336103768582856103e0565b610381858585610461565b506001949350505050565b61039633826104be565b50565b6103a48233836103e0565b6103ae82826104be565b5050565b6060600480546102cf9061081a565b5f3361035d818585610461565b6103db83838360016104f2565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f1981101561045b578181101561044d57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61045b84848484035f6104f2565b50505050565b6001600160a01b03831661048a57604051634b637e8f60e11b81525f6004820152602401610444565b6001600160a01b0382166104b35760405163ec442f0560e01b81525f6004820152602401610444565b6103db8383836105c4565b6001600160a01b0382166104e757604051634b637e8f60e11b81525f6004820152602401610444565b6103ae825f836105c4565b6001600160a01b03841661051b5760405163e602df0560e01b81525f6004820152602401610444565b6001600160a01b03831661054457604051634a1406b160e11b81525f6004820152602401610444565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561045b57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516105b691815260200190565b60405180910390a350505050565b6001600160a01b0383166105ee578060025f8282546105e39190610852565b9091555061065e9050565b6001600160a01b0383165f90815260208190526040902054818110156106405760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610444565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661067a57600280548290039055610698565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516106dd91815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b81811015610716578581018301518582016040015282016106fa565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461074c575f80fd5b919050565b5f8060408385031215610762575f80fd5b61076b83610736565b946020939093013593505050565b5f805f6060848603121561078b575f80fd5b61079484610736565b92506107a260208501610736565b9150604084013590509250925092565b5f602082840312156107c2575f80fd5b5035919050565b5f602082840312156107d9575f80fd5b6107e282610736565b9392505050565b5f80604083850312156107fa575f80fd5b61080383610736565b915061081160208401610736565b90509250929050565b600181811c9082168061082e57607f821691505b60208210810361084c57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561036357634e487b7160e01b5f52601160045260245ffdfea264697066735822122001730799c3516b6ea3798778721a8f1832f3c6320f9464ec7dda16aacae654f664736f6c6343000818003361016060405234801562000011575f80fd5b506040516200155338038062001553833981016040819052620000349162000644565b805160208201516003620000498382620007f2565b506004620000588282620007f2565b5050506080818101516001600160a01b0390811661010090815260c08085018051841661012090815260a0808801805187166101405260e0808a018051891690915260608a01516040808c018051909201909752968a015163ffffffff169092529188015161ffff16909652805185165f908152600560209081528482208054600160ff19918216811790925598518816835285832080548a16821790559251871682526006905283812080548816831790559151909416815220805490931690911790915551156200013f576200013f8160a001518260400151620001c660201b60201c565b6060810151156200016457620001648160c001518260600151620001c660201b60201c565b61010051610140516101205160c051604080516001600160a01b03948516815292841660208401528201529116907f8149dd5a43c422d56e6156bb0211a2029f1a9fbf919ddbeba47a86d7655d88659060600160405180910390a25062000938565b6001600160a01b038216620001f55760405163ec442f0560e01b81525f60048201526024015b60405180910390fd5b620002025f838362000206565b5050565b608051811580159062000221575060a05163ffffffff164210155b80156200024657506001600160a01b0384165f9081526005602052604090205460ff16155b80156200026b57506001600160a01b0383165f9081526006602052604090205460ff16155b156200031d575f620002828361ffff841662000330565b90505f620002918285620008d2565b9050620002a0868684620003d8565b620002b58660e05183620003d860201b60201c565b604080516001600160a01b03878116825261ffff861660208301529181018690526060810183905260808101849052908716907fb675d44afccfb8f0f10932b6cf03058fbae5859482eedcd7fa3195d0c02c42669060a00160405180910390a2505050505050565b6200032a848484620003d8565b50505050565b5f6103e88311156200038d576c0c9f2c9cd04674edea40000000620003568385620008e8565b6200036d906a52b7d2dcc80cd2e4000000620008e8565b62000379919062000902565b620003859084620008d2565b9050620003d2565b6064831115620003a45762000385603284620008d2565b600a831115620003bb5762000385600584620008d2565b8215620003cf5762000385600184620008d2565b505f5b92915050565b6001600160a01b03831662000406578060025f828254620003fa919062000922565b90915550620004789050565b6001600160a01b0383165f90815260208190526040902054818110156200045a5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401620001ec565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b0382166200049657600280548290039055620004b4565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620004fa91815260200190565b60405180910390a3505050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b038111828210171562000541576200054162000507565b60405290565b604051601f8201601f191681016001600160401b038111828210171562000572576200057262000507565b604052919050565b5f82601f8301126200058a575f80fd5b81516001600160401b03811115620005a657620005a662000507565b6020620005bc601f8301601f1916820162000547565b8281528582848701011115620005d0575f80fd5b5f5b83811015620005ef578581018301518282018401528201620005d2565b505f928101909101919091529392505050565b80516001600160a01b038116811462000619575f80fd5b919050565b805163ffffffff8116811462000619575f80fd5b805161ffff8116811462000619575f80fd5b5f6020828403121562000655575f80fd5b81516001600160401b03808211156200066c575f80fd5b90830190610140828603121562000681575f80fd5b6200068b6200051b565b8251828111156200069a575f80fd5b620006a8878286016200057a565b825250602083015182811115620006bd575f80fd5b620006cb878286016200057a565b6020830152506040830151604082015260608301516060820152620006f36080840162000602565b60808201526200070660a0840162000602565b60a08201526200071960c0840162000602565b60c08201526200072c60e0840162000602565b60e08201526101009150620007438284016200061e565b8282015261012091506200075982840162000632565b91810191909152949350505050565b600181811c908216806200077d57607f821691505b6020821081036200079c57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620007ed57805f5260205f20601f840160051c81016020851015620007c95750805b601f840160051c820191505b81811015620007ea575f8155600101620007d5565b50505b505050565b81516001600160401b038111156200080e576200080e62000507565b62000826816200081f845462000768565b84620007a2565b602080601f8311600181146200085c575f8415620008445750858301515b5f19600386901b1c1916600185901b178555620008b6565b5f85815260208120601f198616915b828110156200088c578886015182559484019460019091019084016200086b565b5085821015620008aa57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115620003d257620003d2620008be565b8082028115828204841417620003d257620003d2620008be565b5f826200091d57634e487b7160e01b5f52601260045260245ffd5b500490565b80820180821115620003d257620003d2620008be565b60805160a05160c05160e051610100516101205161014051610bb46200099f5f395f61018f01525f6101ce01525f61035c01525f8181610287015261075401525f6102ea01525f81816102ae01526106af01525f818161024501526106840152610bb45ff3fe608060405234801561000f575f80fd5b5060043610610111575f3560e01c806379cc67901161009e5780639e8ac22a1161006e5780639e8ac22a146102a95780639fd4da40146102e5578063a9059cbb1461030c578063dd62ed3e1461031f578063e927fc5c14610357575f80fd5b806379cc67901461022d57806383f170be1461024057806395d89b411461027a5780639ba1483a14610282575f80fd5b8063313ce567116100e4578063313ce5671461017b578063353916e11461018a578063419dd9a5146101c957806342966c68146101f057806370a0823114610205575f80fd5b806306fdde0314610115578063095ea7b31461013357806318160ddd1461015657806323b872dd14610168575b5f80fd5b61011d61037e565b60405161012a91906109a6565b60405180910390f35b610146610141366004610a0d565b61040e565b604051901515815260200161012a565b6002545b60405190815260200161012a565b610146610176366004610a35565b610427565b6040516012815260200161012a565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161012a565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6102036101fe366004610a6e565b61044a565b005b61015a610213366004610a85565b6001600160a01b03165f9081526020819052604090205490565b61020361023b366004610a0d565b610457565b6102677f000000000000000000000000000000000000000000000000000000000000000081565b60405161ffff909116815260200161012a565b61011d610470565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b6102d07f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff909116815260200161012a565b61015a7f000000000000000000000000000000000000000000000000000000000000000081565b61014661031a366004610a0d565b61047f565b61015a61032d366004610aa5565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6101b17f000000000000000000000000000000000000000000000000000000000000000081565b60606003805461038d90610ad6565b80601f01602080910402602001604051908101604052809291908181526020018280546103b990610ad6565b80156104045780601f106103db57610100808354040283529160200191610404565b820191905f5260205f20905b8154815290600101906020018083116103e757829003601f168201915b5050505050905090565b5f3361041b81858561048c565b60019150505b92915050565b5f3361043485828561049e565b61043f85858561051f565b506001949350505050565b610454338261057c565b50565b61046282338361049e565b61046c828261057c565b5050565b60606004805461038d90610ad6565b5f3361041b81858561051f565b61049983838360016105b0565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610519578181101561050b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61051984848484035f6105b0565b50505050565b6001600160a01b03831661054857604051634b637e8f60e11b81525f6004820152602401610502565b6001600160a01b0382166105715760405163ec442f0560e01b81525f6004820152602401610502565b610499838383610682565b6001600160a01b0382166105a557604051634b637e8f60e11b81525f6004820152602401610502565b61046c825f83610682565b6001600160a01b0384166105d95760405163e602df0560e01b81525f6004820152602401610502565b6001600160a01b03831661060257604051634a1406b160e11b81525f6004820152602401610502565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561051957826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161067491815260200190565b60405180910390a350505050565b7f000000000000000000000000000000000000000000000000000000000000000081158015906106d857507f000000000000000000000000000000000000000000000000000000000000000063ffffffff164210155b80156106fc57506001600160a01b0384165f9081526005602052604090205460ff16155b801561072057506001600160a01b0383165f9081526006602052604090205460ff16155b156107e1575f610734838361ffff166107ec565b90505f6107418285610b22565b905061074e868684610880565b610779867f000000000000000000000000000000000000000000000000000000000000000083610880565b604080516001600160a01b03878116825261ffff861660208301529181018690526060810183905260808101849052908716907fb675d44afccfb8f0f10932b6cf03058fbae5859482eedcd7fa3195d0c02c42669060a00160405180910390a2505050505050565b610519848484610880565b5f6103e883111561083f576c0c9f2c9cd04674edea4000000061080f8385610b35565b610824906a52b7d2dcc80cd2e4000000610b35565b61082e9190610b4c565b6108389084610b22565b9050610421565b606483111561085357610838603284610b22565b600a83111561086757610838600584610b22565b821561087857610838600184610b22565b505f92915050565b6001600160a01b0383166108aa578060025f82825461089f9190610b6b565b9091555061091a9050565b6001600160a01b0383165f90815260208190526040902054818110156108fc5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610502565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661093657600280548290039055610954565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161099991815260200190565b60405180910390a3505050565b5f602080835283518060208501525f5b818110156109d2578581018301518582016040015282016109b6565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610a08575f80fd5b919050565b5f8060408385031215610a1e575f80fd5b610a27836109f2565b946020939093013593505050565b5f805f60608486031215610a47575f80fd5b610a50846109f2565b9250610a5e602085016109f2565b9150604084013590509250925092565b5f60208284031215610a7e575f80fd5b5035919050565b5f60208284031215610a95575f80fd5b610a9e826109f2565b9392505050565b5f8060408385031215610ab6575f80fd5b610abf836109f2565b9150610acd602084016109f2565b90509250929050565b600181811c90821680610aea57607f821691505b602082108103610b0857634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561042157610421610b0e565b808202811582820484141761042157610421610b0e565b5f82610b6657634e487b7160e01b5f52601260045260245ffd5b500490565b8082018082111561042157610421610b0e56fea26469706673582212205dc7922f423974ceda3fe19a409b724b9ceba6ab0a09bb17cfe59e189f9babe864736f6c63430008180033a26469706673582212200da3b93799cf0848d6aa08d0cf361fa097daa47f30e2396cae98720535acecdb64736f6c63430008180033