ETH Price: $2,988.02 (-0.83%)
Gas: 5 Gwei

Token

SudoRug Token (SUDORUG)
 

Overview

Max Total Supply

100,000,000 SUDORUG

Holders

248

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 9 Decimals)

Filtered by Token Holder
sudoswaplp.eth
Balance
89,000 SUDORUG

Value
$0.00
0x00003183f59e825911d98fb509a157cd2abbae25
Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

Contract Name:
SudoRug

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU AGPLv3 license
File 1 of 9 : SudoRug.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity ^0.8.16;

/*

   ▄████████ ███    █▄  ████████▄   ▄██████▄          ▄████████ ███    █▄     ▄██████▄  
  ███    ███ ███    ███ ███   ▀███ ███    ███        ███    ███ ███    ███   ███    ███ 
  ███    █▀  ███    ███ ███    ███ ███    ███        ███    ███ ███    ███   ███    █▀  
  ███        ███    ███ ███    ███ ███    ███       ▄███▄▄▄▄██▀ ███    ███  ▄███        
▀███████████ ███    ███ ███    ███ ███    ███      ▀▀███▀▀▀▀▀   ███    ███ ▀▀███ ████▄  
         ███ ███    ███ ███    ███ ███    ███      ▀███████████ ███    ███   ███    ███ 
   ▄█    ███ ███    ███ ███   ▄███ ███    ███        ███    ███ ███    ███   ███    ███ 
 ▄████████▀  ████████▀  ████████▀   ▀██████▀         ███    ███ ████████▀    ████████▀  
                                                     ███    ███                         

Self-rugging contract that sells its own tokens for ETH, buys NFTs with ETH, and sends
NFTs to holders above a minimum eligibility (100k tokens).

The rugging mechanism can be manually triggered by anyone who wants to call the public 
function called PUSH_THE_RUG_BUTTON. To avoid dumping the token supply too quickly 
there are a few limits on calls to PUSH_THE_RUG_BUTTON:

1) You can only call it once every 10 minutes.
2) The max rug supply per call is a random fraction of recently purchased tokens.
3) There has to be at least one buy between two subsequent rug calls.

There's some bias towards holders with larger balances by picking three candidate winners and 
sending the NFT to one with the highest balance. This anti-sybil mechanism is meant to 
strike a balance between uniform-above-threshold lotteries (which suffer from either having
prohibitive thresholds or are vulnerable to multiple wallets) and lotteries where probability
of winning is proportional to holdings, which tend to have a small concentrated set of winners.

The NFT contracts which can be purchased are initially just Based Ghouls and Re-based Ghouls 
but any NFT project can add itself to the buy list by creating a sudoswap pool for their
NFT and passing it addNFTContractAndRegisterPool on this contract. There is a 750k token
fee for registering your NFT: calling wallet must hold those tokens, they are then burned
upon successful registration.

Token supply:
    - 100M total $rug
    - ~5M for v1 holders
    - ~7M claimable by ghouls (6667 ghouls * 1000 $sudorug each)
    - 40M slow-rug supply
    - 60M - (5M+7M) = ~48M floating supply used for initial liquidity on Uniswap

Taxes:
    - None. If you're paying someone 12% to exit a position you should re-evaluate your life choices.  

Contract states:
    - AIRDROP: tokens sent to v1 holders and claimable by Based Ghoul holders
    - HONEYPOT: catch sniper bots for first few blocks
    - WARMUP: max purchase is 500k for the first 20 blocks
    - SLOWRUG: normal operations

Actions on each transaction:
    - BUY_NFT: buy a random NFT from sudoswap
    - SEND_NFT: send NFT from the treasury to a random eligible holder
    - CHILL: do nothing this txn

v2: the previous version of this token was called $rug (v1) and was designed to end in 
1-2 weeks with a big dramatic distribution of 99% of the token supply to random holders. 
v2 ($sudorug) is different in that less of the supply is set aside for rugging, it's sold 
off for ETH slowly, and the ETH is used to buy NFTs which are continuously distributed 
to random holders. 
*/

import {IUniswapV2Factory, IUniswapV2Pair, IUniswapV2Router02} from "Uniswap.sol";
import {IERC20} from "IERC20.sol";
import {IERC721} from "IERC721.sol";
import {IERC721Metadata} from "IERC721Metadata.sol";
import {ISudoGate} from "ISudoGate.sol";
import {ILSSVMPairFactoryLike} from "ILSSVMPairFactoryLike.sol";
import {LSSVMPair} from "LSSVMPair.sol";

