false
true
0

Contract Address Details

0x3e9a5b61d88782dCe7267642417cbAB05E801c25

Contract Name
SWAP_V3
Creator
0x99ab03–301446 at 0x597b72–aa0bbb
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
25891705
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
SWAP_V3




Optimization enabled
true
Compiler version
v0.8.20+commit.a1b79de6




Optimization runs
200
EVM Version
paris




Verified at
2026-02-25T19:08:05.942670Z

Constructor Arguments

0000000000000000000000000f7f24c7f22e2ca7052f051a295e1a5d3369cace

Arg [0] (address) : 0x0f7f24c7f22e2ca7052f051a295e1a5d3369cace

              

src/AuctionSwap.sol

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {TOKEN_V3} from "./Tokens.sol";
import "./libraries/TimeUtilsLib.sol";
import "./libraries/AuctionLib.sol";
import "./libraries/NormalAuctionCalculations.sol";
import "./libraries/ReverseAuctionCalculations.sol";
import { SwapErrors } from "./interfaces/SwapErrors.sol";
import { SwapEvents } from "./interfaces/SwapEvents.sol";
import { IPair } from "./interfaces/IPair.sol";
import "./interfaces/IAuctionAdmin.sol";
import "./libraries/SwapCoreLib.sol";
import "./libraries/BurnLib.sol";
import "./libraries/AuctionUtilsLib.sol";

interface IPulseXFactory {
    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface IPulseXRouter02 {
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
    
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

interface IDAV {
    function transferGovernanceImmediate(address newGovernance) external;
    function governance() external view returns (address);
    function getActiveBalance(address user) external view returns (uint256);
}

interface IAirdropDistributor {
    function consumedDavUnits(address token, address user) external view returns (uint256);
    function getConsumedDavUnitsByCycle(address token, address user, uint256 cycle) external view returns (uint256);
}

/**
 * @title SWAP_V3 - State Protocol Auction System
 * @author State Protocol Team
 * @notice Main contract implementing normal and reverse auctions for token distribution
 * @dev Manages auction system with normal/reverse auctions, DAV-based token claims, and PulseX integration
 * @custom:security Uses Solidity 0.8.20 built-in overflow protection, ReentrancyGuard on external functions
 * @custom:access Governance-controlled with admin delegation pattern
 * @custom:schedule 50 auction tokens rotating daily, 20 cycles per token, 1000-day total period
 * @custom:participants Maximum 2500 unique participants across all auctions
 * @custom:timezone All daily boundaries calculated at GMT+3 17:00 (5 PM)
 */
contract SWAP_V3 is Ownable, ReentrancyGuard, SwapErrors, SwapEvents {
    using SafeERC20 for IERC20;
    using TimeUtilsLib for uint256;
    using AuctionLib for AuctionLib.AuctionCycle;
    using BurnLib for BurnLib.BurnParams;
    using AuctionUtilsLib for AuctionUtilsLib.AuctionSchedule;
    
    error DeadlineExpired();
    error SlippageExceeded();

    // ================= State Variables =================
    
    IDAV public dav;
    
    // Constants
    uint256 constant MIN_DAV_REQUIRED = 1 ether;
    address private constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    uint256 constant MAX_CYCLES_PER_TOKEN = 20; // Each token can run for maximum 20 cycles (20 days per token)
    
    // Core addresses
    address public stateToken;
    address public davToken;
    address public governanceAddress;
    address public airdropDistributor;
    IAuctionAdmin public auctionAdmin;
    address public buyAndBurnController;
    address public swapLens;
    address public pulseXRouter;
    address public pulseXFactory;
    
    // State variables
    uint256 public TotalBurnedStates;
    bool public paused = false;
    uint256 public maxAuctionParticipants = 2500;
    
    // Daily tracking
    uint256 public currentDayStart;
    uint256 public dailyStateReleased;
    uint256 public dailyStateReleasedNormal;
    uint256 public dailyStateReleasedReverse;
    uint256 public dailySwapsCount;
    uint256 public dailyUniqueSwappersCount;
    
    // Auction schedule
    AuctionUtilsLib.AuctionSchedule public auctionSchedule;
    
    // Auto registration
    address[] public autoRegisteredTokens;
    mapping(address => bool) public isAutoRegistered;
    bool public autoScheduleLocked;
    uint256 public totalAuctionsCompleted = 0;
    
    // Unified user registration system
    mapping(address => bool) public isRegisteredForAuctions; // Auto-registered during first participation (max 5000)
    uint256 public totalRegisteredUsers = 0; // Counter for auto-registered users
    mapping(uint256 => uint256) public stateReleasedByDayIndex;
    mapping(uint256 => uint256) public stateReleasedNormalByDayIndex;
    mapping(uint256 => uint256) public stateReleasedReverseByDayIndex;
    mapping(uint256 => uint256) public swapsCountByDayIndex;
    mapping(uint256 => uint256) public uniqueSwappersCountByDayIndex;
    mapping(address => uint256) private lastCountedDayIdxForUser;
    
    mapping(address => string[]) private userToTokenNames;
    mapping(address => mapping(string => address)) private deployedTokensByUser;
    mapping(address => address) private pairAddresses;
    mapping(address => bool) private supportedTokens;
    mapping(address => address) private tokenOwners;
    mapping(address => address[]) private ownerToTokens;
    mapping(string => bool) private isTokenNameUsed;
    uint256 public tokenCount;
    mapping(address => uint256) private tokenNumber;
    mapping(bytes32 => SwapCoreLib.UserSwapInfo) private userSwapTotalInfo;
    
    mapping(address => mapping(address => mapping(uint256 => bool))) private hasUserBurnedTokens;
    mapping(address => mapping(address => mapping(uint256 => uint256))) private userStateBalance;
    mapping(address => mapping(address => mapping(uint256 => uint256))) private tokensBurnedByUser;
    mapping(address => mapping(address => mapping(uint256 => uint256))) private davTokensUsed;
    
    mapping(address => mapping(address => mapping(uint256 => bool))) private hasCompletedReverseStep1;
    mapping(address => mapping(address => mapping(uint256 => uint256))) private reverseStateBalance;
    mapping(address => mapping(address => mapping(uint256 => bool))) private hasCompletedReverseStep2;

    // Cycle-wide STATE tracking for reverse auction limits (per user, per token, per cycle)
    mapping(address => mapping(address => mapping(uint256 => uint256))) private cycleNormalStateEarned;
    
    // NEW: Track tokens received from normal auction Step 3 (pool swap output)
    mapping(address => mapping(address => mapping(uint256 => uint256))) private normalAuctionSwapOutput;

    mapping(address => uint256) private TotalTokensBurned;
    
    // Airdrop constant (must match AirdropDistributor.AIRDROP_PER_DAV)
    uint256 constant AIRDROP_PER_DAV = 10_000 ether; // 10,000 tokens per DAV unit

    // ================= Modifiers =================
    
    modifier onlyGovernance() {
        if (msg.sender != governanceAddress) revert NotGovernance();
        _;
    }
    
    modifier onlyTokenOwnerOrGovernance(address token) {
        if (!(msg.sender == tokenOwners[token] || msg.sender == governanceAddress)) revert Unauthorized();
        _;
    }
    
    modifier whenNotPaused() {
        if (paused) revert PausedErr();
        _;
    }

    // ================= Constructor =================
    
    constructor(address _gov) Ownable(msg.sender) {
        governanceAddress = _gov;
        currentDayStart = _calcCurrentDayStart(block.timestamp);
        auctionSchedule.scheduleSize = 2; // 50 tokens for production
        // Each token gets 20 cycles: 50 tokens × 20 cycles = 1000 total auction days (1000-day cycle)
        auctionSchedule.auctionDaysLimit = auctionSchedule.scheduleSize * 20; // 50 × 20 = 1000 days
        renounceOwnership();
    }

    // ================= Core Auction Functions (Simplified) =================
    
    /**
     * @notice Normal Auction Step 3 - Swap STATE tokens for auction tokens via PulseX pool
     * @dev Users can call multiple times per cycle if they acquire more DAV and burn more tokens
     * @custom:balance userStateBalance tracks accumulated STATE from burns, deducts on each swap
     * @custom:tracking STATE release counted in burnTokensForState (Step 2), not here
     */
    function swapTokens() external nonReentrant whenNotPaused {
        _rollDailyIfNeeded();
        
        // Auto-detect today's token
        (address inputToken, bool active) = getTodayToken();
        if (!active || inputToken == address(0)) revert NoActiveAuction();
        if (isReverseAuctionActive(inputToken)) revert ReverseAuctionActive();
        
        address user = msg.sender;
        
        // Validation
        _validateSwap(user, inputToken);
        
        // Enforce global unique participant cap on first-ever swap
        if (!isRegisteredForAuctions[user]) {
            if (totalRegisteredUsers >= maxAuctionParticipants) revert ParticipantCapReached();
            isRegisteredForAuctions[user] = true;
            totalRegisteredUsers += 1;
            emit UserAutoRegistered(user, block.timestamp);
        }
        
        uint256 currentAuctionCycle = getCurrentAuctionCycle(inputToken);
        bytes32 key = SwapCoreLib.getSwapInfoKey(user, inputToken, stateToken, currentAuctionCycle);
        SwapCoreLib.UserSwapInfo storage userSwapInfo = userSwapTotalInfo[key];
        userSwapInfo.cycle = currentAuctionCycle;

        bool isReverseActive = isReverseAuctionActive(inputToken);
        if (isReverseActive) revert ReverseDayLPOonly();
        
        if (!auctionSchedule.isAuctionActive(inputToken, block.timestamp)) 
            revert NotStarted();
        
        // Allow multiple swaps per cycle if user has STATE balance
        uint256 userStateFromBurn = userStateBalance[user][inputToken][currentAuctionCycle];
        if (userStateFromBurn == 0) revert Step2NotCompleted();
        
        // Check if user has approved this contract to spend their STATE tokens
        uint256 stateAllowance = IERC20(stateToken).allowance(user, address(this));
        if (stateAllowance < userStateFromBurn) revert InsufficientAllowance();
        
        // STEP 3: Execute actual swap through PulseX pool (not from contract balance)
        uint256 amountOut = _executePoolSwap(user, inputToken, userStateFromBurn);
        if (amountOut == 0) revert AmountZero();
        
        // Track tokens received from pool swap - cumulative for multiple swaps
        normalAuctionSwapOutput[user][inputToken][currentAuctionCycle] += amountOut;
        
        // Mark swap as completed and deduct used STATE balance
        userSwapInfo.hasSwapped = true;
        userStateBalance[user][inputToken][currentAuctionCycle] -= userStateFromBurn;
        
        // Update daily tracking
        dailySwapsCount += 1;
        uint256 todayIdx = currentDayStart / 1 days;
        if (lastCountedDayIdxForUser[user] != todayIdx) {
            lastCountedDayIdxForUser[user] = todayIdx;
            dailyUniqueSwappersCount += 1;
        }
        // This would be used in reverse auctions where STATE might be the output
        // For now, keeping consistent with original logic - no tracking for normal auctions
        
        emit TokensSwapped(user, stateToken, inputToken, userStateFromBurn, amountOut);
    }

    /**
     * @notice Normal Auction Step 2 - Burn auction tokens to receive STATE tokens
     * @dev User must have completed Step 1 (claim airdrop via DAV). Multiple burns allowed per cycle.
    * @custom:burn User burns 25% of airdrop tokens (e.g., 2500 from 10K airdrop per 1 DAV)
     * @custom:reward Receives 2x STATE tokens based on pool ratio
     * @custom:fee 0.5% auction fee deducted from STATE output
     * @custom:requirement Minimum 1 DAV balance, Step 1 completed, sufficient auction token balance
     */
    function burnTokensForState() external nonReentrant whenNotPaused {
        _rollDailyIfNeeded();
        
        // Auto-detect today's token
        (address auctionToken, bool active) = getTodayToken();
        if (!active || auctionToken == address(0)) revert NoActiveAuction();
        if (isReverseAuctionActive(auctionToken)) revert ReverseAuctionActive();
        
        // User should already be registered from Step 1 (AirdropDistributor.claim)
        
        if (!supportedTokens[auctionToken]) revert UnsupportedToken();
        if (stateToken == address(0)) revert StateNotSet();
        
        // STEP 1 VALIDATION
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        if (!_hasCompletedStep1(msg.sender, auctionToken)) revert Step1NotCompleted();
        
        uint256 totalDavBalance = getDavBalance(msg.sender);
        if (totalDavBalance < MIN_DAV_REQUIRED) revert DavInsufficient();
        
        // Allow multiple burns per cycle if user mints more DAV
        uint256 davAlreadyUsed = davTokensUsed[msg.sender][auctionToken][currentCycle];
        uint256 availableDav = totalDavBalance > davAlreadyUsed ? totalDavBalance - davAlreadyUsed : 0;
        
        if (availableDav < MIN_DAV_REQUIRED) revert DavInsufficient();
        
        uint256 tokensToBurn = NormalAuctionCalculations.calculateTokensToBurn(availableDav);
        if (tokensToBurn == 0) revert AmountZero();
        
        uint256 userBalance = IERC20(auctionToken).balanceOf(msg.sender);
        if (userBalance < tokensToBurn) revert InsufficientBalance();
        
        // Check if user has approved this contract to spend their auction tokens
        uint256 allowance = IERC20(auctionToken).allowance(msg.sender, address(this));
        if (allowance < tokensToBurn) revert InsufficientAllowance();
        
        uint256 poolRatio = getRatioPrice(auctionToken);
        if (poolRatio == 0) revert InvalidParam();
        
        uint256 stateToGive = NormalAuctionCalculations.calculateStateToGive(tokensToBurn, poolRatio);
        if (stateToGive == 0) revert AmountZero();
        
        // Deduct 0.5% auction fee from STATE output
        uint256 auctionFee = (stateToGive * 50) / 10000; // 0.5% = 50/10000
        stateToGive -= auctionFee;
        
        if (IERC20(stateToken).balanceOf(address(this)) < (stateToGive + auctionFee)) revert InsufficientVault();
        
        // Use library for burn execution
        BurnLib.BurnParams memory burnParams = BurnLib.BurnParams({
            user: msg.sender,
            auctionToken: auctionToken,
            stateToken: stateToken,
            currentCycle: currentCycle,
            tokensToBurn: tokensToBurn,
            stateToGive: stateToGive,
            availableDav: availableDav
        });
        
        burnParams.executeTokenBurn(
            hasUserBurnedTokens,
            userStateBalance,
            tokensBurnedByUser,
            davTokensUsed,
            TotalTokensBurned
        );
        
        // Distribute auction fee to development wallets
        _distributeAuctionFee(stateToken, auctionFee);
        emit AuctionFeeCollected(stateToken, auctionFee, msg.sender);
        
        // Track cycle-wide STATE earned from normal auctions (per token)
        cycleNormalStateEarned[msg.sender][auctionToken][currentCycle] += stateToGive;
        
        emit TokensSwapped(msg.sender, auctionToken, stateToken, tokensToBurn, stateToGive);
    }

    /**
     * @notice Reverse Auction Step 1 - Swap auction tokens for STATE tokens
     * @param tokenAmount Amount of auction tokens to swap (auto-adjusted to maximum if exceeds limit)
     * @dev Only tokens earned from previous 3 normal auction cycles can be swapped
     * @custom:requirement Minimum 1 DAV balance, participated in previous normal auctions
     * @custom:lookback Calculates net tokens from previous 3 cycles as maximum swap limit
     */
    function reverseSwapTokensForState(uint256 tokenAmount) external nonReentrant whenNotPaused {
        _rollDailyIfNeeded();
        
        // Auto-detect today's token
        (address auctionToken, bool active) = getTodayToken();
        if (!active || auctionToken == address(0)) revert NoActiveAuction();
        if (!isReverseAuctionActive(auctionToken)) revert NormalAuctionActive();
        
        // Auto-register user if not already registered
        _autoRegisterUser(msg.sender);
        
        if (!supportedTokens[auctionToken]) revert UnsupportedToken();
        if (stateToken == address(0)) revert StateNotSet();
        
        if (!isReverseAuctionActive(auctionToken)) revert NotStarted();
        
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        
        // Check if token has completed maximum cycles
        if (currentCycle > MAX_CYCLES_PER_TOKEN) revert AuctionCyclesCompleted();
        
        if (hasCompletedReverseStep1[msg.sender][auctionToken][currentCycle]) revert AlreadySwapped();
        
        uint256 davBalance = getDavBalance(msg.sender);
        if (davBalance < MIN_DAV_REQUIRED) revert DavInsufficient();
        
        // NEW: Calculate maximum allowed tokens from previous 3 normal auction cycles
        uint256 maxAllowedTokens = 0;
        uint256 lookbackCount = 3;
        for (uint256 i = 1; i <= lookbackCount && currentCycle > i; i++) {
            uint256 netTokens = calculateNetTokensFromNormalAuction(
                msg.sender,
                auctionToken,
                currentCycle - i
            );
            maxAllowedTokens += netTokens;
        }
        
        // User must have participated in at least one previous normal auction
        if (maxAllowedTokens == 0) revert NoNormalAuctionParticipation();
        
        // NEW: Enforce token limit - user can only swap tokens they earned from auctions
        // Even if user bought extra tokens from market, they can only use auction-earned tokens
        if (tokenAmount > maxAllowedTokens) {
            // Auto-adjust to maximum allowed instead of reverting
            tokenAmount = maxAllowedTokens;
        }
        
        if (tokenAmount == 0) revert AmountZero();
        
        uint256 userBalance = IERC20(auctionToken).balanceOf(msg.sender);
        if (userBalance < tokenAmount) revert InsufficientBalance();
        
        uint256 stateOutput = calculatePoolSwapOutputReverse(auctionToken, tokenAmount);
        if (stateOutput == 0) revert AmountZero();
        
        if (IERC20(stateToken).balanceOf(address(this)) < stateOutput) revert InsufficientVault();
        
        hasCompletedReverseStep1[msg.sender][auctionToken][currentCycle] = true;
        reverseStateBalance[msg.sender][auctionToken][currentCycle] = stateOutput;
        
        IERC20(auctionToken).safeTransferFrom(msg.sender, address(this), tokenAmount);
        IERC20(stateToken).safeTransfer(msg.sender, stateOutput);
        
        dailyStateReleased += stateOutput;
        dailyStateReleasedReverse += stateOutput;
        
        emit TokensSwapped(msg.sender, auctionToken, stateToken, tokenAmount, stateOutput);
    }

    /**
     * @notice Reverse Auction Step 2 - Burn STATE tokens to receive auction tokens
     * @param stateToBurn Amount of STATE to burn (parameter ignored, uses exact balance from Step 1)
     * @dev User must burn exact STATE amount received in Step 1, cannot choose custom amount
     * @custom:burn User burns STATE from Step 1 to receive auction tokens
     * @custom:reward Receives auction tokens based on pool ratio (inverse of normal auction)
     * @custom:fee 0.5% auction fee deducted from token output
     * @custom:requirement Step 1 completed, sufficient STATE balance to burn
     */
    function burnStateForTokens(uint256 stateToBurn) external nonReentrant whenNotPaused {
        _rollDailyIfNeeded();
        
        // Auto-detect today's token
        (address auctionToken, bool active) = getTodayToken();
        if (!active || auctionToken == address(0)) revert NoActiveAuction();
        if (!isReverseAuctionActive(auctionToken)) revert NormalAuctionActive();
        
        if (!supportedTokens[auctionToken]) revert UnsupportedToken();
        if (stateToken == address(0)) revert StateNotSet();
        
        if (!isReverseAuctionActive(auctionToken)) revert NotStarted();
        
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        
        if (!hasCompletedReverseStep1[msg.sender][auctionToken][currentCycle]) revert Step1NotCompleted();
        if (hasCompletedReverseStep2[msg.sender][auctionToken][currentCycle]) revert AlreadySwapped();
        
        // NEW: IGNORE user input - force burning EXACTLY the STATE received from reverse Step 1
        // User must burn all STATE they got from swapping their auction tokens
        stateToBurn = reverseStateBalance[msg.sender][auctionToken][currentCycle];
        if (stateToBurn == 0) revert AmountZero();
        
        // User must hold sufficient STATE balance to complete burn
        uint256 userCurrentStateBalance = IERC20(stateToken).balanceOf(msg.sender);
        if (userCurrentStateBalance < stateToBurn) revert InsufficientBalance();
        
        uint256 poolRatio = getRatioPrice(auctionToken);
        if (poolRatio == 0) revert InvalidParam();
        
        uint256 tokensToGive = ReverseAuctionCalculations.calculateTokensToGive(stateToBurn, poolRatio);
        if (tokensToGive == 0) revert AmountZero();
        
        // Deduct 0.5% auction fee from token output
        uint256 auctionFee = (tokensToGive * 50) / 10000; // 0.5% = 50/10000
        tokensToGive -= auctionFee;
        
        if (IERC20(auctionToken).balanceOf(address(this)) < (tokensToGive + auctionFee)) revert InsufficientVault();
        
        // Use library for reverse burn execution
        BurnLib.ReverseBurnParams memory reverseParams = BurnLib.ReverseBurnParams({
            user: msg.sender,
            auctionToken: auctionToken,
            stateToken: stateToken,
            currentCycle: currentCycle,
            stateToBurn: stateToBurn,
            tokensToGive: tokensToGive
        });
        
        BurnLib.executeReverseBurn(
            reverseParams,
            hasCompletedReverseStep2,
            reverseStateBalance,
            TotalTokensBurned
        );
        
        // Distribute auction fee to development wallets
        _distributeAuctionFee(auctionToken, auctionFee);
        emit AuctionFeeCollected(auctionToken, auctionFee, msg.sender);
        
        emit TokensSwapped(msg.sender, stateToken, auctionToken, stateToBurn, tokensToGive);
    }

    // ================= Internal Functions =================
    
    /**
     * @notice Validate swap eligibility and requirements
     * @param user User address performing swap
     * @param inputToken Token being swapped
     * @dev Checks DAV balance, auction status, schedule, and participant cap
     */
    function _validateSwap(address user, address inputToken) internal view {
        if (msg.sender != user) revert Unauthorized();
        if (user == address(0)) revert ZeroAddr();
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        if (stateToken == address(0)) revert StateNotSet();
        if (getDavBalance(user) < MIN_DAV_REQUIRED) revert DavInsufficient();
        if (!auctionSchedule.scheduleSet) revert ScheduleNotSet();
        if (block.timestamp < auctionSchedule.scheduleStart) revert NotStarted();
        
        (address todayToken, bool activeWindow) = auctionSchedule.getTodayToken(block.timestamp);
        if (!(activeWindow && todayToken == inputToken)) revert NotToday();

        if (!isRegisteredForAuctions[user]) {
            if (totalRegisteredUsers >= maxAuctionParticipants) revert ParticipantCapReached();
        }
    }

    /**
     * @notice Check if user has completed Step 1 (airdrop claim) for current auction cycle
     * @dev CRITICAL: DAV tokens are mint-only (non-transferable)
     *      User's DAV balance can ONLY increase between claims
     *      Must ensure consumed >= current balance to prevent exploitation
     * @param user User address to check
     * @param token Auction token address
     * @return true if user has claimed airdrops for ALL their current DAV balance
     */
    function _hasCompletedStep1(address user, address token) internal view returns (bool) {
        if (airdropDistributor == address(0)) return false;
        
        // Get current auction cycle
        uint256 currentCycle = getCurrentAuctionCycle(token);
        
        // Get consumed DAV units from airdrop claims
        uint256 consumed = IAirdropDistributor(airdropDistributor).getConsumedDavUnitsByCycle(token, user, currentCycle);
        
        // Get current ACTIVE DAV balance (respects expiration)
        uint256 activeDavBalance = getDavBalance(user);
        uint256 davUnits = activeDavBalance / 1e18;
        
        // CRITICAL FIX: Enforce consumed >= davUnits
        // This prevents users from minting new DAV without claiming
        // Since DAV is mint-only, balance never decreases (until expiration)
        // consumed >= davUnits ensures ALL minted DAV has corresponding airdrop claims
        return consumed >= davUnits;
    }

    /**
     * @notice Get user's active DAV balance
     * @param user User address to check
     * @return User's active DAV balance
     * @dev Validates DAV contract is initialized before querying balance
     */
    function getDavBalance(address user) internal view returns (uint256) {
        if (address(dav).code.length == 0) revert InvalidParam();
        return dav.getActiveBalance(user);
    }

    /**
     * @notice Set exact token allowance for spender
     * @param token Token to approve
     * @param spender Address receiving allowance
     * @param amount Exact allowance amount to set
     * @dev Safely adjusts allowance up or down to reach exact target amount
     */
    function _setExactAllowance(address token, address spender, uint256 amount) internal {
        uint256 current = IERC20(token).allowance(address(this), spender);
        if (current == amount) return;
        if (current > amount) {
            uint256 delta = current - amount;
            IERC20(token).safeDecreaseAllowance(spender, delta);
        } else {
            uint256 delta = amount - current;
            IERC20(token).safeIncreaseAllowance(spender, delta);
        }
    }

    /**
     * @notice Roll over daily tracking if 24 hours passed since last roll
     * @dev Saves current day metrics and resets counters at GMT+3 17:00 boundary
     */
    function _rollDailyIfNeeded() internal {
        if (block.timestamp >= currentDayStart + 1 days) {
            uint256 dayIndex = currentDayStart / 1 days;
            stateReleasedByDayIndex[dayIndex] = dailyStateReleased;
            stateReleasedNormalByDayIndex[dayIndex] = dailyStateReleasedNormal;
            stateReleasedReverseByDayIndex[dayIndex] = dailyStateReleasedReverse;
            swapsCountByDayIndex[dayIndex] = dailySwapsCount;
            uniqueSwappersCountByDayIndex[dayIndex] = dailyUniqueSwappersCount;
            emit DailyStateReleaseRolled(dayIndex, dailyStateReleased, currentDayStart + 1 days);
            currentDayStart = _calcCurrentDayStart(block.timestamp);
            dailyStateReleased = 0;
            dailyStateReleasedNormal = 0;
            dailyStateReleasedReverse = 0;
            dailySwapsCount = 0;
            dailyUniqueSwappersCount = 0;
        }
    }

    /**
     * @notice Calculate current day start timestamp
     * @param ts Reference timestamp
     * @return Day start timestamp aligned to GMT+3 17:00 boundary
     */
    function _calcCurrentDayStart(uint256 ts) internal pure returns (uint256) {
        uint256 next = TimeUtilsLib.calculateNextClaimStartGMTPlus3(ts);
        return next - 1 days;
    }

    /**
     * @notice Auto-register user for auction participation
     * @param user User address to register
     * @dev First-come-first-served registration up to maxAuctionParticipants limit (2500)
     */
    function _autoRegisterUser(address user) internal {
        if (!isRegisteredForAuctions[user]) {
            if (totalRegisteredUsers >= maxAuctionParticipants) {
                revert ParticipantCapReached();
            }
            isRegisteredForAuctions[user] = true;
            totalRegisteredUsers += 1;
            emit UserAutoRegistered(user, block.timestamp);
        }
    }

    /**
     * @notice Public function to register user for auctions (called by AirdropDistributor)
     * @param user Address to register
     * @dev Only callable by AirdropDistributor contract or internal delegation
     */
    function registerUserForAuctions(address user) external {
        // Only allow specific contracts to register users
        if (msg.sender != airdropDistributor && msg.sender != address(this)) revert UnauthorizedRegistration();
        _autoRegisterUser(user);
    }

    // ================= View Functions =================
    
    function isReverseAuctionActive(address inputToken) public view returns (bool) {
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        return auctionSchedule.isReverseAuctionActive(inputToken, block.timestamp);
    }

    function isAuctionActive(address inputToken) public view returns (bool) {
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        return auctionSchedule.isAuctionActive(inputToken, block.timestamp);
    }

    function getCurrentAuctionCycle(address inputToken) public view returns (uint256) {
        return auctionSchedule.getCurrentAuctionCycle(inputToken, block.timestamp);
    }

    function getAuctionTimeLeft(address inputToken) public view returns (uint256) {
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        return auctionSchedule.getAuctionTimeLeft(inputToken, block.timestamp);
    }

    function getTodayToken() public view returns (address tokenOfDay, bool active) {
        return auctionSchedule.getTodayToken(block.timestamp);
    }

    function getUserHasSwapped(address user, address inputToken) public view returns (bool) {
        uint256 getCycle = getCurrentAuctionCycle(inputToken);
        bytes32 key = SwapCoreLib.getSwapInfoKey(user, inputToken, stateToken, getCycle);
        return userSwapTotalInfo[key].hasSwapped;
    }

    function getUserHasReverseSwapped(address user, address inputToken) public view returns (bool) {
        uint256 getCycle = getCurrentAuctionCycle(inputToken);
        bytes32 key = SwapCoreLib.getSwapInfoKey(user, inputToken, stateToken, getCycle);
        return userSwapTotalInfo[key].hasReverseSwap;
    }

    function getUserAuctionStatus(address user, address auctionToken) 
        external view returns (bool step1, bool step2, bool step3, uint256 stateBalance) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        
        step1 = _hasCompletedStep1(user, auctionToken);
        step2 = hasUserBurnedTokens[user][auctionToken][currentCycle];
        
        bytes32 key = SwapCoreLib.getSwapInfoKey(user, auctionToken, stateToken, currentCycle);
        SwapCoreLib.UserSwapInfo storage userSwapInfo = userSwapTotalInfo[key];
        step3 = userSwapInfo.hasSwapped;
        
        stateBalance = userStateBalance[user][auctionToken][currentCycle];
    }

    function hasUserBurnedForToken(address user, address auctionToken) external view returns (bool) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return hasUserBurnedTokens[user][auctionToken][currentCycle];
    }