contract SudoRug is IERC20 {
    /********************************************************
     * 
     *              CORE ECR-20 FIELDS
     * 
     ********************************************************/
    
    string public constant symbol = "SUDORUG";
    string public constant name = "SudoRug Token";
    uint256 public constant decimals = 9;

    // make total supply 100M, so we're going to slow-rug 40M/100M tokens 
    // this is very different from the v1 contract which would send 99%
    // of tokens to winners at one moment
    uint256 public constant totalSupply =  100_000_000 * (10 ** decimals);      

    mapping(address => uint256) balances;
    mapping(address => mapping(address => uint256)) allowed;

    /*
    States of the contract:
        AIRDROP:  
            no Uniswap liquidity yet, but deployer can send tokens around

        HONEYPOT: 
            anyone buying in the first few blocks after liquidity added gets rekt

        WARMUP:
            only allow buying up to 500k tokens at a time for the first 10 minutes
        
        SLOWRUG: 
            normal operations: buy NFTs, send NFTs to random holders, 
            anyone can call PUSH_THE_RUG_BUTTON
    */
    enum State {AIRDROP, HONEYPOT, WARMUP, SLOWRUG}


    /* 
    Random actions which can be taken on each turn:
        BUY_NFT:
            buy a random NFT from sudoswap
        
        SEND_NFT:
            send NFT from the treasury to a random eligible holder

        CHILL:
            do nothing this txn

    Selling tokens for ETH is not included in this list because it does
    done manually via a public function called PUSH_THE_RUG_BUTTON.
    */
    enum Action { BUY_NFT, SEND_NFT, CHILL }


    /********************************************************
     * 
     *                      ADDRESSES
     * 
     ********************************************************/
     

    address constant UNISWAP_V2_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address constant BASED_GHOULS_CONTRACT_ADDRESS = 0xeF1a89cbfAbE59397FfdA11Fc5DF293E9bC5Db90;
    address constant REBASED_GHOULS_CONTRACT_ADDRESS = 0x9185a69970A150EC9D0DEA6F18e62F40Db9e94d2;
    address constant SUDO_PAIR_FACTORY_ADDRESS = 0xb16c1342E617A5B6E4b631EB114483FDB289c0A4;
    address public SUDOGATE_ADDRESS = 0x3473ba28c97E8D2fdDBc6f95764BAE6429e31885;
    


    /********************************************************
     * 
     *                  MISC DATA
     * 
     ********************************************************/

    // if any address tries to snipe the liquidity add or buy+sell in the same block,
    // prevent any further txns from them
    mapping(address => bool) public isBot;
    
    IUniswapV2Router02 public immutable uniswapV2Router;
    IUniswapV2Pair public immutable uniswapV2Pair_WETH;

    mapping(address => bool) isAMM;

    // keep track of which Based Ghoul token IDs have been claimed
    mapping(uint256 => bool) claimed;

    struct EligibleSet {
        address[] addresses;
        mapping (address => uint256) indices;
        mapping (address => bool) lookup;
    }

    EligibleSet eligibleSet;
    
    address owner;

    // honestly using this ritualistically since I'm not sure
    // what the possibilities are for reentrancy during a Uniswap 
    // swap 
    bool inSwap = false;

    // used for RNG below
    uint256 randNonce = 0;

    struct NFT {
        address addr;
        uint256 tokenID;
    }

    NFT[] public treasury;

    address[] public nftContracts;

    mapping (address => bool) knownNFTContract;

    /********************************************************
     * 
     *     TRACKING BLOCK NUMBERS & TIMESTEMPS
     * 
     ********************************************************/
    

    // track last block of buys and sells to catch sandwich bots
    mapping(address => uint256) lastBuy;

    mapping(address => uint256) lastSell;

    // how many tokens have been bought since the last sell
    uint256 public recentlyBoughtTokens = 0;
    
    // how many minutes until we set recently bought tokens back to 0
    uint256 public recentlyBoughtTokensResetMinutes = 30;

    // timestamp from liquidity getting added 
    // for the first time
    uint256 public liquidityAddedBlock = 0;
    
    // timestamp for last buy
    uint256 public lastBuyTimestamp = 0;

    /********************************************************
     * 
     *                 PARAMETERS
     * 
     ********************************************************/

    // try to trap sniper bots for first 2 blocks
    uint256 constant public honeypotDurationBlocks = 2;
    
    // limit size of buys for next 20 blocks
    uint256 constant public warmupDurationBlocks = 20;
    
    // maximum number of tokens you can buy per txn in the first blocks of open trading
    uint256 constant public maxBuyDuringWarmup = 500_000 * (10 ** decimals);

    // balance of any one wallet can't exceed this amount during warmup period
    uint256 constant public maxBalanceDuringWarmup = 1_000_000 * (10 ** decimals);

    // any NFT project that wants to get added to our buy list needs to have
    // 750k tokens, which we'll burn when registering them
    uint256 public costToAddNFTContract = 750_000 * (10 ** decimals);
    
    // minimum number of tokens you need to be eligible to receive NFTs
    uint256 constant public minEligibleTokens = 100_000 * (10 ** decimals);
    
    // used to slowly extract ETH from the liquidity pool 
    // to buy NFTs, the available rug supply at any point 
    // in time will get smaller than this number as the tokens
    // are used up, see rugSupply()
    uint256 constant initialRugSupply  = 40_000_000 * (10 ** decimals);

    // how many tokens do you get for each ghoul
    uint256 constant tokensPerGhoul = 1000 * (10 ** decimals);

    // tokens reserved for ghouls
    uint256 public ghoulSupply = 6667 * tokensPerGhoul;

    // don't bother rugging if you're not going to sell at least
    // this many tokens
    uint256 public minTokensForRug = 1000 * (10 ** decimals);
    
    // keep track when we last pushed the rug button
    uint256 public lastRugTimestamp = 0;

    // how long to wait between rugging
    uint256 public minMinutesBetweenRugs = 10;
    
    // percent of time to try buying an NFT per txn
    uint256 public actionPercentBuy = 60;

    // percent of time to try sending an NFT per txn
    uint256 public actionPercentSend = 20;

    /********************************************************
     * 
     *                  SETTERS
     * 
     ********************************************************/
    
    function setOwner(address newOwner) public {
        require(owner == msg.sender, "Only owner allowed to call setOwner");
        owner = newOwner;
    }

    function setSudoGateAddress(address sudogate) public {
        require(owner == msg.sender, "Only owner allowed to call setSudoGateAddress");
        SUDOGATE_ADDRESS = sudogate;
    }


    function setCostToAddNFTContract(uint256 cost) public {
        require(owner == msg.sender, "Only owner allowed to call setCostToAddNFTContract");
        costToAddNFTContract = cost;
    }

    function setMinTokensForRug(uint256 numTokens) public {
        require(owner == msg.sender, "Only owner allowed to call setMinTokensForRug");
        minTokensForRug = numTokens;
    }

    function setMinMinutesBetweenRugs(uint256 m) public {
        require(owner == msg.sender, "Only owner allowed to call setMinMinutesBetweenRugs");
        minMinutesBetweenRugs = m;
    }


    function setActionPercentBuy(uint256 percent) public {
        require(owner == msg.sender, "Only owner allowed to call setActionPercentBuy");
        require(percent <= 100, "Percent cannot exceed 100");
        require(actionPercentSend  + percent <= 100, "Combined percentages cannot exceed 100");
        actionPercentBuy = percent;
    }


    function setActionPercentSend(uint256 percent) public {
        require(owner == msg.sender, "Only owner allowed to call setActionPercentSend");
        require(percent <= 100, "Percent cannot exceed 100");
        require(actionPercentBuy  + percent <= 100, "Combined percentages cannot exceed 100");
        actionPercentSend = percent;
    }


    /********************************************************
     * 
     *                      EVENTS
     * 
     ********************************************************/

     // records every sniper bot that buys in the first 15s
    event FellInHoney(address indexed bot, uint256 value);

    // emit when we successfully buy an NFT through SudoGate
    event ReceivedNFT(address indexed nft, uint256 tokenID);

    // emit when we send an NFT from the contract to a holder
    event SentNFT(address indexed nft, uint256 tokenID, address indexed recipient);
    

    /********************************************************
     * 
     *                  CORE ERC-20 FUNCTIONS
     * 
     ********************************************************/



    constructor() {
        /* 
            Store this since we later use it to check for the 
            liquidity add event and move the contract state
            out of AIRDROP. 

            Also, send trapped ETH on the contract to this address.
        */
        owner = msg.sender;

        /* 
        Use the Uniswap V2 router to find the RUG/WETH pair
        and register it as an AMM so we can figure out which txns
        are buys/sells vs. just transfers
        */
        uniswapV2Router = IUniswapV2Router02(UNISWAP_V2_ROUTER_ADDRESS);
        IUniswapV2Factory factory = IUniswapV2Factory(uniswapV2Router.factory());
        uniswapV2Pair_WETH = IUniswapV2Pair(factory.createPair(address(this), uniswapV2Router.WETH()));

        isAMM[address(uniswapV2Pair_WETH)] = true;
        isAMM[address(uniswapV2Router)] = true;

        // keep tokens for ghouls and rugging on the contract
        uint256 sendToContract = ghoulSupply + initialRugSupply;
        balances[address(this)] = sendToContract;
        emit Transfer(address(0), address(this), sendToContract);

        // sum of v1 holders excluding contract and uniswap liquidity
        uint256 v1AirdropSupply = 4_633_893 * (10 ** decimals);

        // combination of Uniswap Supply and v1 airdrop supply
        uint256 sendToDeployer = totalSupply - sendToContract;
        require (sendToDeployer > v1AirdropSupply, "At least need to be able to send v1 tokens!");
        // send airdrop and Uniswap liquidity tokens to deployer
        balances[owner] = sendToDeployer;
        emit Transfer(address(0), owner, sendToDeployer);
        
        // add Based Ghouls and Re-based Ghouls to the NFT contract list
        knownNFTContract[BASED_GHOULS_CONTRACT_ADDRESS] = true; 
        knownNFTContract[REBASED_GHOULS_CONTRACT_ADDRESS] = true;
        
        nftContracts.push(BASED_GHOULS_CONTRACT_ADDRESS);
        nftContracts.push(REBASED_GHOULS_CONTRACT_ADDRESS);
        
    }

    receive() external payable {  }

    function balanceOf(address addr) public view returns (uint256) {
        return balances[addr];
    }

    function allowance(address _owner, address _spender) public view returns (uint256) {
        // pre-approve Uniswap
        if (_spender == address(uniswapV2Router)) { return balances[_owner]; } 
        else { return allowed[_owner][_spender]; }
    }

    function _approve(address _owner, address _spender, uint256 _value) internal {
        allowed[_owner][_spender] = _value;
        emit Approval(_owner, _spender, _value);
    }

    function approve(address _spender, uint256 _value) public returns (bool) {
        _approve(msg.sender, _spender, _value);
        return true;
    }

    function _burn(address _from, uint256 _numTokens) internal {
        require(balances[_from] >= _numTokens, "Not enough tokens");
        _simple_transfer_with_burn(
            _from, 
            address(0),
            _numTokens,
            0, 
            _numTokens);
    }

    function burn(uint256 numTokens) public {
        _burn(msg.sender, numTokens);
    }

    function transfer(address _to, uint256 _value) public returns (bool) {
        _transfer(msg.sender, _to, _value);        
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool){
        if (_from != msg.sender && msg.sender != address(uniswapV2Router)) {
            require(allowed[_from][msg.sender] >= _value, "Insufficient allowance");
            allowed[_from][msg.sender] -= _value;
        }
        _transfer(_from, _to, _value);
        return true;
    }


    /********************************************************
     * 
     *              ADD LIQUIDITY
     * 
     ********************************************************/


    function addLiquidity(uint256 numTokens) public payable {
        require(msg.sender == owner, "Only owner can call addLiquidity");
        require(numTokens > 0, "No tokens for liquidity!");
        require(msg.value > 0, "No ETH for liquidity!");

        _transfer(msg.sender, address(this), numTokens);
        _approve(address(this), address(uniswapV2Router), numTokens);

        uniswapV2Router.addLiquidityETH{value: msg.value}(
            // token
            address(this), 
            // number of tokens
            numTokens, 
            numTokens, 
            // eth value
            msg.value, 
            // LP token recipient
            msg.sender, 
            block.timestamp + 15);

        require(
            IERC20(uniswapV2Router.WETH()).balanceOf(
                address(uniswapV2Pair_WETH)) >= msg.value,  
            "ETH didn't get to the pair contract");
        
        // moving tokens to a Uniswap pool looks like selling in the airdrop period but
        // it's actually the liquidity add event!
        liquidityAddedBlock = block.number;
    }
    
    /********************************************************
     * 
     *       CORE LOGIC (BALANCE & STATE MANAGEMENT)
     * 
     ********************************************************/

    function liquidityAdded() public view returns (bool) {
        return (liquidityAddedBlock > 0);
    }

    function currentState() public view returns (State) {
        if (!liquidityAdded()) {
            return State.AIRDROP;
        } 
        uint256 blocksSinceLiquidity = block.number - liquidityAddedBlock;
        if (blocksSinceLiquidity < honeypotDurationBlocks) {
            return State.HONEYPOT;
        } else if (blocksSinceLiquidity < warmupDurationBlocks) {
            return State.WARMUP;
        } else {
            return State.SLOWRUG;
        }
    }


    function isTradingOpen() public view returns (bool) {
        // can we actually trade now?
        State state = currentState();
        return (state == State.SLOWRUG || state == State.WARMUP);
    }


    function _updateRecentlyBoughtTokens(bool buying, bool selling, uint256 _value) internal {
        if (buying) {
            recentlyBoughtTokens += _value;
        } else if (selling) {
            if (minutesSinceLastBuy() > recentlyBoughtTokensResetMinutes) { 
                recentlyBoughtTokens = 0; 
            } else if (recentlyBoughtTokens <= _value) { 
                recentlyBoughtTokens = 0; 
            } else {
                recentlyBoughtTokens -= _value;
            }
        }
    }

    function _insanity(address _from, address _to, uint256 _value) internal {
        // transfer logic outside of contrat interactions with Uniswap
        bool selling = isAMM[_to];
        bool buying = isAMM[_from];

        State state = currentState();

        /* manage state transitions first */
        if (state == State.AIRDROP) {
            require((_from == owner) || (_from == address(this)), "Only deployer and contract can move tokens now");
        } 

        if ((state == State.HONEYPOT) && buying) {
            // if you're trying to buy  in the first few blocks then you're 
            // going to have a bad time
            bool addedBotInHoneypot = _addBotAndOrigin(_to);
            if (addedBotInHoneypot) { emit FellInHoney(_to, _value); }
        } 
                 
        // store the initial value on _from without changing
        // balances, touching any element of balances which Uniswap
        // may be currently using will cause the most maddening 
        // cryptic errors
        uint256 initialValue = _value;
                
        // vague attempt at thwarting sandwich bots
        uint256 toBurn = 0;

        if (isTradingOpen()) {
            // check if this is a sandwich bot buying after selling
            // in the same block
            if (buying && (lastSell[_to] == block.number)) { 
                bool caughtSandiwchBotBuying = _addBotAndOrigin(_to);
                if (caughtSandiwchBotBuying) {
                    // burn 99% of their tokens
                    toBurn = _value * 99 / 100;
                }
            } else if (selling && (lastBuy[_from] == block.number)) {
                // check if this is a sandwich bot selling after
                // buying the same block    
                bool caughtSandwichBotSelling = _addBotAndOrigin(_from);
                if (caughtSandwichBotSelling) {
                    // burn 99% of their tokens
                    toBurn = _value * 99 / 100;
                }
            }
        }

        // update balance and eligibility of token sender and recipient, burn 
        // any tokens if we hit bot logic
        require(initialValue > toBurn, "Can't burn more than the total number of tokens");

        _simple_transfer_with_burn(
            _from,
            _to,
            initialValue,
            initialValue - toBurn,
            toBurn);

        if (toBurn == 0) {
            // if we didn't burn a bot's tokens then update the recently bought tokens counter
            _updateRecentlyBoughtTokens(buying, selling, _value);
        }
    
        if (state == State.WARMUP && buying && !isBot[_to]) {
            require(_value <= maxBuyDuringWarmup, "Only small buys during warmup period");
            require(balances[_to] <= maxBalanceDuringWarmup, "Balance too large for warmup period");
        } 
        
        // try to buy or send an NFT
        if (isTradingOpen()) { _performRandomAction(); }

        // record block numbers and timestamps of any buy/sell txns
        if (buying) { 
            lastBuyTimestamp = block.timestamp; 
            lastBuy[_to] = block.number;
        } else if (selling) { 
            lastSell[_from] = block.number;
        }



    }

    function _simple_transfer_with_burn(
            address _from, 
            address _to,
            uint256 _fromValue,
            uint256 _toValue,
            uint256 _burnValue) internal {
        /* 
        Update balances for a transfer, allows for possibility of
        burning some of the tokens instead of sending them all 
        to the destination address.

        Also updates eligibility for NFT lottery
        */
        require(
            _fromValue == (_toValue + _burnValue), 
            "Source and destination token amounts must be the same");

        // decrease balance and update eligibility       
        balances[_from] -= _fromValue;
        updateEligibility(_from);

        // increase balance and update eligibility
        balances[_to] += _toValue;        
        updateEligibility(_to);

        emit Transfer(_from, _to, _toValue);
        
        if (_burnValue > 0) {
            balances[address(0)] += _burnValue;
            emit Transfer(_from, address(0), _burnValue);
        }
    }

    function _simple_transfer(address _from, address _to, uint256 _value) internal {
        _simple_transfer_with_burn(
            _from,
            _to,
            _value,
            _value,
            0);
    }

    function _transfer(address _from, address _to, uint256 _value) internal {
        require(balances[_from] >= _value, "Insufficient balance");

        State state = currentState();
        require(!isBot[_from] || state == State.HONEYPOT, "Sorry bot, can't let you out");

        if (inSwap || 
                _from == address(this) || 
                _to == address(this) || 
                ((state == State.AIRDROP) && (_from == owner))) {
            // if this transfer was invoked by Uniswap while selling tokens for ETH,
            // then don't do anything fancy
            _simple_transfer(_from, _to, _value);
        } else {
            _insanity(_from, _to, _value);
        }
    }

    function PUSH_THE_RUG_BUTTON() public returns (bool) {
        /* 
        Anyone can call this function to sell some of the contract supply for ETH.
        Keeping this from totally wrecking the chart by:
            1) Can only be called once every 10 minutes
            2) There must be a buy between rugs.
            2) Tokens sold are between 10%-35% of what has been recently purchased.
        */
        require(currentState() == State.SLOWRUG, "Can't rug yet!");
        require(lastBuyTimestamp > lastRugTimestamp, "Must buy between rugs");
        if (lastRugTimestamp > 0) {
            require(minutesSinceLastRug() >= minMinutesBetweenRugs, "Hold your horses ruggers");
        }

        // randomly chosen action is selling tokens then sell a random fraction 
        // between 10% and 35% of the recently purchased tokens
        uint256 percentRug = 10 + randomModulo(25);
        uint256 rugTokens = recentlyBoughtTokens * percentRug / 100;
        bool success = false;
        if (rugTokens >= minTokensForRug) {
            uint256 ethReceived = _slowrug(rugTokens);
            success = ethReceived > 0;
            if (success) { 
                lastRugTimestamp = block.timestamp; 
                _updateRecentlyBoughtTokens(false, true, rugTokens);
            }
        }
       return success;
    }
    
    function _performRandomAction() internal returns (Action action, bool success) {
        /* 
        buy an NFT, sell an NFT, or do nothing
        */
        action = _chooseRandomAction();
        success = false;
        if (action == Action.BUY_NFT) {
            success = _buyRandomNFT();
        } else if (action == Action.SEND_NFT) { 
            success = _sendRandomNFT(); 
        }  
        return (action, success); 
    }

    function _chooseRandomAction() internal returns (Action) {
        uint256 n = randomModulo(100);
        if (n < actionPercentBuy ) { return Action.BUY_NFT; } 
        else if (n < (actionPercentBuy + actionPercentSend)) { return Action.SEND_NFT; }
        else { return Action.CHILL; }
    }


    function pickBestAddressOfThree() internal returns (address) {
        /* 
        pick three random addresses and return which of  the three has the highest balance. 
        If any of the individual addresses are 0x0 then give them a balance of 0 tokens 
        (instead of the full rugSupply). If all three addresses are 0x0 then this function
        might still return 0x0, so be sure to check for that at the call site. 
        */
        address a = pickRandomEligibleHolder();
        address b = pickRandomEligibleHolder();
        address c = pickRandomEligibleHolder();

        uint256 t_a = (a == address(0) ? 0 : balances[a]);
        uint256 t_b = (b == address(0) ? 0 : balances[b]);
        uint256 t_c = (c == address(0) ? 0 : balances[c]);

        return (t_a > t_b) ? 
            (t_a > t_c ? a : c) : 
            (t_b > t_c ? b : c);
    }

    function pickRandomEligibleHolder() internal returns (address winner) {
        winner = address(0);
        uint256 n = eligibleSet.addresses.length;
        if (n > 0) {
            winner = eligibleSet.addresses[randomModulo(n)];
        }
    }

    function removeFromEligibleSet(address addr) internal {
        eligibleSet.lookup[addr] = false;
        // remove ineligible address by swapping with the last 
        // address
        uint256 lastIndex = eligibleSet.addresses.length - 1;
        uint256 addrIndex = eligibleSet.indices[addr];
        if (addrIndex < lastIndex) {
            address lastAddr = eligibleSet.addresses[lastIndex];
            eligibleSet.indices[lastAddr] = addrIndex;
            eligibleSet.addresses[addrIndex] = lastAddr;

        }
        // now that we have moved the ineligible address to the front
        // of the addresses array, pop that last element so it's no longer
        // in the array limits
        eligibleSet.indices[addr] = type(uint256).max;
        eligibleSet.addresses.pop();
    }

    function addToEligibleSet(address addr) internal {
        eligibleSet.lookup[addr] = true;
        eligibleSet.indices[addr] = eligibleSet.addresses.length;
        eligibleSet.addresses.push(addr);
    }

    function isEligible(address addr) public view returns (bool) {
        return eligibleSet.lookup[addr];
    }

    function isSpecialAddress(address addr) public view returns (bool) {
        return (addr == address(this) || 
                addr == address(0) || 
                addr == owner || 
                isAMM[addr] || 
                isBot[addr] || 
                knownNFTContract[addr]);
    }

    function updateEligibility(address addr) internal {
        if (balances[addr] < minEligibleTokens || isSpecialAddress(addr)) {
            // if either the address has too few tokens or it's something we want to exclude
            // from the lottery then make sure it's not in the eligible set. if it is in the
            // eligible set then remove it
            if (eligibleSet.lookup[addr]) { 
                removeFromEligibleSet(addr);    
            } 
        } else if (!eligibleSet.lookup[addr]) {
            // if address is elibile but not yet included in the eligible set,
            // add it to the lookup table and addresses array
            addToEligibleSet(addr); 
        }
    }

    /********************************************************
     * 
     *                  SUPPLY VIEWS
     * 
     ********************************************************/

    function burntSupply() public view returns (uint256) {
        return balances[address(0)];
    }

    function rugSupply() public view returns (uint256) {
        require(balances[address(this)] >= ghoulSupply, "Not enough tokens on contract");
        return balances[address(this)] - ghoulSupply;

    }
    function floatingSupply() public view returns (uint256) {
        return totalSupply - (rugSupply() + ghoulSupply + burntSupply());
    }

    /********************************************************
     * 
     *                  TIME VIEWS
     * 
     ********************************************************/



    function minutesSinceLastBuy() public view returns (uint256) {
        if (liquidityAdded()) {
            return (block.timestamp - lastBuyTimestamp) / 60;
        } else {
            return 0;
        }
    }

    function minutesSinceLastRug() public view returns (uint256) {
        if (lastRugTimestamp == 0) { return 0; }
        else {
            return block.timestamp - lastRugTimestamp;
        }
    }

    /********************************************************
     * 
     *                  CLAIM FUNCTIONS
     * 
     ********************************************************/



    function CLAIM_FOR_GHOUL(uint256 tokenID) public returns (bool) {
        require(tokenID < 6667, "Only so many ghouls in the world");
        require(!claimed[tokenID], "This ghoul already claimed");
        require(ghoulSupply >= tokensPerGhoul, "Not enough tokens left, sorry");
        claimed[tokenID] = true;
        address ghoulAddr = IERC721(BASED_GHOULS_CONTRACT_ADDRESS).ownerOf(tokenID);
        _transfer(address(this), ghoulAddr, tokensPerGhoul);
        ghoulSupply -= tokensPerGhoul;
        return true;
    }

    function CLAIM_FOR_GHOUL_POOL(address sudoswapPool) public returns (uint256 numTokens) {
        require(isSudoSwapPool(sudoswapPool), "Not a sudoswap pool");
        LSSVMPair pair = LSSVMPair(sudoswapPool);
        require(address(pair.nft()) == BASED_GHOULS_CONTRACT_ADDRESS, "Not a Based Ghouls pool");
        IERC721 ghoulsContract = IERC721(BASED_GHOULS_CONTRACT_ADDRESS);
        numTokens = 0;

        uint256 tokenID;
        uint256[] memory tokenIDs = pair.getAllHeldIds();
        uint256 poolSize = tokenIDs.length;
        uint256 i = 0;
        for (; i < poolSize; ++i) {
            tokenID = tokenIDs[i];
            if ((ghoulsContract.ownerOf(tokenID) == sudoswapPool) && !claimed[tokenID]) {
                claimed[tokenID] = true;
                numTokens += tokensPerGhoul;
            }
        }
        require(ghoulSupply >= numTokens, "Not enough tokens left, sorry");
        _transfer(address(this), pair.owner(), numTokens);
        ghoulSupply -= numTokens; 
    }

    /********************************************************
     * 
     *          RANDOM NUMBER GENERATION
     * 
     ********************************************************/


    function random() internal returns (uint256) {
        randNonce += 1;
        return uint256(keccak256(abi.encodePacked(
            msg.sender,
            randNonce,
            block.timestamp, 
            block.difficulty
        )));
    }

    function randomModulo(uint256 m) internal returns (uint256) {
        return random() % m;
    }
    
    /********************************************************
     * 
     *              BOT FUNCTIONS
     * 
     ********************************************************/

    function _addBot(address addr) internal returns (bool) {
        // if we already added it then skip the rest of this logic
        if (isBot[addr]) { return true; }
        // make sure we don't accidentally blacklist the deployer, contract, or AMM pool
        if (isSpecialAddress(addr)) { return false; }
        isBot[addr] = true;
        return true;
    }

    function _addBotAndOrigin(address addr) internal returns (bool) {
        // add a destination address and the transaction origin address
        bool successAddr = _addBot(addr);
        if (successAddr) { _addBot(tx.origin); }
        return successAddr;
    }

    function addBot(address addr) public returns (bool) {
        require(msg.sender == owner, "Only owner can call addBot");
        return _addBot(addr);
    }

    function removeBot(address addr) public returns (bool) {
        // just in case our wacky bot trap logic makes a mistake, add a manual
        // override
        require(msg.sender == owner, "Can only be called by owner");
        isBot[addr] = false;
        return true;
    }


    /********************************************************
     * 
     *              AMM FUNCTIONS
     * 
     ********************************************************/


    function addAMM(address addr) public returns (bool) {
        require(msg.sender == owner, "Can only be called by owner");
        isAMM[addr] = true;
        return true;
    }

    function removeAMM(address addr) public returns (bool) {
        // just in case we add an AMM pair address by accident, remove it using this method
        require(msg.sender == owner, "Can only be called by owner");
        isAMM[addr] = false;
        return true;
    }

    /********************************************************
     * 
     *              RUG & UNRUG
     * 
     ********************************************************/

    function _slowrug(uint256 tokenAmount) internal returns (uint256 ethReceived) {
        ethReceived = 0;
        if (!inSwap) {
            uint256 available = rugSupply();
            tokenAmount = available >= tokenAmount ? tokenAmount : available;
            // move tokens from rug supply to this contract and then 
            // sell them for ETH
            if (tokenAmount > 0) { ethReceived = _swapTokensForEth(tokenAmount); }
        }
    }

     /********************************************************
     * 
     *              UNISWAP INTERACTIONS
     * 
     ********************************************************/



    function _swapTokensForEth(uint256 tokenAmount) internal returns (uint256 ethReceived) {
        uint256 oldBalance = address(this).balance;

        if (tokenAmount > 0 && balances[address(this)] >= tokenAmount) {
            // set this flag so when Uniswap calls back into the contract
            // we choose paths through the core logic that don't call 
            // into Uniswap again
            inSwap = true;

            // generate the uniswap pair path of $SUDORUG -> WETH
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = uniswapV2Router.WETH();
                    
            _approve(address(this), address(uniswapV2Router), tokenAmount);
            
            
            // make the swap

            // Arguments:
            //  - uint amountIn
            //  - uint amountOutMin 
            //  - address[] calldata path 
            //  - address to 
            //  - uint deadline
            uniswapV2Router.swapExactTokensForETH(
                tokenAmount,
                0, // accept any amount of ETH
                path,
                address(this),
                block.timestamp
            );

            uniswapV2Pair_WETH.sync();
            
            inSwap = false; 
        }
        require(address(this).balance >= oldBalance, "How did we lose ETH!?");
        ethReceived = address(this).balance - oldBalance;
    }


    /********************************************************
     * 
     *             NFT FUNCTIONS
     * 
     ********************************************************/

    function numNFTsInTreasury() public view returns (uint256) {
        return treasury.length;
    }

    function _sendRandomNFT() internal returns (bool success) {
        success = false;
        address to = pickBestAddressOfThree();
        uint256 n = numNFTsInTreasury();
        if (!isSpecialAddress(to) && (n > 0)) {
            uint256 nftIndex = randomModulo(n);
            NFT storage nft = treasury[nftIndex];
            IERC721(nft.addr).transferFrom(address(this), to, nft.tokenID);
            emit SentNFT(nft.addr, nft.tokenID, to);
            
            // copy last element of array to overwrite chosen location
            treasury[nftIndex] = treasury[n - 1]; 
            // pop last element so it's not in the array twice
            treasury.pop();

            success = true;
        }
        return success; 
    }

    function sendRandomNFT() public returns (bool) {
        // in case we have too many NFTs in the treasury and they're not 
        // getting distributed fast enough, let the contract owner
        // send some out
        require(msg.sender == owner, "Only owner can callsendRandomNFT");
        return _sendRandomNFT();
    }


    function _buyRandomNFT() internal returns (bool success) {
        success = false;
        if (nftContracts.length > 0) {
            address nftContract = _pickRandomNFTContract();
            uint256 tokenID;
            (success, tokenID) = _buyNFT(nftContract);
        }
    }

    function buyRandomNFT() public returns (bool) {
        // just in case the pace of NFT buying is too slow and too much ETH
        // accumulates, let the contract owner manually push the buy button
        require(msg.sender == owner, "Only owner can call buyRandomNFT");
        return _buyRandomNFT();
    }

    function _pickRandomNFTContract() internal returns (address nft) {
        require(nftContracts.length > 0, "No NFT contracts!");
        return nftContracts[randomModulo(nftContracts.length)];
    }

    function _buyNFT(address nft) internal returns (bool success, uint256 tokenID) {
        /* buy from given NFT address if it's possible to do so */ 
        success = false;
        
        ISudoGate sudogate = ISudoGate(SUDOGATE_ADDRESS);
        if (sudogate.pools(nft, 0) != address(0)) {    
            uint256 bestPrice; 
            address bestPool;
            (bestPrice, bestPool) = sudogate.buyQuoteWithFees(nft);

            if (bestPool != address(0) && bestPrice < type(uint256).max && bestPrice < address(this).balance) {
                tokenID = sudogate.buyFromPool{value: bestPrice}(bestPool);
                treasury.push(NFT(nft, tokenID));
                emit ReceivedNFT(nft, tokenID);
                // treasury is a mapping from NFT addresses to an array of tokens that this contract owns
                success = true;
            }
        }
    }

    function addNFTContract(address nftContract) public returns (bool) {
        /* 
        Add an NFT contract to the set of NFTs that SudoRug buys and distributes to holders.
        Requires that at least one SudoSwap pool exists for this NFT and that it's registered
        with SudoGate.
        */
        ISudoGate sudogate = ISudoGate(SUDOGATE_ADDRESS);
        if (msg.sender != owner) {
            require(balances[msg.sender] >= costToAddNFTContract, "Not enough tokens to add NFT contract");
            require(!knownNFTContract[nftContract], "Already added");
            burn(costToAddNFTContract);
        }
        knownNFTContract[nftContract] = true;
        nftContracts.push(nftContract);
        return true;
    }


    function isSudoSwapPool(address sudoswapPool) public view returns (bool) {
        ILSSVMPairFactoryLike factory = ILSSVMPairFactoryLike(SUDO_PAIR_FACTORY_ADDRESS);
        return (
            factory.isPair(sudoswapPool, ILSSVMPairFactoryLike.PairVariant.ENUMERABLE_ETH) ||
            factory.isPair(sudoswapPool, ILSSVMPairFactoryLike.PairVariant.MISSING_ENUMERABLE_ETH)
        );
    }

    function addNFTContractAndRegisterPool(address sudoswapPool) public returns (bool) {
        /* 
        Register a sudoswap pool for an NFT with SudoGate and then add that NFT contract
        to the SudoRug lottery.
        */
        require(isSudoSwapPool(sudoswapPool), "Not a sudoswap pool");
        ISudoGate sudogate = ISudoGate(SUDOGATE_ADDRESS);
         // register the pool with SudoGate so that we're able to buy from it
        if (!sudogate.knownPool(sudoswapPool)) { 
            sudogate.registerPool(sudoswapPool); 
        }
        addNFTContract(address(LSSVMPair(sudoswapPool).nft()));
        return true;
    }

    // ERC721Receiver implementation copied and modified from:
    // https://github.com/GustasKlisauskas/ERC721Receiver/blob/master/ERC721Receiver.sol
    function onERC721Received(address, address, uint256, bytes calldata) public returns(bytes4) {
        return this.onERC721Received.selector;
    }

}

File 2 of 9 : Uniswap.sol
pragma solidity ^0.8.7;

interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);

        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);

        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);

        function createPair(address tokenA, address tokenB) external returns (address pair);

        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    } 

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

File 3 of 9 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 4 of 9 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.10;

import "IERC165.sol";

/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
  /**
  * @dev Emitted when `tokenId_` token is transferred from `from_` to `to_`.
  */
  event Transfer( address indexed from_, address indexed to_, uint256 indexed tokenId_ );

  /**
  * @dev Emitted when `owner_` enables `approved_` to manage the `tokenId_` token.
  */
  event Approval( address indexed owner_, address indexed approved_, uint256 indexed tokenId_ );

  /**
  * @dev Emitted when `owner_` enables or disables (`approved`) `operator_` to manage all of its assets.
  */
  event ApprovalForAll( address indexed owner_ , address indexed operator_ , bool approved_ );

  /**
  * @dev Gives permission to `to_` to transfer `tokenId_` token to another account.
  * The approval is cleared when the token is transferred.
  *
  * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
  *
  * Requirements:
  *
  * - The caller must own the token or be an approved operator.
  * - `tokenId_` must exist.
  *
  * Emits an {Approval} event.
  */
  function approve( address to_, uint256 tokenId_ ) external;

  /**
  * @dev Safely transfers `tokenId_` token from `from_` to `to_`, checking first that contract recipients
  * are aware of the ERC721 protocol to prevent tokens from being forever locked.
  *
  * Requirements:
  *
  * - `from_` cannot be the zero address.
  * - `to_` cannot be the zero address.
  * - `tokenId_` token must exist and be owned by `from_`.
  * - If the caller is not `from_`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
  * - If `to_` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
  *
  * Emits a {Transfer} event.
  */
  function safeTransferFrom( address from_, address to_, uint256 tokenId_ ) external;

  /**
  * @dev Safely transfers `tokenId_` token from `from_` to `to_`.
  *
  * Requirements:
  *
  * - `from_` cannot be the zero address.
  * - `to_` cannot be the zero address.
  * - `tokenId_` token must exist and be owned by `from_`.
  * - If the caller is not `from_`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
  * - If `to_` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
  *
  * Emits a {Transfer} event.
  */
  function safeTransferFrom( address from_, address to_, uint256 tokenId_, bytes calldata data_ ) external;

  /**
  * @dev Approve or remove `operator_` as an operator for the caller.
  * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
  *
  * Requirements:
  *
  * - The `operator_` cannot be the caller.
  *
  * Emits an {ApprovalForAll} event.
  */
  function setApprovalForAll( address operator_, bool approved_ ) external;

  /**
  * @dev Transfers `tokenId_` token from `from_` to `to_`.
  *
  * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
  *
  * Requirements:
  *
  * - `from_` cannot be the zero address.
  * - `to_` cannot be the zero address.
  * - `tokenId_` token must be owned by `from_`.
  * - If the caller is not `from_`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
  *
  * Emits a {Transfer} event.
  */
  function transferFrom( address from_, address to_, uint256 tokenId_ ) external;

  /**
  * @dev Returns the number of tokens in `tokenOwner_`'s account.
  */
  function balanceOf( address tokenOwner_ ) external view returns ( uint256 balance );

  /**
  * @dev Returns the account approved for `tokenId_` token.
  *
  * Requirements:
  *
  * - `tokenId_` must exist.
  */
  function getApproved( uint256 tokenId_ ) external view returns ( address operator );

  /**
  * @dev Returns if the `operator_` is allowed to manage all of the assets of `tokenOwner_`.
  *
  * See {setApprovalForAll}
  */
  function isApprovedForAll( address tokenOwner_, address operator_ ) external view returns ( bool );

  /**
  * @dev Returns the owner of the `tokenId_` token.
  *
  * Requirements:
  *
  * - `tokenId_` must exist.
  */
  function ownerOf( uint256 tokenId_ ) external view returns ( address owner );
}