    function getUserStateBalance(address user, address auctionToken) external view returns (uint256) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return userStateBalance[user][auctionToken][currentCycle];
    }

    function getTokensBurnedByUser(address user, address auctionToken) external view returns (uint256) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return tokensBurnedByUser[user][auctionToken][currentCycle];
    }

    function hasCompletedStep1(address user, address auctionToken) external view returns (bool) {
        return _hasCompletedStep1(user, auctionToken);
    }

    function hasCompletedStep2(address user, address auctionToken) external view returns (bool) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return hasUserBurnedTokens[user][auctionToken][currentCycle];
    }

    function hasCompletedStep3(address user, address auctionToken) external view returns (bool) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        bytes32 key = SwapCoreLib.getSwapInfoKey(user, auctionToken, stateToken, currentCycle);
        SwapCoreLib.UserSwapInfo storage userSwapInfo = userSwapTotalInfo[key];
        return userSwapInfo.hasSwapped;
    }

    function getDavTokensUsed(address user, address auctionToken) external view returns (uint256) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return davTokensUsed[user][auctionToken][currentCycle];
    }

    /**
     * @notice Get available DAV balance for auction participation
     * @param user User address
     * @param auctionToken Token being auctioned
     * @return Available DAV amount (total - used)
     * @dev Calculates remaining DAV balance after accounting for usage in current cycle
     */
    function getAvailableDavForAuction(address user, address auctionToken) external view returns (uint256) {
        uint256 totalDav = getDavBalance(user);
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        uint256 usedDav = davTokensUsed[user][auctionToken][currentCycle];
        
        return totalDav > usedDav ? totalDav - usedDav : 0;
    }

    function canParticipateInAuction(address user, address auctionToken) external view returns (bool) {
        uint256 availableDav = this.getAvailableDavForAuction(user, auctionToken);
        return availableDav >= MIN_DAV_REQUIRED;
    }

    // ================= Reverse Auction View Functions =================
    
    /**
     * @notice Calculate net tokens user received from a specific normal auction cycle
     * @param user User address
     * @param token Token address
     * @param cycle Auction cycle number
     * @return netTokens Net tokens = airdrop + swapOutput - burned
     * @dev DAV tokens are always whole units (1, 2, 3 DAV), matching AirdropDistributor logic
     * @custom:formula netTokens = (davUsed/1e18 * 10000) + swapOutput - tokensBurned
     */
    function calculateNetTokensFromNormalAuction(
        address user,
        address token,
        uint256 cycle
    ) public view returns (uint256 netTokens) {
        // Get DAV used in Step 2 (always whole units: 1 DAV, 2 DAV, etc.)
        uint256 davUsed = davTokensUsed[user][token][cycle];
        
        // Calculate airdrop from Step 1 (whole DAV units)
        // No precision loss: DAV tokens are minted in whole units only
        uint256 davWholeUnits = davUsed / 1e18; // Always exact whole number
        uint256 airdropAmount = davWholeUnits * AIRDROP_PER_DAV; // 10,000 tokens per DAV
        
        // Get tokens burned in Step 2 (already tracked)
        uint256 tokensBurned = tokensBurnedByUser[user][token][cycle];
        
        // Get tokens received from pool swap in Step 3 (newly tracked)
        uint256 swapOutput = normalAuctionSwapOutput[user][token][cycle];
        
        // Calculate net tokens: airdrop + swap - burned
        netTokens = airdropAmount + swapOutput - tokensBurned;
        
        return netTokens;
    }
    
    function hasUserCompletedReverseStep1(address user, address auctionToken) external view returns (bool) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return hasCompletedReverseStep1[user][auctionToken][currentCycle];
    }

    function hasUserCompletedReverseStep2(address user, address auctionToken) external view returns (bool) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return hasCompletedReverseStep2[user][auctionToken][currentCycle];
    }

    function getReverseStateBalance(address user, address auctionToken) external view returns (uint256) {
        uint256 currentCycle = getCurrentAuctionCycle(auctionToken);
        return reverseStateBalance[user][auctionToken][currentCycle];
    }

    function calculateReverseStep2Output(address auctionToken, uint256 stateToBurn) external view returns (uint256) {
        uint256 poolRatio = getRatioPrice(auctionToken);
        if (poolRatio == 0) return 0;
        return ReverseAuctionCalculations.calculateTokensToGive(stateToBurn, poolRatio);
    }

    function isTokenSupported(address token) external view returns (bool) {
        return supportedTokens[token];
    }

    function getCurrentDayIndex() external view returns (uint256) {
        return AuctionUtilsLib._currentDayIndex(auctionSchedule, block.timestamp);
    }

    function getPairAddress(address token) external view returns (address) {
        return pairAddresses[token];
    }

    function getTotalTokensBurned(address token) external view returns (uint256) {
        return TotalTokensBurned[token];
    }

    function isUserEligible(address user) external view returns (bool) {
        return isRegisteredForAuctions[user];
    }

    function getEligibilityInfo() external view returns (uint256 totalEligible, uint256 maxAllowed, uint256 remaining) {
        totalEligible = totalRegisteredUsers;
        maxAllowed = maxAuctionParticipants;
        remaining = maxAllowed > totalEligible ? maxAllowed - totalEligible : 0;
    }

    /**
     * @notice Get auction schedule information as tuple for external contracts
     * @return scheduleSet Whether auction schedule has been initialized
     * @return scheduleStart Unix timestamp when first auction started
     * @return registeredTokenCount Number of tokens currently registered in schedule
     * @return scheduleSize Number of tokens in rotation (50)
     * @return auctionDaysLimit Total days in schedule (1000)
     */
    function getAuctionScheduleInfo() external view returns (
        bool scheduleSet,
        uint256 scheduleStart,
        uint256 registeredTokenCount,
        uint256 scheduleSize,
        uint256 auctionDaysLimit
    ) {
        return (
            auctionSchedule.scheduleSet,
            auctionSchedule.scheduleStart,
            auctionSchedule.tokenCount,
            auctionSchedule.scheduleSize,
            auctionSchedule.auctionDaysLimit
        );
    }
    
    /**
     * @notice Get auction statistics for a specific token
     * @param token Address of the token to query
     * @return completedAuctions Number of auctions completed for this token (cycle number)
     * @return maxAuctions Maximum number of auctions allowed per token (20 cycles)
     * @return remainingAuctions Number of auctions remaining for this token
     * @return isActive Whether the token is currently in an active auction
     * @return isScheduled Whether the token is scheduled in the auction rotation
     */
    function getTokenAuctionStats(address token) external view returns (
        uint256 completedAuctions,
        uint256 maxAuctions,
        uint256 remainingAuctions,
        bool isActive,
        bool isScheduled
    ) {
        // Get current cycle (this is the number of completed auctions)
        completedAuctions = getCurrentAuctionCycle(token);
        
        // Max auctions per token is 20
        maxAuctions = 20;
        
        // Calculate remaining auctions
        remainingAuctions = completedAuctions >= maxAuctions ? 0 : maxAuctions - completedAuctions;
        
        // Check if currently active
        isActive = isAuctionActive(token);
        
        // Check if scheduled (has a scheduledIndex > 0)
        isScheduled = auctionSchedule.scheduledIndex[token] > 0;
    }
    
    /**
     * @notice Get global auction progress
     * @return totalSlots Total number of auction slots that can be completed (50 tokens × 20 cycles = 1000 days)
     * @return completedSlots Number of auction slots completed so far
     * @return remainingSlots Number of auction slots remaining
     * @return auctionsEnded Whether all auctions have been completed
     */
    function getGlobalAuctionProgress() external view returns (
        uint256 totalSlots,
        uint256 completedSlots,
        uint256 remainingSlots,
        bool auctionsEnded
    ) {
        totalSlots = auctionSchedule.auctionDaysLimit; // scheduleSize × 20
        
        // Calculate completed slots
        if (!auctionSchedule.scheduleSet || block.timestamp < auctionSchedule.scheduleStart) {
            completedSlots = 0;
        } else {
            uint256 timeSinceStart = block.timestamp - auctionSchedule.scheduleStart;
            uint256 slotDuration = AuctionLib.AUCTION_DURATION + AuctionLib.AUCTION_INTERVAL;
            completedSlots = timeSinceStart / slotDuration;
        }
        
        // Calculate remaining slots
        remainingSlots = completedSlots >= totalSlots ? 0 : totalSlots - completedSlots;
        
        // Check if auctions have ended
        auctionsEnded = completedSlots >= totalSlots;
    }

    // ================= Admin Functions (Delegated) =================
    
    /**
     * @notice One-click token deployment - only requires name and symbol
     * @param name Token name
     * @param symbol Token symbol
     * @return tokenAddress Address of the deployed token
     * @dev User only needs to provide name and symbol, function handles the rest
     * @dev Can only deploy up to scheduleSize (50) tokens
     */
    function deployTokenOneClick(
        string memory name,
        string memory symbol
    ) external onlyGovernance returns (address tokenAddress) {
        // Check if token limit reached
        if (autoRegisteredTokens.length >= auctionSchedule.scheduleSize) revert TokenDeploymentLimitReached();
        
        if (address(auctionAdmin) != address(0)) {
            tokenAddress = auctionAdmin.deployTokenOneClick(address(this), name, symbol);
            
            // AUTOMATIC ALLOWANCE SETUP: Set up allowances for newly deployed token
            _setupAutomaticTokenAllowances(tokenAddress);
            
            return tokenAddress;
        } else {
            // Fallback implementation for when admin is not set
            revert("Admin contract required");
        }
    }

    /**
     * @notice Automatically set up allowances for a newly deployed token
     * @param tokenAddress The newly deployed token address
     * @dev Called internally after token deployment to set up required allowances
     */
    function _setupAutomaticTokenAllowances(address tokenAddress) internal {
        // Set vault allowance for airdrop distributor to access the new token
        if (airdropDistributor != address(0)) {
            _setExactAllowance(tokenAddress, airdropDistributor, type(uint256).max);
        }
        
        // Set allowance for PulseX router for automatic pool creation
        if (pulseXRouter != address(0)) {
            _setExactAllowance(tokenAddress, pulseXRouter, type(uint256).max);
        }
    }

    /**
     * @notice One-click pool creation with PERMANENT liquidity (LP tokens burned)
     * @dev Creates STATE/token pool and burns LP tokens making it unruggable
     * @param token Token to pair with STATE
     * @param tokenAmount Amount of token for initial liquidity
     * @param stateAmount Amount of STATE for initial liquidity
     * @return pair Address of the created pair
     * @custom:access Governance-only function, requires proper token validation
     */
    function createPoolOneClick(
        address token,
        uint256 tokenAmount,
        uint256 stateAmount
    ) external onlyGovernance returns (address pair) {
        if (token == address(0)) revert InvalidToken();
        if (tokenAmount == 0 || stateAmount == 0) revert InvalidAmounts();
        if (stateToken == address(0)) revert StateNotSet();
        if (pulseXRouter == address(0)) revert RouterNotSet();
        if (pulseXFactory == address(0)) revert FactoryNotSet();
        
        // Check balances
        if (IERC20(stateToken).balanceOf(address(this)) < stateAmount) revert InsufficientSTATE();
        if (IERC20(token).balanceOf(address(this)) < tokenAmount) revert InsufficientToken();
        
        // Get or create the pair directly through PulseX
        IPulseXFactory factory = IPulseXFactory(pulseXFactory);
        pair = factory.getPair(token, stateToken);
        if (pair == address(0)) {
            pair = factory.createPair(token, stateToken);
        }
        
        // Approve tokens for PulseX router
        IERC20(stateToken).approve(pulseXRouter, stateAmount);
        IERC20(token).approve(pulseXRouter, tokenAmount);
        
        // Add liquidity directly through PulseX router
        (uint256 amountTokenUsed, uint256 amountStateUsed, uint256 liquidity) = IPulseXRouter02(pulseXRouter).addLiquidity(
            token,
            stateToken,
            tokenAmount,
            stateAmount,
            tokenAmount * 95 / 100, // 5% slippage tolerance
            stateAmount * 95 / 100, // 5% slippage tolerance
            address(this), // LP tokens go to this contract first
            block.timestamp + 3600  // 1 hour deadline
        );
        
        // BURN LP TOKENS PERMANENTLY - makes pool unruggable
        IERC20(pair).transfer(BURN_ADDRESS, liquidity);
        emit LPTokensBurned(pair, liquidity, BURN_ADDRESS);
        
        // Register the token as supported and set pair address
        supportedTokens[token] = true;
        pairAddresses[token] = pair;
        
        // Register token in autoRegisteredTokens if not already registered
        // This ensures tokens show up in the frontend list
        if (!isAutoRegistered[token]) {
            // Get token name for registration (fallback to symbol if needed)
            string memory tokenName;
            try IERC20Metadata(token).name() returns (string memory name) {
                tokenName = name;
            } catch {
                try IERC20Metadata(token).symbol() returns (string memory symbol) {
                    tokenName = symbol;
                } catch {
                    tokenName = "Unknown Token";
                }
            }
            
            // Add to auto-registered list
            autoRegisteredTokens.push(token);
            isAutoRegistered[token] = true;
            tokenOwners[token] = governanceAddress;
            ownerToTokens[governanceAddress].push(token);
            userToTokenNames[governanceAddress].push(tokenName);
            tokenCount++;
            
            if (autoRegisteredTokens.length == auctionSchedule.scheduleSize) {
                autoScheduleLocked = true;
            }
        }
        
        // Emit events with actual amounts used and note that LP tokens are burned
        emit PoolCreated(token, pair, amountTokenUsed, amountStateUsed);
        return pair;
    }

    /**
     * @notice Start auction using auto-registered tokens from pool creation
     * @dev Automatically calculates next GMT+3 17:00 (5 PM) as start time
     * @dev Uses tokens that were automatically registered during createPoolOneClick calls
     * @dev This is the ONLY function needed to start auctions - just click "Start Auction"
     */
    function startAuctionWithAutoTokens() external onlyGovernance {
        if (!autoScheduleLocked) revert TokensNotRegistered();
        if (autoRegisteredTokens.length == 0) revert NoAutoTokens();
        if (auctionSchedule.scheduleSet) revert ScheduleAlreadySet();
        
        // Automatically calculate next GMT+3 17:00 (5 PM) time
        uint256 startAt = TimeUtilsLib.calculateNextClaimStartGMTPlus3(block.timestamp);
        
        // Use auto-registered tokens for auction schedule
        AuctionUtilsLib.setAuctionSchedule(
            auctionSchedule, 
            autoRegisteredTokens, 
            startAt, 
            supportedTokens
        );
        
        emit AuctionScheduleSet(startAt, autoRegisteredTokens.length);
    }

    /**
     * @notice Approves all currently supported tokens for the airdrop distributor
     * @dev This is essential for Step 1 (airdrop) to work in mainnet environment
     */
    function _approveAllTokensForAirdrop(address _airdropDistributor) internal {
        // Approve all tokens in the auction schedule using mapping
        for (uint256 i = 0; i < auctionSchedule.tokenCount; i++) {
            address token = auctionSchedule.tokenByIndex[i];
            if (token != address(0)) {
                // Reset approval to 0 first (for tokens that require it)
                IERC20(token).approve(_airdropDistributor, 0);
                // Set maximum approval
                IERC20(token).approve(_airdropDistributor, type(uint256).max);
            }
        }
    }
    
    // ================= User Registration Management =================
    
    /**
     * @notice Update maximum auction participant limit
     * @param newMax New maximum participant count (must be >= current registered users)
     * @dev Governance-only function to adjust participant cap (default 2500)
     */
    function setMaxAuctionParticipants(uint256 newMax) external onlyGovernance {
        if (newMax == 0) revert InvalidParam();
        if (newMax < totalRegisteredUsers) revert InvalidParam(); // Cannot be less than current registered users
        
        uint256 oldMax = maxAuctionParticipants;
        maxAuctionParticipants = newMax;
        emit MaxParticipantsUpdated(oldMax, newMax);
    }

    // ================= Internal Helper Functions =================
    
    /**
     * @notice Execute token swap through PulseX pool
     * @param user User address performing the swap
     * @param auctionToken Token to receive from swap
     * @param stateAmountIn Amount of STATE to swap
     * @return Amount of auction tokens received
     * @dev Protected by ReentrancyGuard on calling functions, uses SafeERC20 for transfers
     * @custom:slippage 5% tolerance for low-liquidity pools, adjustable via governance
     */
    function _executePoolSwap(address user, address auctionToken, uint256 stateAmountIn) internal returns (uint256) {
        if (pulseXRouter == address(0)) revert RouterNotSet();
        
        // Transfer STATE tokens from user to this contract
        IERC20(stateToken).safeTransferFrom(user, address(this), stateAmountIn);
        
        // Approve router to spend STATE tokens
        IERC20(stateToken).approve(pulseXRouter, stateAmountIn);
        
        // Set up swap path: STATE -> auction token
        address[] memory path = new address[](2);
        path[0] = stateToken;
        path[1] = auctionToken;
        
        // Calculate minimum output with 5% slippage tolerance
        uint256 expectedOut = calculatePoolSwapOutput(auctionToken, stateAmountIn);
        uint256 minAmountOut = expectedOut * 95 / 100;
        
        // Execute swap through PulseX router
        uint256[] memory amounts = IPulseXRouter02(pulseXRouter).swapExactTokensForTokens(
            stateAmountIn,
            minAmountOut,
            path,
            user, // Send auction tokens directly to user
            block.timestamp + 300 // 5 minute deadline
        );
        
        return amounts[1]; // Return amount of auction tokens received
    }
    
    function getRatioPrice(address inputToken) public view returns (uint256) {
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        return NormalAuctionCalculations.getRatioPrice(
            inputToken,
            stateToken,
            pairAddresses[inputToken]
        );
    }

    function calculatePoolSwapOutput(address inputToken, uint256 stateAmountIn) internal view returns (uint256) {
        if (!supportedTokens[inputToken]) revert UnsupportedToken();
        return NormalAuctionCalculations.calculatePoolSwapOutput(
            stateAmountIn,
            stateToken,
            inputToken,
            pairAddresses[inputToken]
        );
    }

    function calculatePoolSwapOutputReverse(address auctionToken, uint256 tokenAmountIn) internal view returns (uint256) {
        if (!supportedTokens[auctionToken]) revert UnsupportedToken();
        return ReverseAuctionCalculations.calculatePoolSwapOutputReverse(
            tokenAmountIn,
            auctionToken,
            stateToken,
            pairAddresses[auctionToken]
        );
    }

    // ================= Governance Functions (Delegate to Admin) =================
    // ... (Keep all the governance delegation functions from previous version)
    // They remain the same as in the previous split

    /**
     * @notice Pause all auction operations
     * @dev Governance emergency function, blocks all whenNotPaused operations
     */
    function pause() external onlyGovernance {
        paused = true;
        emit ContractPaused(msg.sender);
    }

    /**
     * @notice Resume auction operations
     * @dev Governance function to unpause contract after emergency
     */
    function unpause() external onlyGovernance {
        paused = false;
        emit ContractUnpaused(msg.sender);
    }

    // ... (All other governance functions remain the same as previous version)

    // ================= Internal State Setters for Admin Contract =================
    
    /**
     * @notice Internal setter for max auction participants (called by AuctionAdmin only)
     * @param newMax New maximum participant count
     */
    function _setMaxAuctionParticipants(uint256 newMax) external {
        if (msg.sender != address(auctionAdmin)) revert OnlyAdmin();
        maxAuctionParticipants = newMax;
    }
    
    /**
     * @notice Internal setter for governance address (called by AuctionAdmin only)
     * @param newGov New governance address
     */
    function _setGovernance(address newGov) external {
        if (msg.sender != address(auctionAdmin)) revert OnlyAdmin();
        governanceAddress = newGov;
    }
    
    /**
     * @notice Register deployed token (called by AuctionAdmin only)
     * @param tokenAddress Address of deployed token
     * @param name Token name
     * @param deployer Address of token deployer
     * @dev Array limited to scheduleSize (50 tokens max) to ensure reasonable gas costs
     */
    function _registerDeployedToken(address tokenAddress, string memory name, address deployer) external {
        if (msg.sender != address(auctionAdmin)) revert OnlyAdmin();
        
        // Check if token limit reached - cannot register more than scheduleSize tokens
        if (autoRegisteredTokens.length >= auctionSchedule.scheduleSize) revert TokenDeploymentLimitReached();
        
        tokenOwners[tokenAddress] = deployer;
        ownerToTokens[deployer].push(tokenAddress);
        userToTokenNames[deployer].push(name);
        autoRegisteredTokens.push(tokenAddress);
        isAutoRegistered[tokenAddress] = true;
        supportedTokens[tokenAddress] = true;
        tokenCount++;
        if (autoRegisteredTokens.length == auctionSchedule.scheduleSize) {
            autoScheduleLocked = true;
        }
    }

    /**
     * @notice One-click governance setup for complete system initialization
     * @dev This is the MAIN function governance calls via UI to configure everything
     * @param _stateToken STATE token address
     * @param _davToken DAV token address
     * @param _airdropDistributor AirdropDistributor contract address
     * @param _auctionAdmin AuctionAdmin contract address
     * @param _buyBurnController BuyAndBurnController address for allowance setup
     * @param _swapLens SwapLens contract address (optional - can be address(0))
     * @param _pulseXRouter PulseX router address
     * @param _pulseXFactory PulseX factory address
     */
    function initializeCompleteSystem(
        address _stateToken,
        address _davToken,
        address _airdropDistributor,
        address _auctionAdmin,
        address _buyBurnController,
        address _swapLens,
        address _pulseXRouter,
        address _pulseXFactory
    ) external onlyGovernance nonReentrant {
        if (_stateToken == address(0)) revert StateNotSet();
        if (_davToken == address(0)) revert InvalidParam();
        if (_airdropDistributor == address(0)) revert InvalidParam();
        if (_auctionAdmin == address(0)) revert InvalidParam();
        if (_buyBurnController == address(0)) revert InvalidParam();
        if (_pulseXRouter == address(0)) revert RouterNotSet();
        if (_pulseXFactory == address(0)) revert FactoryNotSet();
        
        // Set all contract addresses
        stateToken = _stateToken;
        davToken = _davToken;
        dav = IDAV(_davToken);
        airdropDistributor = _airdropDistributor;
        auctionAdmin = IAuctionAdmin(_auctionAdmin);
        buyAndBurnController = _buyBurnController;
        swapLens = _swapLens;
        pulseXRouter = _pulseXRouter;
        pulseXFactory = _pulseXFactory;
        
        // CRITICAL: Set vault allowance for BuyAndBurnController to access STATE
        _setExactAllowance(_stateToken, _buyBurnController, type(uint256).max);
        
        // Auto-approve all tokens for airdrop distributor (enables Step 1)
        _approveAllTokensForAirdrop(_airdropDistributor);
        
        emit AirdropDistributorSet(_airdropDistributor);
        emit SystemInitialized(
            _stateToken,
            _davToken,
            address(0), // lpHelper removed - using SWAP_V3.createPoolOneClick() instead
            _airdropDistributor,
            _auctionAdmin,
            _buyBurnController,
            _pulseXRouter,
            _pulseXFactory
        );
    }

    // ================= AUCTION FEE DISTRIBUTION =================
    
    /**
     * @notice Internal function to distribute 0.5% auction fees to development wallets
     * @param token Address of the token to distribute (STATE or auction token)
     * @param feeAmount Amount of tokens to distribute as fee
     * @dev Transfers fee to AuctionAdmin which handles distribution to dev wallets
     */
    function _distributeAuctionFee(address token, uint256 feeAmount) internal {
        IERC20(token).transfer(address(auctionAdmin), feeAmount);
        auctionAdmin.distributeFeeToWallets(token, feeAmount);
    }
    
    // ================= LIQUIDITY MANAGEMENT =================
    
    /**
     * @notice Add liquidity to existing pool from vault (governance-only)
     * @param token Auction token address
     * @param tokenAmount Token amount from vault
     * @param stateAmount STATE amount from vault
     * @return liquidity LP tokens burned
     */
    function addLiquidityToPool(
        address token,
        uint256 tokenAmount,
        uint256 stateAmount
    ) external onlyGovernance nonReentrant returns (uint256 liquidity) {
        if (token == address(0)) revert ZeroAddr();
        if (token == stateToken) revert InvalidParam();
        if (tokenAmount == 0 || stateAmount == 0) revert AmountZero();
        
        address pool = IPulseXFactory(pulseXFactory).getPair(token, stateToken);
        if (pool == address(0)) revert InvalidParam();
        
        if (IERC20(token).balanceOf(address(this)) < tokenAmount) revert InsufficientVault();
        if (IERC20(stateToken).balanceOf(address(this)) < stateAmount) revert InsufficientVault();
        
        _setExactAllowance(token, pulseXRouter, tokenAmount);
        _setExactAllowance(stateToken, pulseXRouter, stateAmount);
        
        (uint256 usedToken, uint256 usedState, uint256 lpAmount) = 
            IPulseXRouter02(pulseXRouter).addLiquidity(
                token,
                stateToken,
                tokenAmount,
                stateAmount,
                (tokenAmount * 95) / 100,
                (stateAmount * 95) / 100,
                BURN_ADDRESS,
                block.timestamp + 3600
            );
        
        if (lpAmount == 0) revert AmountZero();
        
        _setExactAllowance(token, pulseXRouter, 0);
        _setExactAllowance(stateToken, pulseXRouter, 0);
        
        emit LiquidityAdded(token, pool, usedState, usedToken, lpAmount);
        
        return lpAmount;
    }
}
        

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

/

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

/**
 * @title TimeUtilsLib
 * @author State Protocol Team
 * @notice Utility library for timezone-aware time calculations
 * @dev Provides functions to calculate time boundaries with timezone offsets
 * @custom:timezone Default: GMT+3 (17:00 local = 14:00 UTC for DAV mint timing)
 * @custom:validation Bounds checking for timezone offsets (-14 to +14 hours)
 */
library TimeUtilsLib {
    uint256 internal constant SECONDS_IN_DAY = 86400;
    // Default time boundary: 17:00 GMT+3 (5:00 PM GMT+3)
    uint256 internal constant TARGET_GMT_HOUR = 17;
    uint256 internal constant TARGET_GMT_MINUTE = 0;

    /**
     * @notice Validates time calculation parameters
     * @custom:bounds Timezone offset: -14 to +14 hours, Hour: 0-23, Minute: 0-59
     * @custom:safety Prevents overflow in signed integer operations
     */
    function _validateTimeParams(
        uint256 blockTimestamp,
        int256 tzOffsetHours,
        uint256 targetLocalHour,
        uint256 targetLocalMinute
    ) private pure {
        require(blockTimestamp > 0, "Invalid timestamp");
        require(tzOffsetHours >= -14 && tzOffsetHours <= 14, "Invalid offset");
        require(targetLocalHour < 24, "Invalid hour");
        require(targetLocalMinute < 60, "Invalid minute");
        require(blockTimestamp <= uint256(type(int256).max), "Timestamp too large");
    }

    /**
     * @notice Calculates the next claim start time using a timezone offset and local-hour target
     * @param blockTimestamp Current block timestamp (UTC seconds)
     * @param tzOffsetHours Signed hour offset from UTC (e.g. +3 for GMT+3, -4 for GMT-4)
     * @param targetLocalHour Target hour in the local timezone (0-23)
     * @param targetLocalMinute Target minute in the local timezone (0-59)
     * @return finalTimestamp UTC timestamp of the next target local-time boundary
     * @custom:timezone Fixed offsets (does not account for DST)
     * @custom:safety Overflow protection on all signed math operations
     */
    function calculateNextClaimStartTZ(
        uint256 blockTimestamp,
        int256 tzOffsetHours,
        uint256 targetLocalHour,
        uint256 targetLocalMinute
    ) internal pure returns (uint256 finalTimestamp) {
        _validateTimeParams(blockTimestamp, tzOffsetHours, targetLocalHour, targetLocalMinute);
        
        int256 offsetSeconds = tzOffsetHours * int256(1 hours);
        int256 tsInt = int256(blockTimestamp);
        
        if (offsetSeconds > 0) {
            require(tsInt <= type(int256).max - offsetSeconds, "Local overflow");
        } else if (offsetSeconds < 0) {
            require(tsInt >= type(int256).min - offsetSeconds, "Local underflow");
        }
        
        int256 localTs = tsInt + offsetSeconds;
        require(localTs >= 0, "Local time < 0");
        
        uint256 localTsUint = uint256(localTs);
        uint256 localDayStart = (localTsUint / SECONDS_IN_DAY) * SECONDS_IN_DAY;
        
        uint256 hoursSec = targetLocalHour * 1 hours;
        uint256 minutesSec = targetLocalMinute * 1 minutes;
        require(localDayStart <= type(uint256).max - hoursSec, "Target calc ovf (h)");
        uint256 targetLocal = localDayStart + hoursSec;
        require(targetLocal <= type(uint256).max - minutesSec, "Target calc ovf (m)");
        targetLocal += minutesSec;
        
        if (localTsUint >= targetLocal) {
            require(targetLocal <= type(uint256).max - SECONDS_IN_DAY, "Next day ovf");
            targetLocal += SECONDS_IN_DAY;
        }
        
        require(targetLocal <= uint256(type(int256).max), "UTC cast ovf");
        int256 utcTs = int256(targetLocal) - offsetSeconds;
        require(utcTs >= 0, "UTC time < 0");
        finalTimestamp = uint256(utcTs);
        require(finalTimestamp > blockTimestamp, "Non-future ts");
    }

    /**
     * @notice Calculate next 17:00 GMT+3 (5:00 PM GMT+3)
     * @custom:utc 17:00 GMT+3 = 14:00 UTC (2:00 PM UTC)
     */
    function calculateNextClaimStartGMTPlus3(uint256 blockTimestamp)
        internal
        pure
        returns (uint256)
    {
        return calculateNextClaimStartTZ(blockTimestamp, int256(3), 17, 0);
    }

    /**
     * @notice LEGACY: Calculate next 23:00 Pakistan Standard Time (11:00 PM UTC+5)
     * @dev Not used in current protocol. Use calculateNextClaimStartGMTPlus3() instead
     * @custom:utc 23:00 UTC+5 = 18:00 UTC (6:00 PM UTC)
     */
    function calculateNextClaimStartPakistan(uint256 blockTimestamp) internal pure returns (uint256) {
        return calculateNextClaimStartTZ(blockTimestamp, int256(5), 23, 0);
    }
}
          

/

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

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

/**
 * @title TOKEN_V3 - Auction Token Contract
 * @author State Protocol Team
 * @notice Generic ERC20 token with fixed 5 billion supply minted at deployment
 * @dev Used for auction tokens in State Protocol ecosystem
 *
 * @custom:supply 5 billion tokens (5,000,000,000 with 18 decimals)
 * @custom:allocation Entire supply minted to recipient (typically SWAP_V3) in constructor
 * @custom:ownership Deployed ownerless - constructor accepts address(0) for _owner parameter
 * @custom:ownable-modification Uses modified OpenZeppelin Ownable.sol with constructor validation removed
 *                               to allow address(0) as initial owner (ownerless from deployment)
 */
contract TOKEN_V3 is ERC20, Ownable {
    
    // ============ Constants ============
    
    /// @notice Total supply of auction tokens
    /// @dev 5 billion tokens: 5,000,000,000 × 10^18 wei
    ///      Compile-time constant - does not consume storage slot
    uint256 public constant MAX_SUPPLY = 5000000000 ether;
    
    // ============ Events ============
    
    /// @notice Emitted when initial supply is minted during deployment
    /// @param recipient Address receiving the entire token supply
    /// @param totalAmount Total tokens minted (5 billion with 18 decimals)
    event InitialDistribution(
        address indexed recipient,
        uint256 totalAmount
    );

    // ============ Constructor ============
    
    /// @notice Deploys auction token with entire supply minted to recipient
    /// @param name Human-readable token name (e.g., "MyToken")
    /// @param symbol Token ticker symbol (e.g., "MTK")
    /// @param recipient Address receiving 100% of supply (typically SWAP_V3 auction contract)
    /// @param _owner Owner address - pass address(0) for ownerless deployment (standard practice)
    /// @dev Constructor operations (atomic transaction):
    ///      1. Validate recipient address (non-zero)
    ///      2. Mint 5 billion tokens to recipient
    ///      3. Set owner (address(0) for ownerless tokens - modified OZ Ownable accepts this)
    ///      4. Emit InitialDistribution event
    /// @custom:ownerless-design Tokens deployed with _owner = address(0) have no owner from birth
    ///                          Modified Ownable.sol constructor allows this (standard OZ v5 would revert)
    ///                          This is intentional - saves gas vs deploying with owner then renouncing
    constructor(
        string memory name,
        string memory symbol,
        address recipient,
        address _owner
    ) ERC20(name, symbol) Ownable(_owner) {
        require(recipient != address(0), "Invalid recipient address");
        
        // Mint entire supply to auction contract
        _mint(recipient, MAX_SUPPLY);
        
        emit InitialDistribution(recipient, MAX_SUPPLY);
    }
}
          

/

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

/**
 * @title SwapCoreLib
 * @author State Protocol Team
 * @notice Core data structures and utilities for the auction swap system
 * @dev Pure data/utility library with no state modification logic
 * @custom:security Access control enforced in AuctionSwap.sol
 * @custom:storage Struct instances stored in AuctionSwap contract storage
 * @custom:functions Pure/view only - no state changes, key generation uses fixed-length types
 */
library SwapCoreLib {
    
    /**
     * @notice Tracks user's swap completion status for a specific auction cycle
     * @dev Stored in AuctionSwap's userSwapTotalInfo mapping, keyed by getSwapInfoKey()
     * @custom:storage Optimally packed into single storage slot (2 bools + 1 uint256)
     * @custom:flags hasSwapped (normal auction), hasReverseSwap (reverse auction)
     */
    struct UserSwapInfo {
        bool hasSwapped;        // Normal auction swap completed
        bool hasReverseSwap;    // Reverse auction swap completed
        uint256 cycle;          // Auction cycle number
    }

    /**
     * @notice Generates a unique key for tracking user swap info across cycles
     * @param user The address of the user performing the swap
     * @param inputToken The token being swapped (auction token)
     * @param stateToken The STATE token address
     * @param cycle The current auction cycle number
     * @return bytes32 A unique deterministic hash key for this swap instance
     * @custom:encoding Uses abi.encodePacked with fixed-length types (no collision risk)
     * @custom:gas More efficient than abi.encode for fixed-length types
     */
    function getSwapInfoKey(
        address user,
        address inputToken,
        address stateToken,
        uint256 cycle
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(user, inputToken, stateToken, cycle));
    }
}
          

/

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

import "../interfaces/IPair.sol";

/**
 * @title ReverseAuctionCalculations
 * @author State Protocol Team
 * @notice Library for reverse auction calculations (users swap auction tokens back to STATE)
 * @dev Library functions called only by AuctionSwap.sol (not directly callable by users).
 * @custom:security Access control and validation enforced in AuctionSwap.sol
 * @custom:flow Step 1: User deposits auction tokens → Step 2: Swap for STATE → Step 3: Burn 100% STATE → Step 4: Return 2x pool ratio auction tokens
 * @custom:precision All calculations use 18 decimal precision (1e18), Solidity 0.8.20 overflow protection
 * @custom:bonus STATE_MULTIPLIER = 2 (users receive double market rate for burned STATE)
 */