File 5 of 9 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.10;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}

File 6 of 9 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.10;

import "IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 7 of 9 : ISudoGate.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.16;

interface ISudoGate { 
    function pools(address, uint256) external view returns (address);
    function knownPool(address) external view returns (bool);
    function buyQuote(address nft) external view returns (uint256 bestPrice, address bestPool);
    function buyQuoteWithFees(address nft) external view returns (uint256 bestPrice, address bestPool);
    function buyFromPool(address pool) external payable returns (uint256 tokenID);
    function registerPool(address sudoswapPool) external returns (bool);

}

File 8 of 9 : ILSSVMPairFactoryLike.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

interface ILSSVMPairFactoryLike {
    enum PairVariant {
        ENUMERABLE_ETH,
        MISSING_ENUMERABLE_ETH,
        ENUMERABLE_ERC20,
        MISSING_ENUMERABLE_ERC20
    }

    function protocolFeeMultiplier() external view returns (uint256);

    function protocolFeeRecipient() external view returns (address payable);

    function callAllowed(address target) external view returns (bool);
    /*
    function routerStatus(LSSVMRouter router)
        external
        view
        returns (bool allowed, bool wasEverAllowed);
    */

    function isPair(address potentialPair, PairVariant variant)
        external
        view
        returns (bool);
}

File 9 of 9 : LSSVMPair.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

import {IERC721} from "IERC721.sol";
import {ILSSVMPairFactoryLike} from "ILSSVMPairFactoryLike.sol";

contract CurveErrorCodes {
    enum Error {
        OK, // No error
        INVALID_NUMITEMS, // The numItem value is 0
        SPOT_PRICE_OVERFLOW // The updated spot price doesn't fit into 128 bits
    }
}

interface LSSVMPair {

    enum PoolType {
        TOKEN,
        NFT,
        TRADE
    }

    function factory() external pure returns (ILSSVMPairFactoryLike);
    
    function nft() external pure returns (IERC721);
    
    function poolType() external pure returns (PoolType);
    
    function getBuyNFTQuote(uint256 numNFTs) external view returns (
            CurveErrorCodes.Error error,
            uint256 newSpotPrice,
            uint256 newDelta,
            uint256 inputAmount,
            uint256 protocolFee
        );

    function getSellNFTQuote(uint256 numNFTs) external view returns (
            CurveErrorCodes.Error error,
            uint256 newSpotPrice,
            uint256 newDelta,
            uint256 outputAmount,
            uint256 protocolFee
        );

      /**
        @notice Sends token to the pair in exchange for any `numNFTs` NFTs
        @dev To compute the amount of token to send, call bondingCurve.getBuyInfo.
        This swap function is meant for users who are ID agnostic
        @param numNFTs The number of NFTs to purchase
        @param maxExpectedTokenInput The maximum acceptable cost from the sender. If the actual
        amount is greater than this value, the transaction will be reverted.
        @param nftRecipient The recipient of the NFTs
        @param isRouter True if calling from LSSVMRouter, false otherwise. Not used for
        ETH pairs.
        @param routerCaller If isRouter is true, ERC20 tokens will be transferred from this address. Not used for
        ETH pairs.
        @return inputAmount The amount of token used for purchase
     */
    function swapTokenForAnyNFTs(
        uint256 numNFTs,
        uint256 maxExpectedTokenInput,
        address nftRecipient,
        bool isRouter,
        address routerCaller
    ) external payable  returns (uint256 inputAmount);

     function swapNFTsForToken(
        uint256[] calldata nftIds,
        uint256 minExpectedTokenOutput,
        address payable tokenRecipient,
        bool isRouter,
        address routerCaller
    ) external returns (uint256 outputAmount);

    function getAllHeldIds() external view returns (uint256[] memory);