library ReverseAuctionCalculations {

    /// @notice STATE token bonus multiplier: 2x the pool ratio
    /// @dev Users receive double the market rate for burned STATE tokens
    uint256 internal constant STATE_MULTIPLIER = 2;
    
    /// @notice Precision factor for decimal calculations
    /// @dev Standard 18 decimal precision used throughout DeFi
    uint256 internal constant PRECISION_FACTOR = 1e18;

    // ================= Errors =================
    
    /// @notice Thrown when liquidity pool reserves are zero
    error InvalidReserves();
    
    /// @notice Thrown when pair tokens don't match expected auction/STATE tokens
    error PairInvalid();
    
    /// @notice Thrown when swap amount is zero
    error AmountZero();

    /**
     * @notice Calculate STATE output from swapping auction tokens in pool
     * @param tokenAmountIn Amount of auction tokens to swap (in wei, 18 decimals)
     * @param auctionToken The auction token address
     * @param stateToken The STATE token address
     * @param pairAddress The Uniswap V2-style pair contract address
     * @return stateOutput Amount of STATE tokens to receive (in wei, 18 decimals)
     * @dev Step 2 of reverse auction: swaps auction tokens for STATE using Uniswap V2 AMM
     * @custom:formula stateOut = (tokenIn × 997 × stateReserve) ÷ (tokenReserve × 1000 + tokenIn × 997)
     * @custom:fee 0.3% trading fee (997/1000 = 99.7% effective)
     * @custom:flow User deposits tokens → Calculate STATE → Must burn 100% STATE → Receive 2x auction tokens
     * @custom:precision 18 decimals, integer division truncation favors pool
     */
    function calculatePoolSwapOutputReverse(
        uint256 tokenAmountIn,
        address auctionToken,
        address stateToken,
        address pairAddress
    ) internal view returns (uint256 stateOutput) {
        if (tokenAmountIn == 0) revert AmountZero();
        
        IPair pair = IPair(pairAddress);
        (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
        
        address token0 = pair.token0();
        address token1 = pair.token1();
        
        if (reserve0 == 0 || reserve1 == 0) revert InvalidReserves();
        
        uint256 tokenReserve;
        uint256 stateReserve;
        
        if (token0 == auctionToken && token1 == stateToken) {
            tokenReserve = uint256(reserve0);
            stateReserve = uint256(reserve1);
        } else if (token0 == stateToken && token1 == auctionToken) {
            tokenReserve = uint256(reserve1);
            stateReserve = uint256(reserve0);
        } else {
            revert PairInvalid();
        }
        
        // AMM formula with 0.3% fee: stateOut = (tokenIn * 997 * stateReserve) / (tokenReserve * 1000 + tokenIn * 997)
        uint256 tokenInWithFee = tokenAmountIn * 997;
        uint256 numerator = tokenInWithFee * stateReserve;
        uint256 denominator = (tokenReserve * 1000) + tokenInWithFee;
        
        return numerator / denominator;
    }

    /**
     * @notice Calculate auction tokens to return based on burned STATE and apply 2x bonus
     * @param stateToBurn Amount of STATE tokens being burned (in wei, 18 decimals)
     * @param poolRatio Current pool ratio: STATE per auction token (in wei, 18 decimals)
     * @return tokensToGive Amount of auction tokens to return (includes 2x multiplier, in wei, 18 decimals)
     * @dev Step 4 of reverse auction: calculates final reward with 2x multiplier
     * @custom:formula tokensToGive = (stateToBurn × 1e18 × 2) ÷ poolRatio
     * @custom:multiplier 2x bonus incentivizes STATE burning (deflationary mechanism)
     * @custom:requirement User must burn 100% of received STATE (enforced by AuctionSwap.sol)
     * @custom:precision Multiplies before dividing to minimize precision loss
     */
    function calculateTokensToGive(
        uint256 stateToBurn,
        uint256 poolRatio
    ) internal pure returns (uint256 tokensToGive) {
        if (poolRatio == 0) return 0;
        
        // tokensToGive = (stateToBurn / poolRatio) * 2
        // Rewritten to avoid precision loss: (stateToBurn * 1e18 * 2) / poolRatio
        return (stateToBurn * PRECISION_FACTOR * STATE_MULTIPLIER) / poolRatio;
    }
}
          

/

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

import "../interfaces/IPair.sol";

/**
 * @title NormalAuctionCalculations
 * @author State Protocol Team
 * @notice Library for normal auction calculations (users burn DAV to get auction tokens)
 * @dev Library functions called only by AuctionSwap.sol (not directly callable by users).
 * @custom:security Access control and validation enforced in AuctionSwap.sol
 * @custom:flow Burn DAV → Calculate burn amount (30% of airdrop) and STATE bonus (2x pool ratio) → Transfer tokens
 * @custom:precision 18 decimal precision (1e18), Solidity 0.8.20 overflow protection
 * @custom:bonus STATE_MULTIPLIER = 2 (users receive double market rate STATE tokens)
 */
library NormalAuctionCalculations {

    /// @notice Conversion rate: 2500 auction tokens per 1 DAV token
    /// @dev Fixed rate set by protocol design (18 decimals)
    uint256 constant TOKENS_PER_DAV = 2500 ether;
    
    /// @notice STATE token bonus multiplier: 2x the pool ratio
    /// @dev Users receive double the market rate as incentive for burning DAV
    uint256 constant STATE_MULTIPLIER = 2;
    
    /// @notice Precision factor for decimal calculations
    /// @dev Standard 18 decimal precision used throughout DeFi
    uint256 constant PRECISION_FACTOR = 1e18;

    // ================= Errors =================
    
    /// @notice Thrown when liquidity pool reserves are zero
    error InvalidReserves();
    
    /// @notice Thrown when pair tokens don't match expected auction/STATE tokens
    error PairInvalid();
    
    /// @notice Thrown when swap amount is zero
    error AmountZero();

    // ================= Core Calculation Functions =================

    /**
     * @notice Calculate auction tokens to burn based on available DAV
     * @param availableDav Amount of DAV tokens available for burning (18 decimals)
     * @return tokensToBurn Amount of auction tokens to burn (18 decimals)
    * @custom:formula tokensToBurn = (availableDav × 2500) ÷ 1e18
    * @custom:rate 2500 auction tokens per 1 DAV token
     */
    function calculateTokensToBurn(uint256 availableDav) internal pure returns (uint256 tokensToBurn) {
        return (availableDav * TOKENS_PER_DAV) / 1e18;
    }

    /**
     * @notice Calculate STATE tokens to give based on pool ratio and apply 2x bonus
     * @param tokensToBurn Amount of auction tokens being burned (18 decimals)
     * @param poolRatio Current pool ratio: STATE per auction token (18 decimals)
     * @return stateToGive Amount of STATE tokens to give with 2x multiplier (18 decimals)
     * @custom:formula stateToGive = (tokensToBurn × poolRatio × 2) ÷ 1e18
     * @custom:multiplier 2x bonus incentivizes DAV burning and participation
     */
    function calculateStateToGive(
        uint256 tokensToBurn, 
        uint256 poolRatio
    ) internal pure returns (uint256 stateToGive) {
        return (tokensToBurn * poolRatio * STATE_MULTIPLIER) / PRECISION_FACTOR;
    }

    /**
     * @notice Get price ratio from liquidity pool reserves
     * @param inputToken The auction token address
     * @param stateToken The STATE token address
     * @param pairAddress The Uniswap V2-style pair contract address
     * @return ratio Amount of STATE tokens per 1 auction token (18 decimals)
     * @custom:formula ratio = (stateReserve × 1e18) ÷ auctionReserve
     * @custom:ordering Handles both Uniswap V2 token orderings (token0/token1)
     * @custom:zero-check Returns 0 if reserves are zero, reverts if pair invalid
     */
    function getRatioPrice(
        address inputToken,
        address stateToken, 
        address pairAddress
    ) internal view returns (uint256 ratio) {
        IPair pair = IPair(pairAddress);
        (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
        
        address token0 = pair.token0();
        address token1 = pair.token1();
        
        if (reserve0 == 0 || reserve1 == 0) return 0;

        if (token0 == inputToken && token1 == stateToken) {
            ratio = (uint256(reserve1) * PRECISION_FACTOR) / uint256(reserve0);
        } else if (token0 == stateToken && token1 == inputToken) {
            ratio = (uint256(reserve0) * PRECISION_FACTOR) / uint256(reserve1);
        } else {
            revert PairInvalid();
        }

        return ratio;
    }

    /**
     * @notice Calculate expected output from swapping STATE tokens for auction tokens in pool
     * @param stateAmountIn Amount of STATE tokens to swap (18 decimals)
     * @param stateToken The STATE token address
     * @param inputToken The auction token address to receive
     * @param pairAddress The Uniswap V2-style pair contract address
     * @return amountOut Amount of auction tokens to receive (18 decimals)
     * @custom:formula amountOut = (amountIn × 997 × reserveOut) ÷ (reserveIn × 1000 + amountIn × 997)
     * @custom:fee 0.3% Uniswap V2 trading fee (997/1000 effective input)
     * @custom:validation Reverts if amount is zero, reserves are zero, or pair is invalid
     */
    function calculatePoolSwapOutput(
        uint256 stateAmountIn,
        address stateToken,
        address inputToken,
        address pairAddress
    ) internal view returns (uint256 amountOut) {
        if (stateAmountIn == 0) revert AmountZero();
        
        IPair pair = IPair(pairAddress);
        (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
        
        address token0 = pair.token0();
        address token1 = pair.token1();
        
        if (reserve0 == 0 || reserve1 == 0) revert InvalidReserves();
        
        uint256 stateReserve;
        uint256 tokenReserve;
        
        if (token0 == stateToken && token1 == inputToken) {
            stateReserve = uint256(reserve0);
            tokenReserve = uint256(reserve1);
        } else if (token0 == inputToken && token1 == stateToken) {
            stateReserve = uint256(reserve1);
            tokenReserve = uint256(reserve0);
        } else {
            revert PairInvalid();
        }
        
        // AMM formula with 0.3% fee: amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997)
        uint256 amountInWithFee = stateAmountIn * 997;
        uint256 numerator = amountInWithFee * tokenReserve;
        uint256 denominator = (stateReserve * 1000) + amountInWithFee;
        
        return numerator / denominator;
    }
}
          

/

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title BurnLib
 * @author State Protocol Team
 * @notice Library for handling token burning operations in the auction system
 * @dev Library functions called only by AuctionSwap.sol (not directly callable by users).
 * @custom:security Access control enforced in AuctionSwap.sol (nonReentrant, whenNotPaused)
 * @custom:normal-flow Users burn auction tokens → receive STATE from vault
 * @custom:reverse-flow Users burn STATE → receive auction tokens
 * @custom:burn-address All burned tokens sent to 0xdead (permanent removal)
 * @custom:validation Input validation handled by AuctionSwap.sol before library calls
 */
library BurnLib {
    using SafeERC20 for IERC20;
    
    // Burn address for permanently removing tokens from circulation
    address private constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    
    // ================= Events =================
    
    /// @notice Emitted when tokens are permanently burned to dead address
    /// @param token Address of the token burned
    /// @param amount Amount of tokens burned
    /// @param user Address of the user who initiated the burn
    event TokensBurned(address indexed token, uint256 amount, address indexed user);
    
    /// @notice Emitted when STATE tokens are issued to user in normal auction
    /// @param user Address receiving STATE tokens
    /// @param amount Amount of STATE tokens issued
    /// @param cycle Auction cycle number
    event StateTokensIssued(address indexed user, uint256 amount, uint256 indexed cycle);
    
    /// @notice Emitted when STATE tokens are burned in reverse auction
    /// @param user Address burning STATE tokens
    /// @param amount Amount of STATE tokens burned
    /// @param cycle Auction cycle number
    event ReverseStateTokensBurned(address indexed user, uint256 amount, uint256 indexed cycle);
    
    /// @notice Emitted when auction tokens are issued to user in reverse auction
    /// @param user Address receiving auction tokens
    /// @param amount Amount of auction tokens issued
    /// @param cycle Auction cycle number
    event AuctionTokensIssued(address indexed user, uint256 amount, uint256 indexed cycle);

    // ================= Structs =================
    
    /**
     * @notice Parameters for normal auction token burning (Step 2)
     * @param user Address of the user performing the burn
     * @param auctionToken Address of the auction token being burned
     * @param stateToken Address of the STATE token being issued
     * @param currentCycle Current auction cycle number
     * @param tokensToBurn Amount of auction tokens to burn
     * @param stateToGive Amount of STATE tokens to issue from vault
     * @param availableDav Amount of DAV used for this burn operation
     */
    struct BurnParams {
        address user;
        address auctionToken;
        address stateToken;
        uint256 currentCycle;
        uint256 tokensToBurn;
        uint256 stateToGive;
        uint256 availableDav;
    }

    /**
     * @notice Parameters for reverse auction STATE burning (Step 2)
     * @param user Address of the user performing the burn
     * @param auctionToken Address of the auction token being issued
     * @param stateToken Address of the STATE token being burned
     * @param currentCycle Current auction cycle number
     * @param stateToBurn Amount of STATE tokens to burn
     * @param tokensToGive Amount of auction tokens to issue from vault
     */
    struct ReverseBurnParams {
        address user;
        address auctionToken;
        address stateToken;
        uint256 currentCycle;
        uint256 stateToBurn;
        uint256 tokensToGive;
    }

    // ================= Core Functions =================
    
    /**
     * @notice Execute normal auction token burn (Step 2 of normal auction)
     * @dev Called by AuctionSwap.burnTokensForState() after validation
     * @custom:flow Update tracking → Burn auction tokens to 0xdead → Transfer STATE from vault → Emit events
     * @custom:validation All checks performed in AuctionSwap.sol before calling
     * @custom:accumulation Allows multiple burns per cycle (values accumulate)
     * @param params Struct containing burn operation parameters (pre-validated)
     * @param hasUserBurnedTokens Mapping tracking if user has burned tokens for a cycle
     * @param userStateBalance Mapping of user's STATE balance earned per cycle
     * @param tokensBurnedByUser Mapping of tokens burned by user per cycle
     * @param davTokensUsed Mapping of DAV tokens used by user per cycle
     * @param TotalTokensBurned Mapping tracking total burned tokens globally
     */
    function executeTokenBurn(
        BurnParams memory params,
        mapping(address => mapping(address => mapping(uint256 => bool))) storage hasUserBurnedTokens,
        mapping(address => mapping(address => mapping(uint256 => uint256))) storage userStateBalance,
        mapping(address => mapping(address => mapping(uint256 => uint256))) storage tokensBurnedByUser,
        mapping(address => mapping(address => mapping(uint256 => uint256))) storage davTokensUsed,
        mapping(address => uint256) storage TotalTokensBurned
    ) external {
        // Input validation
        require(params.user != address(0), "BurnLib: Invalid user address");
        require(params.auctionToken != address(0), "BurnLib: Invalid auction token");
        require(params.stateToken != address(0), "BurnLib: Invalid state token");
        require(params.tokensToBurn > 0, "BurnLib: Invalid burn amount");
        require(params.stateToGive > 0, "BurnLib: Invalid state amount");
        
        // Allow multiple burns per cycle - accumulate values
        hasUserBurnedTokens[params.user][params.auctionToken][params.currentCycle] = true;
        userStateBalance[params.user][params.auctionToken][params.currentCycle] += params.stateToGive;
        tokensBurnedByUser[params.user][params.auctionToken][params.currentCycle] += params.tokensToBurn;
        davTokensUsed[params.user][params.auctionToken][params.currentCycle] += params.availableDav;
        
        // Burn the auction tokens permanently by sending to dead address
        IERC20(params.auctionToken).safeTransferFrom(params.user, BURN_ADDRESS, params.tokensToBurn);
        TotalTokensBurned[params.auctionToken] += params.tokensToBurn;
        
        // Give STATE tokens to user from contract vault
        IERC20(params.stateToken).safeTransfer(params.user, params.stateToGive);
        
        // Emit burn event to track the burning of auction tokens
        emit TokensBurned(params.auctionToken, params.tokensToBurn, params.user);
        emit StateTokensIssued(params.user, params.stateToGive, params.currentCycle);
    }

    /**
     * @notice Execute reverse auction STATE burn (Step 2 of reverse auction)
     * @dev Called by AuctionSwap.burnStateForTokens() after validation
     * @custom:flow Verify not completed → Mark completed → Burn STATE to 0xdead → Transfer auction tokens → Emit events
     * @custom:validation All checks performed in AuctionSwap.sol before calling
     * @custom:once-per-cycle Prevents double execution via hasCompletedReverseStep2 flag
     * @param params Struct containing reverse burn operation parameters (pre-validated)
     * @param hasCompletedReverseStep2 Mapping tracking reverse step 2 completion
     * @param reverseStateBalance Mapping of STATE balance from reverse step 1
     * @param TotalTokensBurned Mapping tracking total burned tokens globally
     */
    function executeReverseBurn(
        ReverseBurnParams memory params,
        mapping(address => mapping(address => mapping(uint256 => bool))) storage hasCompletedReverseStep2,
        mapping(address => mapping(address => mapping(uint256 => uint256))) storage reverseStateBalance,
        mapping(address => uint256) storage TotalTokensBurned
    ) external {
        // Input validation
        require(params.user != address(0), "BurnLib: Invalid user address");
        require(params.auctionToken != address(0), "BurnLib: Invalid auction token");
        require(params.stateToken != address(0), "BurnLib: Invalid state token");
        require(params.stateToBurn > 0, "BurnLib: Invalid burn amount");
        require(params.tokensToGive > 0, "BurnLib: Invalid token amount");
        
        // Check for double execution
        require(!hasCompletedReverseStep2[params.user][params.auctionToken][params.currentCycle], "BurnLib: Already completed reverse step 2");
        
        hasCompletedReverseStep2[params.user][params.auctionToken][params.currentCycle] = true;
        
        // Safe subtraction with underflow protection
        uint256 currentBalance = reverseStateBalance[params.user][params.auctionToken][params.currentCycle];
        require(currentBalance >= params.stateToBurn, "BurnLib: Insufficient reverse balance");
        reverseStateBalance[params.user][params.auctionToken][params.currentCycle] = currentBalance - params.stateToBurn;
        
        // Burn STATE tokens permanently by sending to dead address
        IERC20(params.stateToken).safeTransferFrom(params.user, BURN_ADDRESS, params.stateToBurn);
        TotalTokensBurned[params.stateToken] += params.stateToBurn;
        
        // Give auction tokens to user from contract vault
        IERC20(params.auctionToken).safeTransfer(params.user, params.tokensToGive);
        
        // Emit burn event to track the burning of STATE tokens in reverse auction
        emit TokensBurned(params.stateToken, params.stateToBurn, params.user);
        emit ReverseStateTokensBurned(params.user, params.stateToBurn, params.currentCycle);
        emit AuctionTokensIssued(params.user, params.tokensToGive, params.currentCycle);
    }
}
          

/

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

import "./AuctionLib.sol";

/**
 * @title AuctionUtilsLib
 * @author State Protocol Team
 * @notice Library for auction scheduling and timing calculations
 * @dev Provides reusable calculation functions for auction timing and token rotation
 * @custom:architecture Library provides calculations only, business rules enforced in AuctionSwap.sol
 * @custom:timing Configurable via AuctionLib constants (AUCTION_DURATION, AUCTION_INTERVAL)
 * @custom:rotation Tokens rotate continuously: Index 0, 1, 2... % tokenCount (perfect sequential rotation)
 * @custom:cycles Maximum cycles per token (20) enforced in AuctionSwap.sol, not in library layer
 */
library AuctionUtilsLib {
    
    /**
     * @notice Auction schedule configuration and state tracking
     * @dev This struct manages the rotation schedule for auction tokens
     * @param tokenByIndex Mapping from index to token address (0-based: 0 = token1, 1 = token2)
     * @param tokenCount Number of tokens currently registered in the schedule
     * @param scheduledIndex Mapping from token address to its position (1-based, 0 = not scheduled)
     * @param scheduleSet Whether the auction schedule has been initialized
     * @param scheduleStart Unix timestamp when auctions begin (typically GMT+3 17:00 / 5 PM)
     * @param auctionDaysLimit Total number of auction slots available (scheduleSize × MAX_CYCLES_PER_TOKEN)
     * @param scheduleSize Maximum number of tokens that can be scheduled (configurable, testing with various values)
     */
    struct AuctionSchedule {
        mapping(uint256 => address) tokenByIndex; // index 0 = token1, index 1 = token2, etc.
        uint256 tokenCount; // actual number of tokens registered
        mapping(address => uint256) scheduledIndex;
        bool scheduleSet;
        uint256 scheduleStart;
        uint256 auctionDaysLimit;
        uint256 scheduleSize;
    }

    /**
     * @notice Get the currently active auction token
     * @param schedule Auction schedule storage reference
     * @param currentTime Current block timestamp
     * @return tokenOfDay Address of the token scheduled for current slot (address(0) if none)
     * @return active Whether we're currently within an active auction window (not in interval)
     * @dev Calculates which auction slot we're in based on AUCTION_DURATION + AUCTION_INTERVAL.
     *      Duration and interval are configurable via AuctionLib constants.
     *      Tokens rotate through schedule continuously.
     */
    function getTodayToken(AuctionSchedule storage schedule, uint256 currentTime) 
        internal 
        view 
        returns (address tokenOfDay, bool active) 
    {
        if (!_auctionStarted(schedule, currentTime)) return (address(0), false);
        if (schedule.tokenCount == 0) return (address(0), false);
        
        // Calculate which auction slot we're in (15 min auction + 5 min interval = 20 min per slot)
        uint256 timeSinceStart = currentTime - schedule.scheduleStart;
        uint256 slotDuration = AuctionLib.AUCTION_DURATION + AuctionLib.AUCTION_INTERVAL;
        uint256 auctionSlot = timeSinceStart / slotDuration; // Each slot is 20 minutes (15 min auction + 5 min break)
        uint256 tokenIndex = auctionSlot % schedule.tokenCount; // Use tokenCount instead of array length
        
        tokenOfDay = schedule.tokenByIndex[tokenIndex]; // Use mapping access instead of array
        
        // Check if we're within the active 15-minute auction window (not in the 5-minute break)
        uint256 auctionStart = schedule.scheduleStart + (auctionSlot * slotDuration);
        uint256 auctionEnd = auctionStart + AuctionLib.AUCTION_DURATION;
        
        active = currentTime >= auctionStart && currentTime < auctionEnd;
    }

    /**
     * @notice Check if auctions have started
     * @param schedule Auction schedule storage reference
     * @param currentTime Current block timestamp
     * @return True if schedule is set and current time is past start time
     */
    function _auctionStarted(AuctionSchedule storage schedule, uint256 currentTime) internal view returns (bool) {
        return schedule.scheduleSet && currentTime >= schedule.scheduleStart;
    }

    /**
     * @notice Calculate current day index since auction start
     * @param schedule Auction schedule storage reference
     * @param currentTime Current block timestamp
     * @return Day index (0-based) for analytics tracking
     * @dev Used for daily statistics aggregation (GMT+3 17:00 / 5 PM timezone boundaries)
     */
    function _currentDayIndex(AuctionSchedule storage schedule, uint256 currentTime) internal view returns (uint256) {
        if (!_auctionStarted(schedule, currentTime)) return 0;
        return (currentTime - schedule.scheduleStart) / 1 days;
    }

    /**
     * @notice Calculate start and end timestamps for current day window
     * @param schedule Auction schedule storage reference
     * @param currentTime Current block timestamp
     * @return start Unix timestamp for start of current day
     * @return end Unix timestamp for end of current day
     * @dev Used for daily analytics boundaries (GMT+3 17:00 / 5 PM timezone)
     */
    function _todayWindow(AuctionSchedule storage schedule, uint256 currentTime) 
        internal 
        view 
        returns (uint256 start, uint256 end) 
    {
        uint256 d = _currentDayIndex(schedule, currentTime);
        start = schedule.scheduleStart + d * 1 days;
        end = start + 1 days;
    }

    /**
     * @notice Calculate how many times a token has appeared in auction rotation
     * @param schedule Auction schedule storage reference
     * @param token Token address to check
     * @param currentTime Current block timestamp
     * @return Number of completed auction cycles for this token (1-based count)
     * @dev This is the "cycle number" used throughout the system.
     * @custom:rotation With N tokens in schedule, each token appears every N slots
     * @custom:formula appearances = (completedSlots - tokenIndex) / tokenCount + 1
     * @custom:limit Cycle maximum (20) enforced in AuctionSwap.sol, not here
     */
    function _appearanceCount(AuctionSchedule storage schedule, address token, uint256 currentTime) 
        internal 
        view 
        returns (uint256) 
    {
        if (!_auctionStarted(schedule, currentTime)) return 0;
        uint256 idx1 = schedule.scheduledIndex[token];
        if (idx1 == 0) return 0;
        uint256 idx = idx1 - 1;
        
        // Count completed auction slots (each slot is 20 minutes: 15 min auction + 5 min interval)
        uint256 timeSinceStart = currentTime - schedule.scheduleStart;
        uint256 slotDuration = AuctionLib.AUCTION_DURATION + AuctionLib.AUCTION_INTERVAL;
        uint256 completedSlots = timeSinceStart / slotDuration;
        
        // Calculate how many times this token has appeared
        if (completedSlots < idx) return 0;
        return (completedSlots - idx) / schedule.tokenCount + 1;
    }

    /**
     * @notice Calculate total number of completed auction slots across all tokens
     * @param schedule Auction schedule storage reference
     * @param currentTime Current block timestamp
     * @return Number of completed auction slots
     * @dev Total slots = MAX_CYCLES_PER_TOKEN × scheduleSize
     *      Used for global progress tracking in AuctionSwap.getGlobalAuctionProgress()
     */
    function _getCompletedSlots(AuctionSchedule storage schedule, uint256 currentTime) 
        internal 
        view 
        returns (uint256) 
    {
        if (!_auctionStarted(schedule, currentTime)) return 0;
        uint256 timeSinceStart = currentTime - schedule.scheduleStart;
        uint256 slotDuration = AuctionLib.AUCTION_DURATION + AuctionLib.AUCTION_INTERVAL;
        return timeSinceStart / slotDuration;
    }

    /**
     * @notice Check if a specific token has an active normal auction right now
     * @param schedule Auction schedule storage reference
     * @param inputToken Token to check
     * @param currentTime Current block timestamp
     * @return True if it's this token's turn AND we're within the auction window
     * @dev This checks TIMING only. Business rules (max cycles, participant limits) are
     *      enforced in AuctionSwap.sol. See contract header for architecture explanation.
     */
    function isAuctionActive(
        AuctionSchedule storage schedule, 
        address inputToken, 
        uint256 currentTime
    ) external view returns (bool) {
        (address today, bool activeWindow) = getTodayToken(schedule, currentTime);
        return activeWindow && today == inputToken;
    }

    /**
     * @notice Check if a specific token has an active reverse auction right now
     * @param schedule Auction schedule storage reference
     * @param inputToken Token to check
     * @param currentTime Current block timestamp
     * @return True if it's this token's turn AND it's a reverse cycle (every 4th appearance)
     * @dev Reverse auctions happen on cycles 4, 8, 12, 16, 20 for each token.
     *      Users can swap earned tokens back to STATE during these cycles.
     *      Business rules (token limits) enforced in AuctionSwap.reverseSwapTokensForState()
     */
    function isReverseAuctionActive(
        AuctionSchedule storage schedule,
        address inputToken,
        uint256 currentTime
    ) external view returns (bool) {
        if (!_isReverseToday(schedule, inputToken, currentTime)) return false;
        
        // Check if we're within the active 2-hour window
        (address today, bool activeWindow) = getTodayToken(schedule, currentTime);
        return activeWindow && today == inputToken;
    }

    /**
     * @notice Internal check if today is a reverse auction day for specific token
     * @param schedule Auction schedule storage reference
     * @param token Token to check
     * @param currentTime Current block timestamp
     * @return True if token is scheduled today AND cycle count is divisible by 4
     * @custom:pattern Reverse cycles occur every 4th appearance (cycles 4, 8, 12, 16, 20)
     * @custom:economic Users can swap earned tokens back to STATE during reverse auctions
     */
    function _isReverseToday(AuctionSchedule storage schedule, address token, uint256 currentTime) internal view returns (bool) {
        (address today, bool active) = getTodayToken(schedule, currentTime);
        if (!active || today != token) return false;
        uint256 count = _appearanceCount(schedule, token, currentTime);
        // Reverse on 4,8,12,... (every 4th appearance)
        return count > 0 && (count % 4 == 0);
    }

    /**
     * @notice Get current auction cycle number for a specific token
     * @param schedule Auction schedule storage reference
     * @param inputToken Token to check
     * @param currentTime Current block timestamp
     * @return Cycle number (1-based count)
     * @dev This is the primary cycle counter used throughout the system.
     *      Returns 0 if auctions haven't started or token not scheduled.
     *      Maximum value is enforced at application layer (see AuctionSwap.MAX_CYCLES_PER_TOKEN)
     */
    function getCurrentAuctionCycle(
        AuctionSchedule storage schedule, 
        address inputToken, 
        uint256 currentTime
    ) external view returns (uint256) {
        return _appearanceCount(schedule, inputToken, currentTime);
    }

    /**
     * @notice Get remaining time in current auction slot
     * @param schedule Auction schedule storage reference
     * @param inputToken Token to check
     * @param currentTime Current block timestamp
     * @return Seconds remaining in current auction window (0 if not active)
     * @custom:precision Integer division used only for slot index; time remaining maintains full second precision
     * @custom:display Frontend displays as countdown timer (e.g., "1h 1m 1s")
     */
    function getAuctionTimeLeft(
        AuctionSchedule storage schedule,
        address inputToken,
        uint256 currentTime
    ) external view returns (uint256) {
        (address today, bool activeWindow) = getTodayToken(schedule, currentTime);
        if (!activeWindow || today != inputToken) return 0;
        
        // Calculate time left in current auction slot
        uint256 timeSinceStart = currentTime - schedule.scheduleStart;
        uint256 slotDuration = AuctionLib.AUCTION_DURATION + AuctionLib.AUCTION_INTERVAL;
        uint256 auctionSlot = timeSinceStart / slotDuration;
        uint256 auctionEnd = schedule.scheduleStart + (auctionSlot * slotDuration) + AuctionLib.AUCTION_DURATION;
        
        return auctionEnd > currentTime ? auctionEnd - currentTime : 0;
    }

    /**
     * @notice Initialize auction schedule with token rotation
     * @param schedule Auction schedule storage reference
     * @param tokens Array of token addresses to rotate (must equal scheduleSize)
     * @param startAt Unix timestamp when auctions begin (typically GMT+3 17:00 / 5 PM)
     * @param supportedTokens Mapping to verify tokens are supported
     * @custom:immutable Can only be called once (scheduleSet prevents re-initialization)
     * @custom:validation Tokens must be supported, unique, and non-zero
     * @custom:indexing Uses 1-based indexing for cycle counting (scheduledIndex[token] = position + 1)
     */
    function setAuctionSchedule(
        AuctionSchedule storage schedule,
        address[] memory tokens,
        uint256 startAt,
        mapping(address => bool) storage supportedTokens
    ) external {
        require(!schedule.scheduleSet, "Schedule already set");
        require(tokens.length == schedule.scheduleSize, "Invalid token count");
        
        // Validate tokens are supported and unique
        for (uint256 i = 0; i < tokens.length; i++) {
            address t = tokens[i];
            require(t != address(0), "Zero address");
            require(supportedTokens[t], "Unsupported token");
            require(schedule.scheduledIndex[t] == 0, "Duplicate token");
            schedule.scheduledIndex[t] = i + 1; // store index+1
            schedule.tokenByIndex[i] = t; // Add token to mapping
        }
        
        schedule.tokenCount = tokens.length;
        schedule.scheduleStart = startAt;
        schedule.scheduleSet = true;
    }

    /**
     * @notice Set maximum number of tokens that can participate in rotation
     * @param schedule Auction schedule storage reference
     * @param newSize New schedule size (1-50)
     * @custom:immutable Can only be called before schedule initialization (scheduleSet = false)
     * @custom:limit Total auction slots calculated as scheduleSize × MAX_CYCLES_PER_TOKEN in AuctionSwap.sol
     */
    function setScheduleSize(AuctionSchedule storage schedule, uint256 newSize) external {
        require(!schedule.scheduleSet, "Schedule already set");
        require(newSize > 0 && newSize <= 50, "Invalid size");
        schedule.scheduleSize = newSize;
    }

    /**
     * @notice Set total number of auction slots available
     * @param schedule Auction schedule storage reference
     * @param daysLimit Total auction slots (typically scheduleSize × MAX_CYCLES_PER_TOKEN)
     * @custom:calculation Set in AuctionSwap constructor: auctionDaysLimit = scheduleSize * 20
     * @custom:scope Global auction capacity limit, not per-token limit
     */
    function setAuctionDaysLimit(AuctionSchedule storage schedule, uint256 daysLimit) external {
        require(daysLimit > 0, "Invalid limit");
        schedule.auctionDaysLimit = daysLimit;
    }
}
          

/

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

/**
 * @title AuctionLib
 * @author State Protocol Team
 * @notice Core constants for auction timing configuration
 * @dev Centralized constants used across auction system for timing calculations
 * @custom:duration 24 hours per auction (production configuration)
 * @custom:interval 0 minutes between auctions (continuous rotation)
 */
library AuctionLib {
    uint256 public constant AUCTION_DURATION = 24 hours; // 24 hours auction duration for production
    uint256 public constant AUCTION_INTERVAL = 0; // No gap between auctions
    
    struct AuctionCycle {
        uint256 firstAuctionStart;
        bool isInitialized;
        uint256 auctionCount;
    }
}
          

/

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

interface SwapEvents {
    event DailyStateReleaseRolled(uint256 indexed dayIndex, uint256 amount, uint256 newDayStart);
    event AuctionStarted(uint256 startTime, uint256 endTime, address inputToken, address stateToken);
    event TokenDeployed(string name, address tokenAddress, uint256 tokenNo);
    event TokensDeposited(address indexed token, uint256 amount);
    event RewardDistributed(address indexed user, uint256 amount);
    event TokensSwapped(address indexed user, address indexed inputToken, address indexed stateToken, uint256 amountIn, uint256 amountOut);
    event TokenAdded(address indexed token, address pairAddress);
    event GovernanceUpdateProposed(address newGov, uint256 timestamp);
    event GovernanceUpdated(address newGov);
    event ContractPaused(address by);
    event ContractUnpaused(address by);
    event AuctionScheduleSet(uint256 startAt, uint256 count);
    event AuctionDaysLimitUpdated(uint256 newLimit);
    event TreasuryUpdated(address indexed oldTreasury, address indexed newTreasury);
    event ProtocolFeeAccrued(address indexed token, uint256 amount);
    event BurnAccrued(address indexed token, uint256 amount);
    event NewDayStarted(uint256 newDayStart);
    event AuctionAdminSet(address indexed admin);
    event AirdropDistributorSet(address indexed airdropDistributor);
    event DavTokenAddressSet(address indexed davToken);
    event DexAddressesUpdated(address router, address factory);
    event UserAutoRegistered(address indexed user, uint256 timestamp);
    event MaxParticipantsUpdated(uint256 oldMax, uint256 newMax);
    event RegistrationCapReached(uint256 maxParticipants);
    event LiquidityAdded(address indexed token, address indexed pair, uint256 amountState, uint256 amountToken, uint256 liquidity);
    event PoolCreated(address indexed token, address indexed pair, uint256 tokenAmount, uint256 stateAmount);
    event LPTokensBurned(address indexed pair, uint256 liquidity, address burnAddress);
    // Protocol governance transfer events
    event ProtocolGovernanceTransferInitiated(address indexed newGovernance, uint256 timestamp);
    event ProtocolGovernanceTransferCompleted(address indexed newGovernance);
    // Vault distribution events
    event VaultDistribution(address indexed token, address indexed recipient, uint256 amount);
    // Auction fee collection event
    event AuctionFeeCollected(address indexed token, uint256 feeAmount, address indexed user);
    // System initialization event
    event SystemInitialized(
        address indexed stateToken,
        address indexed davToken,
        address lpHelper,
        address airdropDistributor,
        address auctionAdmin,
        address buyBurnController,
        address pulseXRouter,
        address pulseXFactory
    );
}
          

/

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

interface SwapErrors {
    error NotGovernance();
    error Unauthorized();
    error PausedErr();
    error UnsupportedToken();
    error ZeroAddr();
    error AlreadySet();
    error ScheduleNotSet();
    error NotStarted();
    error NotToday();
    error Ended();
    error AlreadySwapped();
    error AlreadyReverse();
    error StateNotSet();
    error DavInsufficient();
    error InvalidParam();
    error PairInvalid();
    error PairUsed();
    error TokenExists();
    error NoDAV();
    error InvalidReserves();
    error ReserveFetchFail();
    error InsufficientVault();
    error AmountZero();
    error TimelockNotExpired();
    error NoPendingGov();
    error BadTreasury();
    error ReverseDayLPOonly();
    error ParticipantCapReached();
    error InsufficientBalance();
    error InsufficientAllowance();
    error Step1NotCompleted();
    error Step2NotCompleted();
    error UserNotEligible();
    error NoNormalAuctionParticipation();
    error ExceedsReverseLimit();
    
    // Phase 2 Optimization - New Errors
    error NoActiveAuction();
    error ReverseAuctionActive();
    error NormalAuctionActive();
    error InvalidToken();
    error InvalidAmounts();
    error RouterNotSet();
    error FactoryNotSet();
    error InsufficientSTATE();
    error InsufficientToken();
    error OnlyAdmin();
    error TokensNotRegistered();
    error NoAutoTokens();
    error ScheduleAlreadySet();
    error InvalidStartTime();
    error ArrayLengthMismatch();
    error EmptyArrays();
    error AirdropNotSet();
    error TokenNotSupported();
    error UnauthorizedRegistration();
    error AuctionCyclesCompleted();
    error TokenDeploymentLimitReached();
    error MustClaimAllDavAirdrops();
}
          

/

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

interface IPair {
    function getReserves()
        external
        view
        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function token0() external view returns (address);
    function token1() external view returns (address);
}
          

/

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

interface IAuctionAdmin {
    function pause() external;
    function unpause() external;
    function setMaxAuctionParticipants(address swapContract, uint256 newMax) external;
    function setDexAddresses(address swapContract, address _router, address _factory) external;
    function deployTokenOneClick(address swapContract, string memory name, string memory symbol) external returns (address tokenAddress);
    function updateGovernance(address swapContract, address newGov) external;
    function confirmGovernanceUpdate(address swapContract) external;
    function transferProtocolGovernance(address swapContract, address newGovernance) external;
    function setDavTokenAddress(address swapContract, address _davToken) external;
    function depositTokens(address swapContract, address token, uint256 amount) external;
    function deployUserToken(address swapContract, string memory name, string memory symbol, address _One, address _swap, address _owner) external returns (address);
    function addToken(address swapContract, address token, address pairAddress, address _tokenOwner) external;
    function setAuctionSchedule(address swapContract, address[] calldata tokens) external;
    function setScheduleSize(address swapContract, uint256 newSize) external;
    function setAuctionDaysLimit(address swapContract, uint256 daysLimit) external;
    function setLPHelper(address swapContract, address helper) external;
    function setTreasury(address swapContract, address _treasury) external;
    function withdrawAccruedFees(address swapContract, address token, uint256 amount, address to) external;
    function setVaultAllowance(address swapContract, address token, address spender, uint256 amount) external;
    function setVaultAllowances(address swapContract, address[] calldata tokens, address spender, uint256 amount) external;
    function createPoolForToken(address swapContract, address auctionToken, uint256 tokenAmount, uint256 stateAmount, address tokenOwner) external returns (address pair);
    function startAutoAuction(address swapContract) external;
    function registerTokenWithPair(address swapContract, address token, address tokenOwner, address pairAddress) external;
    
    // Development Fee Wallet Management
    function addDevelopmentFeeWallet(address wallet, uint256 percentage) external;
    function removeDevelopmentFeeWallet(address wallet) external;
    function updateDevelopmentFeeWalletPercentage(address wallet, uint256 newPercentage) external;
    function getDevelopmentFeeWalletsInfo() external view returns (
        address[] memory wallets,
        uint256[] memory percentages,
        bool[] memory activeStatuses
    );
    function getWalletPercentage(address wallet) external view returns (uint256);
    function distributeFeeToWallets(address token, uint256 amount) external;
}
          

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

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

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

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

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

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

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

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

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

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

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

Compiler Settings

{"viaIR":true,"remappings":[":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",":forge-std/=lib/forge-std/src/",":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",":openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"runs":200,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"paris","compilationTarget":{"src/AuctionSwap.sol":"SWAP_V3"}}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_gov","internalType":"address"}]},{"type":"error","name":"AirdropNotSet","inputs":[]},{"type":"error","name":"AlreadyReverse","inputs":[]},{"type":"error","name":"AlreadySet","inputs":[]},{"type":"error","name":"AlreadySwapped","inputs":[]},{"type":"error","name":"AmountZero","inputs":[]},{"type":"error","name":"AmountZero","inputs":[]},{"type":"error","name":"AmountZero","inputs":[]},{"type":"error","name":"ArrayLengthMismatch","inputs":[]},{"type":"error","name":"AuctionCyclesCompleted","inputs":[]},{"type":"error","name":"BadTreasury","inputs":[]},{"type":"error","name":"DavInsufficient","inputs":[]},{"type":"error","name":"DeadlineExpired","inputs":[]},{"type":"error","name":"EmptyArrays","inputs":[]},{"type":"error","name":"Ended","inputs":[]},{"type":"error","name":"ExceedsReverseLimit","inputs":[]},{"type":"error","name":"FactoryNotSet","inputs":[]},{"type":"error","name":"InsufficientAllowance","inputs":[]},{"type":"error","name":"InsufficientBalance","inputs":[]},{"type":"error","name":"InsufficientSTATE","inputs":[]},{"type":"error","name":"InsufficientToken","inputs":[]},{"type":"error","name":"InsufficientVault","inputs":[]},{"type":"error","name":"InvalidAmounts","inputs":[]},{"type":"error","name":"InvalidParam","inputs":[]},{"type":"error","name":"InvalidReserves","inputs":[]},{"type":"error","name":"InvalidReserves","inputs":[]},{"type":"error","name":"InvalidReserves","inputs":[]},{"type":"error","name":"InvalidStartTime","inputs":[]},{"type":"error","name":"InvalidToken","inputs":[]},{"type":"error","name":"MustClaimAllDavAirdrops","inputs":[]},{"type":"error","name":"NoActiveAuction","inputs":[]},{"type":"error","name":"NoAutoTokens","inputs":[]},{"type":"error","name":"NoDAV","inputs":[]},{"type":"error","name":"NoNormalAuctionParticipation","inputs":[]},{"type":"error","name":"NoPendingGov","inputs":[]},{"type":"error","name":"NormalAuctionActive","inputs":[]},{"type":"error","name":"NotGovernance","inputs":[]},{"type":"error","name":"NotStarted","inputs":[]},{"type":"error","name":"NotToday","inputs":[]},{"type":"error","name":"OnlyAdmin","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":"PairInvalid","inputs":[]},{"type":"error","name":"PairInvalid","inputs":[]},{"type":"error","name":"PairInvalid","inputs":[]},{"type":"error","name":"PairUsed","inputs":[]},{"type":"error","name":"ParticipantCapReached","inputs":[]},{"type":"error","name":"PausedErr","inputs":[]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"ReserveFetchFail","inputs":[]},{"type":"error","name":"ReverseAuctionActive","inputs":[]},{"type":"error","name":"ReverseDayLPOonly","inputs":[]},{"type":"error","name":"RouterNotSet","inputs":[]},{"type":"error","name":"SafeERC20FailedDecreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"currentAllowance","internalType":"uint256"},{"type":"uint256","name":"requestedDecrease","internalType":"uint256"}]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"error","name":"ScheduleAlreadySet","inputs":[]},{"type":"error","name":"ScheduleNotSet","inputs":[]},{"type":"error","name":"SlippageExceeded","inputs":[]},{"type":"error","name":"StateNotSet","inputs":[]},{"type":"error","name":"Step1NotCompleted","inputs":[]},{"type":"error","name":"Step2NotCompleted","inputs":[]},{"type":"error","name":"TimelockNotExpired","inputs":[]},{"type":"error","name":"TokenDeploymentLimitReached","inputs":[]},{"type":"error","name":"TokenExists","inputs":[]},{"type":"error","name":"TokenNotSupported","inputs":[]},{"type":"error","name":"TokensNotRegistered","inputs":[]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"error","name":"UnauthorizedRegistration","inputs":[]},{"type":"error","name":"UnsupportedToken","inputs":[]},{"type":"error","name":"UserNotEligible","inputs":[]},{"type":"error","name":"ZeroAddr","inputs":[]},{"type":"event","name":"AirdropDistributorSet","inputs":[{"type":"address","name":"airdropDistributor","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AuctionAdminSet","inputs":[{"type":"address","name":"admin","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AuctionDaysLimitUpdated","inputs":[{"type":"uint256","name":"newLimit","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AuctionFeeCollected","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"uint256","name":"feeAmount","internalType":"uint256","indexed":false},{"type":"address","name":"user","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AuctionScheduleSet","inputs":[{"type":"uint256","name":"startAt","internalType":"uint256","indexed":false},{"type":"uint256","name":"count","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AuctionStarted","inputs":[{"type":"uint256","name":"startTime","internalType":"uint256","indexed":false},{"type":"uint256","name":"endTime","internalType":"uint256","indexed":false},{"type":"address","name":"inputToken","internalType":"address","indexed":false},{"type":"address","name":"stateToken","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"BurnAccrued","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ContractPaused","inputs":[{"type":"address","name":"by","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ContractUnpaused","inputs":[{"type":"address","name":"by","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"DailyStateReleaseRolled","inputs":[{"type":"uint256","name":"dayIndex","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"newDayStart","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DavTokenAddressSet","inputs":[{"type":"address","name":"davToken","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"DexAddressesUpdated","inputs":[{"type":"address","name":"router","internalType":"address","indexed":false},{"type":"address","name":"factory","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceUpdateProposed","inputs":[{"type":"address","name":"newGov","internalType":"address","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceUpdated","inputs":[{"type":"address","name":"newGov","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"LPTokensBurned","inputs":[{"type":"address","name":"pair","internalType":"address","indexed":true},{"type":"uint256","name":"liquidity","internalType":"uint256","indexed":false},{"type":"address","name":"burnAddress","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidityAdded","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"pair","internalType":"address","indexed":true},{"type":"uint256","name":"amountState","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountToken","internalType":"uint256","indexed":false},{"type":"uint256","name":"liquidity","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"MaxParticipantsUpdated","inputs":[{"type":"uint256","name":"oldMax","internalType":"uint256","indexed":false},{"type":"uint256","name":"newMax","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NewDayStarted","inputs":[{"type":"uint256","name":"newDayStart","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":"PoolCreated","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"pair","internalType":"address","indexed":true},{"type":"uint256","name":"tokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"stateAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ProtocolFeeAccrued","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ProtocolGovernanceTransferCompleted","inputs":[{"type":"address","name":"newGovernance","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ProtocolGovernanceTransferInitiated","inputs":[{"type":"address","name":"newGovernance","internalType":"address","indexed":true},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RegistrationCapReached","inputs":[{"type":"uint256","name":"maxParticipants","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RewardDistributed","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SystemInitialized","inputs":[{"type":"address","name":"stateToken","internalType":"address","indexed":true},{"type":"address","name":"davToken","internalType":"address","indexed":true},{"type":"address","name":"lpHelper","internalType":"address","indexed":false},{"type":"address","name":"airdropDistributor","internalType":"address","indexed":false},{"type":"address","name":"auctionAdmin","internalType":"address","indexed":false},{"type":"address","name":"buyBurnController","internalType":"address","indexed":false},{"type":"address","name":"pulseXRouter","internalType":"address","indexed":false},{"type":"address","name":"pulseXFactory","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenAdded","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"pairAddress","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"TokenDeployed","inputs":[{"type":"string","name":"name","internalType":"string","indexed":false},{"type":"address","name":"tokenAddress","internalType":"address","indexed":false},{"type":"uint256","name":"tokenNo","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TokensDeposited","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TokensSwapped","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"inputToken","internalType":"address","indexed":true},{"type":"address","name":"stateToken","internalType":"address","indexed":true},{"type":"uint256","name":"amountIn","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOut","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TreasuryUpdated","inputs":[{"type":"address","name":"oldTreasury","internalType":"address","indexed":true},{"type":"address","name":"newTreasury","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"UserAutoRegistered","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"VaultDistribution","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TotalBurnedStates","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_registerDeployedToken","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"string","name":"name","internalType":"string"},{"type":"address","name":"deployer","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setGovernance","inputs":[{"type":"address","name":"newGov","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"_setMaxAuctionParticipants","inputs":[{"type":"uint256","name":"newMax","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidityToPool","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"tokenAmount","internalType":"uint256"},{"type":"uint256","name":"stateAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"airdropDistributor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IAuctionAdmin"}],"name":"auctionAdmin","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"tokenCount","internalType":"uint256"},{"type":"bool","name":"scheduleSet","internalType":"bool"},{"type":"uint256","name":"scheduleStart","internalType":"uint256"},{"type":"uint256","name":"auctionDaysLimit","internalType":"uint256"},{"type":"uint256","name":"scheduleSize","internalType":"uint256"}],"name":"auctionSchedule","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"autoRegisteredTokens","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"autoScheduleLocked","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burnStateForTokens","inputs":[{"type":"uint256","name":"stateToBurn","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burnTokensForState","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"buyAndBurnController","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"netTokens","internalType":"uint256"}],"name":"calculateNetTokensFromNormalAuction","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"cycle","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateReverseStep2Output","inputs":[{"type":"address","name":"auctionToken","internalType":"address"},{"type":"uint256","name":"stateToBurn","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"canParticipateInAuction","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"pair","internalType":"address"}],"name":"createPoolOneClick","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"tokenAmount","internalType":"uint256"},{"type":"uint256","name":"stateAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentDayStart","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyStateReleased","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyStateReleasedNormal","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyStateReleasedReverse","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailySwapsCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyUniqueSwappersCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IDAV"}],"name":"dav","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"davToken","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"address","name":"tokenAddress","internalType":"address"}],"name":"deployTokenOneClick","inputs":[{"type":"string","name":"name","internalType":"string"},{"type":"string","name":"symbol","internalType":"string"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"scheduleSet","internalType":"bool"},{"type":"uint256","name":"scheduleStart","internalType":"uint256"},{"type":"uint256","name":"registeredTokenCount","internalType":"uint256"},{"type":"uint256","name":"scheduleSize","internalType":"uint256"},{"type":"uint256","name":"auctionDaysLimit","internalType":"uint256"}],"name":"getAuctionScheduleInfo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAuctionTimeLeft","inputs":[{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAvailableDavForAuction","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentAuctionCycle","inputs":[{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentDayIndex","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDavTokensUsed","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"totalEligible","internalType":"uint256"},{"type":"uint256","name":"maxAllowed","internalType":"uint256"},{"type":"uint256","name":"remaining","internalType":"uint256"}],"name":"getEligibilityInfo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"totalSlots","internalType":"uint256"},{"type":"uint256","name":"completedSlots","internalType":"uint256"},{"type":"uint256","name":"remainingSlots","internalType":"uint256"},{"type":"bool","name":"auctionsEnded","internalType":"bool"}],"name":"getGlobalAuctionProgress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getPairAddress","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRatioPrice","inputs":[{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getReverseStateBalance","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"tokenOfDay","internalType":"address"},{"type":"bool","name":"active","internalType":"bool"}],"name":"getTodayToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"completedAuctions","internalType":"uint256"},{"type":"uint256","name":"maxAuctions","internalType":"uint256"},{"type":"uint256","name":"remainingAuctions","internalType":"uint256"},{"type":"bool","name":"isActive","internalType":"bool"},{"type":"bool","name":"isScheduled","internalType":"bool"}],"name":"getTokenAuctionStats","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTokensBurnedByUser","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalTokensBurned","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"step1","internalType":"bool"},{"type":"bool","name":"step2","internalType":"bool"},{"type":"bool","name":"step3","internalType":"bool"},{"type":"uint256","name":"stateBalance","internalType":"uint256"}],"name":"getUserAuctionStatus","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"getUserHasReverseSwapped","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"getUserHasSwapped","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserStateBalance","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governanceAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasCompletedStep1","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasCompletedStep2","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasCompletedStep3","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasUserBurnedForToken","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasUserCompletedReverseStep1","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasUserCompletedReverseStep2","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"address","name":"auctionToken","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initializeCompleteSystem","inputs":[{"type":"address","name":"_stateToken","internalType":"address"},{"type":"address","name":"_davToken","internalType":"address"},{"type":"address","name":"_airdropDistributor","internalType":"address"},{"type":"address","name":"_auctionAdmin","internalType":"address"},{"type":"address","name":"_buyBurnController","internalType":"address"},{"type":"address","name":"_swapLens","internalType":"address"},{"type":"address","name":"_pulseXRouter","internalType":"address"},{"type":"address","name":"_pulseXFactory","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAuctionActive","inputs":[{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAutoRegistered","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isRegisteredForAuctions","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isReverseAuctionActive","inputs":[{"type":"address","name":"inputToken","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTokenSupported","inputs":[{"type":"address","name":"token","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isUserEligible","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxAuctionParticipants","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pulseXFactory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pulseXRouter","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerUserForAuctions","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reverseSwapTokensForState","inputs":[{"type":"uint256","name":"tokenAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMaxAuctionParticipants","inputs":[{"type":"uint256","name":"newMax","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"startAuctionWithAutoTokens","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stateReleasedByDayIndex","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stateReleasedNormalByDayIndex","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stateReleasedReverseByDayIndex","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"stateToken","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"swapLens","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapTokens","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swapsCountByDayIndex","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAuctionsCompleted","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalRegisteredUsers","inputs":[]},{"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":"uniqueSwappersCountByDayIndex","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]}]
              

Contract Creation Code

0x6080346200040f57601f62005a3838819003918201601f1916830192916001600160401b038411838510176200041457808392604095865283396020928391810103126200040f57516001600160a01b038082169290918390036200040f576000928354917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09333908416858780a36001805560ff19600d5416600d556109c4600e5584601f558460215560018060a01b0319918260055416176005554215620003d8576001600160ff1b034281106200039457612a31600160ff1b0342136200035f57612a30908142019182128742129080158216911516176200021e578682126200032a5762015180808304818102918183041490151715620003165761ef10198111620002d25761ef10810190818111620002be57818094101562000265575b505081116200023257612a2f198101916001828413166200021e57868312620001ec5742831115620001b9575062017baf1901908111620001a557600f556002601b556028601a55803316911617825533908280a35161560d90816200042b8239f35b634e487b7160e01b85526011600452602485fd5b60649088519062461bcd60e51b82526004820152600d60248201526c4e6f6e2d66757475726520747360981b6044820152fd5b60649088519062461bcd60e51b82526004820152600c60248201526b05554432074696d65203c20360a41b6044820152fd5b634e487b7160e01b87526011600452602487fd5b865162461bcd60e51b815260048101839052600c60248201526b2aaa219031b0b9ba1037bb3360a11b6044820152606490fd5b91925090620151801982116200028b5762024090018091116200021e5790388062000142565b885162461bcd60e51b815260048101859052600c60248201526b2732bc3a103230bc9037bb3360a11b6044820152606490fd5b634e487b7160e01b89526011600452602489fd5b885162461bcd60e51b815260048101859052601360248201527f5461726765742063616c63206f766620286829000000000000000000000000006044820152606490fd5b634e487b7160e01b88526011600452602488fd5b875162461bcd60e51b815260048101849052600e60248201526d04c6f63616c2074696d65203c20360941b6044820152606490fd5b865162461bcd60e51b815260048101839052600e60248201526d4c6f63616c206f766572666c6f7760901b6044820152606490fd5b865162461bcd60e51b815260048101839052601360248201527f54696d657374616d7020746f6f206c61726765000000000000000000000000006044820152606490fd5b60649086519062461bcd60e51b8252600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001257600080fd5b6000803560e01c806303728c5014613b4257806303ed352a146134655780630591bcf214613447578063088041ea146133d05780630d6c720014612ecf5780631071d8b714612eb157806313f9afb314612e885780631656feb614612e4f57806316779c5e14612e3157806319653b7314612e0d5780631979a67314612de95780631ac57d5514612dbf578063266959d514612d965780632d5413c414612d335780632f871e8114612cd557806333d5cd0814612bb057806334172cff14612afe57806335cbfffc14612ad45780633c7afca514612ab65780633f4ba83a14612a5c578063465521d0146129bf5780634dce4e10146129a15780634f01a3a7146129785780634fc4a94214612907578063513e18a5146128dd578063546567481461288157806354ab8ea9146128635780635a4d30cd146128455780635c975abb146128225780635df2f5bc1461245d57806360f8d76c14612423578063684f879c146123f95780636d4c8b6e146114bd5780636e89e43d146123b3578063715018a61461235957806373d0022414611bc857806375151b6314611b895780637519ddef14611b4a57806375858e3514611aef578063795053d314611ac657806379c7ce3914611a635780637dfadcbb14611a0b5780637fa33aea146119ed5780638456cb591461199057806385e694b614611954578063879df1f31461146157806389610dc11461192b5780638c660298146118ab5780638da5cb5b146118845780638e7952c61461185b5780638fdee2841461182457806390e986fc146117e557806392a2c8e114611787578063933ed7cc146105a257806394b6b453146115ef57806395f7d9d31461156c57806397f009631461153c5780639bf4ca5e146115185780639cf20866146114bd5780639f181b5e1461149f578063a0951b4414611461578063a0eb8cce14611438578063aada452c14611404578063ad0d71f814611083578063ad2b3e4014611059578063aec9b6f414611030578063b104839214611012578063bb6260b214610fb4578063ca99911d14610f90578063cea4a38314610c05578063cf0fce4814610ba7578063d0a9317314610b7e578063d36483f7146106f7578063d4817d04146106d9578063da237e8e14610615578063dfdd4ea4146105ec578063e20e9db2146105a7578063e606adeb146105a2578063e701be961461057e578063eb638bb21461055b578063f2c52a6314610480578063f2fde38b146103f55763f644d82a146103ae57600080fd5b346103f257806003193601126103f25760a060ff60185416601954601654601b5490601a549260405194151585526020850152604084015260608301526080820152f35b80fd5b50346103f25760203660031901126103f25761040f613b6a565b610417613d00565b6001600160a01b0390811690811561046757600054826001600160601b0360a01b821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b604051631e4fbdf760e01b815260048101849052602490fd5b50346103f257602090816003193601126103f2576001600160a01b036104a4613b6a565b16808252602b835260ff60408320541615610549576040519063c4b65a7d60e01b8252601560048301526024820152426044820152828160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af491821561053d579161050b575b50604051908152f35b90508181813d8311610536575b6105228183613c04565b81010312610531575138610502565b600080fd5b503d610518565b604051903d90823e3d90fd5b60405163350b944160e11b8152600490fd5b50346103f257806003193601126103f257602060ff601e54166040519015158152f35b50346103f257806003193601126103f257602061059a42614de1565b604051908152f35b613c9f565b50346103f257806003193601126103f25760a060165460ff60185416601954601a5490601b549260405194855215156020850152604084015260608301526080820152f35b50346103f257806003193601126103f2576007546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f257608090610632613b6a565b61063a613b80565b61064381614229565b9160406106508383613e85565b9460018060a01b0394858416948583526106948260209660328852868620938a81169485600052895287600020838852895260ff88882054169a60035416916152b7565b83526031855260ff8484205416958352603385528383209060005284528260002090825283522054926040519415158552151590840152151560408301526060820152f35b50346103f257806003193601126103f2576020601454604051908152f35b50346103f257602080600319360112610b7a57610712613d94565b60ff600d5416610b6857610724613fc9565b61072d42614d2b565b919091158015610b57575b610b455761074582614130565b15610b33576001600160a01b03828116808552602b8352604085205490939060ff161561054957816003541615610b215761077f81614130565b15610b0f5761078d81614229565b9133865260368452604086208560005284526040600020838752845260ff60408720541615610afd5733865260388452604086208560005284526040600020838752845260ff604087205416610aeb573386526037845260408620856000528452604060002083875284526040862054928315610a79576003546040516370a0823160e01b80825233600483015294918416908781602481855afa8015610ae05787918b91610aaf575b5010610a9d5761084682614c28565b8015610a8b576108569087615274565b928315610a795760328402848104603203610a655761271061087a91048095613d67565b9560405190815230600482015288816024818d5afa908115610a5a578b91610a2d575b506108a88588613d2c565b11610a1b5760405160c08101918183106001600160401b03841117610a05578b92604052338252898201938b8552604083019081526060830191825260808301928a845260a081018a815273cc9b07bdcd2402c10adf6684647d278026dc5b8a90813b15610a015786958b958661012496816040519c8d9a8b99632244635360e01b8b52511660048a015251166024880152511660448601525160648501525160848401525160a4830152603860c4830152603760e4830152603b6101048301525af49182156109f45784926109d9575b505061098491614c6c565b604051908152857f1353e99c221a4396b23e48ce384c25087a6e2981ef2ccc6ed3960c885665e229863393a360035416926040519283528201526000805160206155b883398151915260403392a46001805580f35b6109e4919250613bd6565b6109f057818838610979565b8780fd5b50604051903d90823e3d90fd5b8680fd5b634e487b7160e01b600052604160045260246000fd5b604051631b74457560e31b8152600490fd5b90508881813d8311610a53575b610a448183613c04565b8101031261053157513861089d565b503d610a3a565b6040513d8d823e3d90fd5b634e487b7160e01b8b52601160045260248bfd5b6040516365e52d5160e11b8152600490fd5b604051633494a40d60e21b8152600490fd5b604051631e9acf1760e31b8152600490fd5b809250898092503d8311610ad9575b610ac88183613c04565b810103126105315786905138610837565b503d610abe565b6040513d8c823e3d90fd5b60405163757d07f760e01b8152600490fd5b604051630c340a6160e01b8152600490fd5b604051636f312cbd60e01b8152600490fd5b604051632b50d3bd60e11b8152600490fd5b60405163438e694160e11b8152600490fd5b60405163568ca25760e01b8152600490fd5b506001600160a01b03821615610738565b60405163da82933960e01b8152600490fd5b5080fd5b50346103f257806003193601126103f2576006546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576040602091610bc6613b6a565b610bce613b80565b610bd781614229565b9160018060a01b03809116845260348652848420911660005284528260002090825283522054604051908152f35b50346103f2576101003660031901126103f257610c20613b6a565b90610c29613b80565b610c31613b96565b916064359360018060a01b03851680950361053157608435926001600160a01b03841684036105315760a4356001600160a01b038116908190036105315760c4356001600160a01b03811681036105315760e435916001600160a01b0383168303610531576005546001600160a01b03163303610f7e57610cb0613d94565b6001600160a01b03851615610b21576001600160a01b03841615610a8b576001600160a01b03881615610a8b578815610a8b576001600160a01b03871615610a8b576001600160a01b03821615610f6c576001600160a01b03831615610f5a57600380546001600160a01b03199081166001600160a01b038881169190911790925560048054821687841690811790915560028054831690911790556006805482168b84161790556007805482168c1790556008805482168a8416179055600980548216909317909255600a80548316848316179055600b8054909216908416179055610d9d86856145e4565b845b601654811015610ebb57808652601560205260408620546001600160a01b031680610dd4575b50610dcf90613dca565b610d9f565b60405163095ea7b360e01b81526001600160a01b038a166004820152602481018890526020816044818b865af18015610eb057916020918993610e93575b5060405163095ea7b360e01b81526001600160a01b038c166004820152600019602482015292839160449183915af18015610e885790610dcf9291610e59575b5090610dc5565b610e7a9060203d602011610e81575b610e728183613c04565b810190613d4f565b5038610e52565b503d610e68565b6040513d89823e3d90fd5b610ea990833d8511610e8157610e728183613c04565b5038610e12565b6040513d8a823e3d90fd5b506040519497939694957f7e0f8631140eafdd46d4ee7cb1f77ee3266b38d53d291291670e77e7a0c1ac1c9560c095906001600160a01b0381167f515670ca58d45a6a4fef4f8838f89179a87c989784c028b16c6979d5eed1d1898c80a28a89526001600160a01b0390811660208a015260408901919091529081166060880152908116608087015290811660a0860152908116941692a36001805580f35b6040516329f7dfeb60e21b8152600490fd5b60405163179ce99f60e01b8152600490fd5b604051632d5be4cb60e21b8152600490fd5b50346103f25760203660031901126103f257602061059a610faf613b6a565b614229565b50346103f25760403660031901126103f2576040602091610fd3613b6a565b610fdb613b80565b610fe481614229565b9160018060a01b03809116845260378652848420911660005284528260002090825283522054604051908152f35b50346103f257806003193601126103f2576020601054604051908152f35b50346103f257806003193601126103f257600a546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602683522054604051908152f35b50346103f257602090816003193601126103f257600435906110a3613d94565b60ff600d5416610b685781926110b7613fc9565b6110c042614d2b565b9290921580156113f3575b610b45576110d883614130565b15610b33576110e633614098565b6001600160a01b03838116808352602b8452604083205491939160ff1615610549576003968488541615610b215761111d86614130565b15610b0f5761112b86614229565b94601486116113e15733855260368452604085208360005284526040600020868652845260ff604086205416610aeb57670de0b6b3a764000061116d33613f53565b106113cf57600198855b818b1115806113c6575b156111b3576111a76111ad916111a161119a8e8c613d67565b8c33614282565b90613d2c565b9a613dca565b99611177565b90878993928b81156113b45781106113ac575b508415610a79576040516370a0823160e01b808252336004830152949088816024818b5afa8015610ae05787918b9161137b575b5010610a9d57868952602b885260ff60408a20541615610549576112328484541691888b52602a8a52828660408d2054169189615078565b948515610a7957889060246040518094819382523060048301525afa80156113705785918a9161133f575b5010610a1b5733885260368752604088208660005287526040600020818952875260408820600160ff198254161790553388526037875260408820866000528752604060002090885286528260408820556112ba84303388613dd9565b805460405163a9059cbb60e01b88820152336024820152604481018590526112fa9184166112f582606481015b03601f198101845283613c04565b613e1d565b61130683601054613d2c565b60105561131583601254613d2c565b6012555416936040519283528201526000805160206155b883398151915260403392a46001805580f35b809250898092503d8311611369575b6113588183613c04565b81010312610531578490518a61125d565b503d61134e565b6040513d8b823e3d90fd5b8092508a8092503d83116113a5575b6113948183613c04565b81010312610531578690518b6111fa565b503d61138a565b9450886111c6565b6040516315d933e760e21b8152600490fd5b508a8811611181565b604051632525e44360e01b8152600490fd5b6040516331f6627f60e01b8152600490fd5b506001600160a01b038316156110cb565b50346103f257806003193601126103f257604061142042614d2b565b82516001600160a01b03909216825215156020820152f35b50346103f257806003193601126103f2576008546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760209060ff906040906001600160a01b0361148c613b6a565b1681528380522054166040519015158152f35b50346103f257806003193601126103f2576020602f54604051908152f35b50346103f25760403660031901126103f25760ff60406020926115056114e1613b6a565b6114e9613b80565b906114f382614229565b9160018060a01b0360035416916152b7565b8152603184522054166040519015158152f35b50346103f25760203660031901126103f257602061059a611537613b6a565b614c28565b50346103f25760603660031901126103f257602061059a61155b613b6a565b611563613b80565b60443591614282565b50346103f257806003193601126103f257601a546018546080929060ff161580156115e4575b156115ce57805b828110801592906115bd57905b604051938452602084015260408301526060820152f35b506115c88184613d67565b906115a6565b620151806115de60195442613d67565b04611599565b506019544210611592565b50346103f257806003193601126103f2576005546001600160a01b039081163303610f7e5760ff601e54161561177557601c5480156117635760ff601854166117515761163b426152ff565b9173a767651f7fe10d7a9cf1081e22da6d980d51fc5290813b1561174d5790849260405192634a19951960e11b8452816084850160156004870152608060248701525260a4840191601c86527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a2119186905b82821061172e5750505050839183808093886044830152602b606483015203915af480156117235761170f575b507f106566fd12b1b74887bf4a53c2c6a7cd6736796703181540521a8f6dff49a068604083601c5482519182526020820152a180f35b61171890613bd6565b610b7a5781386116d9565b6040513d84823e3d90fd5b83548116855289975060209094019360019384019391909101906116ac565b8480fd5b604051631f8c8d2360e11b8152600490fd5b604051636fca87f160e11b8152600490fd5b6040516317a4461160e11b8152600490fd5b50346103f25760403660031901126103f25760406020916117a6613b6a565b6117ae613b80565b6117b781614229565b9160018060a01b03809116845260358652848420911660005284528260002090825283522054604051908152f35b50346103f25760203660031901126103f2576007546001600160a01b0316330361181257600435600e5580f35b604051634755657960e01b8152600490fd5b50346103f25760403660031901126103f2576020611851611843613b6a565b61184b613b80565b90613e85565b6040519015158152f35b50346103f257806003193601126103f2576004546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f257546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576020906118c8613b6a565b6118d0613b80565b906118da81613f53565b916118e481614229565b9160018060a01b038091168552603586526040852091166000528452604060002090835283526040822054908181116000146119245761059a9250613d67565b505061059a565b50346103f257806003193601126103f2576009546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f2576020906001600160a01b03906040908261197e613b6a565b168152602a8452205416604051908152f35b50346103f257806003193601126103f2576005546001600160a01b03163303610f7e57600160ff19600d541617600d557f81990fd9a5c552b8e3677917d8a03c07678f0d2cb68f88b634aca2022e9bd19f6020604051338152a180f35b50346103f257806003193601126103f2576020601f54604051908152f35b50346103f257806003193601126103f257602154600e549181831115611a5a5750611a56611a398284613d67565b604051938493846040919493926060820195825260208201520152565b0390f35b611a5690611a39565b50346103f25760403660031901126103f25760ff6040602092611a84613b6a565b611a8c613b80565b611a9581614229565b9160018060a01b03809116845260368752848420911660005285528260002090825284522054166040519015158152f35b50346103f257806003193601126103f2576005546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f257611b09613b6a565b6006546001600160a01b031633141580611b40575b611b2e57611b2b90614098565b80f35b6040516313444ccd60e11b8152600490fd5b5030331415611b1e565b50346103f25760203660031901126103f25760209060ff906040906001600160a01b03611b75613b6a565b168152601d84522054166040519015158152f35b50346103f25760203660031901126103f25760209060ff906040906001600160a01b03611bb4613b6a565b168152602b84522054166040519015158152f35b50346103f257806003193601126103f257611be1613d94565b60ff600d5416610b6857611bf3613fc9565b611bfc42614d2b565b158015612348575b610b4557611c1181614130565b612336573315612324576001600160a01b0381168252602b602052604082205460ff1615610549576003546001600160a01b031615610b2157670de0b6b3a7640000611c5c33613f53565b106113cf5760ff6018541615612312576019544210610b0f57611c7e42614d2b565b90816122fc575b50156122ea573382526020805260ff604083205416156122cd575b3382526020805260ff60408320541615612243575b611cbe81614229565b90611cd58260018060a01b036003541683336152b7565b835260316020526040832090826001830155611cf081614130565b612231576040516302a41d0b60e11b8152601560048201526001600160a01b038216602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115612226578591612207575b5015610b0f5733845260336020526040842060018060a01b038216600052602052604060002083855260205260408420549283156121f557600354604051636eb1769f60e11b81523360048201523060248201526001600160a01b0390911690602081604481855afa8015610e8857869188916121c0575b50106121ae57600a546001600160a01b031615610f6c5784611de59130903390613dd9565b600354600a5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810187905291602091839160449183918b91165af180156121215761218f575b50604051606081018181106001600160401b03821117610a055760405260028152604036602083013760018060a01b036003541681511561217957602082018190526001600160a01b038416611e7f83614c18565b526001600160a01b0384168752602b602052604087205460ff1615610549576001600160a01b038481168852602a6020526040882054611ec492911690859088615078565b9081605f810204605f148215171561216557600a546001600160a01b0316904261012c810110612151579187916040519384926338ed173960e01b84526064605f60a48601928c60048801520204602485015260a060448501528151809152602060c48501920190855b81811061212c575050508383809233606483015261012c4201608483015203925af18015612121578690612081575b611f679150614c18565b51928315610a7957338652603a6020526040862060018060a01b038416600052602052604060002082875260205260408620611fa4858254613d2c565b9055600160ff1982541617905533855260336020526040852060018060a01b038316600052602052604060002090855260205260408420611fe6848254613d67565b90556013546001810180911161206d5760135562015180600f540433855260276020526040852081815403612054575b50506003546040805194855260208501939093526001600160a01b039182169391169133916000805160206155b88339815191529190a46001805580f35b556014546001810180911161206d576014553880612016565b634e487b7160e01b85526011600452602485fd5b503d8087833e6120918183613c04565b810190602081830312610a01578051906001600160401b0382116109f057019080601f83011215610a01578151916001600160401b038311610a05578260051b90604051936120e36020840186613c04565b845260208085019282010192831161211d57602001905b82821061210d57505050611f6790611f5d565b81518152602091820191016120fa565b8880fd5b6040513d88823e3d90fd5b82516001600160a01b031684528c965087955060209384019390920191600101611f2e565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b87526011600452602487fd5b634e487b7160e01b600052603260045260246000fd5b6121a79060203d602011610e8157610e728183613c04565b5038611e2a565b6040516313be252b60e01b8152600490fd5b9150506020813d6020116121ed575b816121dc60209383613c04565b810103126105315785905138611dc0565b3d91506121cf565b6040516315cb5c9360e11b8152600490fd5b612220915060203d602011610e8157610e728183613c04565b38611d48565b6040513d87823e3d90fd5b6040516349426f9160e11b8152600490fd5b602154600e5411156122bb573382526020805260408220600160ff19825416179055602154600181018091116122a7576021556040514281527ffefcfe9bb8f97bbb317ab61b7f1139aa02e376de0f944a2b4f81a925c5ac804060203392a2611cb5565b634e487b7160e01b83526011600452602483fd5b6040516330bf333360e11b8152600490fd5b602154600e5411611ca0576040516330bf333360e11b8152600490fd5b604051635f2f180d60e01b8152600490fd5b6001600160a01b03838116911614905038611c85565b604051636fd49a7d60e11b8152600490fd5b60405163ae9954c360e01b8152600490fd5b6040516301a6772f60e11b8152600490fd5b506001600160a01b03811615611c04565b50346103f257806003193601126103f257612372613d00565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346103f25760203660031901126103f2576123cd613b6a565b6007546001600160a01b0391908216330361181257166001600160601b0360a01b600554161760055580f35b50346103f25760203660031901126103f25760406020916004358152602283522054604051908152f35b50346103f25760403660031901126103f25760ff60406020926124476114e1613b6a565b815260318452205460081c166040519015158152f35b50346103f25761246c36613bac565b600554919392916001600160a01b03163303610f7e5761248a613d94565b6001600160a01b03821615612324576003546001600160a01b03908116939083168414610a8b5784159384801561281a575b610a7957600b5460405163e6a4390560e01b81526001600160a01b0386811660048301528381166024830152909160209183916044918391165afa90811561280f5783916127e0575b506001600160a01b0316928315610a8b576040516370a0823160e01b8082523060048301526024939160208186816001600160a01b038c165afa90811561212157908a9187916127ab575b5010610a1b57602090846040518094819382523060048301525afa9081156127a057908291859161276b575b5010610a1b57600a5461259a9088906001600160a01b0316876146e7565b600354600a546125b89183916001600160a01b0390811691166146e7565b600a546003546001600160a01b039081169791169190605f898102918a8304909114171561275857605f820290828204605f148315171561274557610e1042019283421161273257869798999a6040519b8c9762e8e33760e81b8952600160a01b600190038d1660048a0152880152604487015260648601526064900460848501526064900460a484015260c4830161dead905260e4830152815a9361010492606095f19283156109f457819482946126fb575b508315610a7957600a546020957f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f79290916126b0906001600160a01b0316826144c6565b600354600a546126cc916001600160a01b0391821691166144c6565b6040805194855260208501929092529083018590526001600160a01b031691606090a360018055604051908152f35b9150925061272191935060603d811161272b575b6127198183613c04565b81019061498b565b919391923861266c565b503d61270f565b634e487b7160e01b875260116004528587fd5b634e487b7160e01b865260116004528486fd5b634e487b7160e01b855260116004528385fd5b9150506020813d602011612798575b8161278760209383613c04565b81010312610531578190513861257c565b3d915061277a565b6040513d86823e3d90fd5b9150506020813d6020116127d8575b816127c760209383613c04565b810103126105315789905138612550565b3d91506127ba565b612802915060203d602011612808575b6127fa8183613c04565b810190614346565b38612505565b503d6127f0565b6040513d85823e3d90fd5b5082156124bc565b50346103f257806003193601126103f257602060ff600d54166040519015158152f35b50346103f257806003193601126103f2576020600f54604051908152f35b50346103f257806003193601126103f2576020601354604051908152f35b50346103f25760203660031901126103f257600435601c54811015610b7a57601c6000527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a21101546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602583522054604051908152f35b50346103f25760203660031901126103f257600554600435906001600160a01b03163303610f7e578015610a8b576021548110610a8b5760407f43f71dfd216eaaf2f75bdb23d66ae4f5b7e2975e422538b68ef11b46b387409891600e549080600e5582519182526020820152a180f35b50346103f257806003193601126103f257600b546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f2576020601154604051908152f35b50346103f25760203660031901126103f2576129d9613b6a565b906129e382614229565b9060148210612a305760a0926040825b6129fc836141ba565b92600180881b0316815260176020522054151591604051938452601460208501526040840152151560608301526080820152f35b816014039260148411612a4857604060a094926129f3565b634e487b7160e01b82526011600452602482fd5b50346103f257806003193601126103f2576005546001600160a01b03163303610f7e5760ff19600d5416600d557f5b65b0c1363b3003db9bcc5e1fd8805a6d6bf5bf6dc9d3431ee4494cd7d117666020604051338152a180f35b50346103f257806003193601126103f2576020602154604051908152f35b50346103f25760203660031901126103f25760406020916004358152602483522054604051908152f35b50346103f25760403660031901126103f257612b56906020612b1e613b6a565b612b26613b80565b60405163118cc05360e31b81526001600160a01b0392831660048201529116602482015292839081906044820190565b0381305afa90811561053d5790612b7e575b602090670de0b6b3a76400006040519110158152f35b506020813d8211612ba8575b81612b9760209383613c04565b810103126105315760209051612b68565b3d9150612b8a565b50346103f25760603660031901126103f257612bca613b6a565b6024356001600160401b038111612cd157612be9903690600401613c40565b612bf1613b96565b6007546001600160a01b039391908416330361181257601c54601b541115612cbf57612c6a92612c659185841695868852602c60205260408820911690816001600160601b0360a01b825416179055808752602d602052612c558460408920614a68565b8652602860205260408620614aa3565b614a04565b8152601d6020526040812060ff1990600182825416179055602b60205260408220600182825416179055612c9f602f54613dca565b602f55601c54601b5414612cb1575080f35b600190601e541617601e5580f35b604051636eff6d6b60e11b8152600490fd5b8280fd5b50346103f25760403660031901126103f2576040602091612cf4613b6a565b612cfc613b80565b612d0581614229565b9160018060a01b03809116845260338652848420911660005284528260002090825283522054604051908152f35b50346103f25760403660031901126103f25760ff6040602092612d54613b6a565b612d5c613b80565b612d6581614229565b9160018060a01b03809116845260388752848420911660005285528260002090825284522054166040519015158152f35b50346103f257806003193601126103f2576002546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602383522054604051908152f35b50346103f25760203660031901126103f2576020611851612e08613b6a565b6141ba565b50346103f25760203660031901126103f2576020611851612e2c613b6a565b614130565b50346103f257806003193601126103f2576020600c54604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b03612e77613b6a565b168152603b83522054604051908152f35b50346103f257806003193601126103f2576003546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f2576020600e54604051908152f35b50346103f257806003193601126103f257612ee8613d94565b60ff600d5416610b6857612efa613fc9565b612f0342614d2b565b1580156133bf575b610b4557612f1881614130565b612336576001600160a01b0381168252602b602052604082205460ff1615610549576003546001600160a01b031615610b2157612f5481614229565b612f5e8233613e85565b15610afd57612f6c33613f53565b91670de0b6b3a7640000928381106113cf573385526035602090815260408087206001600160a01b03851688528252808720858852909152852054808211156133b757612fb891613d67565b8381106113cf5768878678326eac900000928382029382850414821517156133a35784840415610a79576040516370a0823160e01b80825233600483015292906020816024816001600160a01b0389165afa908115610eb0578891613371575b5086860411610a9d57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b0389165afa908115610eb057889161333f575b50868604116121ae5761306c84614c28565b8015610a8b5761307e90878704613db7565b8060011b908082046002149015171561215157869004908115610a79576032820282810460320361332b576127106130b891048093613d67565b9360018060a01b036003541690604051908152306004820152602081602481855afa908115610ae0578a916132f5575b506130f38487613d2c565b11610a1b5760405160e081018181106001600160401b038211176132e157604052338152602081019260018060a01b0388168452604082019283526060820186815260808301908b8b04825260a084019289845260c085015273cc9b07bdcd2402c10adf6684647d278026dc5b8a3b156132dd57604051632823ecdb60e21b815284516001600160a01b039081166004830152965187166024820152945190951660448501529351606484015292516084830152915160a482015260c09091015160c4820152603260e4820152603361010482015260346101248201526035610144820152603b61016482015287816101848173cc9b07bdcd2402c10adf6684647d278026dc5b8a5af48015610eb0576132ca575b506003546132209082906001600160a01b0316614c6c565b60018060a01b03600354166040519182527f1353e99c221a4396b23e48ce384c25087a6e2981ef2ccc6ed3960c885665e22960203393a333865260396020526040862060018060a01b03841660005260205260406000209086526020526040852061328c828254613d2c565b905560018060a01b03600354169360405193048352602083015260018060a01b0316906000805160206155b883398151915260403392a46001805580f35b6132d690979197613bd6565b9538613208565b8c80fd5b634e487b7160e01b8b52604160045260248bfd5b90506020813d602011613323575b8161331060209383613c04565b8101031261331f5751386130e8565b8980fd5b3d9150613303565b634e487b7160e01b89526011600452602489fd5b90506020813d602011613369575b8161335a60209383613c04565b810103126109f057513861305a565b3d915061334d565b90506020813d60201161339b575b8161338c60209383613c04565b810103126109f0575138613018565b3d915061337f565b634e487b7160e01b86526011600452602486fd5b505083612fb8565b506001600160a01b03811615612f0b565b50346103f25760403660031901126103f2576001600160401b03600435818111612cd157613402903690600401613c40565b916024359182116103f2575061341c903690600401613c40565b60055490916001600160a01b039182163303610f7e5760209261343e916143ad565b60405191168152f35b50346103f257806003193601126103f2576020601254604051908152f35b50346103f25761347436613bac565b60055490926001600160a01b03929183163303610f7e57828116918215613b3057801591828015613b28575b613b16578460035416928315610b215785600a541615610f6c5785600b5416968715610f5a576040516370a0823160e01b808252306004830152602099918a816024818b5afa8015613b0b5784918d91613ad6575b5010613ac45760405190815230600482015289816024818b5afa8015610a5a5786918c91613a8f575b5010613a7d5760405163e6a4390560e01b81526001600160a01b038581166004830152871660248201528a918a82604481845afa97881561280f578b928499613a5e575b508a8916156139f4575b5050600354600a5460405163095ea7b360e01b808252918c166001600160a01b0316600482015260248101869052935091508a9083908b16818e816044810103925af1908115610a5a5786928b926139d7575b50600a546040519182528a166001600160a01b031660048201526024810192909252816044818d8b5af18015610ae0576139ba575b5086600a541690876003541692605f860290868204605f141715610a6557605f820290828204605f14831517156139a657610e1042019283421161399257936101049360648b948f94829060609a996040519d8e9b8c9a62e8e33760e81b8c5260048c015260248b015260448a0152828901520460848701520460a48501523060c485015260e48401525af1948515610e885787809481948298613967575b508216966040519063a9059cbb60e01b8252898260448161dead968760048301528560248301528d5af1908115610a5a5789937fee362beb50174d6f1b6792512b350a67767a22b6750da5940b7f0ee69818c45b9360409361394a575b5082519182528b820152a2848852602b8752604088209160ff1992600184825416179055602a8852604089206001600160601b0360a01b908882825416179055601d895260ff60408b2054161561377d575b888880897fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c60408b8b825191825287820152a3604051908152f35b6040516306fdde0360e01b81528a816004818b5afa8b918161392d575b506138cc57506040516395d89b4160e01b81528a816004818b5afa8b9181613908575b506138cc5750604051604081018181106001600160401b038211176138b8578a9b604097956138707fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c9a9896602d8e9f613880988f988e988952600d81526c2ab735b737bbb7102a37b5b2b760991b83820152985b61383b85614a04565b8752601d825287872060018c8254161790558560055416602c8352888820918254161790558460055416865252848420614a68565b60055416815260288d5220614aa3565b61388b602f54613dca565b602f55601c54601b54146138a7575b8897839550829450613742565b600190601e541617601e553861389a565b634e487b7160e01b8c52604160045260248cfd5b60408a9b8197956138707fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c9a9896602d8e9f613880988f613832565b6139269192508c3d8091833e61391e8183613c04565b8101906149a6565b90386137bd565b6139439192508c3d8091833e61391e8183613c04565b903861379a565b613960908d803d10610e8157610e728183613c04565b50386136f0565b90975082955061398691945060603d811161272b576127198183613c04565b94919590949790613693565b634e487b7160e01b8d52601160045260248dfd5b634e487b7160e01b8c52601160045260248cfd5b6139d090893d8b11610e8157610e728183613c04565b50386135f4565b6139ed90833d8511610e8157610e728183613c04565b50386135bf565b6040516364e329cb60e11b81526001600160a01b0388811660048301529190911660248201529397509091839160449183915af1908115611370578991613a41575b50938888388061356c565b613a589150883d8a11612808576127fa8183613c04565b38613a36565b613a76919950833d8511612808576127fa8183613c04565b9738613562565b604051630ceb95c760e31b8152600490fd5b8092508b8092503d8311613abd575b613aa88183613c04565b81010312613ab9578590513861351e565b8a80fd5b503d613a9e565b604051637f6dc38d60e01b8152600490fd5b8092508c8092503d8311613b04575b613aef8183613c04565b81010312613b0057839051386134f5565b8b80fd5b503d613ae5565b6040513d8e823e3d90fd5b604051636c2b7e2d60e11b8152600490fd5b5085156134a0565b60405163c1ab6dc160e01b8152600490fd5b50346103f25760403660031901126103f257602061059a613b61613b6a565b60243590614327565b600435906001600160a01b038216820361053157565b602435906001600160a01b038216820361053157565b604435906001600160a01b038216820361053157565b6060906003190112610531576004356001600160a01b038116810361053157906024359060443590565b6001600160401b038111610a0557604052565b608081019081106001600160401b03821117610a0557604052565b90601f801991011681019081106001600160401b03821117610a0557604052565b6001600160401b038111610a0557601f01601f191660200190565b81601f8201121561053157803590613c5782613c25565b92613c656040519485613c04565b8284526020838301011161053157816000926020809301838601378301015290565b80548210156121795760005260206000200190600090565b3461053157604036600319011261053157602060ff6040613cbe613b6a565b613cc6613b80565b613ccf81614229565b9060018060a01b03806000941684526032875284842091168352855282822090825284522054166040519015158152f35b6000546001600160a01b03163303613d1457565b60405163118cdaa760e01b8152336004820152602490fd5b91908201809211613d3957565b634e487b7160e01b600052601160045260246000fd5b90816020910312610531575180151581036105315790565b91908203918211613d3957565b8115613d7e570490565b634e487b7160e01b600052601260045260246000fd5b600260015414613da5576002600155565b604051633ee5aeb560e01b8152600490fd5b81810292918115918404141715613d3957565b6000198114613d395760010190565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064820192909252613e1b916112f582608481016112e7565b565b906000602091828151910182855af115613e79576000513d613e7057506001600160a01b0381163b155b613e4e5750565b604051635274afe760e01b81526001600160a01b039091166004820152602490fd5b60011415613e47565b6040513d6000823e3d90fd5b6006546001600160a01b039290831615613f4b57613eea928183613eaa602094614229565b60065460405163680a301160e01b81526001600160a01b039485166004820152929093166024830152604482015294859290911690829081906064820190565b03915afa918215613e7957600092613f16575b50613f10670de0b6b3a764000091613f53565b04111590565b90916020823d8211613f43575b81613f3060209383613c04565b810103126103f257505190613f10613efd565b3d9150613f23565b505050600090565b6002546001600160a01b039081169190823b15610a8b5760246020926040519485938492630dd5008760e11b84521660048301525afa908115613e7957600091613f9b575090565b906020823d8211613fc1575b81613fb460209383613c04565b810103126103f257505190565b3d9150613fa7565b600f546201518090818101808211613d3957421015613fe6575050565b8190046010546000928284526020906022825260409283862055601154602383528386205560125460248352838620556013546025835283862055601454602683528386205560105491600f549182018092116133a35783519283528201527ffe9470e7dc0cae980a79a006aef8e71149217282e3e1965ba51d9bf4ec18dc239190a2614072426152ff565b6201517f198101908111612a4857600f5580601055806011558060125580601355601455565b6001600160a01b0316600081815260208052604081205460ff16156140bb575050565b602154600e5411156122bb578181526020805260408120600160ff19825416179055602154906001820180921161411c57506021557ffefcfe9bb8f97bbb317ab61b7f1139aa02e376de0f944a2b4f81a925c5ac80406020604051428152a2565b634e487b7160e01b81526011600452602490fd5b6001600160a01b03166000818152602b602052604090205460ff161561054957604051906361665f0960e11b825260156004830152602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e795760009161419f575090565b6141b7915060203d8111610e8157610e728183613c04565b90565b6001600160a01b03166000818152602b602052604090205460ff161561054957604051906302a41d0b60e11b825260156004830152602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e795760009161419f575090565b604051632e8ad07f60e11b8152601560048201526001600160a01b03909116602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e7957600091613f9b575090565b9092919260018060a01b038092169160009483865260206035815260409283882094169384885281528287208288528152670de0b6b3a764000083882054049369021e19e0c9bab24000009485810295818704149015171561215157858852603482528388208189528252838820838952825283882054958852603a82528388209088528152828720918752529093205491926141b79261432291613d2c565b613d67565b61433090614c28565b801561433f576141b791615274565b5050600090565b9081602091031261053157516001600160a01b03811681036105315790565b60005b8381106143785750506000910152565b8181015183820152602001614368565b906020916143a181518092818552858086019101614365565b601f01601f1916010190565b601c54601b541115612cbf576007546001600160a01b039081169290919083156144815761441a93614408600060209460405197889586948593636b0740d960e11b8552306004860152606060248601526064850190614388565b83810360031901604485015290614388565b03925af1918215613e7957600092614461575b50806006541680614451575b50600a541680614447575090565b6141b790826145e4565b61445b90836145e4565b38614439565b61447a91925060203d8111612808576127fa8183613c04565b903861442d565b60405162461bcd60e51b815260206004820152601760248201527f41646d696e20636f6e74726163742072657175697265640000000000000000006044820152606490fd5b604051636eb1769f60e11b8082523060048301526001600160a01b038481166024840152949392851692919060208083604481885afa928315613e79576000936145b5575b5082156145ac576040519182523060048301526001600160a01b03841660248301528082604481885afa908115613e7957600091614580575b50905081811061455b57613e1b94955003916148c5565b60405163e570110f60e01b815292861660048401526024830152604482015260649150fd5b82813d83116145a5575b6145948183613c04565b810103126103f25750518038614544565b503d61458a565b50505050509050565b90928382813d83116145dd575b6145cc8183613c04565b810103126103f2575051913861450b565b503d6145c2565b604051636eb1769f60e11b8082523060048301526001600160a01b0384811660248401529194939290911691906020908186604481875afa958615613e79576000966146b8575b5060001986146146b0576040519081523060048201526001600160a01b03831660248201528181604481875afa918215613e795760009261467f575b505061467990613e1b94951990613d2c565b916148c5565b81819392933d83116146a9575b6146968183613c04565b810103126103f257505184614679614667565b503d61468c565b505050509050565b90958282813d83116146e0575b6146cf8183613c04565b810103126103f2575051943861462b565b503d6146c5565b60408051636eb1769f60e11b8082523060048301526001600160a01b0385811660248401529296959383169493909260209290919083816044818a5afa9081156148ba5760009161488d575b5081811461488257818111156147f5579061474d91613d67565b87519384523060048501526001600160a01b0385166024850152928281604481895afa9283156147ea576000936147b9575b50508282106147965750613e1b94955003916148c5565b865163e570110f60e01b8152931660048401526024830152604482015260649150fd5b8181949293943d83116147e3575b6147d18183613c04565b810103126103f257505190388061477f565b503d6147c7565b88513d6000823e3d90fd5b90614804925097939297613d67565b82519182523060048301526001600160a01b0384166024830152958082604481885afa9283156148785750600092614847575b5050613e1b939461467991613d2c565b81819392933d8311614871575b61485e8183613c04565b810103126103f257505184614679614837565b503d614854565b513d6000823e3d90fd5b505050505050509050565b908482813d83116148b3575b6148a38183613c04565b810103126103f257505138614733565b503d614899565b89513d6000823e3d90fd5b60405163095ea7b360e01b60208083018281526001600160a01b03861660248501526044808501979097529583529094919391600090614906606487613c04565b85519082865af16000513d82614966575b505015614925575b50505050565b61495d936112f59160405191602083015260018060a01b03166024820152600060448201526044815261495781613be9565b82613e1d565b3880808061491f565b90915061498357506001600160a01b0382163b15155b3880614917565b60011461497c565b90816060910312610531578051916040602083015192015190565b602081830312610531578051906001600160401b038211610531570181601f820112156105315780516149d881613c25565b926149e66040519485613c04565b81845260208284010111610531576141b79160208085019101614365565b601c54600160401b811015610a05576001810180601c5581101561217957601c6000527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a2110180546001600160a01b0319166001600160a01b03909216919091179055565b8054600160401b811015610a0557614a8591600182018155613c87565b819291549060031b9160018060a01b03809116831b921b1916179055565b8054600160401b811015610a0557614ac2906001928382018155613c87565b929092614c02578051906001600160401b038211610a055783548381169190841c8215614bfa575b60209283821014614be457601f8111614b9b575b5081601f8411600114614b385750928293918392600094614b2d575b50501b916000199060031b1c1916179055565b015192503880614b1a565b919083601f1981168760005284600020946000905b88838310614b815750505010614b68575b505050811b019055565b015160001960f88460031b161c19169055388080614b5e565b858701518855909601959485019487935090810190614b4d565b8560005282600020601f850160051c810191848610614bda575b601f0160051c019085905b828110614bce575050614afe565b60008155018590614bc0565b9091508190614bb5565b634e487b7160e01b600052602260045260246000fd5b607f16614aea565b634e487b7160e01b600052600060045260246000fd5b8051600110156121795760400190565b6001600160a01b038181166000818152602b602052604090205491929160ff161561054957826141b7936003541691600052602a6020526040600020541691614e63565b60075460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018490529293926000929190602081604481878787165af180156127a057614d0d575b506007541690813b15612cd15760405163e87bfbe160e01b81526001600160a01b03919091166004820152602481019490945291929181908390604490829084905af190811561053d5750614d045750565b613e1b90613bd6565b614d249060203d8111610e8157610e728183613c04565b5038614cb2565b9060ff6018541680614dd5575b15614dcd57601654918215614dc457601954926201518080614d5a8685613d67565b0460009281068352601560205260018060a01b036040842054169582820291808304841490151715614db05790614d9091613d2c565b9081019182821161411c57508210159182614daa57505090565b10919050565b634e487b7160e01b84526011600452602484fd5b50600091508190565b600091508190565b50601954821015614d38565b60ff6018541680614e0d575b15614e0757614e03620151809160195490613d67565b0490565b50600090565b50601954811015614ded565b51906001600160701b038216820361053157565b9081606091031261053157614e4181614e19565b916040614e5060208401614e19565b92015163ffffffff811681036105315790565b60408051630240bc6b60e21b81526004959490926001600160a01b0392909183166060858981845afa93841561506d576000958695615036575b50825190630dfe168160e01b825260209283838c81845afa92831561500c578b918591600095615017575b50865163d21220a760e01b815292839182905afa93841561500c57600094614fed575b50506001600160701b0380971697881595868015614fe3575b614fd35782169282169081841480614fc6575b15614f5e575050505050505016670de0b6b3a764000090818102918183041490151715614f49576141b7929350613d74565b601184634e487b7160e01b6000525260246000fd5b8216149283614fba575b505050600014614fab5750670de0b6b3a7640000808502948504141715614f96576141b79394501690613d74565b601185634e487b7160e01b6000525260246000fd5b5163ce4bfb8f60e01b81528690fd5b16149050388080614f68565b5082811683861614614f17565b5060009a50505050505050505050565b5088881615614f04565b615004929450803d10612808576127fa8183613c04565b913880614eeb565b85513d6000823e3d90fd5b61502f919550823d8411612808576127fa8183613c04565b9338614ec8565b90945061505b91955060603d8111615066575b6150538183613c04565b810190614e2d565b509490949338614e9d565b503d615049565b82513d6000823e3d90fd5b9192908215610a795760408051630240bc6b60e21b81526004956001600160a01b0394919392918516906060858981855afa958615615269576000958697615242575b50835191630dfe168160e01b835260209384848c81845afa938415615218578b918691600096615223575b50875163d21220a760e01b815292839182905afa948515615218576000956151f9575b50506001600160701b0380971695861580156151ef575b6151df57821692821690818414806151d2575b156151985750505050505091165b6103e580840293840403614f49576151599083613db7565b926103e89182810292818404149015171561518357506141b7929161517d91613d2c565b90613d74565b601190634e487b7160e01b6000525260246000fd5b95979582161492836151c6575b5050506000146151b757501690615141565b5163ce4bfb8f60e01b81528590fd5b161490503880806151a5565b5082811683861614615133565b8551633dce448b60e11b81528b90fd5b5087891615615120565b615210929550803d10612808576127fa8183613c04565b923880615109565b86513d6000823e3d90fd5b61523b919650823d8411612808576127fa8183613c04565b94386150e6565b90965061525e91955060603d8111615066576150538183613c04565b5094909495386150bb565b83513d6000823e3d90fd5b90801561433f57670de0b6b3a764000091828102928184041481151715613d3957671bc16d674ec80000029180830460021490151715613d39576141b791613d74565b92906040519260208401946001600160601b03199283809260601b16875260601b16603485015260601b166048830152605c820152605c81526152f981613be9565b51902090565b801561557e576001600160ff1b039081811161554357612a31600160ff1b03811361550d57612a3081810192600091828412801591861291821691151617612a48578184126154d757620151808085048181029181830414901517156122a75761ef1019811161549c5761ef10810190818111614db0578180961015615445575b5050831161541157612a2f19830192831360011661411c5782126153dd578111156153a85790565b60405162461bcd60e51b815260206004820152600d60248201526c4e6f6e2d66757475726520747360981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b05554432074696d65203c20360a41b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b2aaa219031b0b9ba1037bb3360a11b6044820152606490fd5b9194509062015180198211615468576202409001809111612a4857923880615380565b60405162461bcd60e51b815260206004820152600c60248201526b2732bc3a103230bc9037bb3360a11b6044820152606490fd5b60405162461bcd60e51b81526020600482015260136024820152725461726765742063616c63206f76662028682960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d04c6f63616c2074696d65203c20360941b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d4c6f63616c206f766572666c6f7760901b6044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527254696d657374616d7020746f6f206c6172676560681b6044820152606490fd5b60405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606490fdfead56699d0f375866eb895ed27203058a36a713382aaded78eb6b67da266d4332a2646970667358221220897e634830c0aa254773085f082f519572157d746bf88ae448cdb7005324566864736f6c634300081400330000000000000000000000000f7f24c7f22e2ca7052f051a295e1a5d3369cace

Deployed ByteCode

0x6080604052600436101561001257600080fd5b6000803560e01c806303728c5014613b4257806303ed352a146134655780630591bcf214613447578063088041ea146133d05780630d6c720014612ecf5780631071d8b714612eb157806313f9afb314612e885780631656feb614612e4f57806316779c5e14612e3157806319653b7314612e0d5780631979a67314612de95780631ac57d5514612dbf578063266959d514612d965780632d5413c414612d335780632f871e8114612cd557806333d5cd0814612bb057806334172cff14612afe57806335cbfffc14612ad45780633c7afca514612ab65780633f4ba83a14612a5c578063465521d0146129bf5780634dce4e10146129a15780634f01a3a7146129785780634fc4a94214612907578063513e18a5146128dd578063546567481461288157806354ab8ea9146128635780635a4d30cd146128455780635c975abb146128225780635df2f5bc1461245d57806360f8d76c14612423578063684f879c146123f95780636d4c8b6e146114bd5780636e89e43d146123b3578063715018a61461235957806373d0022414611bc857806375151b6314611b895780637519ddef14611b4a57806375858e3514611aef578063795053d314611ac657806379c7ce3914611a635780637dfadcbb14611a0b5780637fa33aea146119ed5780638456cb591461199057806385e694b614611954578063879df1f31461146157806389610dc11461192b5780638c660298146118ab5780638da5cb5b146118845780638e7952c61461185b5780638fdee2841461182457806390e986fc146117e557806392a2c8e114611787578063933ed7cc146105a257806394b6b453146115ef57806395f7d9d31461156c57806397f009631461153c5780639bf4ca5e146115185780639cf20866146114bd5780639f181b5e1461149f578063a0951b4414611461578063a0eb8cce14611438578063aada452c14611404578063ad0d71f814611083578063ad2b3e4014611059578063aec9b6f414611030578063b104839214611012578063bb6260b214610fb4578063ca99911d14610f90578063cea4a38314610c05578063cf0fce4814610ba7578063d0a9317314610b7e578063d36483f7146106f7578063d4817d04146106d9578063da237e8e14610615578063dfdd4ea4146105ec578063e20e9db2146105a7578063e606adeb146105a2578063e701be961461057e578063eb638bb21461055b578063f2c52a6314610480578063f2fde38b146103f55763f644d82a146103ae57600080fd5b346103f257806003193601126103f25760a060ff60185416601954601654601b5490601a549260405194151585526020850152604084015260608301526080820152f35b80fd5b50346103f25760203660031901126103f25761040f613b6a565b610417613d00565b6001600160a01b0390811690811561046757600054826001600160601b0360a01b821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b604051631e4fbdf760e01b815260048101849052602490fd5b50346103f257602090816003193601126103f2576001600160a01b036104a4613b6a565b16808252602b835260ff60408320541615610549576040519063c4b65a7d60e01b8252601560048301526024820152426044820152828160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af491821561053d579161050b575b50604051908152f35b90508181813d8311610536575b6105228183613c04565b81010312610531575138610502565b600080fd5b503d610518565b604051903d90823e3d90fd5b60405163350b944160e11b8152600490fd5b50346103f257806003193601126103f257602060ff601e54166040519015158152f35b50346103f257806003193601126103f257602061059a42614de1565b604051908152f35b613c9f565b50346103f257806003193601126103f25760a060165460ff60185416601954601a5490601b549260405194855215156020850152604084015260608301526080820152f35b50346103f257806003193601126103f2576007546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f257608090610632613b6a565b61063a613b80565b61064381614229565b9160406106508383613e85565b9460018060a01b0394858416948583526106948260209660328852868620938a81169485600052895287600020838852895260ff88882054169a60035416916152b7565b83526031855260ff8484205416958352603385528383209060005284528260002090825283522054926040519415158552151590840152151560408301526060820152f35b50346103f257806003193601126103f2576020601454604051908152f35b50346103f257602080600319360112610b7a57610712613d94565b60ff600d5416610b6857610724613fc9565b61072d42614d2b565b919091158015610b57575b610b455761074582614130565b15610b33576001600160a01b03828116808552602b8352604085205490939060ff161561054957816003541615610b215761077f81614130565b15610b0f5761078d81614229565b9133865260368452604086208560005284526040600020838752845260ff60408720541615610afd5733865260388452604086208560005284526040600020838752845260ff604087205416610aeb573386526037845260408620856000528452604060002083875284526040862054928315610a79576003546040516370a0823160e01b80825233600483015294918416908781602481855afa8015610ae05787918b91610aaf575b5010610a9d5761084682614c28565b8015610a8b576108569087615274565b928315610a795760328402848104603203610a655761271061087a91048095613d67565b9560405190815230600482015288816024818d5afa908115610a5a578b91610a2d575b506108a88588613d2c565b11610a1b5760405160c08101918183106001600160401b03841117610a05578b92604052338252898201938b8552604083019081526060830191825260808301928a845260a081018a815273cc9b07bdcd2402c10adf6684647d278026dc5b8a90813b15610a015786958b958661012496816040519c8d9a8b99632244635360e01b8b52511660048a015251166024880152511660448601525160648501525160848401525160a4830152603860c4830152603760e4830152603b6101048301525af49182156109f45784926109d9575b505061098491614c6c565b604051908152857f1353e99c221a4396b23e48ce384c25087a6e2981ef2ccc6ed3960c885665e229863393a360035416926040519283528201526000805160206155b883398151915260403392a46001805580f35b6109e4919250613bd6565b6109f057818838610979565b8780fd5b50604051903d90823e3d90fd5b8680fd5b634e487b7160e01b600052604160045260246000fd5b604051631b74457560e31b8152600490fd5b90508881813d8311610a53575b610a448183613c04565b8101031261053157513861089d565b503d610a3a565b6040513d8d823e3d90fd5b634e487b7160e01b8b52601160045260248bfd5b6040516365e52d5160e11b8152600490fd5b604051633494a40d60e21b8152600490fd5b604051631e9acf1760e31b8152600490fd5b809250898092503d8311610ad9575b610ac88183613c04565b810103126105315786905138610837565b503d610abe565b6040513d8c823e3d90fd5b60405163757d07f760e01b8152600490fd5b604051630c340a6160e01b8152600490fd5b604051636f312cbd60e01b8152600490fd5b604051632b50d3bd60e11b8152600490fd5b60405163438e694160e11b8152600490fd5b60405163568ca25760e01b8152600490fd5b506001600160a01b03821615610738565b60405163da82933960e01b8152600490fd5b5080fd5b50346103f257806003193601126103f2576006546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576040602091610bc6613b6a565b610bce613b80565b610bd781614229565b9160018060a01b03809116845260348652848420911660005284528260002090825283522054604051908152f35b50346103f2576101003660031901126103f257610c20613b6a565b90610c29613b80565b610c31613b96565b916064359360018060a01b03851680950361053157608435926001600160a01b03841684036105315760a4356001600160a01b038116908190036105315760c4356001600160a01b03811681036105315760e435916001600160a01b0383168303610531576005546001600160a01b03163303610f7e57610cb0613d94565b6001600160a01b03851615610b21576001600160a01b03841615610a8b576001600160a01b03881615610a8b578815610a8b576001600160a01b03871615610a8b576001600160a01b03821615610f6c576001600160a01b03831615610f5a57600380546001600160a01b03199081166001600160a01b038881169190911790925560048054821687841690811790915560028054831690911790556006805482168b84161790556007805482168c1790556008805482168a8416179055600980548216909317909255600a80548316848316179055600b8054909216908416179055610d9d86856145e4565b845b601654811015610ebb57808652601560205260408620546001600160a01b031680610dd4575b50610dcf90613dca565b610d9f565b60405163095ea7b360e01b81526001600160a01b038a166004820152602481018890526020816044818b865af18015610eb057916020918993610e93575b5060405163095ea7b360e01b81526001600160a01b038c166004820152600019602482015292839160449183915af18015610e885790610dcf9291610e59575b5090610dc5565b610e7a9060203d602011610e81575b610e728183613c04565b810190613d4f565b5038610e52565b503d610e68565b6040513d89823e3d90fd5b610ea990833d8511610e8157610e728183613c04565b5038610e12565b6040513d8a823e3d90fd5b506040519497939694957f7e0f8631140eafdd46d4ee7cb1f77ee3266b38d53d291291670e77e7a0c1ac1c9560c095906001600160a01b0381167f515670ca58d45a6a4fef4f8838f89179a87c989784c028b16c6979d5eed1d1898c80a28a89526001600160a01b0390811660208a015260408901919091529081166060880152908116608087015290811660a0860152908116941692a36001805580f35b6040516329f7dfeb60e21b8152600490fd5b60405163179ce99f60e01b8152600490fd5b604051632d5be4cb60e21b8152600490fd5b50346103f25760203660031901126103f257602061059a610faf613b6a565b614229565b50346103f25760403660031901126103f2576040602091610fd3613b6a565b610fdb613b80565b610fe481614229565b9160018060a01b03809116845260378652848420911660005284528260002090825283522054604051908152f35b50346103f257806003193601126103f2576020601054604051908152f35b50346103f257806003193601126103f257600a546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602683522054604051908152f35b50346103f257602090816003193601126103f257600435906110a3613d94565b60ff600d5416610b685781926110b7613fc9565b6110c042614d2b565b9290921580156113f3575b610b45576110d883614130565b15610b33576110e633614098565b6001600160a01b03838116808352602b8452604083205491939160ff1615610549576003968488541615610b215761111d86614130565b15610b0f5761112b86614229565b94601486116113e15733855260368452604085208360005284526040600020868652845260ff604086205416610aeb57670de0b6b3a764000061116d33613f53565b106113cf57600198855b818b1115806113c6575b156111b3576111a76111ad916111a161119a8e8c613d67565b8c33614282565b90613d2c565b9a613dca565b99611177565b90878993928b81156113b45781106113ac575b508415610a79576040516370a0823160e01b808252336004830152949088816024818b5afa8015610ae05787918b9161137b575b5010610a9d57868952602b885260ff60408a20541615610549576112328484541691888b52602a8a52828660408d2054169189615078565b948515610a7957889060246040518094819382523060048301525afa80156113705785918a9161133f575b5010610a1b5733885260368752604088208660005287526040600020818952875260408820600160ff198254161790553388526037875260408820866000528752604060002090885286528260408820556112ba84303388613dd9565b805460405163a9059cbb60e01b88820152336024820152604481018590526112fa9184166112f582606481015b03601f198101845283613c04565b613e1d565b61130683601054613d2c565b60105561131583601254613d2c565b6012555416936040519283528201526000805160206155b883398151915260403392a46001805580f35b809250898092503d8311611369575b6113588183613c04565b81010312610531578490518a61125d565b503d61134e565b6040513d8b823e3d90fd5b8092508a8092503d83116113a5575b6113948183613c04565b81010312610531578690518b6111fa565b503d61138a565b9450886111c6565b6040516315d933e760e21b8152600490fd5b508a8811611181565b604051632525e44360e01b8152600490fd5b6040516331f6627f60e01b8152600490fd5b506001600160a01b038316156110cb565b50346103f257806003193601126103f257604061142042614d2b565b82516001600160a01b03909216825215156020820152f35b50346103f257806003193601126103f2576008546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760209060ff906040906001600160a01b0361148c613b6a565b1681528380522054166040519015158152f35b50346103f257806003193601126103f2576020602f54604051908152f35b50346103f25760403660031901126103f25760ff60406020926115056114e1613b6a565b6114e9613b80565b906114f382614229565b9160018060a01b0360035416916152b7565b8152603184522054166040519015158152f35b50346103f25760203660031901126103f257602061059a611537613b6a565b614c28565b50346103f25760603660031901126103f257602061059a61155b613b6a565b611563613b80565b60443591614282565b50346103f257806003193601126103f257601a546018546080929060ff161580156115e4575b156115ce57805b828110801592906115bd57905b604051938452602084015260408301526060820152f35b506115c88184613d67565b906115a6565b620151806115de60195442613d67565b04611599565b506019544210611592565b50346103f257806003193601126103f2576005546001600160a01b039081163303610f7e5760ff601e54161561177557601c5480156117635760ff601854166117515761163b426152ff565b9173a767651f7fe10d7a9cf1081e22da6d980d51fc5290813b1561174d5790849260405192634a19951960e11b8452816084850160156004870152608060248701525260a4840191601c86527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a2119186905b82821061172e5750505050839183808093886044830152602b606483015203915af480156117235761170f575b507f106566fd12b1b74887bf4a53c2c6a7cd6736796703181540521a8f6dff49a068604083601c5482519182526020820152a180f35b61171890613bd6565b610b7a5781386116d9565b6040513d84823e3d90fd5b83548116855289975060209094019360019384019391909101906116ac565b8480fd5b604051631f8c8d2360e11b8152600490fd5b604051636fca87f160e11b8152600490fd5b6040516317a4461160e11b8152600490fd5b50346103f25760403660031901126103f25760406020916117a6613b6a565b6117ae613b80565b6117b781614229565b9160018060a01b03809116845260358652848420911660005284528260002090825283522054604051908152f35b50346103f25760203660031901126103f2576007546001600160a01b0316330361181257600435600e5580f35b604051634755657960e01b8152600490fd5b50346103f25760403660031901126103f2576020611851611843613b6a565b61184b613b80565b90613e85565b6040519015158152f35b50346103f257806003193601126103f2576004546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f257546040516001600160a01b039091168152602090f35b50346103f25760403660031901126103f2576020906118c8613b6a565b6118d0613b80565b906118da81613f53565b916118e481614229565b9160018060a01b038091168552603586526040852091166000528452604060002090835283526040822054908181116000146119245761059a9250613d67565b505061059a565b50346103f257806003193601126103f2576009546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f2576020906001600160a01b03906040908261197e613b6a565b168152602a8452205416604051908152f35b50346103f257806003193601126103f2576005546001600160a01b03163303610f7e57600160ff19600d541617600d557f81990fd9a5c552b8e3677917d8a03c07678f0d2cb68f88b634aca2022e9bd19f6020604051338152a180f35b50346103f257806003193601126103f2576020601f54604051908152f35b50346103f257806003193601126103f257602154600e549181831115611a5a5750611a56611a398284613d67565b604051938493846040919493926060820195825260208201520152565b0390f35b611a5690611a39565b50346103f25760403660031901126103f25760ff6040602092611a84613b6a565b611a8c613b80565b611a9581614229565b9160018060a01b03809116845260368752848420911660005285528260002090825284522054166040519015158152f35b50346103f257806003193601126103f2576005546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f257611b09613b6a565b6006546001600160a01b031633141580611b40575b611b2e57611b2b90614098565b80f35b6040516313444ccd60e11b8152600490fd5b5030331415611b1e565b50346103f25760203660031901126103f25760209060ff906040906001600160a01b03611b75613b6a565b168152601d84522054166040519015158152f35b50346103f25760203660031901126103f25760209060ff906040906001600160a01b03611bb4613b6a565b168152602b84522054166040519015158152f35b50346103f257806003193601126103f257611be1613d94565b60ff600d5416610b6857611bf3613fc9565b611bfc42614d2b565b158015612348575b610b4557611c1181614130565b612336573315612324576001600160a01b0381168252602b602052604082205460ff1615610549576003546001600160a01b031615610b2157670de0b6b3a7640000611c5c33613f53565b106113cf5760ff6018541615612312576019544210610b0f57611c7e42614d2b565b90816122fc575b50156122ea573382526020805260ff604083205416156122cd575b3382526020805260ff60408320541615612243575b611cbe81614229565b90611cd58260018060a01b036003541683336152b7565b835260316020526040832090826001830155611cf081614130565b612231576040516302a41d0b60e11b8152601560048201526001600160a01b038216602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115612226578591612207575b5015610b0f5733845260336020526040842060018060a01b038216600052602052604060002083855260205260408420549283156121f557600354604051636eb1769f60e11b81523360048201523060248201526001600160a01b0390911690602081604481855afa8015610e8857869188916121c0575b50106121ae57600a546001600160a01b031615610f6c5784611de59130903390613dd9565b600354600a5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810187905291602091839160449183918b91165af180156121215761218f575b50604051606081018181106001600160401b03821117610a055760405260028152604036602083013760018060a01b036003541681511561217957602082018190526001600160a01b038416611e7f83614c18565b526001600160a01b0384168752602b602052604087205460ff1615610549576001600160a01b038481168852602a6020526040882054611ec492911690859088615078565b9081605f810204605f148215171561216557600a546001600160a01b0316904261012c810110612151579187916040519384926338ed173960e01b84526064605f60a48601928c60048801520204602485015260a060448501528151809152602060c48501920190855b81811061212c575050508383809233606483015261012c4201608483015203925af18015612121578690612081575b611f679150614c18565b51928315610a7957338652603a6020526040862060018060a01b038416600052602052604060002082875260205260408620611fa4858254613d2c565b9055600160ff1982541617905533855260336020526040852060018060a01b038316600052602052604060002090855260205260408420611fe6848254613d67565b90556013546001810180911161206d5760135562015180600f540433855260276020526040852081815403612054575b50506003546040805194855260208501939093526001600160a01b039182169391169133916000805160206155b88339815191529190a46001805580f35b556014546001810180911161206d576014553880612016565b634e487b7160e01b85526011600452602485fd5b503d8087833e6120918183613c04565b810190602081830312610a01578051906001600160401b0382116109f057019080601f83011215610a01578151916001600160401b038311610a05578260051b90604051936120e36020840186613c04565b845260208085019282010192831161211d57602001905b82821061210d57505050611f6790611f5d565b81518152602091820191016120fa565b8880fd5b6040513d88823e3d90fd5b82516001600160a01b031684528c965087955060209384019390920191600101611f2e565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b87526011600452602487fd5b634e487b7160e01b600052603260045260246000fd5b6121a79060203d602011610e8157610e728183613c04565b5038611e2a565b6040516313be252b60e01b8152600490fd5b9150506020813d6020116121ed575b816121dc60209383613c04565b810103126105315785905138611dc0565b3d91506121cf565b6040516315cb5c9360e11b8152600490fd5b612220915060203d602011610e8157610e728183613c04565b38611d48565b6040513d87823e3d90fd5b6040516349426f9160e11b8152600490fd5b602154600e5411156122bb573382526020805260408220600160ff19825416179055602154600181018091116122a7576021556040514281527ffefcfe9bb8f97bbb317ab61b7f1139aa02e376de0f944a2b4f81a925c5ac804060203392a2611cb5565b634e487b7160e01b83526011600452602483fd5b6040516330bf333360e11b8152600490fd5b602154600e5411611ca0576040516330bf333360e11b8152600490fd5b604051635f2f180d60e01b8152600490fd5b6001600160a01b03838116911614905038611c85565b604051636fd49a7d60e11b8152600490fd5b60405163ae9954c360e01b8152600490fd5b6040516301a6772f60e11b8152600490fd5b506001600160a01b03811615611c04565b50346103f257806003193601126103f257612372613d00565b600080546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346103f25760203660031901126103f2576123cd613b6a565b6007546001600160a01b0391908216330361181257166001600160601b0360a01b600554161760055580f35b50346103f25760203660031901126103f25760406020916004358152602283522054604051908152f35b50346103f25760403660031901126103f25760ff60406020926124476114e1613b6a565b815260318452205460081c166040519015158152f35b50346103f25761246c36613bac565b600554919392916001600160a01b03163303610f7e5761248a613d94565b6001600160a01b03821615612324576003546001600160a01b03908116939083168414610a8b5784159384801561281a575b610a7957600b5460405163e6a4390560e01b81526001600160a01b0386811660048301528381166024830152909160209183916044918391165afa90811561280f5783916127e0575b506001600160a01b0316928315610a8b576040516370a0823160e01b8082523060048301526024939160208186816001600160a01b038c165afa90811561212157908a9187916127ab575b5010610a1b57602090846040518094819382523060048301525afa9081156127a057908291859161276b575b5010610a1b57600a5461259a9088906001600160a01b0316876146e7565b600354600a546125b89183916001600160a01b0390811691166146e7565b600a546003546001600160a01b039081169791169190605f898102918a8304909114171561275857605f820290828204605f148315171561274557610e1042019283421161273257869798999a6040519b8c9762e8e33760e81b8952600160a01b600190038d1660048a0152880152604487015260648601526064900460848501526064900460a484015260c4830161dead905260e4830152815a9361010492606095f19283156109f457819482946126fb575b508315610a7957600a546020957f4a1a2a6176e9646d9e3157f7c2ab3c499f18337c0b0828cfb28e0a61de4a11f79290916126b0906001600160a01b0316826144c6565b600354600a546126cc916001600160a01b0391821691166144c6565b6040805194855260208501929092529083018590526001600160a01b031691606090a360018055604051908152f35b9150925061272191935060603d811161272b575b6127198183613c04565b81019061498b565b919391923861266c565b503d61270f565b634e487b7160e01b875260116004528587fd5b634e487b7160e01b865260116004528486fd5b634e487b7160e01b855260116004528385fd5b9150506020813d602011612798575b8161278760209383613c04565b81010312610531578190513861257c565b3d915061277a565b6040513d86823e3d90fd5b9150506020813d6020116127d8575b816127c760209383613c04565b810103126105315789905138612550565b3d91506127ba565b612802915060203d602011612808575b6127fa8183613c04565b810190614346565b38612505565b503d6127f0565b6040513d85823e3d90fd5b5082156124bc565b50346103f257806003193601126103f257602060ff600d54166040519015158152f35b50346103f257806003193601126103f2576020600f54604051908152f35b50346103f257806003193601126103f2576020601354604051908152f35b50346103f25760203660031901126103f257600435601c54811015610b7a57601c6000527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a21101546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602583522054604051908152f35b50346103f25760203660031901126103f257600554600435906001600160a01b03163303610f7e578015610a8b576021548110610a8b5760407f43f71dfd216eaaf2f75bdb23d66ae4f5b7e2975e422538b68ef11b46b387409891600e549080600e5582519182526020820152a180f35b50346103f257806003193601126103f257600b546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f2576020601154604051908152f35b50346103f25760203660031901126103f2576129d9613b6a565b906129e382614229565b9060148210612a305760a0926040825b6129fc836141ba565b92600180881b0316815260176020522054151591604051938452601460208501526040840152151560608301526080820152f35b816014039260148411612a4857604060a094926129f3565b634e487b7160e01b82526011600452602482fd5b50346103f257806003193601126103f2576005546001600160a01b03163303610f7e5760ff19600d5416600d557f5b65b0c1363b3003db9bcc5e1fd8805a6d6bf5bf6dc9d3431ee4494cd7d117666020604051338152a180f35b50346103f257806003193601126103f2576020602154604051908152f35b50346103f25760203660031901126103f25760406020916004358152602483522054604051908152f35b50346103f25760403660031901126103f257612b56906020612b1e613b6a565b612b26613b80565b60405163118cc05360e31b81526001600160a01b0392831660048201529116602482015292839081906044820190565b0381305afa90811561053d5790612b7e575b602090670de0b6b3a76400006040519110158152f35b506020813d8211612ba8575b81612b9760209383613c04565b810103126105315760209051612b68565b3d9150612b8a565b50346103f25760603660031901126103f257612bca613b6a565b6024356001600160401b038111612cd157612be9903690600401613c40565b612bf1613b96565b6007546001600160a01b039391908416330361181257601c54601b541115612cbf57612c6a92612c659185841695868852602c60205260408820911690816001600160601b0360a01b825416179055808752602d602052612c558460408920614a68565b8652602860205260408620614aa3565b614a04565b8152601d6020526040812060ff1990600182825416179055602b60205260408220600182825416179055612c9f602f54613dca565b602f55601c54601b5414612cb1575080f35b600190601e541617601e5580f35b604051636eff6d6b60e11b8152600490fd5b8280fd5b50346103f25760403660031901126103f2576040602091612cf4613b6a565b612cfc613b80565b612d0581614229565b9160018060a01b03809116845260338652848420911660005284528260002090825283522054604051908152f35b50346103f25760403660031901126103f25760ff6040602092612d54613b6a565b612d5c613b80565b612d6581614229565b9160018060a01b03809116845260388752848420911660005285528260002090825284522054166040519015158152f35b50346103f257806003193601126103f2576002546040516001600160a01b039091168152602090f35b50346103f25760203660031901126103f25760406020916004358152602383522054604051908152f35b50346103f25760203660031901126103f2576020611851612e08613b6a565b6141ba565b50346103f25760203660031901126103f2576020611851612e2c613b6a565b614130565b50346103f257806003193601126103f2576020600c54604051908152f35b50346103f25760203660031901126103f2576020906040906001600160a01b03612e77613b6a565b168152603b83522054604051908152f35b50346103f257806003193601126103f2576003546040516001600160a01b039091168152602090f35b50346103f257806003193601126103f2576020600e54604051908152f35b50346103f257806003193601126103f257612ee8613d94565b60ff600d5416610b6857612efa613fc9565b612f0342614d2b565b1580156133bf575b610b4557612f1881614130565b612336576001600160a01b0381168252602b602052604082205460ff1615610549576003546001600160a01b031615610b2157612f5481614229565b612f5e8233613e85565b15610afd57612f6c33613f53565b91670de0b6b3a7640000928381106113cf573385526035602090815260408087206001600160a01b03851688528252808720858852909152852054808211156133b757612fb891613d67565b8381106113cf5768878678326eac900000928382029382850414821517156133a35784840415610a79576040516370a0823160e01b80825233600483015292906020816024816001600160a01b0389165afa908115610eb0578891613371575b5086860411610a9d57604051636eb1769f60e11b81523360048201523060248201526020816044816001600160a01b0389165afa908115610eb057889161333f575b50868604116121ae5761306c84614c28565b8015610a8b5761307e90878704613db7565b8060011b908082046002149015171561215157869004908115610a79576032820282810460320361332b576127106130b891048093613d67565b9360018060a01b036003541690604051908152306004820152602081602481855afa908115610ae0578a916132f5575b506130f38487613d2c565b11610a1b5760405160e081018181106001600160401b038211176132e157604052338152602081019260018060a01b0388168452604082019283526060820186815260808301908b8b04825260a084019289845260c085015273cc9b07bdcd2402c10adf6684647d278026dc5b8a3b156132dd57604051632823ecdb60e21b815284516001600160a01b039081166004830152965187166024820152945190951660448501529351606484015292516084830152915160a482015260c09091015160c4820152603260e4820152603361010482015260346101248201526035610144820152603b61016482015287816101848173cc9b07bdcd2402c10adf6684647d278026dc5b8a5af48015610eb0576132ca575b506003546132209082906001600160a01b0316614c6c565b60018060a01b03600354166040519182527f1353e99c221a4396b23e48ce384c25087a6e2981ef2ccc6ed3960c885665e22960203393a333865260396020526040862060018060a01b03841660005260205260406000209086526020526040852061328c828254613d2c565b905560018060a01b03600354169360405193048352602083015260018060a01b0316906000805160206155b883398151915260403392a46001805580f35b6132d690979197613bd6565b9538613208565b8c80fd5b634e487b7160e01b8b52604160045260248bfd5b90506020813d602011613323575b8161331060209383613c04565b8101031261331f5751386130e8565b8980fd5b3d9150613303565b634e487b7160e01b89526011600452602489fd5b90506020813d602011613369575b8161335a60209383613c04565b810103126109f057513861305a565b3d915061334d565b90506020813d60201161339b575b8161338c60209383613c04565b810103126109f0575138613018565b3d915061337f565b634e487b7160e01b86526011600452602486fd5b505083612fb8565b506001600160a01b03811615612f0b565b50346103f25760403660031901126103f2576001600160401b03600435818111612cd157613402903690600401613c40565b916024359182116103f2575061341c903690600401613c40565b60055490916001600160a01b039182163303610f7e5760209261343e916143ad565b60405191168152f35b50346103f257806003193601126103f2576020601254604051908152f35b50346103f25761347436613bac565b60055490926001600160a01b03929183163303610f7e57828116918215613b3057801591828015613b28575b613b16578460035416928315610b215785600a541615610f6c5785600b5416968715610f5a576040516370a0823160e01b808252306004830152602099918a816024818b5afa8015613b0b5784918d91613ad6575b5010613ac45760405190815230600482015289816024818b5afa8015610a5a5786918c91613a8f575b5010613a7d5760405163e6a4390560e01b81526001600160a01b038581166004830152871660248201528a918a82604481845afa97881561280f578b928499613a5e575b508a8916156139f4575b5050600354600a5460405163095ea7b360e01b808252918c166001600160a01b0316600482015260248101869052935091508a9083908b16818e816044810103925af1908115610a5a5786928b926139d7575b50600a546040519182528a166001600160a01b031660048201526024810192909252816044818d8b5af18015610ae0576139ba575b5086600a541690876003541692605f860290868204605f141715610a6557605f820290828204605f14831517156139a657610e1042019283421161399257936101049360648b948f94829060609a996040519d8e9b8c9a62e8e33760e81b8c5260048c015260248b015260448a0152828901520460848701520460a48501523060c485015260e48401525af1948515610e885787809481948298613967575b508216966040519063a9059cbb60e01b8252898260448161dead968760048301528560248301528d5af1908115610a5a5789937fee362beb50174d6f1b6792512b350a67767a22b6750da5940b7f0ee69818c45b9360409361394a575b5082519182528b820152a2848852602b8752604088209160ff1992600184825416179055602a8852604089206001600160601b0360a01b908882825416179055601d895260ff60408b2054161561377d575b888880897fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c60408b8b825191825287820152a3604051908152f35b6040516306fdde0360e01b81528a816004818b5afa8b918161392d575b506138cc57506040516395d89b4160e01b81528a816004818b5afa8b9181613908575b506138cc5750604051604081018181106001600160401b038211176138b8578a9b604097956138707fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c9a9896602d8e9f613880988f988e988952600d81526c2ab735b737bbb7102a37b5b2b760991b83820152985b61383b85614a04565b8752601d825287872060018c8254161790558560055416602c8352888820918254161790558460055416865252848420614a68565b60055416815260288d5220614aa3565b61388b602f54613dca565b602f55601c54601b54146138a7575b8897839550829450613742565b600190601e541617601e553861389a565b634e487b7160e01b8c52604160045260248cfd5b60408a9b8197956138707fd569a23a8cff45c641c5d5e4fb55b5e15e918f9acf0fc42b4909adc31f5f806c9a9896602d8e9f613880988f613832565b6139269192508c3d8091833e61391e8183613c04565b8101906149a6565b90386137bd565b6139439192508c3d8091833e61391e8183613c04565b903861379a565b613960908d803d10610e8157610e728183613c04565b50386136f0565b90975082955061398691945060603d811161272b576127198183613c04565b94919590949790613693565b634e487b7160e01b8d52601160045260248dfd5b634e487b7160e01b8c52601160045260248cfd5b6139d090893d8b11610e8157610e728183613c04565b50386135f4565b6139ed90833d8511610e8157610e728183613c04565b50386135bf565b6040516364e329cb60e11b81526001600160a01b0388811660048301529190911660248201529397509091839160449183915af1908115611370578991613a41575b50938888388061356c565b613a589150883d8a11612808576127fa8183613c04565b38613a36565b613a76919950833d8511612808576127fa8183613c04565b9738613562565b604051630ceb95c760e31b8152600490fd5b8092508b8092503d8311613abd575b613aa88183613c04565b81010312613ab9578590513861351e565b8a80fd5b503d613a9e565b604051637f6dc38d60e01b8152600490fd5b8092508c8092503d8311613b04575b613aef8183613c04565b81010312613b0057839051386134f5565b8b80fd5b503d613ae5565b6040513d8e823e3d90fd5b604051636c2b7e2d60e11b8152600490fd5b5085156134a0565b60405163c1ab6dc160e01b8152600490fd5b50346103f25760403660031901126103f257602061059a613b61613b6a565b60243590614327565b600435906001600160a01b038216820361053157565b602435906001600160a01b038216820361053157565b604435906001600160a01b038216820361053157565b6060906003190112610531576004356001600160a01b038116810361053157906024359060443590565b6001600160401b038111610a0557604052565b608081019081106001600160401b03821117610a0557604052565b90601f801991011681019081106001600160401b03821117610a0557604052565b6001600160401b038111610a0557601f01601f191660200190565b81601f8201121561053157803590613c5782613c25565b92613c656040519485613c04565b8284526020838301011161053157816000926020809301838601378301015290565b80548210156121795760005260206000200190600090565b3461053157604036600319011261053157602060ff6040613cbe613b6a565b613cc6613b80565b613ccf81614229565b9060018060a01b03806000941684526032875284842091168352855282822090825284522054166040519015158152f35b6000546001600160a01b03163303613d1457565b60405163118cdaa760e01b8152336004820152602490fd5b91908201809211613d3957565b634e487b7160e01b600052601160045260246000fd5b90816020910312610531575180151581036105315790565b91908203918211613d3957565b8115613d7e570490565b634e487b7160e01b600052601260045260246000fd5b600260015414613da5576002600155565b604051633ee5aeb560e01b8152600490fd5b81810292918115918404141715613d3957565b6000198114613d395760010190565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064820192909252613e1b916112f582608481016112e7565b565b906000602091828151910182855af115613e79576000513d613e7057506001600160a01b0381163b155b613e4e5750565b604051635274afe760e01b81526001600160a01b039091166004820152602490fd5b60011415613e47565b6040513d6000823e3d90fd5b6006546001600160a01b039290831615613f4b57613eea928183613eaa602094614229565b60065460405163680a301160e01b81526001600160a01b039485166004820152929093166024830152604482015294859290911690829081906064820190565b03915afa918215613e7957600092613f16575b50613f10670de0b6b3a764000091613f53565b04111590565b90916020823d8211613f43575b81613f3060209383613c04565b810103126103f257505190613f10613efd565b3d9150613f23565b505050600090565b6002546001600160a01b039081169190823b15610a8b5760246020926040519485938492630dd5008760e11b84521660048301525afa908115613e7957600091613f9b575090565b906020823d8211613fc1575b81613fb460209383613c04565b810103126103f257505190565b3d9150613fa7565b600f546201518090818101808211613d3957421015613fe6575050565b8190046010546000928284526020906022825260409283862055601154602383528386205560125460248352838620556013546025835283862055601454602683528386205560105491600f549182018092116133a35783519283528201527ffe9470e7dc0cae980a79a006aef8e71149217282e3e1965ba51d9bf4ec18dc239190a2614072426152ff565b6201517f198101908111612a4857600f5580601055806011558060125580601355601455565b6001600160a01b0316600081815260208052604081205460ff16156140bb575050565b602154600e5411156122bb578181526020805260408120600160ff19825416179055602154906001820180921161411c57506021557ffefcfe9bb8f97bbb317ab61b7f1139aa02e376de0f944a2b4f81a925c5ac80406020604051428152a2565b634e487b7160e01b81526011600452602490fd5b6001600160a01b03166000818152602b602052604090205460ff161561054957604051906361665f0960e11b825260156004830152602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e795760009161419f575090565b6141b7915060203d8111610e8157610e728183613c04565b90565b6001600160a01b03166000818152602b602052604090205460ff161561054957604051906302a41d0b60e11b825260156004830152602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e795760009161419f575090565b604051632e8ad07f60e11b8152601560048201526001600160a01b03909116602482015242604482015260208160648173a767651f7fe10d7a9cf1081e22da6d980d51fc525af4908115613e7957600091613f9b575090565b9092919260018060a01b038092169160009483865260206035815260409283882094169384885281528287208288528152670de0b6b3a764000083882054049369021e19e0c9bab24000009485810295818704149015171561215157858852603482528388208189528252838820838952825283882054958852603a82528388209088528152828720918752529093205491926141b79261432291613d2c565b613d67565b61433090614c28565b801561433f576141b791615274565b5050600090565b9081602091031261053157516001600160a01b03811681036105315790565b60005b8381106143785750506000910152565b8181015183820152602001614368565b906020916143a181518092818552858086019101614365565b601f01601f1916010190565b601c54601b541115612cbf576007546001600160a01b039081169290919083156144815761441a93614408600060209460405197889586948593636b0740d960e11b8552306004860152606060248601526064850190614388565b83810360031901604485015290614388565b03925af1918215613e7957600092614461575b50806006541680614451575b50600a541680614447575090565b6141b790826145e4565b61445b90836145e4565b38614439565b61447a91925060203d8111612808576127fa8183613c04565b903861442d565b60405162461bcd60e51b815260206004820152601760248201527f41646d696e20636f6e74726163742072657175697265640000000000000000006044820152606490fd5b604051636eb1769f60e11b8082523060048301526001600160a01b038481166024840152949392851692919060208083604481885afa928315613e79576000936145b5575b5082156145ac576040519182523060048301526001600160a01b03841660248301528082604481885afa908115613e7957600091614580575b50905081811061455b57613e1b94955003916148c5565b60405163e570110f60e01b815292861660048401526024830152604482015260649150fd5b82813d83116145a5575b6145948183613c04565b810103126103f25750518038614544565b503d61458a565b50505050509050565b90928382813d83116145dd575b6145cc8183613c04565b810103126103f2575051913861450b565b503d6145c2565b604051636eb1769f60e11b8082523060048301526001600160a01b0384811660248401529194939290911691906020908186604481875afa958615613e79576000966146b8575b5060001986146146b0576040519081523060048201526001600160a01b03831660248201528181604481875afa918215613e795760009261467f575b505061467990613e1b94951990613d2c565b916148c5565b81819392933d83116146a9575b6146968183613c04565b810103126103f257505184614679614667565b503d61468c565b505050509050565b90958282813d83116146e0575b6146cf8183613c04565b810103126103f2575051943861462b565b503d6146c5565b60408051636eb1769f60e11b8082523060048301526001600160a01b0385811660248401529296959383169493909260209290919083816044818a5afa9081156148ba5760009161488d575b5081811461488257818111156147f5579061474d91613d67565b87519384523060048501526001600160a01b0385166024850152928281604481895afa9283156147ea576000936147b9575b50508282106147965750613e1b94955003916148c5565b865163e570110f60e01b8152931660048401526024830152604482015260649150fd5b8181949293943d83116147e3575b6147d18183613c04565b810103126103f257505190388061477f565b503d6147c7565b88513d6000823e3d90fd5b90614804925097939297613d67565b82519182523060048301526001600160a01b0384166024830152958082604481885afa9283156148785750600092614847575b5050613e1b939461467991613d2c565b81819392933d8311614871575b61485e8183613c04565b810103126103f257505184614679614837565b503d614854565b513d6000823e3d90fd5b505050505050509050565b908482813d83116148b3575b6148a38183613c04565b810103126103f257505138614733565b503d614899565b89513d6000823e3d90fd5b60405163095ea7b360e01b60208083018281526001600160a01b03861660248501526044808501979097529583529094919391600090614906606487613c04565b85519082865af16000513d82614966575b505015614925575b50505050565b61495d936112f59160405191602083015260018060a01b03166024820152600060448201526044815261495781613be9565b82613e1d565b3880808061491f565b90915061498357506001600160a01b0382163b15155b3880614917565b60011461497c565b90816060910312610531578051916040602083015192015190565b602081830312610531578051906001600160401b038211610531570181601f820112156105315780516149d881613c25565b926149e66040519485613c04565b81845260208284010111610531576141b79160208085019101614365565b601c54600160401b811015610a05576001810180601c5581101561217957601c6000527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a2110180546001600160a01b0319166001600160a01b03909216919091179055565b8054600160401b811015610a0557614a8591600182018155613c87565b819291549060031b9160018060a01b03809116831b921b1916179055565b8054600160401b811015610a0557614ac2906001928382018155613c87565b929092614c02578051906001600160401b038211610a055783548381169190841c8215614bfa575b60209283821014614be457601f8111614b9b575b5081601f8411600114614b385750928293918392600094614b2d575b50501b916000199060031b1c1916179055565b015192503880614b1a565b919083601f1981168760005284600020946000905b88838310614b815750505010614b68575b505050811b019055565b015160001960f88460031b161c19169055388080614b5e565b858701518855909601959485019487935090810190614b4d565b8560005282600020601f850160051c810191848610614bda575b601f0160051c019085905b828110614bce575050614afe565b60008155018590614bc0565b9091508190614bb5565b634e487b7160e01b600052602260045260246000fd5b607f16614aea565b634e487b7160e01b600052600060045260246000fd5b8051600110156121795760400190565b6001600160a01b038181166000818152602b602052604090205491929160ff161561054957826141b7936003541691600052602a6020526040600020541691614e63565b60075460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018490529293926000929190602081604481878787165af180156127a057614d0d575b506007541690813b15612cd15760405163e87bfbe160e01b81526001600160a01b03919091166004820152602481019490945291929181908390604490829084905af190811561053d5750614d045750565b613e1b90613bd6565b614d249060203d8111610e8157610e728183613c04565b5038614cb2565b9060ff6018541680614dd5575b15614dcd57601654918215614dc457601954926201518080614d5a8685613d67565b0460009281068352601560205260018060a01b036040842054169582820291808304841490151715614db05790614d9091613d2c565b9081019182821161411c57508210159182614daa57505090565b10919050565b634e487b7160e01b84526011600452602484fd5b50600091508190565b600091508190565b50601954821015614d38565b60ff6018541680614e0d575b15614e0757614e03620151809160195490613d67565b0490565b50600090565b50601954811015614ded565b51906001600160701b038216820361053157565b9081606091031261053157614e4181614e19565b916040614e5060208401614e19565b92015163ffffffff811681036105315790565b60408051630240bc6b60e21b81526004959490926001600160a01b0392909183166060858981845afa93841561506d576000958695615036575b50825190630dfe168160e01b825260209283838c81845afa92831561500c578b918591600095615017575b50865163d21220a760e01b815292839182905afa93841561500c57600094614fed575b50506001600160701b0380971697881595868015614fe3575b614fd35782169282169081841480614fc6575b15614f5e575050505050505016670de0b6b3a764000090818102918183041490151715614f49576141b7929350613d74565b601184634e487b7160e01b6000525260246000fd5b8216149283614fba575b505050600014614fab5750670de0b6b3a7640000808502948504141715614f96576141b79394501690613d74565b601185634e487b7160e01b6000525260246000fd5b5163ce4bfb8f60e01b81528690fd5b16149050388080614f68565b5082811683861614614f17565b5060009a50505050505050505050565b5088881615614f04565b615004929450803d10612808576127fa8183613c04565b913880614eeb565b85513d6000823e3d90fd5b61502f919550823d8411612808576127fa8183613c04565b9338614ec8565b90945061505b91955060603d8111615066575b6150538183613c04565b810190614e2d565b509490949338614e9d565b503d615049565b82513d6000823e3d90fd5b9192908215610a795760408051630240bc6b60e21b81526004956001600160a01b0394919392918516906060858981855afa958615615269576000958697615242575b50835191630dfe168160e01b835260209384848c81845afa938415615218578b918691600096615223575b50875163d21220a760e01b815292839182905afa948515615218576000956151f9575b50506001600160701b0380971695861580156151ef575b6151df57821692821690818414806151d2575b156151985750505050505091165b6103e580840293840403614f49576151599083613db7565b926103e89182810292818404149015171561518357506141b7929161517d91613d2c565b90613d74565b601190634e487b7160e01b6000525260246000fd5b95979582161492836151c6575b5050506000146151b757501690615141565b5163ce4bfb8f60e01b81528590fd5b161490503880806151a5565b5082811683861614615133565b8551633dce448b60e11b81528b90fd5b5087891615615120565b615210929550803d10612808576127fa8183613c04565b923880615109565b86513d6000823e3d90fd5b61523b919650823d8411612808576127fa8183613c04565b94386150e6565b90965061525e91955060603d8111615066576150538183613c04565b5094909495386150bb565b83513d6000823e3d90fd5b90801561433f57670de0b6b3a764000091828102928184041481151715613d3957671bc16d674ec80000029180830460021490151715613d39576141b791613d74565b92906040519260208401946001600160601b03199283809260601b16875260601b16603485015260601b166048830152605c820152605c81526152f981613be9565b51902090565b801561557e576001600160ff1b039081811161554357612a31600160ff1b03811361550d57612a3081810192600091828412801591861291821691151617612a48578184126154d757620151808085048181029181830414901517156122a75761ef1019811161549c5761ef10810190818111614db0578180961015615445575b5050831161541157612a2f19830192831360011661411c5782126153dd578111156153a85790565b60405162461bcd60e51b815260206004820152600d60248201526c4e6f6e2d66757475726520747360981b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b05554432074696d65203c20360a41b6044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b2aaa219031b0b9ba1037bb3360a11b6044820152606490fd5b9194509062015180198211615468576202409001809111612a4857923880615380565b60405162461bcd60e51b815260206004820152600c60248201526b2732bc3a103230bc9037bb3360a11b6044820152606490fd5b60405162461bcd60e51b81526020600482015260136024820152725461726765742063616c63206f76662028682960681b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d04c6f63616c2074696d65203c20360941b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d4c6f63616c206f766572666c6f7760901b6044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527254696d657374616d7020746f6f206c6172676560681b6044820152606490fd5b60405162461bcd60e51b81526020600482015260116024820152700496e76616c69642074696d657374616d7607c1b6044820152606490fdfead56699d0f375866eb895ed27203058a36a713382aaded78eb6b67da266d4332a2646970667358221220897e634830c0aa254773085f082f519572157d746bf88ae448cdb7005324566864736f6c63430008140033