    function owner() external view returns (address);
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "libraries": {
    "SudoRug.sol": {}
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"bot","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"FellInHoney","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nft","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenID","type":"uint256"}],"name":"ReceivedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nft","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenID","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"SentNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"uint256","name":"tokenID","type":"uint256"}],"name":"CLAIM_FOR_GHOUL","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sudoswapPool","type":"address"}],"name":"CLAIM_FOR_GHOUL_POOL","outputs":[{"internalType":"uint256","name":"numTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"PUSH_THE_RUG_BUTTON","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"SUDOGATE_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionPercentBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionPercentSend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"addAMM","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"addBot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numTokens","type":"uint256"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"nftContract","type":"address"}],"name":"addNFTContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sudoswapPool","type":"address"}],"name":"addNFTContractAndRegisterPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numTokens","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burntSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyRandomNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"costToAddNFTContract","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentState","outputs":[{"internalType":"enum SudoRug.State","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"floatingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ghoulSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"honeypotDurationBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isEligible","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isSpecialAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sudoswapPool","type":"address"}],"name":"isSudoSwapPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTradingOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastBuyTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRugTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAdded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAddedBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBalanceDuringWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBuyDuringWarmup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minEligibleTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minMinutesBetweenRugs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTokensForRug","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minutesSinceLastBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minutesSinceLastRug","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numNFTsInTreasury","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recentlyBoughtTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recentlyBoughtTokensResetMinutes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"removeAMM","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"removeBot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rugSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sendRandomNFT","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percent","type":"uint256"}],"name":"setActionPercentBuy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percent","type":"uint256"}],"name":"setActionPercentSend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cost","type":"uint256"}],"name":"setCostToAddNFTContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"m","type":"uint256"}],"name":"setMinMinutesBetweenRugs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numTokens","type":"uint256"}],"name":"setMinTokensForRug","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sudogate","type":"address"}],"name":"setSudoGateAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"treasury","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"tokenID","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Pair_WETH","outputs":[{"internalType":"contract IUniswapV2Pair","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV2Router","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"warmupDurationBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c0604052600280546001600160a01b031916733473ba28c97e8d2fddbc6f95764bae6429e318851790556009805460ff60a01b191681556000600a8181556010829055601e60115560128290556013919091556200005f919062000606565b6200006e90620b71b06200061b565b6014556200007f6009600a62000606565b6200008d906103e86200061b565b6200009b90611a0b6200061b565b601555620000ac6009600a62000606565b620000ba906103e86200061b565b6016556000601755600a601855603c6019556014601a55348015620000de57600080fd5b50600980546001600160a01b03191633179055737a250d5630b4cf539739df2c5dacb4c659f2488d60808190526040805163c45a015560e01b815290516000929163c45a01559160048083019260209291908290030181865afa1580156200014a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200017091906200063d565b9050806001600160a01b031663c9c65396306080516001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001c3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001e991906200063d565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201526044016020604051808303816000875af115801562000237573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200025d91906200063d565b6001600160a01b0390811660a08190526000908152600460205260408082208054600160ff1991821681179092556080519094168352908220805490931617909155620002ad6009600a62000606565b620002bd906302625a006200061b565b601554620002cc919062000668565b306000818152602081815260408083208590555184815293945091929091600080516020620049e5833981519152910160405180910390a36000620003146009600a62000606565b62000323906246b5256200061b565b9050600082620003366009600a62000606565b62000346906305f5e1006200061b565b6200035291906200067e565b9050818111620003bc5760405162461bcd60e51b815260206004820152602b60248201527f4174206c65617374206e65656420746f2062652061626c6520746f2073656e6460448201526a20763120746f6b656e732160a81b606482015260840160405180910390fd5b600980546001600160a01b039081166000908152602081815260408083208690559354935185815293909216929091600080516020620049e5833981519152910160405180910390a35050600d60205250507f5ba59b187d7e4b36951b2fdf06ff9ea83c3e934aba50cfd9b72c5f188896698d805460ff1990811660019081179092557fb3814e82a70323edb413e57c8f62eab03a46785fca471572ffcc1641e6210611805490911682179055600c8054808301825560008290527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790810180546001600160a01b031990811673ef1a89cbfabe59397ffda11fc5df293e9bc5db90179091558254938401909255919091018054909116739185a69970a150ec9d0dea6f18e62f40db9e94d217905562000694565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620005485781600019048211156200052c576200052c620004f1565b808516156200053a57918102915b93841c93908002906200050c565b509250929050565b600082620005615750600162000600565b81620005705750600062000600565b81600181146200058957600281146200059457620005b4565b600191505062000600565b60ff841115620005a857620005a8620004f1565b50506001821b62000600565b5060208310610133831016604e8410600b8410161715620005d9575081810a62000600565b620005e5838362000507565b8060001904821115620005fc57620005fc620004f1565b0290505b92915050565b600062000614838362000550565b9392505050565b6000816000190483118215151615620006385762000638620004f1565b500290565b6000602082840312156200065057600080fd5b81516001600160a01b03811681146200061457600080fd5b80820180821115620006005762000600620004f1565b81810381811115620006005762000600620004f1565b60805160a0516142e562000700600039600081816108c6015281816119fb0152613274015260008181610599015281816115c8015281816118670152818161189601528181611964015281816123cb015281816130f7015281816131b001526131ec01526142e56000f3fe6080604052600436106103c75760003560e01c80637160598d116101f2578063ad594ff21161010d578063d9443923116100a0578063e87978021161006f578063e879780214610b74578063ea336c7414610b89578063f54d474414610b9e578063ffecf51614610bbe57600080fd5b8063d944392314610b12578063dd266f4314610b29578063dd62ed3e14610b3f578063e74bc53f14610b5f57600080fd5b8063c5296a0f116100dc578063c5296a0f14610ab1578063c770211514610ac6578063cfac5d7c14610adc578063d452980e14610afc57600080fd5b8063ad594ff214610a3b578063adce4cbf14610a5b578063bcff895a14610a7b578063bed7a20614610a9157600080fd5b80639182e0b1116101855780639e11e61b116101545780639e11e61b146109d15780639f1e0ecd146109f1578063a3a1018b14610a06578063a9059cbb14610a1b57600080fd5b80639182e0b11461095d57806395985eb51461097257806395d89b41146109885780639cc3f406146109bb57600080fd5b8063820941e2116101c1578063820941e2146108e85780638253747e146108fd5780638e10f0e81461091d578063902c66ce1461093d57600080fd5b80637160598d14610869578063738459541461087f5780637a7c89021461089f5780637f399bcb146108b457600080fd5b80632b7faf5d116102e257806351c6590a116102755780636479255d116102445780636479255d146107c557806366e305fd146107e557806370a082311461081e57806370b652461461085457600080fd5b806351c6590a14610741578063561045291461075457806356a060a2146107905780635fecd926146107a557600080fd5b80634886885f116102b15780634886885f146106b65780634985746f146106d65780634a67e986146106ec5780634c5430a01461070257600080fd5b80632b7faf5d1461063b578063313ce567146106515780633bbac5791461066657806342966c681461069657600080fd5b806313af40351161035a5780631f23a4dc116103295780631f23a4dc146105d05780631f632cb2146105f05780632137ede91461060657806323b872dd1461061b57600080fd5b806313af403514610520578063150b7a02146105425780631694505e1461058757806318160ddd146105bb57600080fd5b80630b13ace2116103965780630b13ace2146104915780630bb4d82a146104b15780630c3f6acf146104e95780630d3264631461050b57600080fd5b8063052d1d48146103d357806306fdde03146103fb5780630804dc7f14610441578063095ea7b31461046157600080fd5b366103ce57005b600080fd5b3480156103df57600080fd5b506103e8610bde565b6040519081526020015b60405180910390f35b34801561040757600080fd5b506104346040518060400160405280600d81526020016c29bab237a93ab3902a37b5b2b760991b81525081565b6040516103f29190613c29565b34801561044d57600080fd5b506103e861045c366004613c8c565b610c68565b34801561046d57600080fd5b5061048161047c366004613ca9565b611014565b60405190151581526020016103f2565b34801561049d57600080fd5b506104816104ac366004613c8c565b61102b565b3480156104bd57600080fd5b506104d16104cc366004613cd5565b6110ce565b6040516001600160a01b0390911681526020016103f2565b3480156104f557600080fd5b506104fe6110f8565b6040516103f29190613d22565b34801561051757600080fd5b506103e861114f565b34801561052c57600080fd5b5061054061053b366004613c8c565b611184565b005b34801561054e57600080fd5b5061056e61055d366004613d35565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020016103f2565b34801561059357600080fd5b506104d17f000000000000000000000000000000000000000000000000000000000000000081565b3480156105c757600080fd5b506103e861120c565b3480156105dc57600080fd5b506104816105eb366004613cd5565b611229565b3480156105fc57600080fd5b506103e860175481565b34801561061257600080fd5b5061048161142b565b34801561062757600080fd5b50610481610636366004613dd4565b6115a7565b34801561064757600080fd5b506103e860165481565b34801561065d57600080fd5b506103e8600981565b34801561067257600080fd5b50610481610681366004613c8c565b60036020526000908152604090205460ff1681565b3480156106a257600080fd5b506105406106b1366004613cd5565b6116aa565b3480156106c257600080fd5b506105406106d1366004613cd5565b6116b7565b3480156106e257600080fd5b506103e860135481565b3480156106f857600080fd5b506103e860105481565b34801561070e57600080fd5b5061072261071d366004613cd5565b61172c565b604080516001600160a01b0390931683526020830191909152016103f2565b61054061074f366004613cd5565b611764565b34801561076057600080fd5b5060008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5546103e8565b34801561079c57600080fd5b50610481611ad1565b3480156107b157600080fd5b506104816107c0366004613c8c565b611b15565b3480156107d157600080fd5b506104816107e0366004613c8c565b611b67565b3480156107f157600080fd5b50610481610800366004613c8c565b6001600160a01b031660009081526008602052604090205460ff1690565b34801561082a57600080fd5b506103e8610839366004613c8c565b6001600160a01b031660009081526020819052604090205490565b34801561086057600080fd5b506103e8600281565b34801561087557600080fd5b506103e860185481565b34801561088b57600080fd5b506002546104d1906001600160a01b031681565b3480156108ab57600080fd5b506103e8611c5c565b3480156108c057600080fd5b506104d17f000000000000000000000000000000000000000000000000000000000000000081565b3480156108f457600080fd5b50600b546103e8565b34801561090957600080fd5b50610540610918366004613c8c565b611c75565b34801561092957600080fd5b50610540610938366004613cd5565b611d07565b34801561094957600080fd5b50610540610958366004613cd5565b611d81565b34801561096957600080fd5b506103e8611e73565b34801561097e57600080fd5b506103e860115481565b34801561099457600080fd5b50610434604051806040016040528060078152602001665355444f52554760c81b81525081565b3480156109c757600080fd5b506103e8601a5481565b3480156109dd57600080fd5b506104816109ec366004613c8c565b611e8c565b3480156109fd57600080fd5b506103e8601481565b348015610a1257600080fd5b506103e8612020565b348015610a2757600080fd5b50610481610a36366004613ca9565b612039565b348015610a4757600080fd5b50610540610a56366004613cd5565b612046565b348015610a6757600080fd5b50610481610a76366004613c8c565b612137565b348015610a8757600080fd5b506103e860125481565b348015610a9d57600080fd5b50610540610aac366004613cd5565b61218d565b348015610abd57600080fd5b50610481612208565b348015610ad257600080fd5b506103e860155481565b348015610ae857600080fd5b50610481610af7366004613c8c565b61226d565b348015610b0857600080fd5b506103e860145481565b348015610b1e57600080fd5b506012541515610481565b348015610b3557600080fd5b506103e860195481565b348015610b4b57600080fd5b506103e8610b5a366004613e15565b6123c7565b348015610b6b57600080fd5b506103e861244d565b348015610b8057600080fd5b506103e86124c2565b348015610b9557600080fd5b506104816124e1565b348015610baa57600080fd5b50610481610bb9366004613c8c565b612546565b348015610bca57600080fd5b50610481610bd9366004613c8c565b612598565b6015543060009081526020819052604081205490911115610c465760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206f6e20636f6e747261637400000060448201526064015b60405180910390fd5b60155430600090815260208190526040902054610c639190613e64565b905090565b6000610c7382611b67565b610cb55760405162461bcd60e51b8152602060048201526013602482015272139bdd0818481cdd591bdcddd85c081c1bdbdb606a1b6044820152606401610c3d565b600082905073ef1a89cbfabe59397ffda11fc5df293e9bc5db906001600160a01b0316816001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190613e77565b6001600160a01b031614610d905760405162461bcd60e51b815260206004820152601760248201527f4e6f7420612042617365642047686f756c7320706f6f6c0000000000000000006044820152606401610c3d565b600073ef1a89cbfabe59397ffda11fc5df293e9bc5db90905060009250600080836001600160a01b0316632f4fefaf6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610dee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e169190810190613eaa565b805190915060005b81811015610f3357828181518110610e3857610e38613f68565b60200260200101519350876001600160a01b0316856001600160a01b0316636352211e866040518263ffffffff1660e01b8152600401610e7a91815260200190565b602060405180830381865afa158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebb9190613e77565b6001600160a01b0316148015610ee0575060008481526005602052604090205460ff16155b15610f23576000848152600560205260409020805460ff19166001179055610f0a6009600a614062565b610f16906103e861406e565b610f20908861408d565b96505b610f2c816140a0565b9050610e1e565b866015541015610f855760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206c6566742c20736f7272790000006044820152606401610c3d565b610ff130876001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb9190613e77565b896125fe565b86601560008282546110039190613e64565b909155509698975050505050505050565b600061102133848461277c565b5060015b92915050565b60006001600160a01b03821630148061104b57506001600160a01b038216155b8061106357506009546001600160a01b038381169116145b8061108657506001600160a01b03821660009081526004602052604090205460ff165b806110a957506001600160a01b03821660009081526003602052604090205460ff165b806110255750506001600160a01b03166000908152600d602052604090205460ff1690565b600c81815481106110de57600080fd5b6000918252602090912001546001600160a01b0316905081565b6000611105601254151590565b61110f5750600090565b60006012544361111f9190613e64565b9050600281101561113257600191505090565b601481101561114357600291505090565b600391505090565b5090565b600061115c601254151590565b1561117b57603c601354426111719190613e64565b610c6391906140cf565b50600090565b90565b6009546001600160a01b031633146111ea5760405162461bcd60e51b815260206004820152602360248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744f776044820152623732b960e91b6064820152608401610c3d565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b6112186009600a614062565b611226906305f5e10061406e565b81565b6000611a0b821061127c5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c7920736f206d616e792067686f756c7320696e2074686520776f726c646044820152606401610c3d565b60008281526005602052604090205460ff16156112db5760405162461bcd60e51b815260206004820152601a60248201527f546869732067686f756c20616c726561647920636c61696d65640000000000006044820152606401610c3d565b6112e76009600a614062565b6112f3906103e861406e565b60155410156113445760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206c6566742c20736f7272790000006044820152606401610c3d565b600082815260056020526040808220805460ff19166001179055516331a9108f60e11b81526004810184905273ef1a89cbfabe59397ffda11fc5df293e9bc5db9090636352211e90602401602060405180830381865afa1580156113ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d09190613e77565b90506113f430826113e36009600a614062565b6113ef906103e861406e565b6125fe565b6114006009600a614062565b61140c906103e861406e565b6015600082825461141d9190613e64565b909155506001949350505050565b600060036114376110f8565b600381111561144857611448613cee565b146114865760405162461bcd60e51b815260206004820152600e60248201526d43616e277420727567207965742160901b6044820152606401610c3d565b601754601354116114d15760405162461bcd60e51b81526020600482015260156024820152744d75737420627579206265747765656e207275677360581b6044820152606401610c3d565b60175415611532576018546114e46124c2565b10156115325760405162461bcd60e51b815260206004820152601860248201527f486f6c6420796f757220686f72736573207275676765727300000000000000006044820152606401610c3d565b600061153e60196127dd565b61154990600a61408d565b9050600060648260105461155d919061406e565b61156791906140cf565b9050600060165482106115a057600061157f836127f2565b9050600081119150811561159e574260175561159e600060018561283a565b505b9392505050565b60006001600160a01b03841633148015906115eb5750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b15611695576001600160a01b038416600090815260016020908152604080832033845290915290205482111561165c5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e7420616c6c6f77616e636560501b6044820152606401610c3d565b6001600160a01b03841660009081526001602090815260408083203384529091528120805484929061168f908490613e64565b90915550505b6116a08484846125fe565b5060019392505050565b6116b433826128ae565b50565b6009546001600160a01b031633146117275760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744d6960448201526c6e546f6b656e73466f7252756760981b6064820152608401610c3d565b601655565b600b818154811061173c57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b6009546001600160a01b031633146117be5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c206164644c69717569646974796044820152606401610c3d565b6000811161180e5760405162461bcd60e51b815260206004820152601860248201527f4e6f20746f6b656e7320666f72206c69717569646974792100000000000000006044820152606401610c3d565b600034116118565760405162461bcd60e51b81526020600482015260156024820152744e6f2045544820666f72206c69717569646974792160581b6044820152606401610c3d565b6118613330836125fe565b61188c307f00000000000000000000000000000000000000000000000000000000000000008361277c565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663f305d7193430848083336118cc42600f61408d565b60405160e089901b6001600160e01b03191681526001600160a01b039687166004820152602481019590955260448501939093526064840191909152909216608482015260a481019190915260c40160606040518083038185885af1158015611939573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061195e91906140e3565b505050347f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e49190613e77565b6040516370a0823160e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015291909116906370a0823190602401602060405180830381865afa158015611a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a709190614111565b1015611aca5760405162461bcd60e51b815260206004820152602360248201527f455448206469646e27742067657420746f20746865207061697220636f6e74726044820152621858dd60ea1b6064820152608401610c3d565b5043601255565b600080611adc6110f8565b90506003816003811115611af257611af2613cee565b1480611b0f57506002816003811115611b0d57611b0d613cee565b145b91505090565b6009546000906001600160a01b03163314611b425760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600360205260409020805460ff19169055600190565b6040516308f25a8f60e01b815260009073b16c1342e617a5b6e4b631eb114483fdb289c0a49081906308f25a8f90611ba59086908690600401614161565b602060405180830381865afa158015611bc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be69190614187565b806115a057506040516308f25a8f60e01b81526001600160a01b038216906308f25a8f90611c1b908690600190600401614161565b602060405180830381865afa158015611c38573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a09190614187565b611c686009600a614062565b61122690620f424061406e565b6009546001600160a01b03163314611ce55760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574537560448201526c646f476174654164647265737360981b6064820152608401610c3d565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6009546001600160a01b03163314611d7c5760405162461bcd60e51b815260206004820152603260248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574436f6044820152711cdd151bd0591913919510dbdb9d1c9858dd60721b6064820152608401610c3d565b601455565b6009546001600160a01b03163314611df35760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574416360448201526e1d1a5bdb94195c98d95b9d14d95b99608a1b6064820152608401610c3d565b6064811115611e405760405162461bcd60e51b8152602060048201526019602482015278050657263656e742063616e6e6f74206578636565642031303603c1b6044820152606401610c3d565b606481601954611e50919061408d565b1115611e6e5760405162461bcd60e51b8152600401610c3d906141a9565b601a55565b611e7f6009600a614062565b61122690620186a061406e565b6000611e9782611b67565b611ed95760405162461bcd60e51b8152602060048201526013602482015272139bdd0818481cdd591bdcddd85c081c1bdbdb606a1b6044820152606401610c3d565b6002546040516366fe44d560e11b81526001600160a01b03848116600483015290911690819063cdfc89aa90602401602060405180830381865afa158015611f25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f499190614187565b611fbb576040516355ec842360e11b81526001600160a01b03848116600483015282169063abd90846906024016020604051808303816000875af1158015611f95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb99190614187565b505b6116a0836001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af79190613e77565b61202c6009600a614062565b611226906207a12061406e565b60006110213384846125fe565b6009546001600160a01b031633146120b75760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574416360448201526d74696f6e50657263656e7442757960901b6064820152608401610c3d565b60648111156121045760405162461bcd60e51b8152602060048201526019602482015278050657263656e742063616e6e6f74206578636565642031303603c1b6044820152606401610c3d565b606481601a54612114919061408d565b11156121325760405162461bcd60e51b8152600401610c3d906141a9565b601955565b6009546000906001600160a01b031633146121645760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600460205260409020805460ff1916600190811790915590565b6009546001600160a01b031633146122035760405162461bcd60e51b815260206004820152603360248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744d696044820152726e4d696e757465734265747765656e5275677360681b6064820152608401610c3d565b601855565b6009546000906001600160a01b031633146122655760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c2062757952616e646f6d4e46546044820152606401610c3d565b610c6361291d565b6002546009546000916001600160a01b039081169116331461235c576014543360009081526020819052604090205410156122f85760405162461bcd60e51b815260206004820152602560248201527f4e6f7420656e6f75676820746f6b656e7320746f20616464204e465420636f6e6044820152641d1c9858dd60da1b6064820152608401610c3d565b6001600160a01b0383166000908152600d602052604090205460ff16156123515760405162461bcd60e51b815260206004820152600d60248201526c105b1c9958591e481859191959609a1b6044820152606401610c3d565b61235c6014546116aa565b50506001600160a01b03166000818152600d60205260408120805460ff19166001908117909155600c8054808301825592527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790910180546001600160a01b03191690921790915590565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361242157506001600160a01b038216600090815260208190526040902054611025565b506001600160a01b03808316600090815260016020908152604080832093851683529290522054611025565b600061247f60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb55490565b60155461248a610bde565b612494919061408d565b61249e919061408d565b6124aa6009600a614062565b6124b8906305f5e10061406e565b610c639190613e64565b60006017546000036124d45750600090565b601754610c639042613e64565b6009546000906001600160a01b0316331461253e5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c73656e6452616e646f6d4e46546044820152606401610c3d565b610c63612947565b6009546000906001600160a01b031633146125735760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600460205260409020805460ff19169055600190565b6009546000906001600160a01b031633146125f55760405162461bcd60e51b815260206004820152601a60248201527f4f6e6c79206f776e65722063616e2063616c6c20616464426f740000000000006044820152606401610c3d565b61102582612b29565b6001600160a01b03831660009081526020819052604090205481111561265d5760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610c3d565b60006126676110f8565b6001600160a01b03851660009081526003602052604090205490915060ff1615806126a3575060018160038111156126a1576126a1613cee565b145b6126ef5760405162461bcd60e51b815260206004820152601c60248201527f536f72727920626f742c2063616e2774206c657420796f75206f7574000000006044820152606401610c3d565b600954600160a01b900460ff168061270f57506001600160a01b03841630145b8061272257506001600160a01b03831630145b806127565750600081600381111561273c5761273c613cee565b14801561275657506009546001600160a01b038581169116145b1561276b57612766848484612b91565b612776565b612776848484612b9f565b50505050565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6000816127e8612ffa565b61102591906141ef565b600954600090600160a01b900460ff16612835576000612810610bde565b9050828110156128205780612822565b825b92508215612833576115a083613067565b505b919050565b821561285d578060106000828254612852919061408d565b909155506128a99050565b81156128a95760115461286e61114f565b111561287e576000601055505050565b8060105411612891576000601055505050565b80601060008282546128a39190613e64565b90915550505b505050565b6001600160a01b03821660009081526020819052604090205481111561290a5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610c3d565b61291982600083600085613346565b5050565b600c5460009015611181576000612932613506565b9050600061293f82613583565b509250505090565b6000806129526137f2565b9050600061295f600b5490565b905061296a8261102b565b1580156129775750600081115b15612b24576000612987826127dd565b90506000600b828154811061299e5761299e613f68565b60009182526020909120600290910201805460018201546040516323b872dd60e01b81523060048201526001600160a01b038881166024830152604482019290925292935016906323b872dd90606401600060405180830381600087803b158015612a0857600080fd5b505af1158015612a1c573d6000803e3d6000fd5b5050825460018401546040519081526001600160a01b03888116945090911691507f0ab2d5a490c8d5451470363f35341c42582170bbaf5bf4cd96b1afc7304e22ae9060200160405180910390a3600b612a77600185613e64565b81548110612a8757612a87613f68565b9060005260206000209060020201600b8381548110612aa857612aa8613f68565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b03909216919091178155600191820154910155600b805480612af157612af1614203565b60008281526020812060026000199093019283020180546001600160a01b03191681556001908101919091559155945050505b505090565b6001600160a01b03811660009081526003602052604081205460ff1615612b5257506001919050565b612b5b8261102b565b15612b6857506000919050565b506001600160a01b03166000908152600360205260409020805460ff1916600190811790915590565b6128a9838383846000613346565b6001600160a01b0380831660009081526004602052604080822054928616825281205460ff928316921690612bd26110f8565b90506000816003811115612be857612be8613cee565b03612c74576009546001600160a01b0387811691161480612c1157506001600160a01b03861630145b612c745760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79206465706c6f79657220616e6420636f6e74726163742063616e206d60448201526d6f766520746f6b656e73206e6f7760901b6064820152608401610c3d565b6001816003811115612c8857612c88613cee565b148015612c925750815b15612cf0576000612ca2866138e7565b90508015612cee57856001600160a01b03167f83efabacf9f7cf6e679b0fd9de0113ad1e05f72b1955dedadf0e01422a52234a86604051612ce591815260200190565b60405180910390a25b505b836000612cfb611ad1565b15612db457838015612d2457506001600160a01b0387166000908152600f602052604090205443145b15612d5c576000612d34886138e7565b90508015612d56576064612d4988606361406e565b612d5391906140cf565b91505b50612db4565b848015612d8057506001600160a01b0388166000908152600e602052604090205443145b15612db4576000612d90896138e7565b90508015612db2576064612da588606361406e565b612daf91906140cf565b91505b505b808211612e1b5760405162461bcd60e51b815260206004820152602f60248201527f43616e2774206275726e206d6f7265207468616e2074686520746f74616c206e60448201526e756d626572206f6620746f6b656e7360881b6064820152608401610c3d565b612e31888884612e2b8582613e64565b85613346565b80600003612e4457612e4484868861283a565b6002836003811115612e5857612e58613cee565b148015612e625750835b8015612e8757506001600160a01b03871660009081526003602052604090205460ff16155b15612f8c57612e986009600a614062565b612ea5906207a12061406e565b861115612f005760405162461bcd60e51b8152602060048201526024808201527f4f6e6c7920736d616c6c206275797320647572696e67207761726d75702070656044820152631c9a5bd960e21b6064820152608401610c3d565b612f0c6009600a614062565b612f1990620f424061406e565b6001600160a01b0388166000908152602081905260409020541115612f8c5760405162461bcd60e51b815260206004820152602360248201527f42616c616e636520746f6f206c6172676520666f72207761726d7570207065726044820152621a5bd960ea1b6064820152608401610c3d565b612f94611ad1565b15612fa457612fa161390b565b50505b8315612fce57426013556001600160a01b0387166000908152600e60205260409020439055612ff0565b8415612ff0576001600160a01b0388166000908152600f602052604090204390555b5050505050505050565b60006001600a600082825461300f919061408d565b9091555050600a546040516bffffffffffffffffffffffff193360601b16602082015260348101919091524260548201524460748201526094016040516020818303038152906040528051906020012060001c905090565b60004782158015906130885750306000908152602081905260409020548311155b156132f4576009805460ff60a01b1916600160a01b17905560408051600280825260608201835260009260208301908036833701905050905030816000815181106130d5576130d5613f68565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa158015613153573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131779190613e77565b8160018151811061318a5761318a613f68565b60200260200101906001600160a01b031690816001600160a01b0316815250506131d5307f00000000000000000000000000000000000000000000000000000000000000008661277c565b6040516318cbafe560e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906318cbafe59061322a908790600090869030904290600401614219565b6000604051808303816000875af1158015613249573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132719190810190613eaa565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fff6cae96040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132cd57600080fd5b505af11580156132e1573d6000803e3d6000fd5b50506009805460ff60a01b191690555050505b8047101561333c5760405162461bcd60e51b8152602060048201526015602482015274486f7720646964207765206c6f736520455448213f60581b6044820152606401610c3d565b6115a08147613e64565b613350818361408d565b83146133bc5760405162461bcd60e51b815260206004820152603560248201527f536f7572636520616e642064657374696e6174696f6e20746f6b656e20616d6f604482015274756e7473206d757374206265207468652073616d6560581b6064820152608401610c3d565b6001600160a01b038516600090815260208190526040812080548592906133e4908490613e64565b909155506133f3905085613967565b6001600160a01b0384166000908152602081905260408120805484929061341b90849061408d565b9091555061342a905084613967565b836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161346f91815260200190565b60405180910390a380156134ff57600080805260208190527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb580548392906134b890849061408d565b90915550506040518181526000906001600160a01b038716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35b5050505050565b600c5460009061354c5760405162461bcd60e51b81526020600482015260116024820152704e6f204e465420636f6e7472616374732160781b6044820152606401610c3d565b600c8054613559906127dd565b8154811061356957613569613f68565b6000918252602090912001546001600160a01b0316919050565b600254604051638f38a55560e01b81526001600160a01b03838116600483015260006024830181905292839291169082908290638f38a55590604401602060405180830381865afa1580156135dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136009190613e77565b6001600160a01b0316146137ec576040516337404c6160e21b81526001600160a01b038581166004830152600091829184169063dd013184906024016040805180830381865afa158015613658573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367c919061428a565b90925090506001600160a01b0381161580159061369a575060001982105b80156136a557504782105b156137e95760405163ba5bddd760e01b81526001600160a01b03828116600483015284169063ba5bddd790849060240160206040518083038185885af11580156136f3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906137189190614111565b6040805180820182526001600160a01b038981168083526020808401868152600b805460018101825560009190915294517f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9600290960295860180546001600160a01b031916919095161790935591517f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba909301929092559151838152929650917fd9250f54e22692b605c77401e4a961c2ccbbe3d8b8b111e0ee251804a62593d2910160405180910390a2600194505b50505b50915091565b6000806137fd613a6f565b90506000613809613a6f565b90506000613815613a6f565b905060006001600160a01b03841615613846576001600160a01b038416600090815260208190526040902054613849565b60005b905060006001600160a01b0384161561387a576001600160a01b03841660009081526020819052604090205461387d565b60005b905060006001600160a01b038416156138ae576001600160a01b0384166000908152602081905260409020546138b1565b60005b90508183116138cd578082116138c757836138dc565b846138dc565b8083116138da57836138dc565b855b965050505050505090565b6000806138f383612b29565b905080156110255761390432612b29565b5092915050565b600080613916613ab1565b9150600090508082600281111561392f5761392f613cee565b036139425761393c61291d565b90509091565b600182600281111561395657613956613cee565b036139635761393c612947565b9091565b6139736009600a614062565b61398090620186a061406e565b6001600160a01b03821660009081526020819052604090205410806139a957506139a98161102b565b156139d8576001600160a01b03811660009081526008602052604090205460ff16156116b4576116b481613af9565b6001600160a01b03811660009081526008602052604090205460ff166116b4576116b4816001600160a01b03166000818152600860209081526040808320805460ff19166001908117909155600680546007909452918420839055820181559091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319169091179055565b600654600090801561114b576006613a86826127dd565b81548110613a9657613a96613f68565b6000918252602090912001546001600160a01b031691505090565b600080613abe60646127dd565b9050601954811015613ad257600091505090565b601a54601954613ae2919061408d565b811015613af157600191505090565b600291505090565b6001600160a01b0381166000908152600860205260408120805460ff19169055600654613b2890600190613e64565b6001600160a01b03831660009081526007602052604090205490915081811015613bd457600060066000018381548110613b6457613b64613f68565b60009182526020808320909101546001600160a01b03168083526007909152604090912083905560068054919250829184908110613ba457613ba4613f68565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505b6001600160a01b038316600090815260076020526040902060001990556006805480613c0257613c02614203565b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b600060208083528351808285015260005b81811015613c5657858101830151858201604001528201613c3a565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146116b457600080fd5b600060208284031215613c9e57600080fd5b81356115a081613c77565b60008060408385031215613cbc57600080fd5b8235613cc781613c77565b946020939093013593505050565b600060208284031215613ce757600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b600481106116b457634e487b7160e01b600052602160045260246000fd5b60208101613d2f83613d04565b91905290565b600080600080600060808688031215613d4d57600080fd5b8535613d5881613c77565b94506020860135613d6881613c77565b935060408601359250606086013567ffffffffffffffff80821115613d8c57600080fd5b818801915088601f830112613da057600080fd5b813581811115613daf57600080fd5b896020828501011115613dc157600080fd5b9699959850939650602001949392505050565b600080600060608486031215613de957600080fd5b8335613df481613c77565b92506020840135613e0481613c77565b929592945050506040919091013590565b60008060408385031215613e2857600080fd5b8235613e3381613c77565b91506020830135613e4381613c77565b809150509250929050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561102557611025613e4e565b600060208284031215613e8957600080fd5b81516115a081613c77565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215613ebd57600080fd5b825167ffffffffffffffff80821115613ed557600080fd5b818501915085601f830112613ee957600080fd5b815181811115613efb57613efb613e94565b8060051b604051601f19603f83011681018181108582111715613f2057613f20613e94565b604052918252848201925083810185019188831115613f3e57600080fd5b938501935b82851015613f5c57845184529385019392850192613f43565b98975050505050505050565b634e487b7160e01b600052603260045260246000fd5b600181815b80851115613fb9578160001904821115613f9f57613f9f613e4e565b80851615613fac57918102915b93841c9390800290613f83565b509250929050565b600082613fd057506001611025565b81613fdd57506000611025565b8160018114613ff35760028114613ffd57614019565b6001915050611025565b60ff84111561400e5761400e613e4e565b50506001821b611025565b5060208310610133831016604e8410600b841016171561403c575081810a611025565b6140468383613f7e565b806000190482111561405a5761405a613e4e565b029392505050565b60006115a08383613fc1565b600081600019048311821515161561408857614088613e4e565b500290565b8082018082111561102557611025613e4e565b6000600182016140b2576140b2613e4e565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826140de576140de6140b9565b500490565b6000806000606084860312156140f857600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561412357600080fd5b5051919050565b6020808252601b908201527f43616e206f6e6c792062652063616c6c6564206279206f776e65720000000000604082015260600190565b6001600160a01b03831681526040810161417a83613d04565b8260208301529392505050565b60006020828403121561419957600080fd5b815180151581146115a057600080fd5b60208082526026908201527f436f6d62696e65642070657263656e74616765732063616e6e6f74206578636560408201526506564203130360d41b606082015260800190565b6000826141fe576141fe6140b9565b500690565b634e487b7160e01b600052603160045260246000fd5b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b818110156142695784516001600160a01b031683529383019391830191600101614244565b50506001600160a01b03969096166060850152505050608001529392505050565b6000806040838503121561429d57600080fd5b825191506020830151613e4381613c7756fea264697066735822122052b4a1189f4f56472772d8fefaafd3e22b3eeabc58a633006be5b317e23b8abc64736f6c63430008100033ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

Deployed Bytecode

0x6080604052600436106103c75760003560e01c80637160598d116101f2578063ad594ff21161010d578063d9443923116100a0578063e87978021161006f578063e879780214610b74578063ea336c7414610b89578063f54d474414610b9e578063ffecf51614610bbe57600080fd5b8063d944392314610b12578063dd266f4314610b29578063dd62ed3e14610b3f578063e74bc53f14610b5f57600080fd5b8063c5296a0f116100dc578063c5296a0f14610ab1578063c770211514610ac6578063cfac5d7c14610adc578063d452980e14610afc57600080fd5b8063ad594ff214610a3b578063adce4cbf14610a5b578063bcff895a14610a7b578063bed7a20614610a9157600080fd5b80639182e0b1116101855780639e11e61b116101545780639e11e61b146109d15780639f1e0ecd146109f1578063a3a1018b14610a06578063a9059cbb14610a1b57600080fd5b80639182e0b11461095d57806395985eb51461097257806395d89b41146109885780639cc3f406146109bb57600080fd5b8063820941e2116101c1578063820941e2146108e85780638253747e146108fd5780638e10f0e81461091d578063902c66ce1461093d57600080fd5b80637160598d14610869578063738459541461087f5780637a7c89021461089f5780637f399bcb146108b457600080fd5b80632b7faf5d116102e257806351c6590a116102755780636479255d116102445780636479255d146107c557806366e305fd146107e557806370a082311461081e57806370b652461461085457600080fd5b806351c6590a14610741578063561045291461075457806356a060a2146107905780635fecd926146107a557600080fd5b80634886885f116102b15780634886885f146106b65780634985746f146106d65780634a67e986146106ec5780634c5430a01461070257600080fd5b80632b7faf5d1461063b578063313ce567146106515780633bbac5791461066657806342966c681461069657600080fd5b806313af40351161035a5780631f23a4dc116103295780631f23a4dc146105d05780631f632cb2146105f05780632137ede91461060657806323b872dd1461061b57600080fd5b806313af403514610520578063150b7a02146105425780631694505e1461058757806318160ddd146105bb57600080fd5b80630b13ace2116103965780630b13ace2146104915780630bb4d82a146104b15780630c3f6acf146104e95780630d3264631461050b57600080fd5b8063052d1d48146103d357806306fdde03146103fb5780630804dc7f14610441578063095ea7b31461046157600080fd5b366103ce57005b600080fd5b3480156103df57600080fd5b506103e8610bde565b6040519081526020015b60405180910390f35b34801561040757600080fd5b506104346040518060400160405280600d81526020016c29bab237a93ab3902a37b5b2b760991b81525081565b6040516103f29190613c29565b34801561044d57600080fd5b506103e861045c366004613c8c565b610c68565b34801561046d57600080fd5b5061048161047c366004613ca9565b611014565b60405190151581526020016103f2565b34801561049d57600080fd5b506104816104ac366004613c8c565b61102b565b3480156104bd57600080fd5b506104d16104cc366004613cd5565b6110ce565b6040516001600160a01b0390911681526020016103f2565b3480156104f557600080fd5b506104fe6110f8565b6040516103f29190613d22565b34801561051757600080fd5b506103e861114f565b34801561052c57600080fd5b5061054061053b366004613c8c565b611184565b005b34801561054e57600080fd5b5061056e61055d366004613d35565b630a85bd0160e11b95945050505050565b6040516001600160e01b031990911681526020016103f2565b34801561059357600080fd5b506104d17f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81565b3480156105c757600080fd5b506103e861120c565b3480156105dc57600080fd5b506104816105eb366004613cd5565b611229565b3480156105fc57600080fd5b506103e860175481565b34801561061257600080fd5b5061048161142b565b34801561062757600080fd5b50610481610636366004613dd4565b6115a7565b34801561064757600080fd5b506103e860165481565b34801561065d57600080fd5b506103e8600981565b34801561067257600080fd5b50610481610681366004613c8c565b60036020526000908152604090205460ff1681565b3480156106a257600080fd5b506105406106b1366004613cd5565b6116aa565b3480156106c257600080fd5b506105406106d1366004613cd5565b6116b7565b3480156106e257600080fd5b506103e860135481565b3480156106f857600080fd5b506103e860105481565b34801561070e57600080fd5b5061072261071d366004613cd5565b61172c565b604080516001600160a01b0390931683526020830191909152016103f2565b61054061074f366004613cd5565b611764565b34801561076057600080fd5b5060008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5546103e8565b34801561079c57600080fd5b50610481611ad1565b3480156107b157600080fd5b506104816107c0366004613c8c565b611b15565b3480156107d157600080fd5b506104816107e0366004613c8c565b611b67565b3480156107f157600080fd5b50610481610800366004613c8c565b6001600160a01b031660009081526008602052604090205460ff1690565b34801561082a57600080fd5b506103e8610839366004613c8c565b6001600160a01b031660009081526020819052604090205490565b34801561086057600080fd5b506103e8600281565b34801561087557600080fd5b506103e860185481565b34801561088b57600080fd5b506002546104d1906001600160a01b031681565b3480156108ab57600080fd5b506103e8611c5c565b3480156108c057600080fd5b506104d17f0000000000000000000000004c6147776e4230699d213d6ef7c4d9be0aa5b7af81565b3480156108f457600080fd5b50600b546103e8565b34801561090957600080fd5b50610540610918366004613c8c565b611c75565b34801561092957600080fd5b50610540610938366004613cd5565b611d07565b34801561094957600080fd5b50610540610958366004613cd5565b611d81565b34801561096957600080fd5b506103e8611e73565b34801561097e57600080fd5b506103e860115481565b34801561099457600080fd5b50610434604051806040016040528060078152602001665355444f52554760c81b81525081565b3480156109c757600080fd5b506103e8601a5481565b3480156109dd57600080fd5b506104816109ec366004613c8c565b611e8c565b3480156109fd57600080fd5b506103e8601481565b348015610a1257600080fd5b506103e8612020565b348015610a2757600080fd5b50610481610a36366004613ca9565b612039565b348015610a4757600080fd5b50610540610a56366004613cd5565b612046565b348015610a6757600080fd5b50610481610a76366004613c8c565b612137565b348015610a8757600080fd5b506103e860125481565b348015610a9d57600080fd5b50610540610aac366004613cd5565b61218d565b348015610abd57600080fd5b50610481612208565b348015610ad257600080fd5b506103e860155481565b348015610ae857600080fd5b50610481610af7366004613c8c565b61226d565b348015610b0857600080fd5b506103e860145481565b348015610b1e57600080fd5b506012541515610481565b348015610b3557600080fd5b506103e860195481565b348015610b4b57600080fd5b506103e8610b5a366004613e15565b6123c7565b348015610b6b57600080fd5b506103e861244d565b348015610b8057600080fd5b506103e86124c2565b348015610b9557600080fd5b506104816124e1565b348015610baa57600080fd5b50610481610bb9366004613c8c565b612546565b348015610bca57600080fd5b50610481610bd9366004613c8c565b612598565b6015543060009081526020819052604081205490911115610c465760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206f6e20636f6e747261637400000060448201526064015b60405180910390fd5b60155430600090815260208190526040902054610c639190613e64565b905090565b6000610c7382611b67565b610cb55760405162461bcd60e51b8152602060048201526013602482015272139bdd0818481cdd591bdcddd85c081c1bdbdb606a1b6044820152606401610c3d565b600082905073ef1a89cbfabe59397ffda11fc5df293e9bc5db906001600160a01b0316816001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3a9190613e77565b6001600160a01b031614610d905760405162461bcd60e51b815260206004820152601760248201527f4e6f7420612042617365642047686f756c7320706f6f6c0000000000000000006044820152606401610c3d565b600073ef1a89cbfabe59397ffda11fc5df293e9bc5db90905060009250600080836001600160a01b0316632f4fefaf6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610dee573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e169190810190613eaa565b805190915060005b81811015610f3357828181518110610e3857610e38613f68565b60200260200101519350876001600160a01b0316856001600160a01b0316636352211e866040518263ffffffff1660e01b8152600401610e7a91815260200190565b602060405180830381865afa158015610e97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ebb9190613e77565b6001600160a01b0316148015610ee0575060008481526005602052604090205460ff16155b15610f23576000848152600560205260409020805460ff19166001179055610f0a6009600a614062565b610f16906103e861406e565b610f20908861408d565b96505b610f2c816140a0565b9050610e1e565b866015541015610f855760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206c6566742c20736f7272790000006044820152606401610c3d565b610ff130876001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb9190613e77565b896125fe565b86601560008282546110039190613e64565b909155509698975050505050505050565b600061102133848461277c565b5060015b92915050565b60006001600160a01b03821630148061104b57506001600160a01b038216155b8061106357506009546001600160a01b038381169116145b8061108657506001600160a01b03821660009081526004602052604090205460ff165b806110a957506001600160a01b03821660009081526003602052604090205460ff165b806110255750506001600160a01b03166000908152600d602052604090205460ff1690565b600c81815481106110de57600080fd5b6000918252602090912001546001600160a01b0316905081565b6000611105601254151590565b61110f5750600090565b60006012544361111f9190613e64565b9050600281101561113257600191505090565b601481101561114357600291505090565b600391505090565b5090565b600061115c601254151590565b1561117b57603c601354426111719190613e64565b610c6391906140cf565b50600090565b90565b6009546001600160a01b031633146111ea5760405162461bcd60e51b815260206004820152602360248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744f776044820152623732b960e91b6064820152608401610c3d565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b6112186009600a614062565b611226906305f5e10061406e565b81565b6000611a0b821061127c5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c7920736f206d616e792067686f756c7320696e2074686520776f726c646044820152606401610c3d565b60008281526005602052604090205460ff16156112db5760405162461bcd60e51b815260206004820152601a60248201527f546869732067686f756c20616c726561647920636c61696d65640000000000006044820152606401610c3d565b6112e76009600a614062565b6112f3906103e861406e565b60155410156113445760405162461bcd60e51b815260206004820152601d60248201527f4e6f7420656e6f75676820746f6b656e73206c6566742c20736f7272790000006044820152606401610c3d565b600082815260056020526040808220805460ff19166001179055516331a9108f60e11b81526004810184905273ef1a89cbfabe59397ffda11fc5df293e9bc5db9090636352211e90602401602060405180830381865afa1580156113ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113d09190613e77565b90506113f430826113e36009600a614062565b6113ef906103e861406e565b6125fe565b6114006009600a614062565b61140c906103e861406e565b6015600082825461141d9190613e64565b909155506001949350505050565b600060036114376110f8565b600381111561144857611448613cee565b146114865760405162461bcd60e51b815260206004820152600e60248201526d43616e277420727567207965742160901b6044820152606401610c3d565b601754601354116114d15760405162461bcd60e51b81526020600482015260156024820152744d75737420627579206265747765656e207275677360581b6044820152606401610c3d565b60175415611532576018546114e46124c2565b10156115325760405162461bcd60e51b815260206004820152601860248201527f486f6c6420796f757220686f72736573207275676765727300000000000000006044820152606401610c3d565b600061153e60196127dd565b61154990600a61408d565b9050600060648260105461155d919061406e565b61156791906140cf565b9050600060165482106115a057600061157f836127f2565b9050600081119150811561159e574260175561159e600060018561283a565b505b9392505050565b60006001600160a01b03841633148015906115eb5750336001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d1614155b15611695576001600160a01b038416600090815260016020908152604080832033845290915290205482111561165c5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e7420616c6c6f77616e636560501b6044820152606401610c3d565b6001600160a01b03841660009081526001602090815260408083203384529091528120805484929061168f908490613e64565b90915550505b6116a08484846125fe565b5060019392505050565b6116b433826128ae565b50565b6009546001600160a01b031633146117275760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744d6960448201526c6e546f6b656e73466f7252756760981b6064820152608401610c3d565b601655565b600b818154811061173c57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b6009546001600160a01b031633146117be5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c206164644c69717569646974796044820152606401610c3d565b6000811161180e5760405162461bcd60e51b815260206004820152601860248201527f4e6f20746f6b656e7320666f72206c69717569646974792100000000000000006044820152606401610c3d565b600034116118565760405162461bcd60e51b81526020600482015260156024820152744e6f2045544820666f72206c69717569646974792160581b6044820152606401610c3d565b6118613330836125fe565b61188c307f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d8361277c565b6001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d1663f305d7193430848083336118cc42600f61408d565b60405160e089901b6001600160e01b03191681526001600160a01b039687166004820152602481019590955260448501939093526064840191909152909216608482015260a481019190915260c40160606040518083038185885af1158015611939573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061195e91906140e3565b505050347f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e49190613e77565b6040516370a0823160e01b81526001600160a01b037f0000000000000000000000004c6147776e4230699d213d6ef7c4d9be0aa5b7af8116600483015291909116906370a0823190602401602060405180830381865afa158015611a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a709190614111565b1015611aca5760405162461bcd60e51b815260206004820152602360248201527f455448206469646e27742067657420746f20746865207061697220636f6e74726044820152621858dd60ea1b6064820152608401610c3d565b5043601255565b600080611adc6110f8565b90506003816003811115611af257611af2613cee565b1480611b0f57506002816003811115611b0d57611b0d613cee565b145b91505090565b6009546000906001600160a01b03163314611b425760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600360205260409020805460ff19169055600190565b6040516308f25a8f60e01b815260009073b16c1342e617a5b6e4b631eb114483fdb289c0a49081906308f25a8f90611ba59086908690600401614161565b602060405180830381865afa158015611bc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be69190614187565b806115a057506040516308f25a8f60e01b81526001600160a01b038216906308f25a8f90611c1b908690600190600401614161565b602060405180830381865afa158015611c38573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a09190614187565b611c686009600a614062565b61122690620f424061406e565b6009546001600160a01b03163314611ce55760405162461bcd60e51b815260206004820152602d60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574537560448201526c646f476174654164647265737360981b6064820152608401610c3d565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6009546001600160a01b03163314611d7c5760405162461bcd60e51b815260206004820152603260248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574436f6044820152711cdd151bd0591913919510dbdb9d1c9858dd60721b6064820152608401610c3d565b601455565b6009546001600160a01b03163314611df35760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574416360448201526e1d1a5bdb94195c98d95b9d14d95b99608a1b6064820152608401610c3d565b6064811115611e405760405162461bcd60e51b8152602060048201526019602482015278050657263656e742063616e6e6f74206578636565642031303603c1b6044820152606401610c3d565b606481601954611e50919061408d565b1115611e6e5760405162461bcd60e51b8152600401610c3d906141a9565b601a55565b611e7f6009600a614062565b61122690620186a061406e565b6000611e9782611b67565b611ed95760405162461bcd60e51b8152602060048201526013602482015272139bdd0818481cdd591bdcddd85c081c1bdbdb606a1b6044820152606401610c3d565b6002546040516366fe44d560e11b81526001600160a01b03848116600483015290911690819063cdfc89aa90602401602060405180830381865afa158015611f25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f499190614187565b611fbb576040516355ec842360e11b81526001600160a01b03848116600483015282169063abd90846906024016020604051808303816000875af1158015611f95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb99190614187565b505b6116a0836001600160a01b03166347ccca026040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ffc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af79190613e77565b61202c6009600a614062565b611226906207a12061406e565b60006110213384846125fe565b6009546001600160a01b031633146120b75760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c20736574416360448201526d74696f6e50657263656e7442757960901b6064820152608401610c3d565b60648111156121045760405162461bcd60e51b8152602060048201526019602482015278050657263656e742063616e6e6f74206578636565642031303603c1b6044820152606401610c3d565b606481601a54612114919061408d565b11156121325760405162461bcd60e51b8152600401610c3d906141a9565b601955565b6009546000906001600160a01b031633146121645760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600460205260409020805460ff1916600190811790915590565b6009546001600160a01b031633146122035760405162461bcd60e51b815260206004820152603360248201527f4f6e6c79206f776e657220616c6c6f77656420746f2063616c6c207365744d696044820152726e4d696e757465734265747765656e5275677360681b6064820152608401610c3d565b601855565b6009546000906001600160a01b031633146122655760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c2062757952616e646f6d4e46546044820152606401610c3d565b610c6361291d565b6002546009546000916001600160a01b039081169116331461235c576014543360009081526020819052604090205410156122f85760405162461bcd60e51b815260206004820152602560248201527f4e6f7420656e6f75676820746f6b656e7320746f20616464204e465420636f6e6044820152641d1c9858dd60da1b6064820152608401610c3d565b6001600160a01b0383166000908152600d602052604090205460ff16156123515760405162461bcd60e51b815260206004820152600d60248201526c105b1c9958591e481859191959609a1b6044820152606401610c3d565b61235c6014546116aa565b50506001600160a01b03166000818152600d60205260408120805460ff19166001908117909155600c8054808301825592527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790910180546001600160a01b03191690921790915590565b60007f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b0316826001600160a01b03160361242157506001600160a01b038216600090815260208190526040902054611025565b506001600160a01b03808316600090815260016020908152604080832093851683529290522054611025565b600061247f60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb55490565b60155461248a610bde565b612494919061408d565b61249e919061408d565b6124aa6009600a614062565b6124b8906305f5e10061406e565b610c639190613e64565b60006017546000036124d45750600090565b601754610c639042613e64565b6009546000906001600160a01b0316331461253e5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722063616e2063616c6c73656e6452616e646f6d4e46546044820152606401610c3d565b610c63612947565b6009546000906001600160a01b031633146125735760405162461bcd60e51b8152600401610c3d9061412a565b506001600160a01b03166000908152600460205260409020805460ff19169055600190565b6009546000906001600160a01b031633146125f55760405162461bcd60e51b815260206004820152601a60248201527f4f6e6c79206f776e65722063616e2063616c6c20616464426f740000000000006044820152606401610c3d565b61102582612b29565b6001600160a01b03831660009081526020819052604090205481111561265d5760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610c3d565b60006126676110f8565b6001600160a01b03851660009081526003602052604090205490915060ff1615806126a3575060018160038111156126a1576126a1613cee565b145b6126ef5760405162461bcd60e51b815260206004820152601c60248201527f536f72727920626f742c2063616e2774206c657420796f75206f7574000000006044820152606401610c3d565b600954600160a01b900460ff168061270f57506001600160a01b03841630145b8061272257506001600160a01b03831630145b806127565750600081600381111561273c5761273c613cee565b14801561275657506009546001600160a01b038581169116145b1561276b57612766848484612b91565b612776565b612776848484612b9f565b50505050565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6000816127e8612ffa565b61102591906141ef565b600954600090600160a01b900460ff16612835576000612810610bde565b9050828110156128205780612822565b825b92508215612833576115a083613067565b505b919050565b821561285d578060106000828254612852919061408d565b909155506128a99050565b81156128a95760115461286e61114f565b111561287e576000601055505050565b8060105411612891576000601055505050565b80601060008282546128a39190613e64565b90915550505b505050565b6001600160a01b03821660009081526020819052604090205481111561290a5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610c3d565b61291982600083600085613346565b5050565b600c5460009015611181576000612932613506565b9050600061293f82613583565b509250505090565b6000806129526137f2565b9050600061295f600b5490565b905061296a8261102b565b1580156129775750600081115b15612b24576000612987826127dd565b90506000600b828154811061299e5761299e613f68565b60009182526020909120600290910201805460018201546040516323b872dd60e01b81523060048201526001600160a01b038881166024830152604482019290925292935016906323b872dd90606401600060405180830381600087803b158015612a0857600080fd5b505af1158015612a1c573d6000803e3d6000fd5b5050825460018401546040519081526001600160a01b03888116945090911691507f0ab2d5a490c8d5451470363f35341c42582170bbaf5bf4cd96b1afc7304e22ae9060200160405180910390a3600b612a77600185613e64565b81548110612a8757612a87613f68565b9060005260206000209060020201600b8381548110612aa857612aa8613f68565b60009182526020909120825460029092020180546001600160a01b0319166001600160a01b03909216919091178155600191820154910155600b805480612af157612af1614203565b60008281526020812060026000199093019283020180546001600160a01b03191681556001908101919091559155945050505b505090565b6001600160a01b03811660009081526003602052604081205460ff1615612b5257506001919050565b612b5b8261102b565b15612b6857506000919050565b506001600160a01b03166000908152600360205260409020805460ff1916600190811790915590565b6128a9838383846000613346565b6001600160a01b0380831660009081526004602052604080822054928616825281205460ff928316921690612bd26110f8565b90506000816003811115612be857612be8613cee565b03612c74576009546001600160a01b0387811691161480612c1157506001600160a01b03861630145b612c745760405162461bcd60e51b815260206004820152602e60248201527f4f6e6c79206465706c6f79657220616e6420636f6e74726163742063616e206d60448201526d6f766520746f6b656e73206e6f7760901b6064820152608401610c3d565b6001816003811115612c8857612c88613cee565b148015612c925750815b15612cf0576000612ca2866138e7565b90508015612cee57856001600160a01b03167f83efabacf9f7cf6e679b0fd9de0113ad1e05f72b1955dedadf0e01422a52234a86604051612ce591815260200190565b60405180910390a25b505b836000612cfb611ad1565b15612db457838015612d2457506001600160a01b0387166000908152600f602052604090205443145b15612d5c576000612d34886138e7565b90508015612d56576064612d4988606361406e565b612d5391906140cf565b91505b50612db4565b848015612d8057506001600160a01b0388166000908152600e602052604090205443145b15612db4576000612d90896138e7565b90508015612db2576064612da588606361406e565b612daf91906140cf565b91505b505b808211612e1b5760405162461bcd60e51b815260206004820152602f60248201527f43616e2774206275726e206d6f7265207468616e2074686520746f74616c206e60448201526e756d626572206f6620746f6b656e7360881b6064820152608401610c3d565b612e31888884612e2b8582613e64565b85613346565b80600003612e4457612e4484868861283a565b6002836003811115612e5857612e58613cee565b148015612e625750835b8015612e8757506001600160a01b03871660009081526003602052604090205460ff16155b15612f8c57612e986009600a614062565b612ea5906207a12061406e565b861115612f005760405162461bcd60e51b8152602060048201526024808201527f4f6e6c7920736d616c6c206275797320647572696e67207761726d75702070656044820152631c9a5bd960e21b6064820152608401610c3d565b612f0c6009600a614062565b612f1990620f424061406e565b6001600160a01b0388166000908152602081905260409020541115612f8c5760405162461bcd60e51b815260206004820152602360248201527f42616c616e636520746f6f206c6172676520666f72207761726d7570207065726044820152621a5bd960ea1b6064820152608401610c3d565b612f94611ad1565b15612fa457612fa161390b565b50505b8315612fce57426013556001600160a01b0387166000908152600e60205260409020439055612ff0565b8415612ff0576001600160a01b0388166000908152600f602052604090204390555b5050505050505050565b60006001600a600082825461300f919061408d565b9091555050600a546040516bffffffffffffffffffffffff193360601b16602082015260348101919091524260548201524460748201526094016040516020818303038152906040528051906020012060001c905090565b60004782158015906130885750306000908152602081905260409020548311155b156132f4576009805460ff60a01b1916600160a01b17905560408051600280825260608201835260009260208301908036833701905050905030816000815181106130d5576130d5613f68565b60200260200101906001600160a01b031690816001600160a01b0316815250507f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d6001600160a01b031663ad5c46486040518163ffffffff1660e01b8152600401602060405180830381865afa158015613153573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131779190613e77565b8160018151811061318a5761318a613f68565b60200260200101906001600160a01b031690816001600160a01b0316815250506131d5307f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d8661277c565b6040516318cbafe560e01b81526001600160a01b037f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d16906318cbafe59061322a908790600090869030904290600401614219565b6000604051808303816000875af1158015613249573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132719190810190613eaa565b507f0000000000000000000000004c6147776e4230699d213d6ef7c4d9be0aa5b7af6001600160a01b031663fff6cae96040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156132cd57600080fd5b505af11580156132e1573d6000803e3d6000fd5b50506009805460ff60a01b191690555050505b8047101561333c5760405162461bcd60e51b8152602060048201526015602482015274486f7720646964207765206c6f736520455448213f60581b6044820152606401610c3d565b6115a08147613e64565b613350818361408d565b83146133bc5760405162461bcd60e51b815260206004820152603560248201527f536f7572636520616e642064657374696e6174696f6e20746f6b656e20616d6f604482015274756e7473206d757374206265207468652073616d6560581b6064820152608401610c3d565b6001600160a01b038516600090815260208190526040812080548592906133e4908490613e64565b909155506133f3905085613967565b6001600160a01b0384166000908152602081905260408120805484929061341b90849061408d565b9091555061342a905084613967565b836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161346f91815260200190565b60405180910390a380156134ff57600080805260208190527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb580548392906134b890849061408d565b90915550506040518181526000906001600160a01b038716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35b5050505050565b600c5460009061354c5760405162461bcd60e51b81526020600482015260116024820152704e6f204e465420636f6e7472616374732160781b6044820152606401610c3d565b600c8054613559906127dd565b8154811061356957613569613f68565b6000918252602090912001546001600160a01b0316919050565b600254604051638f38a55560e01b81526001600160a01b03838116600483015260006024830181905292839291169082908290638f38a55590604401602060405180830381865afa1580156135dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136009190613e77565b6001600160a01b0316146137ec576040516337404c6160e21b81526001600160a01b038581166004830152600091829184169063dd013184906024016040805180830381865afa158015613658573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367c919061428a565b90925090506001600160a01b0381161580159061369a575060001982105b80156136a557504782105b156137e95760405163ba5bddd760e01b81526001600160a01b03828116600483015284169063ba5bddd790849060240160206040518083038185885af11580156136f3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906137189190614111565b6040805180820182526001600160a01b038981168083526020808401868152600b805460018101825560009190915294517f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9600290960295860180546001600160a01b031916919095161790935591517f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01dba909301929092559151838152929650917fd9250f54e22692b605c77401e4a961c2ccbbe3d8b8b111e0ee251804a62593d2910160405180910390a2600194505b50505b50915091565b6000806137fd613a6f565b90506000613809613a6f565b90506000613815613a6f565b905060006001600160a01b03841615613846576001600160a01b038416600090815260208190526040902054613849565b60005b905060006001600160a01b0384161561387a576001600160a01b03841660009081526020819052604090205461387d565b60005b905060006001600160a01b038416156138ae576001600160a01b0384166000908152602081905260409020546138b1565b60005b90508183116138cd578082116138c757836138dc565b846138dc565b8083116138da57836138dc565b855b965050505050505090565b6000806138f383612b29565b905080156110255761390432612b29565b5092915050565b600080613916613ab1565b9150600090508082600281111561392f5761392f613cee565b036139425761393c61291d565b90509091565b600182600281111561395657613956613cee565b036139635761393c612947565b9091565b6139736009600a614062565b61398090620186a061406e565b6001600160a01b03821660009081526020819052604090205410806139a957506139a98161102b565b156139d8576001600160a01b03811660009081526008602052604090205460ff16156116b4576116b481613af9565b6001600160a01b03811660009081526008602052604090205460ff166116b4576116b4816001600160a01b03166000818152600860209081526040808320805460ff19166001908117909155600680546007909452918420839055820181559091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319169091179055565b600654600090801561114b576006613a86826127dd565b81548110613a9657613a96613f68565b6000918252602090912001546001600160a01b031691505090565b600080613abe60646127dd565b9050601954811015613ad257600091505090565b601a54601954613ae2919061408d565b811015613af157600191505090565b600291505090565b6001600160a01b0381166000908152600860205260408120805460ff19169055600654613b2890600190613e64565b6001600160a01b03831660009081526007602052604090205490915081811015613bd457600060066000018381548110613b6457613b64613f68565b60009182526020808320909101546001600160a01b03168083526007909152604090912083905560068054919250829184908110613ba457613ba4613f68565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550505b6001600160a01b038316600090815260076020526040902060001990556006805480613c0257613c02614203565b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b600060208083528351808285015260005b81811015613c5657858101830151858201604001528201613c3a565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146116b457600080fd5b600060208284031215613c9e57600080fd5b81356115a081613c77565b60008060408385031215613cbc57600080fd5b8235613cc781613c77565b946020939093013593505050565b600060208284031215613ce757600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b600481106116b457634e487b7160e01b600052602160045260246000fd5b60208101613d2f83613d04565b91905290565b600080600080600060808688031215613d4d57600080fd5b8535613d5881613c77565b94506020860135613d6881613c77565b935060408601359250606086013567ffffffffffffffff80821115613d8c57600080fd5b818801915088601f830112613da057600080fd5b813581811115613daf57600080fd5b896020828501011115613dc157600080fd5b9699959850939650602001949392505050565b600080600060608486031215613de957600080fd5b8335613df481613c77565b92506020840135613e0481613c77565b929592945050506040919091013590565b60008060408385031215613e2857600080fd5b8235613e3381613c77565b91506020830135613e4381613c77565b809150509250929050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561102557611025613e4e565b600060208284031215613e8957600080fd5b81516115a081613c77565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215613ebd57600080fd5b825167ffffffffffffffff80821115613ed557600080fd5b818501915085601f830112613ee957600080fd5b815181811115613efb57613efb613e94565b8060051b604051601f19603f83011681018181108582111715613f2057613f20613e94565b604052918252848201925083810185019188831115613f3e57600080fd5b938501935b82851015613f5c57845184529385019392850192613f43565b98975050505050505050565b634e487b7160e01b600052603260045260246000fd5b600181815b80851115613fb9578160001904821115613f9f57613f9f613e4e565b80851615613fac57918102915b93841c9390800290613f83565b509250929050565b600082613fd057506001611025565b81613fdd57506000611025565b8160018114613ff35760028114613ffd57614019565b6001915050611025565b60ff84111561400e5761400e613e4e565b50506001821b611025565b5060208310610133831016604e8410600b841016171561403c575081810a611025565b6140468383613f7e565b806000190482111561405a5761405a613e4e565b029392505050565b60006115a08383613fc1565b600081600019048311821515161561408857614088613e4e565b500290565b8082018082111561102557611025613e4e565b6000600182016140b2576140b2613e4e565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826140de576140de6140b9565b500490565b6000806000606084860312156140f857600080fd5b8351925060208401519150604084015190509250925092565b60006020828403121561412357600080fd5b5051919050565b6020808252601b908201527f43616e206f6e6c792062652063616c6c6564206279206f776e65720000000000604082015260600190565b6001600160a01b03831681526040810161417a83613d04565b8260208301529392505050565b60006020828403121561419957600080fd5b815180151581146115a057600080fd5b60208082526026908201527f436f6d62696e65642070657263656e74616765732063616e6e6f74206578636560408201526506564203130360d41b606082015260800190565b6000826141fe576141fe6140b9565b500690565b634e487b7160e01b600052603160045260246000fd5b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b818110156142695784516001600160a01b031683529383019391830191600101614244565b50506001600160a01b03969096166060850152505050608001529392505050565b6000806040838503121561429d57600080fd5b825191506020830151613e4381613c7756fea264697066735822122052b4a1189f4f56472772d8fefaafd3e22b3eeabc58a633006be5b317e23b8abc64736f6c63430008100033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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