false
true
0

Contract Address Details

0x6C6D7De6C5f366a1995ed5f1e273C5B3760C6043

Token
RhinoFi (RHINO)
Creator
0x50f6b9–9e587c at 0xbd70c7–1ce96f
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
1,327 Transactions
Transfers
0 Transfers
Gas Used
0
Last Balance Update
25963068
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
RhinoFi




Optimization enabled
true
Compiler version
v0.8.26+commit.8a97fa7a




Optimization runs
200
EVM Version
paris




Verified at
2025-07-08T06:47:45.478838Z

contracts/RHINO.sol

//SPDX-License-Identifier: MIT

pragma solidity 0.8.26;

/* === OZ === */
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/* === UNISWAP V2 === */
import {IUniswapV2Router01, IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Pair} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

/* === SYSTEM === */
import {RhinoCharging} from "./RhinoCharging.sol";

/**
 * Standard SafeMath, stripped down to just add/sub/mul/div
 */
library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }
}

/**
 * ERC20 standard interface.
 */
interface IERC20 {
    function totalSupply() external view returns (uint256);

    function decimals() external view returns (uint8);

    function symbol() external view returns (string memory);

    function name() external view returns (string memory);

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

interface IWPLS {
    function withdraw(uint256 wad) external;
}

interface ISOLIDX {
    function burn(uint256 amount) external;
}

interface IDividendDistributor {
    function setDistributionCriteria(
        address token,
        uint256 minPeriod,
        uint256 minDistribution
    ) external;

    function setShare(address shareholder, uint256 amount) external;

    function setLpShare(address shareholder, uint256 amount) external;

    function depositForReflection(address reflectedToken) external payable;

    function depositForPlsReflection() external payable;

    function depositForRhinoReflection(uint256 amount) external;

    function depositForSolidXBurn() external payable;

    function depositForGelatoBurn() external payable;

    function processDividend(address token, uint256 gas) external;

    function claimDividend(address user) external;

    function claimLpDividend(address user) external;
}

interface IRhino {
    function getLpTokenValueInRhino(
        address user
    ) external view returns (uint256);
    function feeDenominator() external view returns (uint256);
    function lpReflectionPercent() external view returns (uint256);
}

contract DividendDistributor is IDividendDistributor {
    using SafeMath for uint256;

    address public rhino;

    // Dividend Trackers
    struct DividendInfo {
        uint256 currentIndex;
        uint256 totalDividends;
        uint256 totalDistributed;
        uint256 dividendsPerShare;
        uint256 minPeriod;
        uint256 minDistribution;
    }

    struct LpDividendInfo {
        uint256 currentIndex;
        uint256 totalDividends;
        uint256 totalDistributed;
        uint256 dividendsPerShare;
        uint256 minPeriod;
        uint256 minDistribution;
    }

    // Shares & Reward Trackers
    struct Share {
        uint256 amount;
        uint256 totalExcluded;
        uint256 totalRealised;
    }

    struct LpShare {
        uint256 amount;
        uint256 totalExcluded;
        uint256 totalRealised;
    }

    mapping(address token => address[] lpShareholder) public lpShareholders;
    mapping(address => mapping(address => uint256)) public lpShareholderIndexes;
    mapping(address => mapping(address => uint256)) public lpShareholderClaims;
    mapping(address token => mapping(address shareholder => Share))
        public lpShares;
    mapping(address => LpDividendInfo) public lpDividends;
    uint256 public lpTotalShares;

    mapping(address token => address[] shareholder) public shareholders;
    mapping(address => mapping(address => uint256)) public shareholderIndexes;
    mapping(address => mapping(address => uint256)) public shareholderClaims;
    mapping(address token => mapping(address shareholder => Share))
        public shares;
    mapping(address => DividendInfo) public dividends;
    uint256 public totalShares;

    uint256 public dividendsPerShareAccuracyFactor = 10 ** 36;

    IERC20 immutable SOLIDX =
        IERC20(0x8Da17Db850315A34532108f0f5458fc0401525f6);
    IERC20 immutable EHEX = IERC20(0x57fde0a71132198BBeC939B98976993d8D89D225);
    IERC20 immutable PHEX = IERC20(0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39);
    IERC20 immutable GELATO =
        IERC20(0x616cb6a245Ed4c11216Ec58D10B6A2E87271845d);
    IERC20 immutable PLSX = IERC20(0x95B303987A60C71504D99Aa1b13B4DA07b0790ab);
    IERC20 immutable INC = IERC20(0x2fa878Ab3F87CC1C9737Fc071108F904c0B0C95d);
    IERC20 immutable WPLS = IERC20(0xA1077a294dDE1B09bB078844df40758a5D0f9a27);
    address immutable ZERO = 0x0000000000000000000000000000000000000000;
    address immutable DEAD = 0x000000000000000000000000000000000000dEaD;
    IUniswapV2Router02 public immutable pulseRouterV1 =
        IUniswapV2Router02(0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02);
    IUniswapV2Router02 public immutable pulseRouterV2 =
        IUniswapV2Router02(0x165C3410fC91EF562C50559f7d2289fEbed552d9);
    IUniswapV2Router02 public immutable nineinchRouter =
        IUniswapV2Router02(0xeB45a3c4aedd0F47F345fB4c8A1802BB5740d725);

    uint256 public totalSolidXBurned;
    uint256 public totalGelatoBurned;

    modifier onlyToken() {
        require(msg.sender == rhino);
        _;
    }

    bool public distributing;

    modifier inDistribution() {
        distributing = true;
        _;
        distributing = false;
    }

    constructor() {
        rhino = msg.sender;
        dividends[address(WPLS)].minPeriod = 1 hours;
        dividends[address(WPLS)].minDistribution = 30000 * (10 ** 18);
        dividends[msg.sender].minPeriod = 1 hours;
        dividends[msg.sender].minDistribution = 10000 * (10 ** 18);
        dividends[address(SOLIDX)].minPeriod = 1 hours;
        dividends[address(SOLIDX)].minDistribution = (1 * (10 ** 18)) / 10;
        dividends[address(EHEX)].minPeriod = 1 hours;
        dividends[address(EHEX)].minDistribution = 500 * (10 ** 8);
        dividends[address(PHEX)].minPeriod = 1 hours;
        dividends[address(PHEX)].minDistribution = 100 * (10 ** 8);
        dividends[address(GELATO)].minPeriod = 1 hours;
        dividends[address(GELATO)].minDistribution = 10000 * (10 ** 18);
        dividends[address(PLSX)].minPeriod = 1 hours;
        dividends[address(PLSX)].minDistribution = 30000 * (10 ** 18);
        dividends[address(INC)].minPeriod = 1 hours;
        dividends[address(INC)].minDistribution = 1 * (10 ** 18);
    }

    function setDistributionCriteria(
        address token,
        uint256 minPeriod,
        uint256 minDistribution
    ) external override onlyToken {
        dividends[token].minPeriod = minPeriod;
        dividends[token].minDistribution = minDistribution;
    }

    function setShare(address shareholder, uint256 amount) external onlyToken {
        address[] memory tokens = new address[](8);
        tokens[0] = rhino;
        tokens[1] = address(WPLS);
        tokens[2] = address(SOLIDX);
        tokens[3] = address(EHEX);
        tokens[4] = address(PHEX);
        tokens[5] = address(GELATO);
        tokens[6] = address(PLSX);
        tokens[7] = address(INC);

        if (shares[rhino][shareholder].amount > 0) {
            for (uint256 i = 0; i < tokens.length; i++) {
                _distributeDividend(tokens[i], shareholder);
            }
        }

        bool isNewShareholder = (amount > 0 &&
            shares[rhino][shareholder].amount == 0);
        bool isRemovedShareholder = (amount == 0 &&
            shares[rhino][shareholder].amount > 0);

        if (isNewShareholder) {
            _addShareholder(shareholder);
        } else if (isRemovedShareholder) {
            _removeShareholder(shareholder);
        }

        totalShares = totalShares.sub(shares[rhino][shareholder].amount).add(
            amount
        );

        for (uint256 i = 0; i < tokens.length; i++) {
            shares[tokens[i]][shareholder].amount = amount;
            shares[tokens[i]][shareholder]
                .totalExcluded = getCumulativeDividends(tokens[i], amount);
        }
    }

    function setLpShare(
        address shareholder,
        uint256 amount
    ) external onlyToken {
        address[] memory tokens = new address[](8);
        tokens[0] = rhino;
        tokens[1] = address(WPLS);
        tokens[2] = address(SOLIDX);
        tokens[3] = address(EHEX);
        tokens[4] = address(PHEX);
        tokens[5] = address(GELATO);
        tokens[6] = address(PLSX);
        tokens[7] = address(INC);

        if (lpShares[rhino][shareholder].amount > 0) {
            for (uint256 i = 0; i < tokens.length; i++) {
                _distributeLpDividend(tokens[i], shareholder);
            }
        }

        bool isNewShareholder = (amount > 0 &&
            lpShares[rhino][shareholder].amount == 0);
        bool isRemovedShareholder = (amount == 0 &&
            lpShares[rhino][shareholder].amount > 0);

        if (isNewShareholder) {
            _addLpShareholder(shareholder);
        } else if (isRemovedShareholder) {
            _removeLpShareholder(shareholder);
        }

        lpTotalShares = lpTotalShares
            .sub(lpShares[rhino][shareholder].amount)
            .add(amount);

        for (uint256 i = 0; i < tokens.length; i++) {
            lpShares[tokens[i]][shareholder].amount = amount;
            lpShares[tokens[i]][shareholder]
                .totalExcluded = getLpCumulativeDividends(tokens[i], amount);
        }
    }

    function depositForReflection(
        address reflectedToken
    ) external payable override onlyToken {
        uint256 balanceBefore = IERC20(reflectedToken).balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = address(WPLS);
        path[1] = reflectedToken;

        IUniswapV2Router02 router = IUniswapV2Router02(
            getBestRouter(msg.value, path)
        );

        router.swapExactETHForTokensSupportingFeeOnTransferTokens{
            value: msg.value
        }(0, path, address(this), block.timestamp);

        uint256 balanceAfter = IERC20(reflectedToken).balanceOf(address(this));

        uint256 tokenReceived = balanceAfter.sub(balanceBefore);

        uint256 feeDenom = IRhino(rhino).feeDenominator();
        uint256 lpReflectionPercent = IRhino(rhino).lpReflectionPercent();

        if (lpTotalShares > 0 && lpReflectionPercent > 0) {
            uint256 amountReflection = tokenReceived
                .mul(feeDenom.sub(lpReflectionPercent))
                .div(feeDenom);

            dividends[reflectedToken].totalDividends = dividends[reflectedToken]
                .totalDividends
                .add(amountReflection);
            dividends[reflectedToken].dividendsPerShare = dividends[
                reflectedToken
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountReflection).div(
                        totalShares
                    )
                );

            uint256 amountLpReflection = (
                tokenReceived.mul(lpReflectionPercent)
            ).div(feeDenom);

            lpDividends[reflectedToken].totalDividends = lpDividends[
                reflectedToken
            ].totalDividends.add(amountLpReflection);
            lpDividends[reflectedToken].dividendsPerShare = lpDividends[
                reflectedToken
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountLpReflection).div(
                        lpTotalShares
                    )
                );
        } else {
            uint256 amountReflection = tokenReceived;

            dividends[reflectedToken].totalDividends = dividends[reflectedToken]
                .totalDividends
                .add(amountReflection);
            dividends[reflectedToken].dividendsPerShare = dividends[
                reflectedToken
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountReflection).div(
                        totalShares
                    )
                );
        }
    }

    function depositForPlsReflection() external payable override onlyToken {
        uint256 feeDenom = IRhino(rhino).feeDenominator();
        uint256 lpReflectionPercent = IRhino(rhino).lpReflectionPercent();

        if (lpTotalShares > 0 && lpReflectionPercent > 0) {
            uint256 amountReflection = msg
                .value
                .mul((feeDenom).sub(lpReflectionPercent))
                .div(feeDenom);

            dividends[address(WPLS)].totalDividends = dividends[address(WPLS)]
                .totalDividends
                .add(amountReflection);
            dividends[address(WPLS)].dividendsPerShare = dividends[
                address(WPLS)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountReflection).div(
                        totalShares
                    )
                );

            uint256 amountLpReflection = (msg.value.mul(lpReflectionPercent))
                .div(feeDenom);

            lpDividends[address(WPLS)].totalDividends = lpDividends[
                address(WPLS)
            ].totalDividends.add(amountLpReflection);
            lpDividends[address(WPLS)].dividendsPerShare = lpDividends[
                address(WPLS)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountLpReflection).div(
                        lpTotalShares
                    )
                );
        } else {
            uint256 amountReflection = msg.value;

            dividends[address(WPLS)].totalDividends = dividends[address(WPLS)]
                .totalDividends
                .add(amountReflection);
            dividends[address(WPLS)].dividendsPerShare = dividends[
                address(WPLS)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountReflection).div(
                        totalShares
                    )
                );
        }
    }

    function depositForRhinoReflection(
        uint256 amount
    ) external override onlyToken {
        IERC20(rhino).transferFrom(address(rhino), address(this), amount);

        uint256 feeDenom = IRhino(rhino).feeDenominator();
        uint256 lpReflectionPercent = IRhino(rhino).lpReflectionPercent();

        if (lpTotalShares > 0 && lpReflectionPercent > 0) {
            uint256 reflectionAmount = amount
                .mul((feeDenom).sub(lpReflectionPercent))
                .div(feeDenom);

            dividends[address(rhino)].totalDividends = dividends[address(rhino)]
                .totalDividends
                .add(reflectionAmount);
            dividends[address(rhino)].dividendsPerShare = dividends[
                address(rhino)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(reflectionAmount).div(
                        totalShares
                    )
                );

            uint256 amountLpReflection = (amount.mul(lpReflectionPercent)).div(
                feeDenom
            );

            lpDividends[address(rhino)].totalDividends = lpDividends[
                address(rhino)
            ].totalDividends.add(amountLpReflection);
            lpDividends[address(rhino)].dividendsPerShare = lpDividends[
                address(rhino)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(amountLpReflection).div(
                        lpTotalShares
                    )
                );
        } else {
            uint256 reflectionAmount = amount;

            dividends[address(rhino)].totalDividends = dividends[address(rhino)]
                .totalDividends
                .add(reflectionAmount);
            dividends[address(rhino)].dividendsPerShare = dividends[
                address(rhino)
            ].dividendsPerShare.add(
                    dividendsPerShareAccuracyFactor.mul(reflectionAmount).div(
                        totalShares
                    )
                );
        }
    }

    function depositForSolidXBurn() external payable override onlyToken {
        uint256 solidXBefore = SOLIDX.balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = address(WPLS);
        path[1] = address(SOLIDX);

        IUniswapV2Router01 router = IUniswapV2Router01(
            getBestRouter(msg.value, path)
        );

        router.swapExactETHForTokens{value: msg.value}(
            0,
            path,
            address(this),
            block.timestamp
        );

        uint256 solidXGained = SOLIDX.balanceOf(address(this)).sub(
            solidXBefore
        );

        ISOLIDX(address(SOLIDX)).burn(solidXGained);
        totalSolidXBurned = totalSolidXBurned.add(solidXGained);
    }

    function depositForGelatoBurn() external payable override onlyToken {
        uint256 gelatoBefore = GELATO.balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = address(WPLS);
        path[1] = address(GELATO);

        IUniswapV2Router02 router = IUniswapV2Router02(
            getBestRouter(msg.value, path)
        );

        router.swapExactETHForTokensSupportingFeeOnTransferTokens{
            value: msg.value
        }(0, path, address(this), block.timestamp);

        uint256 gelatoGained = GELATO.balanceOf(address(this)).sub(
            gelatoBefore
        );
        GELATO.transfer(DEAD, gelatoGained);
        totalGelatoBurned = totalGelatoBurned.add(gelatoGained);
    }

    function claimDividend(address user) external override onlyToken {
        address[] memory tokens = new address[](8);
        tokens[0] = rhino;
        tokens[1] = address(WPLS);
        tokens[2] = address(SOLIDX);
        tokens[3] = address(EHEX);
        tokens[4] = address(PHEX);
        tokens[5] = address(GELATO);
        tokens[6] = address(PLSX);
        tokens[7] = address(INC);

        for (uint256 i = 0; i < tokens.length; i++) {
            _distributeDividend(tokens[i], user);
        }
    }

    function claimLpDividend(address user) external override onlyToken {
        address[] memory tokens = new address[](8);
        tokens[0] = rhino;
        tokens[1] = address(WPLS);
        tokens[2] = address(SOLIDX);
        tokens[3] = address(EHEX);
        tokens[4] = address(PHEX);
        tokens[5] = address(GELATO);
        tokens[6] = address(PLSX);
        tokens[7] = address(INC);

        for (uint256 i = 0; i < tokens.length; i++) {
            _distributeLpDividend(tokens[i], user);
        }
    }

    function processDividend(
        address token,
        uint256 gas
    ) external override onlyToken {
        uint256 shareholderCount = shareholders[token].length;
        if (shareholderCount == 0) return;

        uint256 gasUsed = 0;
        uint256 gasLeft = gasleft();
        uint256 iterations = 0;
        while (gasUsed < gas && iterations < shareholderCount) {
            if (dividends[token].currentIndex >= shareholderCount) {
                dividends[token].currentIndex = 0;
            }
            if (
                _shouldDistribute(
                    token,
                    shareholders[token][dividends[token].currentIndex]
                )
            ) {
                _distributeDividend(
                    token,
                    shareholders[token][dividends[token].currentIndex]
                );
            }
            gasUsed = gasUsed.add(gasLeft.sub(gasleft()));
            gasLeft = gasleft();
            dividends[token].currentIndex++;
            iterations++;
        }
    }

    function _distributeDividend(address token, address shareholder) internal inDistribution {
        if (shares[token][shareholder].amount == 0) return;

        uint256 amount = getUnpaidEarnings(token, shareholder);
        if (amount > 0) {
            shareholderClaims[token][shareholder] = block.timestamp;
            shares[token][shareholder].totalRealised = shares[token][
                shareholder
            ].totalRealised.add(amount);
            shares[token][shareholder].totalExcluded = getCumulativeDividends(
                token,
                shares[token][shareholder].amount
            );

            dividends[token].totalDistributed = dividends[token]
                .totalDistributed
                .add(amount);
            if (token == address(WPLS)) {
                (bool success, ) = payable(shareholder).call{
                    value: amount,
                    gas: 50000
                }("");
                success;
            } else {
                IERC20(token).transfer(shareholder, amount);
            }
        }
    }

    function _distributeLpDividend(
        address token,
        address shareholder
    ) internal inDistribution {
        if (lpShares[token][shareholder].amount == 0) return;

        uint256 amount = getLpUnpaidEarnings(token, shareholder);
        if (amount > 0) {
            lpShareholderClaims[token][shareholder] = block.timestamp;
            lpShares[token][shareholder].totalRealised = lpShares[token][
                shareholder
            ].totalRealised.add(amount);
            lpShares[token][shareholder]
                .totalExcluded = getLpCumulativeDividends(
                token,
                lpShares[token][shareholder].amount
            );
            lpDividends[token].totalDistributed = lpDividends[token]
                .totalDistributed
                .add(amount);
            if (token == address(WPLS)) {
                (bool success, ) = payable(shareholder).call{
                    value: amount,
                    gas: 50000
                }("");
                success;
            } else {
                IERC20(token).transfer(shareholder, amount);
            }
        }
    }

    function _shouldDistribute(
        address token,
        address shareholder
    ) internal view returns (bool) {
        return
            shareholderClaims[token][shareholder] + dividends[token].minPeriod <
            block.timestamp &&
            getUnpaidEarnings(token, shareholder) >
            dividends[token].minDistribution;
    }

    function getBestRouter(
        uint256 amountIn,
        address[] memory path
    ) public view returns (address bestRouter) {
        address[] memory routers = new address[](3);
        routers[0] = address(pulseRouterV1);
        routers[1] = address(pulseRouterV2);
        routers[2] = address(nineinchRouter);

        uint256 bestAmountOut = 0;

        for (uint256 i = 0; i < routers.length; i++) {
            try
                IUniswapV2Router02(routers[i]).getAmountsOut(amountIn, path)
            returns (uint256[] memory amountsOut) {
                uint256 amountOut = amountsOut[amountsOut.length - 1];
                if (amountOut > bestAmountOut) {
                    bestAmountOut = amountOut;
                    bestRouter = routers[i];
                }
            } catch {
                continue;
            }
        }
    }

    function getUnpaidEarnings(
        address token,
        address shareholder
    ) public view returns (uint256) {
        if (shares[token][shareholder].amount == 0) {
            return 0;
        }
        uint256 totalDividends = getCumulativeDividends(
            token,
            shares[token][shareholder].amount
        );
        uint256 totalExcluded = shares[token][shareholder].totalExcluded;
        if (totalDividends <= totalExcluded) {
            return 0;
        }
        return totalDividends.sub(totalExcluded);
    }

    function getLpUnpaidEarnings(
        address token,
        address shareholder
    ) public view returns (uint256) {
        if (lpShares[token][shareholder].amount == 0) {
            return 0;
        }
        uint256 totalDividends = getLpCumulativeDividends(
            token,
            lpShares[token][shareholder].amount
        );
        uint256 totalExcluded = lpShares[token][shareholder].totalExcluded;
        if (totalDividends <= totalExcluded) {
            return 0;
        }
        return totalDividends.sub(totalExcluded);
    }

    function getCumulativeDividends(
        address token,
        uint256 share
    ) public view returns (uint256) {
        return
            share.mul(dividends[token].dividendsPerShare).div(
                dividendsPerShareAccuracyFactor
            );
    }

    function getLpCumulativeDividends(
        address token,
        uint256 share
    ) public view returns (uint256) {
        return
            share.mul(lpDividends[token].dividendsPerShare).div(
                dividendsPerShareAccuracyFactor
            );
    }

    function _addShareholder(address shareholder) internal {
        address[8] memory tokens = [
            rhino,
            address(WPLS),
            address(SOLIDX),
            address(EHEX),
            address(PHEX),
            address(GELATO),
            address(PLSX),
            address(INC)
        ];

        for (uint256 i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            shareholderIndexes[token][shareholder] = shareholders[token].length;
            shareholders[token].push(shareholder);
        }
    }

    function _removeShareholder(address shareholder) internal {
        address[8] memory tokens = [
            rhino,
            address(WPLS),
            address(SOLIDX),
            address(EHEX),
            address(PHEX),
            address(GELATO),
            address(PLSX),
            address(INC)
        ];

        for (uint256 i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            uint256 index = shareholderIndexes[token][shareholder];
            address lastShareholder = shareholders[token][
                shareholders[token].length - 1
            ];

            shareholders[token][index] = lastShareholder;
            shareholderIndexes[token][lastShareholder] = index;
            shareholders[token].pop();
        }
    }

    function _addLpShareholder(address shareholder) internal {
        address[8] memory tokens = [
            rhino,
            address(WPLS),
            address(SOLIDX),
            address(EHEX),
            address(PHEX),
            address(GELATO),
            address(PLSX),
            address(INC)
        ];

        for (uint256 i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            lpShareholderIndexes[token][shareholder] = lpShareholders[token]
                .length;
            lpShareholders[token].push(shareholder);
        }
    }

    function _removeLpShareholder(address shareholder) internal {
        address[8] memory tokens = [
            rhino,
            address(WPLS),
            address(SOLIDX),
            address(EHEX),
            address(PHEX),
            address(GELATO),
            address(PLSX),
            address(INC)
        ];

        for (uint256 i = 0; i < tokens.length; i++) {
            address token = tokens[i];
            uint256 length = lpShareholders[token].length;
            uint256 index = lpShareholderIndexes[token][shareholder];

            if (length == 1) {
                delete lpShareholderIndexes[token][shareholder];
                lpShareholders[token].pop();
                continue;
            }

            address lastShareholder = lpShareholders[token][length - 1];

            lpShareholders[token][index] = lastShareholder;
            lpShareholderIndexes[token][lastShareholder] = index;
            lpShareholders[token].pop();
        }
    }

    receive() external payable {}
}

contract RhinoFi is IERC20, ReentrancyGuard, Ownable {
    using SafeMath for uint256;

    RhinoCharging public rhinoCharging;

    address immutable WPLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;
    address immutable SOLIDX = 0x8Da17Db850315A34532108f0f5458fc0401525f6;
    address immutable GELATO = 0x616cb6a245Ed4c11216Ec58D10B6A2E87271845d;
    address immutable EHEX = 0x57fde0a71132198BBeC939B98976993d8D89D225;
    address immutable PHEX = 0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39;
    address immutable PLSX = 0x95B303987A60C71504D99Aa1b13B4DA07b0790ab;
    address immutable INC = 0x2fa878Ab3F87CC1C9737Fc071108F904c0B0C95d;
    address immutable DEAD = 0x000000000000000000000000000000000000dEaD;
    address immutable ZERO = 0x0000000000000000000000000000000000000000;

    string constant _name = "RhinoFi";
    string constant _symbol = "RHINO";
    uint8 constant _decimals = 18;

    uint256 _totalSupply = 1_000_000_000e18; // 1,000,000,000
    uint256 public maxTxAmount = _totalSupply; // 100%

    mapping(address => uint256) _balances;
    mapping(address => mapping(address => uint256)) _allowances;
    mapping(address pairAddress => mapping(address user => uint256 lpBalance))
        public lpBalances; // Tracks user's LP token balances.
    mapping(address pairAddress => bool isPair) public isPair; // Tracks addresses that are trading pairs.
    mapping(address user => uint256 userTotalRhinoInLp)
        public userTotalLpRhinoValue; // Tracks amount of Rhino tokens the user has in all LP positions.
    mapping(address pairAddress => mapping(address users => uint256 userRhinoInLp)) public userLpRhinoValue; // Tracks amount of Rhino tokens the user has in one LP position.
    mapping(address user => uint256 startTime) public holderStartTime; // Tracks the timestamp when the wallet became a holder, which starts his 90 day fee decay timer.

    mapping(address => bool) public isFeeExempt; // Returns true or false if the address is exempted from fees.
    mapping(address => bool) public isTxLimitExempt; // Returns true or false if the address is exempted from TX Limits.
    mapping(address => bool) public isDividendExempt; // Returns true or false if the address is exempted from receiving Reflection dividends.

    uint256 public feeDecayTime = 28 days; // Time after which fees decay to the base rate
    uint256 public baseBuyFee = 600; // 6%
    uint256 public baseSellFee = 2500; // 25%
    uint256 public minBuyFee = 600; // 6%
    uint256 public minSellFee = 600; // 6%

    uint256 public plsReflectionFee = 500; // Percent of fee going to provide PLS reflections.
    uint256 public rhinoReflectionFee = 500; // Percent of fee going to provide Rhino reflections.
    uint256 public solidXReflectionFee = 500; // Percent of fee going to provide SolidX reflections.
    uint256 public eHexReflectionFee = 500; // Percent of fee going to provide eHEX reflections.
    uint256 public pHexReflectionFee = 500; // Percent of fee going to provide pHEX reflections.
    uint256 public gelatoReflectionFee = 500; // Percent of fee going to provide Gelato reflections.
    uint256 public plsxReflectionFee = 1000; // Percent of fee going to provide PLSX reflections.
    uint256 public incReflectionFee = 1000; // Percent of fee going to provide INC reflections.
    uint256 public rhinoBurnFee = 1000; // Percent of fee going to provide burns to Rhino token.
    uint256 public solidXBurnFee = 750; // Percent of fee going to provide burns to SolidX token.
    uint256 public gelatoBurnFee = 750; // Percent of fee going to provide burns to Gelato token.
    uint256 public liquidityFee = 1500; // Percent of fee going to provide automatic Liquidity addition.
    uint256 public genesisFee = 1000; // Percent of fee going to provide Rhino tokens to the Genesis address.

    uint256 public lpReflectionPercent = 3333; // Percent of reflections that will go to LP providers.

    uint256 public constant feeDenominator = 10000;
    bool public feesOnNormalTransfers = true; // If true, the buy fee will apply for regular wallet -> wallet transfers.

    address public autoLiquidityReceiver; // The address that will receive the LP tokens which are minted when auto LP is added.
    address public genesisReceiver; // The genesis wallet.

    IUniswapV2Router02 public immutable pulseRouterV1 =
        IUniswapV2Router02(0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02);
    IUniswapV2Router02 public immutable pulseRouterV2 =
        IUniswapV2Router02(0x165C3410fC91EF562C50559f7d2289fEbed552d9);
    IUniswapV2Router02 public immutable nineinchRouter =
        IUniswapV2Router02(0xeB45a3c4aedd0F47F345fB4c8A1802BB5740d725);

    address public pulseV2Pair;
    address[] public pairs;
    mapping(address pairAddress => address router) public routerByPair; // Tracks the router used for a pair.

    uint256 public launchedAt;

    DividendDistributor public distributor;
    uint256 distributorGas = 100000; // The amount of gasLimit provided to processDividend function calls.

    bool public swapEnabled = true; // When true, rhino tokens will be swapped back via swapBack() function once swapThreshold is reached.
    uint256 public swapThreshold = _totalSupply / 1000; // 0.1% of the supply will trigger the swapBack() function to trigger.
    bool inSwap; // Bool for swapping modifier.
    bool public emergencyMode = false; // If true, the contract will not swap tokens and will not charge fees.

    modifier swapping() {
        inSwap = true;
        _;
        inSwap = false;
    }
    bool inLp; // Bool for inLiquidity modifier.
    modifier inLiquidity() {
        inLp = true;
        _;
        inLp = false;
    }

    uint256 public totalRhinoBurned; // Tracker for total amount of Rhino tokens burned.
    uint256 public totalPlsLpAdded; // Tracker for total amount of PLS added to LP.

    struct SwapValues {
        uint256 amountRhinoBurn;
        uint256 amountSolidXBurn;
        uint256 amountGelatoBurn;
        uint256 amountRhinoGenesis;
        uint256 amountRhinoSwap;
        uint256 balanceBefore;
        uint256 amountPls;
        uint256 amountPlsLiquidity;
        uint256 amountPlsReflection;
        uint256 amountRhinoReflection;
        uint256 amountSolidXReflection;
        uint256 amountEhexReflection;
        uint256 amountPhexReflection;
        uint256 amountGelatoReflection;
        uint256 amountPlsxReflection;
        uint256 amountIncReflection;
        uint256 amountSolidXToBurn;
        uint256 amountGelatoToBurn;
    }

    struct LiquidityVars {
        uint256 amount0;
        uint256 amount1;
        uint256 amount0Used;
        uint256 amount1Used;
        uint256 lpValueForThisPair;
        uint256 refundETH;
        uint256 refundRhino;
    }

    constructor() Ownable(msg.sender) {
        pulseV2Pair = IUniswapV2Factory(pulseRouterV2.factory()).createPair(
            WPLS,
            address(this)
        );
        distributor = new DividendDistributor();
        _allowances[address(this)][address(pulseRouterV1)] = type(uint256).max;
        _allowances[address(this)][address(pulseRouterV2)] = type(uint256).max;
        _allowances[address(this)][address(nineinchRouter)] = type(uint256).max;
        _allowances[address(this)][address(distributor)] = type(uint256).max;

        pairs.push(pulseV2Pair);
        isPair[pulseV2Pair] = true;
        routerByPair[pulseV2Pair] = address(pulseRouterV2);

        address owner_ = msg.sender;

        isFeeExempt[owner_] = true;
        isTxLimitExempt[owner_] = true;
        isDividendExempt[pulseV2Pair] = true;
        isDividendExempt[address(this)] = true;
        isFeeExempt[address(this)] = true;
        isFeeExempt[address(distributor)] = true;
        isTxLimitExempt[address(this)] = true;
        isTxLimitExempt[address(distributor)] = true;
        isDividendExempt[address(distributor)] = true;
        isDividendExempt[DEAD] = true;
        isDividendExempt[ZERO] = true;

        autoLiquidityReceiver = owner_;
        genesisReceiver = owner_;

        _balances[owner_] = _totalSupply;
        emit Transfer(address(0), owner_, _totalSupply);
    }

    receive() external payable {}

    function totalSupply() external view override returns (uint256) {
        return _totalSupply;
    }

    function decimals() external pure override returns (uint8) {
        return _decimals;
    }

    function symbol() external pure override returns (string memory) {
        return _symbol;
    }

    function name() external pure override returns (string memory) {
        return _name;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function allowance(
        address holder,
        address spender
    ) external view override returns (uint256) {
        return _allowances[holder][spender];
    }

    function approve(
        address spender,
        uint256 amount
    ) public override returns (bool) {
        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(
        address recipient,
        uint256 amount
    ) external override returns (bool) {
        return _transferFrom(msg.sender, recipient, amount);
    }

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external override returns (bool) {
        if (_allowances[sender][msg.sender] != ~uint256(0)) {
            _allowances[sender][msg.sender] = _allowances[sender][msg.sender]
                .sub(amount, "Insufficient Allowance");
        }

        return _transferFrom(sender, recipient, amount);
    }

    function _transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) internal returns (bool) {
        require(
            launchedAt > 0 || tx.origin == owner(),
            "The contract is not launched yet"
        );

        bool didSwapback;

        if (
            inSwap ||
            launchedAt == 0 ||
            emergencyMode
        ) {
            return _basicTransfer(sender, recipient, amount);
        }
        checkTxLimit(sender, recipient, amount);

        if (
        shouldSwapBack(sender) && 
        !rhinoCharging.swapping() &&
        !distributor.distributing()
        ) {
            swapBack();
            didSwapback = true;
        } else {
            try rhinoCharging.triggerSwap(block.timestamp) {} catch {}
        }

        _balances[sender] = _balances[sender].sub(
            amount,
            "Insufficient Balance"
        );

        if (holderStartTime[recipient] == 0) {
            holderStartTime[recipient] = block.timestamp;
        }

        uint256 amountReceived = shouldTakeFee(sender, recipient)
            ? takeFee(sender, recipient, amount)
            : amount;
        _balances[recipient] = _balances[recipient].add(amountReceived);

        if (!isDividendExempt[sender]) {
            try distributor.setShare(sender, _balances[sender]) {} catch {}
        }
        if (!isDividendExempt[recipient]) {
            try distributor.setShare(recipient, _balances[recipient]) {} catch {}
        }
        
        if (!didSwapback) {
            if (plsReflectionFee > 0) {
                try distributor.processDividend(WPLS, distributorGas) {} catch {}
            }
            if (rhinoReflectionFee > 0) {
                try distributor.processDividend(address(this), distributorGas) {} catch {}
            }
            if (solidXReflectionFee > 0) {
                try distributor.processDividend(SOLIDX, distributorGas) {} catch {}
            }
            if (eHexReflectionFee > 0) {
                try distributor.processDividend(EHEX, distributorGas) {} catch {}
            }
            if (pHexReflectionFee > 0) {
                try distributor.processDividend(PHEX, distributorGas) {} catch {}
            }
            if (gelatoReflectionFee > 0) {
                try distributor.processDividend(GELATO, distributorGas) {} catch {}
            }
            if (plsxReflectionFee > 0) {
                try distributor.processDividend(PLSX, distributorGas) {} catch {}
            }
            if (incReflectionFee > 0) {
                try distributor.processDividend(INC, distributorGas) {} catch {}
            }
        }

        emit Transfer(sender, recipient, amountReceived);
        return true;
    }

    function _basicTransfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal returns (bool) {
        _balances[sender] = _balances[sender].sub(
            amount,
            "Insufficient Balance"
        );
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
        return true;
    }

    function checkTxLimit(
        address sender,
        address recipient,
        uint256 amount
    ) internal view {
        require(
            amount <= maxTxAmount ||
                isTxLimitExempt[sender] ||
                isTxLimitExempt[recipient],
            "TX Limit Exceeded"
        );
    }

    function shouldTakeFee(
        address sender,
        address recipient
    ) internal view returns (bool) {
        if (isFeeExempt[sender] || isFeeExempt[recipient] || launchedAt == 0)
            return false;

        address[] memory liqPairs = pairs;

        for (uint256 i = 0; i < liqPairs.length; i++) {
            if (sender == liqPairs[i] || recipient == liqPairs[i]) return true;
        }

        return feesOnNormalTransfers;
    }

    function getTotalFee(
        address user,
        bool selling
    ) public view returns (uint256) {
        uint256 baseFee = selling ? baseSellFee : baseBuyFee;
        uint256 minFee = selling ? minSellFee : minBuyFee;

        uint256 lastTime = holderStartTime[user];
        uint256 timeElapsed = block.timestamp > lastTime
            ? block.timestamp - lastTime
            : 0;

        if (timeElapsed >= feeDecayTime) {
            return minFee;
        }

        uint256 discount = baseFee.sub(minFee).mul(timeElapsed).div(
            feeDecayTime
        );
        return baseFee.sub(discount);
    }

    function takeFee(
        address sender,
        address recipient,
        uint256 amount
    ) internal returns (uint256) {
        uint256 feeRate = 0;
        if (isSell(recipient)) {
            feeRate = getTotalFee(sender, true);
        } else if (isBuy(sender)) {
            feeRate = getTotalFee(recipient, false);
        } else {
            feeRate = minBuyFee;
        }
        uint256 feeAmount = amount.mul(feeRate).div(feeDenominator);

        _balances[address(this)] = _balances[address(this)].add(feeAmount);
        emit Transfer(sender, address(this), feeAmount);

        return amount.sub(feeAmount);
    }

    function isSell(address recipient) internal view returns (bool) {
        address[] memory liqPairs = pairs;
        for (uint256 i = 0; i < liqPairs.length; i++) {
            if (recipient == liqPairs[i]) return true;
        }
        return false;
    }

    function isBuy(address sender) internal view returns (bool) {
        address[] memory liqPairs = pairs;
        for (uint256 i = 0; i < liqPairs.length; i++) {
            if (sender == liqPairs[i]) return true;
        }
        return false;
    }

    function addLiquidity(
        address otherToken,
        IUniswapV2Pair pairAddress,
        uint256 amountRhino,
        uint256 amountOtherToken,
        uint256 amountRhinoMin,
        uint256 amountOtherTokenMin,
        uint256 deadline
    ) external nonReentrant inLiquidity {
        require(amountRhino > 0, "Invalid Rhino amount");
        require(isPair[address(pairAddress)] == true, "Pair is not supported.");

        if (userLpRhinoValue[address(pairAddress)][msg.sender] > 0) {
            userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[
                msg.sender
            ].sub(userLpRhinoValue[address(pairAddress)][msg.sender]);
            userLpRhinoValue[address(pairAddress)][msg.sender] = 0;
        }

        _transferFrom(msg.sender, address(this), amountRhino);
        IERC20(otherToken).transferFrom(
            msg.sender,
            address(this),
            amountOtherToken
        );
        IERC20(otherToken).approve(
            routerByPair[address(pairAddress)],
            amountOtherToken
        );

        LiquidityVars memory vars;

        (
            uint256 amount0,
            uint256 amount1,
            uint256 liquidity
        ) = IUniswapV2Router02(routerByPair[address(pairAddress)]).addLiquidity(
                address(this),
                otherToken,
                amountRhino,
                amountOtherToken,
                amountRhinoMin,
                amountOtherTokenMin,
                address(this),
                deadline
            );

        vars.amount0Used = amount0;
        vars.amount1Used = amount1;

        if (amountOtherToken.sub(vars.amount1Used) > 0) {
            IERC20(otherToken).transfer(
                msg.sender,
                amountOtherToken.sub(vars.amount1Used)
            );
        }
        if (amountRhino.sub(vars.amount0Used) > 0) {
            _transferFrom(
                address(this),
                msg.sender,
                amountRhino.sub(vars.amount0Used)
            );
        }

        lpBalances[address(pairAddress)][msg.sender] = lpBalances[
            address(pairAddress)
        ][msg.sender].add(liquidity);

        vars.lpValueForThisPair = _computeLpValue(pairAddress, msg.sender);
        userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[msg.sender]
            .add(vars.lpValueForThisPair);
        userLpRhinoValue[address(pairAddress)][msg.sender] = vars
            .lpValueForThisPair;

        try
            distributor.setLpShare(
                msg.sender,
                userTotalLpRhinoValue[msg.sender]
            )
        {} catch {}

        emit LiquidityAdded(
            msg.sender,
            vars.amount0Used,
            vars.amount1Used,
            liquidity
        );
    }

    function addLiquidityETH(
        uint256 amountRhino,
        uint256 amountETHMin,
        uint256 amountRhinoMin,
        uint256 deadline,
        IUniswapV2Pair pairAddress
    ) external payable nonReentrant inLiquidity {
        require(amountRhino > 0, "Invalid Rhino amount");
        require(isPair[address(pairAddress)] == true, "Pair is not supported.");

        if (userLpRhinoValue[address(pairAddress)][msg.sender] > 0) {
            userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[
                msg.sender
            ].sub(userLpRhinoValue[address(pairAddress)][msg.sender]);
            userLpRhinoValue[address(pairAddress)][msg.sender] = 0;
        }

        _transferFrom(msg.sender, address(this), amountRhino);

        LiquidityVars memory vars;

        (
            uint256 amount0,
            uint256 amount1,
            uint256 liquidity
        ) = IUniswapV2Router01(routerByPair[address(pairAddress)])
                .addLiquidityETH{value: msg.value}(
                address(this),
                amountRhino,
                amountRhinoMin,
                amountETHMin,
                address(this),
                deadline
            );

        vars.amount0Used = amount0;
        vars.amount1Used = amount1;

        if (msg.value.sub(vars.amount1Used) > 0) {
            (bool success, ) = payable(msg.sender).call{
                value: msg.value.sub(vars.amount1Used),
                gas: 30000
            }("");
            require(success, "PLS send failed.");
        }
        if (amountRhino.sub(vars.amount0Used) > 0) {
            _transferFrom(
                address(this),
                msg.sender,
                amountRhino.sub(vars.amount0Used)
            );
        }

        lpBalances[address(pairAddress)][msg.sender] = lpBalances[
            address(pairAddress)
        ][msg.sender].add(liquidity);

        vars.lpValueForThisPair = _computeLpValue(pairAddress, msg.sender);
        userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[msg.sender]
            .add(vars.lpValueForThisPair);
        userLpRhinoValue[address(pairAddress)][msg.sender] = vars
            .lpValueForThisPair;

        try
            distributor.setLpShare(
                msg.sender,
                userTotalLpRhinoValue[msg.sender]
            )
        {} catch {}

        emit LiquidityAdded(
            msg.sender,
            vars.amount0Used,
            vars.amount1Used,
            liquidity
        );
    }

    function removeLiquidity(
        uint256 lpAmount,
        IUniswapV2Pair pairAddress,
        uint256 amountRhinoMin,
        uint256 amountOtherTokenMin,
        uint256 deadline
    ) external nonReentrant inLiquidity {
        require(lpAmount > 0, "Invalid LP amount");
        require(
            lpBalances[address(pairAddress)][msg.sender] >= lpAmount,
            "Insufficient LP balance"
        );

        pairAddress.approve(routerByPair[address(pairAddress)], lpAmount);

        LiquidityVars memory vars;

        address otherToken = pairAddress.token0() == address(this)
            ? pairAddress.token1()
            : pairAddress.token0();

        (uint amount0, uint amount1) = IUniswapV2Router01(
            routerByPair[address(pairAddress)]
        ).removeLiquidity(
                address(this),
                otherToken,
                lpAmount,
                amountRhinoMin,
                amountOtherTokenMin,
                address(this),
                deadline
            );

        vars.amount0Used = amount0;
        vars.amount1Used = amount1;

        _transferFrom(address(this), msg.sender, vars.amount0Used);

        if (otherToken == WPLS) {
            IWPLS(WPLS).withdraw(vars.amount1Used);
            (bool success, ) = payable(msg.sender).call{
                value: vars.amount1Used,
                gas: 30000
            }("");
            require(success, "PLS send failed.");
        } else {
            IERC20(otherToken).transfer(msg.sender, vars.amount1Used);
        }

        lpBalances[address(pairAddress)][msg.sender] = lpBalances[
            address(pairAddress)
        ][msg.sender].sub(lpAmount);

        vars.lpValueForThisPair = userLpRhinoValue[address(pairAddress)][
            msg.sender
        ];
        userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[msg.sender]
            .sub(vars.lpValueForThisPair);

        userLpRhinoValue[address(pairAddress)][msg.sender] = _computeLpValue(
            pairAddress,
            msg.sender
        );
        userTotalLpRhinoValue[msg.sender] = userTotalLpRhinoValue[msg.sender]
            .add(userLpRhinoValue[address(pairAddress)][msg.sender]);

        try
            distributor.setLpShare(
                msg.sender,
                userTotalLpRhinoValue[msg.sender]
            )
        {} catch {}

        emit LiquidityRemoved(
            msg.sender,
            vars.amount0Used,
            vars.amount1Used,
            lpAmount
        );
    }

    function emergencyRemoveLiquidity(
        IUniswapV2Pair pairAddress
    ) external nonReentrant inLiquidity {
        require(
            lpBalances[address(pairAddress)][msg.sender] >= 0,
            "Insufficient LP balance"
        );
        require(emergencyMode, "Emergency mode is not enabled");

        uint256 lpAmount = lpBalances[address(pairAddress)][msg.sender];
        lpBalances[address(pairAddress)][msg.sender] = 0;

        pairAddress.transfer(msg.sender, lpAmount);
    }

    function _computeLpValue(
        IUniswapV2Pair pairAddress,
        address user
    ) internal view returns (uint256 lpValue) {
        (uint112 reserve0, uint112 reserve1, ) = pairAddress.getReserves();
        uint256 tokenReserve = pairAddress.token0() == address(this)
            ? uint256(reserve0)
            : uint256(reserve1);
        lpValue = tokenReserve.mul(lpBalances[address(pairAddress)][user]).div(
            pairAddress.totalSupply()
        );
    }

    function getLpTokenValueInRhino(
        address user
    ) public view returns (uint256) {
        return userTotalLpRhinoValue[user];
    }

    function shouldSwapBack(address sender) internal view returns (bool) {
        return
            sender != pulseV2Pair &&
            !inSwap &&
            !inLp &&
            swapEnabled &&
            _balances[address(this)] >= swapThreshold;
    }

    function swapBack() internal swapping inLiquidity {
        SwapValues memory sv;

        sv.amountRhinoReflection = swapThreshold.mul(rhinoReflectionFee).div(
            feeDenominator
        );
        sv.amountRhinoBurn = swapThreshold.mul(rhinoBurnFee).div(
            feeDenominator
        );
        sv.amountRhinoGenesis = swapThreshold.mul(genesisFee).div(
            feeDenominator
        );

        if (sv.amountRhinoReflection > 0) {
            try
                distributor.depositForRhinoReflection(sv.amountRhinoReflection)
            {} catch {}
        }

        if (sv.amountRhinoBurn > 0) {
            _transferFrom(address(this), ZERO, sv.amountRhinoBurn);
            totalRhinoBurned = totalRhinoBurned.add(sv.amountRhinoBurn);
        }

        if (sv.amountRhinoGenesis > 0) {
            _transferFrom(
                address(this),
                genesisReceiver,
                sv.amountRhinoGenesis
            );
        }

        sv.amountRhinoSwap = swapThreshold
            .sub(sv.amountRhinoReflection)
            .sub(sv.amountRhinoBurn)
            .sub(sv.amountRhinoGenesis);

        sv.balanceBefore = address(this).balance;

        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = address(WPLS);

        // Attempt the swap
        try
            pulseRouterV2.swapExactTokensForETHSupportingFeeOnTransferTokens(
                sv.amountRhinoSwap,
                0,
                path,
                address(this),
                block.timestamp
            )
        {
            sv.amountPls = address(this).balance.sub(sv.balanceBefore);

            sv.amountPlsLiquidity = sv.amountPls.mul(liquidityFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountPlsReflection = sv.amountPls.mul(plsReflectionFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountSolidXReflection = sv
                .amountPls
                .mul(solidXReflectionFee)
                .div(
                    feeDenominator
                        .sub(rhinoReflectionFee)
                        .sub(rhinoBurnFee)
                        .sub(genesisFee)
                );
            sv.amountEhexReflection = sv.amountPls.mul(eHexReflectionFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountPhexReflection = sv.amountPls.mul(pHexReflectionFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountGelatoReflection = sv
                .amountPls
                .mul(gelatoReflectionFee)
                .div(
                    feeDenominator
                        .sub(rhinoReflectionFee)
                        .sub(rhinoBurnFee)
                        .sub(genesisFee)
                );
            sv.amountPlsxReflection = sv.amountPls.mul(plsxReflectionFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountIncReflection = sv.amountPls.mul(incReflectionFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountSolidXBurn = sv.amountPls.mul(solidXBurnFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );
            sv.amountGelatoBurn = sv.amountPls.mul(gelatoBurnFee).div(
                feeDenominator.sub(rhinoReflectionFee).sub(rhinoBurnFee).sub(
                    genesisFee
                )
            );

            // Send PLS to Liquidity Receiver
            if (sv.amountPlsLiquidity > 0) {
                (bool success,) = payable(autoLiquidityReceiver).call{
                    value: sv.amountPlsLiquidity
                }("");
                success;
                totalPlsLpAdded = totalPlsLpAdded.add(sv.amountPlsLiquidity);
            }

            // Distribute reflections
            if (sv.amountPlsReflection > 0) {
                try
                    distributor.depositForPlsReflection{
                        value: sv.amountPlsReflection
                    }()
                {} catch {}
            }
            if (sv.amountSolidXReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountSolidXReflection
                    }(address(SOLIDX))
                {} catch {}
            }
            if (sv.amountEhexReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountEhexReflection
                    }(address(EHEX))
                {} catch {}
            }
            if (sv.amountPhexReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountPhexReflection
                    }(address(PHEX))
                {} catch {}
            }
            if (sv.amountGelatoReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountGelatoReflection
                    }(address(GELATO))
                {} catch {}
            }
            if (sv.amountPlsxReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountPlsxReflection
                    }(address(PLSX))
                {} catch {}
            }
            if (sv.amountIncReflection > 0) {
                try
                    distributor.depositForReflection{
                        value: sv.amountIncReflection
                    }(address(INC))
                {} catch {}
            }
            if (sv.amountSolidXBurn > 0) {
                try
                    distributor.depositForSolidXBurn{
                        value: sv.amountSolidXBurn
                    }()
                {} catch {}
            }
            if (sv.amountGelatoBurn > 0) {
                try
                    distributor.depositForGelatoBurn{
                        value: sv.amountGelatoBurn
                    }()
                {} catch {}
            }

            // If any dust remains in the contract, send it to the owner
            if (address(this).balance > 0) {
                (bool success,) = payable(owner()).call{value: address(this).balance, gas: 30000}(
                    ""
                );
                success;
            }

            emit SwapBackSuccess(sv.amountRhinoSwap);
        } catch Error(string memory e) {
            emit SwapBackFailed(
                string(abi.encodePacked("SwapBack failed with error ", e))
            );
        } catch {
            emit SwapBackFailed(
                "SwapBack failed without an error message from PulseX"
            );
        }
    }
    function launch() external onlyOwner {
        require(launchedAt == 0, "Already launched.");
        launchedAt = block.timestamp;

        try distributor.setShare(address(rhinoCharging), _balances[address(rhinoCharging)]) {} catch {}
        try distributor.setShare(address(owner()), _balances[address(owner())]) {} catch {}
        emit Launched(block.number, block.timestamp);
    }

    function setChargingContract(address chargingContract) external onlyOwner {
        require(chargingContract != address(0), "Invalid address");
        rhinoCharging = RhinoCharging(payable(chargingContract));
        isFeeExempt[address(rhinoCharging)] = true;
        isTxLimitExempt[address(rhinoCharging)] = true;
        isFeeExempt[address(rhinoCharging.rhinoBuyAndBurn())] = true;
        isTxLimitExempt[address(rhinoCharging.rhinoBuyAndBurn())] = true;
        emit ParameterUpdated();
    }

    function setTxLimit(uint256 amount) external onlyOwner {
        require(amount >= _totalSupply / 2000);
        maxTxAmount = amount;
    }

    function setIsDividendExempt(
        address holder,
        bool exempt
    ) external onlyOwner {
        require(holder != address(this) && holder != pulseV2Pair);
        isDividendExempt[holder] = exempt;
        if (exempt) {
            distributor.setShare(holder, 0);
        } else {
            distributor.setShare(holder, _balances[holder]);
        }
    }

    function setIsFeeExempt(address holder, bool exempt) external onlyOwner {
        isFeeExempt[holder] = exempt;
    }

    function setIsTxLimitExempt(
        address holder,
        bool exempt
    ) external onlyOwner {
        isTxLimitExempt[holder] = exempt;
    }

    function setFeeParameters(
        uint256 _baseBuyFee,
        uint256 _baseSellFee,
        uint256 _minBuyFee,
        uint256 _minSellFee,
        uint256 _feeDecayTime
    ) external onlyOwner {
        require(_baseBuyFee >= _minBuyFee, "Buy fee must be >= minimum");
        require(_baseSellFee >= _minSellFee, "Sell fee must be >= minimum");
        require(_feeDecayTime > 0, "Decay time must be positive");
        require(_baseBuyFee <= 2500, "25 Percent max allowed.");
        require(_baseSellFee <= 2500, "25 Percent max allowed.");

        baseBuyFee = _baseBuyFee;
        baseSellFee = _baseSellFee;
        minBuyFee = _minBuyFee;
        minSellFee = _minSellFee;
        feeDecayTime = _feeDecayTime;

        emit FeeParametersUpdated(
            _baseBuyFee,
            _baseSellFee,
            _minBuyFee,
            _minSellFee,
            _feeDecayTime
        );
    }

    function setFeeDistribution(
        uint256[13] calldata feeParams
    ) external onlyOwner {
        uint256 totalFee;
        for (uint256 i = 0; i < feeParams.length; i++) {
            totalFee = totalFee.add(feeParams[i]);
        }
        require(
            totalFee == feeDenominator,
            "Total fees must equal feeDenominator"
        );

        rhinoBurnFee = feeParams[0];
        gelatoBurnFee = feeParams[1];
        solidXBurnFee = feeParams[2];
        genesisFee = feeParams[3];
        plsReflectionFee = feeParams[4];
        rhinoReflectionFee = feeParams[5];
        solidXReflectionFee = feeParams[6];
        eHexReflectionFee = feeParams[7];
        pHexReflectionFee = feeParams[8];
        gelatoReflectionFee = feeParams[9];
        plsxReflectionFee = feeParams[10];
        incReflectionFee = feeParams[11];
        liquidityFee = feeParams[12];

        emit FeeDistributionUpdated(
            feeParams[0],
            feeParams[1],
            feeParams[2],
            feeParams[3],
            feeParams[4],
            feeParams[5],
            feeParams[6],
            feeParams[7],
            feeParams[8],
            feeParams[9],
            feeParams[10],
            feeParams[11],
            feeParams[12]
        );
    }

    function setLpRewardsPercent(
        uint256 newLpReflectionPercent
    ) external onlyOwner {
        lpReflectionPercent = newLpReflectionPercent;
        require(
            lpReflectionPercent <= 5000,
            "No more than 50 percent can be directed to LP providers."
        );
    }

    function setLiquidityFeeReceiver(
        address _autoLiquidityReceiver
    ) external onlyOwner {
        autoLiquidityReceiver = _autoLiquidityReceiver;

        emit ParameterUpdated();
    }

    function setDistributor(address _newDistributor) external onlyOwner {
        require(_newDistributor != address(0), "Invalid address");
        distributor = DividendDistributor(payable(_newDistributor));

        isFeeExempt[address(distributor)] = true;
        isTxLimitExempt[address(distributor)] = true;
        isDividendExempt[address(distributor)] = true;

        _allowances[address(this)][address(distributor)] = type(uint256).max;

        emit ParameterUpdated();
    }

    function setGenesisReceiver(address _newGenesis) external onlyOwner {
        genesisReceiver = _newGenesis;

        emit ParameterUpdated();
    }

    function setSwapBackSettings(
        bool _enabled,
        uint256 _amount
    ) external onlyOwner {
        swapEnabled = _enabled;
        swapThreshold = _amount;

        require(swapThreshold > 0, "Must be greater than zero.");

        emit ParameterUpdated();
    }

    function setDistributionCriteria(
        address token,
        uint256 minPeriod,
        uint256 minDistribution
    ) external onlyOwner {
        distributor.setDistributionCriteria(token, minPeriod, minDistribution);

        emit ParameterUpdated();
    }

    function setDistributorSettings(uint256 gas) external onlyOwner {
        distributorGas = gas;
        require(distributorGas <= 1000000, "Max gas is 1000000");

        emit ParameterUpdated();
    }

    function getCirculatingSupply() public view returns (uint256) {
        return _totalSupply.sub(balanceOf(DEAD)).sub(balanceOf(ZERO));
    }

    function claimDividend() external nonReentrant {
        distributor.claimDividend(msg.sender);
    }

    function claimLpDividend() external nonReentrant {
        distributor.claimLpDividend(msg.sender);
    }

    function getPairs() public view returns (address[] memory) {
        return pairs;
    }

    function setEmergencyMode(bool mode) public onlyOwner {
        emergencyMode = mode;

        emit ParameterUpdated();
    }

    function addPair(address pair, address router) external onlyOwner {
        require(isPair[pair] == false, "Pair already added.");
        require(
            router == address(pulseRouterV1) ||
                router == address(pulseRouterV2) ||
                router == address(nineinchRouter),
            "Invalid router"
        );
        pairs.push(pair);
        isPair[pair] = true;
        routerByPair[pair] = router;
        isDividendExempt[pair] = true;
        distributor.setShare(pair, 0);

        emit ParameterUpdated();
    }

    function removePair(address pair) external onlyOwner {
        require(isPair[pair], "Pair does not exist");

        uint256 indexToRemove;
        bool found = false;
        for (uint256 i = 0; i < pairs.length; i++) {
            if (pairs[i] == pair) {
                indexToRemove = i;
                found = true;
                break;
            }
        }
        require(found, "Pair not found in array");

        pairs[indexToRemove] = pairs[pairs.length - 1];
        routerByPair[pair] = address(0);
        pairs.pop();
        isPair[pair] = false;

        emit ParameterUpdated();
    }

    event AutoLiquify(uint256 amountWPLS, uint256 amountRhino);
    event Launched(uint256 blockNumber, uint256 timestamp);
    event ParameterUpdated();
    event SwapBackSuccess(uint256 amount);
    event SwapBackFailed(string message);
    event LiquidityAdded(
        address indexed user,
        uint256 amountRhino,
        uint256 amountOther,
        uint256 lpAmount
    );
    event LiquidityRemoved(
        address indexed user,
        uint256 amountRhino,
        uint256 amountOther,
        uint256 lpAmount
    );
    event FeeParametersUpdated(
        uint256 baseBuyFee,
        uint256 baseSellFee,
        uint256 minBuyFee,
        uint256 minSellFee,
        uint256 feeDecayTime
    );
    event FeeDistributionUpdated(
        uint256 rhinoBurnPercent,
        uint256 gelatoBurnPercent,
        uint256 solidXBurnPercent,
        uint256 genesisPercent,
        uint256 plsReflectionPercent,
        uint256 rhinoReflectionPercent,
        uint256 solidXReflectionPercent,
        uint256 eHexReflectionPercent,
        uint256 pHexReflectionPercent,
        uint256 gelatoReflectionPercent,
        uint256 plsxReflectionPercent,
        uint256 incReflectionPercent,
        uint256 liquidityPercent
    );
}
        

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

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

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

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
          

@openzeppelin/contracts/utils/Address.sol

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

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

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

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

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

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

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

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

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

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

@openzeppelin/contracts/utils/Context.sol

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

pragma solidity ^0.8.20;

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

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

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

@openzeppelin/contracts/interfaces/IERC20.sol

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

pragma solidity ^0.8.20;

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

@openzeppelin/contracts/access/Ownable.sol

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

@openzeppelin/contracts/interfaces/IERC1363.sol

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

@openzeppelin/contracts/interfaces/IERC165.sol

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

pragma solidity ^0.8.20;

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

@openzeppelin/contracts/utils/Errors.sol

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

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

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}
          

@openzeppelin/contracts/utils/ReentrancyGuard.sol

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

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol

pragma solidity >=0.5.0;

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

@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol

pragma solidity >=0.5.0;

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

@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol

pragma solidity >=0.6.2;

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

@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol

pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

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

contracts/RhinoBuyAndBurn.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/* === OZ === */
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/* === UNISWAP V2 === */
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

/* === SYSTEM === */
import {RhinoCharging} from "./RhinoCharging.sol";


/**
 * @title RhinoBuyAndBurn Contract
 * @author BuildTheTech.com
 * @notice This contract receives PLS from a charging contract, and at regular intervals,
 * uses an allocated portion of the PLS to buy RHINO tokens and immediately burn them.
 * The process is triggered by any external user, who receives an incentive for doing so.
 */
contract RhinoBuyAndBurn is ReentrancyGuard, Ownable {
    
    /* ================================================================
                         STATE VARIABLES & CONSTANTS
    ================================================================ */  
    RhinoCharging public rhinoCharging;

    // --- Interval Mechanics ---
    struct Interval {
        uint256 amountAllocated;
        uint256 amountProcessed;
    }
    uint256 public startTimeStamp;
    uint256 public lastBurnIntervalNumber;
    uint256 public lastBurnIntervalStartTimestamp;
    mapping(uint256 => Interval) public plsIntervalsBurn;

    // --- Constants & Immutable ---
    IUniswapV2Router02 public immutable pulseXRouterV1 = IUniswapV2Router02(0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02);
    IUniswapV2Router02 public immutable pulseXRouterV2 = IUniswapV2Router02(0x165C3410fC91EF562C50559f7d2289fEbed552d9);
    IUniswapV2Router02 public immutable nineinchRouter = IUniswapV2Router02(0xeB45a3c4aedd0F47F345fB4c8A1802BB5740d725);

    address public immutable WPLS;
    address public immutable RHINO;
    address private constant ZERO = 0x000000000000000000000000000000000000dEaD;
    
    uint256 public constant incentiveFeeBps = 150;
    uint256 public constant intervalTime = 30 minutes;

    // --- Configurable Settings ---
    uint256 public dailyAllocationBps;

    // --- Balances and Totals ---
    uint256 public totalPlsForBurning;
    uint256 public totalRhinoBurned;

    /* ================================================================
                                     EVENTS
    ================================================================ */

    event PlsReceived(address indexed from, uint256 amount);
    event BuyAndBurn(
        uint256 plsUsedForSwap,
        uint256 rhinoBurned,
        uint256 incentivePaid,
        address indexed caller
    );
    event SettingsUpdated(string setting, uint256 newValue);


    /* ================================================================
                                     ERRORS
    ================================================================ */

    error NotStartedYet();
    error IntervalAlreadyProcessed();
    error InvalidSetting();
    error NoAllocationAvailable();
    error InvalidInput();

    /* ================================================================
                                   MODIFIERS
    ================================================================ */

    modifier intervalUpdate() {
        _intervalUpdatePlsForBurning();
        _;
    }

    /* ================================================================
                                  CONSTRUCTOR
    ================================================================ */

    constructor(
        address _initialOwner,
        uint32 _startTimestamp,
        address _rhinoAddress,
        address _wplsAddress
    ) Ownable(_initialOwner) {
        require(_rhinoAddress != address(0) && _wplsAddress != address(0), "Invalid token address");

        startTimeStamp = _startTimestamp;
        RHINO = _rhinoAddress;
        WPLS = _wplsAddress;
        rhinoCharging = RhinoCharging(payable(msg.sender));

        dailyAllocationBps = 100;
        lastBurnIntervalStartTimestamp = _startTimestamp;

        IERC20(WPLS).approve(address(pulseXRouterV1), type(uint256).max);
        IERC20(WPLS).approve(address(pulseXRouterV2), type(uint256).max);
        IERC20(WPLS).approve(address(nineinchRouter), type(uint256).max);
        IERC20(RHINO).approve(address(rhinoCharging), type(uint256).max);
    }


    /* ================================================================
                                 CORE LOGIC
    ================================================================ */

    receive() external payable {
        if (
            block.timestamp > startTimeStamp &&
            block.timestamp - lastBurnIntervalStartTimestamp >
            intervalTime
        ) {
            _intervalUpdatePlsForBurning();
        }

        totalPlsForBurning += msg.value;
        emit PlsReceived(msg.sender, msg.value);
    }

    function distributePLSForBurning() external payable {
        if (msg.value == 0) revert InvalidInput();

        if (
            block.timestamp > startTimeStamp &&
            block.timestamp - lastBurnIntervalStartTimestamp >
            intervalTime
        ) {
            _intervalUpdatePlsForBurning();
        }

        totalPlsForBurning += msg.value;
        emit PlsReceived(msg.sender, msg.value);
    }

    function buyAndBurn(uint256 _deadline) external nonReentrant intervalUpdate {
        Interval storage currentInterval = plsIntervalsBurn[lastBurnIntervalNumber];
        if (currentInterval.amountProcessed != 0) revert IntervalAlreadyProcessed();

        uint256 plsToProcess = currentInterval.amountAllocated;
        if (plsToProcess == 0) revert NoAllocationAvailable();
    
        currentInterval.amountProcessed = currentInterval.amountAllocated;

        uint256 incentiveAmount = (plsToProcess * incentiveFeeBps) / 10_000;
        uint256 plsToSwap = plsToProcess - incentiveAmount;

        uint256 rhinoBought = _swapPlsForRhino(plsToSwap, _deadline);
        uint256 halfRhino = rhinoBought / 2;
        if (rhinoBought > 0) {
            _burnRhino(halfRhino);
            rhinoCharging.depositRhinoFromBurn(rhinoBought - halfRhino);
        }

        totalPlsForBurning -= plsToProcess;
        totalRhinoBurned += rhinoBought;

        if (incentiveAmount > 0) {
            (bool success, ) = msg.sender.call{value: incentiveAmount}("");
            require(success, "Incentive payment failed");
        }

        try rhinoCharging.triggerSwap(block.timestamp) {} catch {}

        emit BuyAndBurn(plsToSwap, rhinoBought, incentiveAmount, msg.sender);
    }
    
    /* ================================================================
                              INTERNAL FUNCTIONS
    ================================================================ */

    function _intervalUpdatePlsForBurning() internal {
        if (block.timestamp < startTimeStamp) revert NotStartedYet();

        uint256 timeElapsed = block.timestamp - lastBurnIntervalStartTimestamp;
        if (timeElapsed < intervalTime) return;

        uint256 intervals_per_day = (24 hours) / intervalTime;
        
        uint256 missedIntervals = (timeElapsed / intervalTime) - 1;

        uint256 dailyAllocation = (totalPlsForBurning * dailyAllocationBps) / 10_000;
        uint256 amountPerInterval = dailyAllocation / intervals_per_day;
        
        uint256 totalAmountForInterval = amountPerInterval * (missedIntervals + 1);

        if (totalAmountForInterval > totalPlsForBurning) {
            totalAmountForInterval = uint256(totalPlsForBurning);
        }

        uint256 newIntervalNumber = lastBurnIntervalNumber + missedIntervals + 1;
        
        plsIntervalsBurn[newIntervalNumber] = Interval({
            amountAllocated: totalAmountForInterval,
            amountProcessed: 0
        });

        lastBurnIntervalNumber = newIntervalNumber;
        lastBurnIntervalStartTimestamp = lastBurnIntervalStartTimestamp + ((missedIntervals + 1) * uint32(intervalTime));
    }

    function _swapPlsForRhino(uint256 _plsAmount, uint256 _deadline) internal returns (uint256) {
        if (_plsAmount == 0) return 0;

        address[] memory path = new address[](2);
        path[0] = WPLS;
        path[1] = RHINO;

        address bestRouter = getBestRouter(_plsAmount, path);
        if (bestRouter == address(0)) return 0;

        uint[] memory amounts = IUniswapV2Router02(bestRouter).swapExactETHForTokens{value: _plsAmount}(
            0,
            path,
            address(this),
            _deadline
        );

        return amounts[1];
    }
    
    function _burnRhino(uint256 _rhinoAmount) internal {
        IERC20(RHINO).transfer(ZERO, _rhinoAmount);
    }
    
    /* ================================================================
                             OWNER FUNCTIONS
    ================================================================ */
    
    function setDailyAllocationBps(uint256 _newAllocationBps) external onlyOwner {
        if (_newAllocationBps > 2_500) revert InvalidSetting();
        dailyAllocationBps = _newAllocationBps;
        emit SettingsUpdated("DailyAllocationBps", _newAllocationBps);
    }

    function setChargingContract(address _newChargingContract) external onlyOwner {
        if (_newChargingContract == address(0)) revert InvalidSetting();
        rhinoCharging = RhinoCharging(payable(_newChargingContract));
        emit SettingsUpdated("ChargingContract", uint256(uint160(_newChargingContract)));
    }

    function withdrawTokens(address _token, address _to) external onlyOwner {
        require(_token != RHINO, "Cannot withdraw Rhino tokens");
        uint256 balance = IERC20(_token).balanceOf(address(this));
        require(balance > 0, "No tokens to withdraw");
        IERC20(_token).transfer(_to, balance);
    }

    /* ================================================================
                                VIEW FUNCTIONS
    ================================================================ */

    function getBestRouter(
        uint256 amountIn,
        address[] memory path
    ) public view returns (address bestRouter) {
        address[] memory routers = new address[](3);
        routers[0] = address(pulseXRouterV1);
        routers[1] = address(pulseXRouterV2);
        routers[2] = address(nineinchRouter);

        uint256 bestAmountOut = 0;

        for (uint256 i = 0; i < routers.length; i++) {
            try
                IUniswapV2Router02(routers[i]).getAmountsOut(amountIn, path)
            returns (uint256[] memory amountsOut) {
                uint256 amountOut = amountsOut[amountsOut.length - 1];
                if (amountOut > bestAmountOut) {
                    bestAmountOut = amountOut;
                    bestRouter = routers[i];
                }
            } catch {
                continue;
            }
        }
    }
}
          

contracts/RhinoCharging.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/* === OZ === */
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/* === UNISWAP V2 === */
import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

/* === SYSTEM === */
import {RhinoBuyAndBurn} from "./RhinoBuyAndBurn.sol";

interface IMasterChef {
    function deposit(uint256 _pid, uint256 _amount) external;
    function withdraw(uint256 _pid, uint256 _amount) external;
    function userInfo(uint256 _pid, address _user) external view returns (uint256 amount, uint256 rewardDebt);
}

interface IWPLS is IERC20 {
    function deposit() external payable;
    function withdraw(uint wad) external;
}

interface IGelato {
    function _maxTxAmount() external view returns (uint256);
}

/**
 * @title Rhino Charging
 * @author BuildTheTech.com
 * @notice This contract automates the process of collecting token reflections, swapping them to PLS,
 * and deploying the capital into a PulseX yield farm (DAI/WPLS). It operates in adjustable "Charging Cycles."
 */
contract RhinoCharging is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    struct CycleData {
        uint256 cycle;
        uint256 rhinoBurned;
        uint256 plsEarned;
    }

    uint256 public farmStopPercentage = 50;

    uint256 public currentCycleNumber = 1;
    uint256 public chargingCycleDuration = 28 days;
    uint256 public nextChargingCycleDuration = 28 days;
    uint256 public lastCycleEndTime;
    RhinoBuyAndBurn public rhinoBuyAndBurn;
    uint256 public rhinoChargingBalance;
    uint256 public rhinoChargingBalanceAtCycleStart;
    uint256 public slippageBps;

    uint256 public totalRhinoBurned;
    bool public swapping;
    bool cycleEnding;
    bool inFarming;

    IUniswapV2Router02 public immutable pulseXRouterV1 = IUniswapV2Router02(0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02);
    IUniswapV2Router02 public immutable pulseXRouterV2 = IUniswapV2Router02(0x165C3410fC91EF562C50559f7d2289fEbed552d9);
    IUniswapV2Router02 public immutable nineinchRouter = IUniswapV2Router02(0xeB45a3c4aedd0F47F345fB4c8A1802BB5740d725);
    IMasterChef public immutable pulseXMasterChef = IMasterChef(0xB2Ca4A66d3e57a5a9A12043B6bAD28249fE302d4);
    uint256 public constant DAI_WPLS_PID = 1;
    uint256 public plsFarmThreshold = 1_000_000e18;

    address public immutable WPLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;
    address public immutable DAI = 0xefD766cCb38EaF1dfd701853BFCe31359239F305;
    address public immutable WPLS_DAI_LP = 0xE56043671df55dE5CDf8459710433C10324DE0aE;

    address immutable ZERO = 0x0000000000000000000000000000000000000000;
    address immutable SOLIDX = 0x8Da17Db850315A34532108f0f5458fc0401525f6;
    address immutable GELATO = 0x616cb6a245Ed4c11216Ec58D10B6A2E87271845d;
    address immutable EHEX = 0x57fde0a71132198BBeC939B98976993d8D89D225;
    address immutable PHEX = 0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39;
    address immutable PLSX = 0x95B303987A60C71504D99Aa1b13B4DA07b0790ab;
    address immutable INC = 0x2fa878Ab3F87CC1C9737Fc071108F904c0B0C95d;
    address immutable RHINO;

    address[] public rewardTokens;
    mapping(address => bool) public approvedRewardTokens;
    mapping(address => uint256) public swapThresholds;
    mapping(address => uint256) public failedSwapAmounts;
    mapping(uint256 => CycleData) public cycleData;
    mapping(uint256 => uint256) public estimatedPlsEarnedThisCycle;

    event TokensSwapped(address indexed token, uint256 amountIn);
    event SwapFailed(address indexed token, uint256 amount);
    event RhinoBurnedOnPerformance(uint256 amount);
    event Farmed(uint256 lpAmount);
    event CycleEnded(uint256 indexed cycleNumber, uint256 plsForwarded, uint256 rhinoBurned);
    event RescuedTokens(address indexed token, uint256 amount);
    event RescuedPLS(uint256 amount);

    modifier inSwap() {
        swapping = true;
        _;
        swapping = false;
    }

    modifier endingCycle() {
        cycleEnding = true;
        _;
        cycleEnding = false;
    }

    modifier farming() {
        inFarming = true;
        _;
        inFarming = false;
    }

    constructor(
        address _initialOwner,
        uint32 _initialCycleDuration,
        uint32 _initialCycleStartTime,
        address _rhinoAddress
    ) Ownable(_initialOwner) {
        rhinoBuyAndBurn = new RhinoBuyAndBurn(_initialOwner, _initialCycleStartTime + _initialCycleDuration, _rhinoAddress, WPLS);
        chargingCycleDuration = _initialCycleDuration;
        lastCycleEndTime = _initialCycleStartTime;
        cycleData[currentCycleNumber] = CycleData({
            cycle: currentCycleNumber,
            rhinoBurned: 0,
            plsEarned: 0
        });

        RHINO = _rhinoAddress;
        slippageBps = 10000;
        
        IERC20(WPLS_DAI_LP).approve(address(pulseXRouterV1), type(uint256).max);
        IERC20(WPLS_DAI_LP).approve(address(pulseXMasterChef), type(uint256).max);
    
        IERC20(WPLS).approve(address(pulseXRouterV1), type(uint256).max);
        IERC20(WPLS).approve(address(pulseXRouterV2), type(uint256).max);
        IERC20(WPLS).approve(address(nineinchRouter), type(uint256).max);
        IERC20(DAI).approve(address(pulseXRouterV1), type(uint256).max);
        IERC20(DAI).approve(address(pulseXRouterV2), type(uint256).max);
        IERC20(DAI).approve(address(nineinchRouter), type(uint256).max);

        _addRewardToken(SOLIDX, 1e18);
        _addRewardToken(GELATO, 200_000e18);
        _addRewardToken(EHEX, 5_000e8);
        _addRewardToken(PHEX, 5_000e8);
        _addRewardToken(PLSX, 500_000e18);
        _addRewardToken(INC, 10e18);
        _addRewardToken(RHINO, 10_000e18);
    }

    receive() external payable {
        if (!cycleEnding && !inFarming) {
            estimatedPlsEarnedThisCycle[currentCycleNumber] += msg.value;
        }
    }

    function depositRhinoFromBurn(uint256 amount) public {
        require(msg.sender == address(rhinoBuyAndBurn), "Only burning contract can deposit RHINO");
        IERC20(RHINO).safeTransferFrom(msg.sender, address(this), amount);
        rhinoChargingBalance += amount;
    }  

    function triggerSwap(uint256 deadline) public {
        if (swapping || cycleEnding) {
            return;
        }
        _triggerSwap(deadline);
    }

    function _triggerSwap(uint256 deadline) internal inSwap {
    uint256 rewardsLength = rewardTokens.length;

    address[] memory path = new address[](2);
    path[1] = WPLS;

    for (uint i = 0; i < rewardsLength; ) {
        address currentToken = rewardTokens[i];
        uint256 balance = IERC20(currentToken).balanceOf(address(this));

        if (currentToken == RHINO) {
            if (balance > rhinoChargingBalance) {
                rhinoChargingBalance = balance;
            }
        } else {
            if (currentToken == GELATO) {
                uint256 maxTx = IGelato(GELATO)._maxTxAmount();
                if (balance > maxTx) {
                    balance = maxTx;
                }
            }
            
            uint256 threshold = swapThresholds[currentToken]; 

            if (balance > threshold) {
                path[0] = currentToken;

                IUniswapV2Router02 router = IUniswapV2Router02(getBestRouter(balance, path));

                if (address(router) != address(0)) {
                    try router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                        balance, 0, path, address(this), deadline
                    ) {} catch {}
                    emit TokensSwapped(currentToken, balance);
                }
            }
        }

        unchecked {
            ++i;
        }
    }

    if (address(this).balance >= plsFarmThreshold && block.timestamp < lastCycleEndTime + ((chargingCycleDuration * farmStopPercentage) / 100) && !cycleEnding && !inFarming) {
        addLiquidityAndFarm(deadline);
    }

    if (block.timestamp >= lastCycleEndTime + chargingCycleDuration && !cycleEnding) {
        endChargingCycle();
    }
}
    
    function addLiquidityAndFarm(uint256 deadline) internal farming {
        uint256 plsBalance = address(this).balance;
        if (plsBalance == 0) return;

        uint256 amountToSwapForDai = plsBalance / 2;
        address[] memory path = new address[](2);
        path[0] = WPLS;
        path[1] = DAI;

        IUniswapV2Router02 router = IUniswapV2Router02(getBestRouter(amountToSwapForDai, path));
        if (address(router) == address(0)) return;

        uint amountOutMin = _getMinAmountOut(router, amountToSwapForDai, path);

        try router.swapExactETHForTokens{value: amountToSwapForDai}(amountOutMin, path, address(this), deadline) {
            uint256 remainingPls = address(this).balance;
            if(remainingPls > 0) {
                IWPLS(WPLS).deposit{value: remainingPls}();
            }

            uint256 daiBalance = IERC20(DAI).balanceOf(address(this));
            uint256 wplsBalance = IWPLS(WPLS).balanceOf(address(this));
            
            if(daiBalance > 0 && wplsBalance > 0) {
                (, , uint liquidity) = pulseXRouterV1.addLiquidity(
                    DAI, WPLS, daiBalance, wplsBalance, 0, 0, address(this), deadline
                );
                pulseXMasterChef.deposit(DAI_WPLS_PID, liquidity);
                emit Farmed(liquidity);
            }
        } catch {}
    }

    function endChargingCycle() internal endingCycle {
        (uint256 lpTokenAmount, ) = pulseXMasterChef.userInfo(DAI_WPLS_PID, address(this));
        if (lpTokenAmount > 0) {
            pulseXMasterChef.withdraw(DAI_WPLS_PID, lpTokenAmount);
        }

        uint256 lpBalance = IERC20(WPLS_DAI_LP).balanceOf(address(this));
        if (lpBalance > 0) {
            pulseXRouterV1.removeLiquidity(
                DAI, WPLS, lpBalance, 0, 0, address(this), block.timestamp
            );
        }
        
        uint256 wplsBalance = IWPLS(WPLS).balanceOf(address(this));
        if(wplsBalance > 0) {
            IWPLS(WPLS).withdraw(wplsBalance);
        }

        uint256 daiBalance = IERC20(DAI).balanceOf(address(this));
        if(daiBalance > 0) {
            address[] memory path = new address[](2);
            path[0] = DAI;
            path[1] = WPLS;

            uint amountOutMin = _getMinAmountOut(pulseXRouterV1, daiBalance, path);
            try pulseXRouterV1.swapExactTokensForETH(daiBalance, amountOutMin, path, address(this), block.timestamp) {} catch {}
        }

        uint256 incBalance = IERC20(INC).balanceOf(address(this));
        if (incBalance > 0) {
            address[] memory path = new address[](2);
            path[0] = INC;
            path[1] = WPLS;

            uint256 amountOutMin = _getMinAmountOut(pulseXRouterV1, incBalance, path);
            try pulseXRouterV1.swapExactTokensForETH(incBalance, amountOutMin, path, address(this), block.timestamp) {} catch {}
        }

        uint256 totalPlsEarnedThisCycle = address(this).balance;
        
        uint256 rhinoToBurn = _calculatePerformanceBurn(totalPlsEarnedThisCycle, rhinoChargingBalanceAtCycleStart);

        if (rhinoToBurn > 0) {
            _burnRhino(rhinoToBurn);
            rhinoChargingBalance -= rhinoToBurn;
            totalRhinoBurned += rhinoToBurn;
            
            emit RhinoBurnedOnPerformance(rhinoToBurn);
        }

        cycleData[currentCycleNumber] = CycleData({
            cycle: currentCycleNumber,
            rhinoBurned: rhinoToBurn,
            plsEarned: totalPlsEarnedThisCycle
        });

        if(totalPlsEarnedThisCycle > 0) {
            try rhinoBuyAndBurn.distributePLSForBurning{value: totalPlsEarnedThisCycle}() {} catch {}
        }
        emit CycleEnded(currentCycleNumber, totalPlsEarnedThisCycle, rhinoToBurn);

        lastCycleEndTime = lastCycleEndTime + chargingCycleDuration;

        if (nextChargingCycleDuration > 0) {
            chargingCycleDuration = nextChargingCycleDuration;
        }

        rhinoChargingBalanceAtCycleStart = rhinoChargingBalance;
        currentCycleNumber++;

        cycleData[currentCycleNumber] = CycleData({
            cycle: currentCycleNumber,
            rhinoBurned: 0,
            plsEarned: 0
        });
        
        estimatedPlsEarnedThisCycle[currentCycleNumber] = address(this).balance;
    }

    function _calculatePerformanceBurn(uint256 totalPlsEarned, uint256 currentRhinoBalance) internal view returns (uint256) {
        if (totalPlsEarned >= rhinoChargingBalanceAtCycleStart) {
            return 0;
        }

        uint256 shortfall = rhinoChargingBalanceAtCycleStart - totalPlsEarned;
        uint256 maxBurnAmount = (currentRhinoBalance * 10) / 100;

        if (shortfall < maxBurnAmount) {
            return shortfall;
        } else {
            return maxBurnAmount;
        }
    }

    function _burnRhino(uint256 amount) internal {
        require(IERC20(RHINO).balanceOf(address(this)) >= amount, "Insufficient RHINO balance");
        IERC20(RHINO).transfer(ZERO, amount);
    }

    function _getMinAmountOut(IUniswapV2Router02 router, uint256 amountIn, address[] memory path) internal view returns (uint256) {
        try router.getAmountsOut(amountIn, path) returns (uint[] memory amounts) {
            return (amounts[amounts.length - 1] * (10_000 - slippageBps)) / 10_000;
        } catch {
            return 0;
        }
    }

    function setNextChargingCycleDuration(uint256 newDurationInDays) external onlyOwner {
        require(newDurationInDays >= 7 && newDurationInDays <= 60, "Duration must be between 7 and 60 days");
        nextChargingCycleDuration = newDurationInDays * 1 days;
    }

    function setFarmStopPercentage(uint256 newPercentage) external onlyOwner {
        require(newPercentage >= 0 && newPercentage <= 100, "Percentage must be between 0 and 100");
        farmStopPercentage = newPercentage;
    }

    function setBuyAndBurnContract(address newBurningContract) external onlyOwner {
        require(newBurningContract != address(0), "Cannot be zero address");
        rhinoBuyAndBurn = RhinoBuyAndBurn(payable(newBurningContract));
    }

    function setSlippageBps(uint256 _newSlippageBps) external onlyOwner {
        require(_newSlippageBps >= 200 && _newSlippageBps <= 10000, "Slippage must be between 2% and 100%");
        slippageBps = _newSlippageBps;
    }

    function overrideRhinoChargingBalanceAtCycleStart(uint256 newBalance) external onlyOwner {
        require(newBalance <= rhinoChargingBalance, "New balance cannot exceed current charging balance");
        rhinoChargingBalanceAtCycleStart = newBalance;
    }

    function _addRewardToken(address tokenAddress, uint256 threshold) internal {
        approvedRewardTokens[tokenAddress] = true;
        swapThresholds[tokenAddress] = threshold;
        rewardTokens.push(tokenAddress);

        IERC20(tokenAddress).approve(address(pulseXRouterV1), type(uint256).max);
        IERC20(tokenAddress).approve(address(pulseXRouterV2), type(uint256).max);
        IERC20(tokenAddress).approve(address(nineinchRouter), type(uint256).max);
    }

    function removeRewardToken(address tokenAddress) external onlyOwner {
        require(approvedRewardTokens[tokenAddress], "Token not approved");
        
        approvedRewardTokens[tokenAddress] = false;
        if (failedSwapAmounts[tokenAddress] > 0) {
            failedSwapAmounts[tokenAddress] = 0;
        }

        for (uint i = 0; i < rewardTokens.length; i++) {
            if (rewardTokens[i] == tokenAddress) {
                rewardTokens[i] = rewardTokens[rewardTokens.length - 1];
                rewardTokens.pop();
                break;
            }
        }
    }

    function setSwapThreshold(address tokenAddress, uint256 threshold) external onlyOwner {
        require(approvedRewardTokens[tokenAddress], "Token not approved");
        swapThresholds[tokenAddress] = threshold;
    }

    function setFarmPlsThreshold(uint256 newThreshold) external onlyOwner {
        require(newThreshold <= 100_000_000e18, "Threshold cannot be too high");
        plsFarmThreshold = newThreshold;
    }

    function depositRhino(uint256 amount) public nonReentrant onlyOwner {
        rhinoChargingBalance += amount;
        IERC20(RHINO).safeTransferFrom(msg.sender, address(this), amount);
        if (rhinoChargingBalanceAtCycleStart == 0) {
            rhinoChargingBalanceAtCycleStart = rhinoChargingBalance;
        }
    }

    function withdrawRhino(uint256 amount) public onlyOwner {
        IERC20(RHINO).safeTransfer(msg.sender, amount);
        if (amount <= rhinoChargingBalance) {
            rhinoChargingBalance -= amount;
        } else {
            rhinoChargingBalance = 0;
        }

        if (amount <= rhinoChargingBalanceAtCycleStart) {
            rhinoChargingBalanceAtCycleStart -= amount;
        } else {
            rhinoChargingBalanceAtCycleStart = 0;
        }
    }

    function getBestRouter(
        uint256 amountIn,
        address[] memory path
    ) public view returns (address bestRouter) {
        address[] memory routers = new address[](3);
        routers[0] = address(pulseXRouterV1);
        routers[1] = address(pulseXRouterV2);
        routers[2] = address(nineinchRouter);

        uint256 bestAmountOut = 0;

        for (uint256 i = 0; i < routers.length; i++) {
            try
                IUniswapV2Router02(routers[i]).getAmountsOut(amountIn, path)
            returns (uint256[] memory amountsOut) {
                uint256 amountOut = amountsOut[amountsOut.length - 1];
                if (amountOut > bestAmountOut) {
                    bestAmountOut = amountOut;
                    bestRouter = routers[i];
                }
            } catch {
                continue;
            }
        }
    }
}
          

Compiler Settings

{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"paris"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AutoLiquify","inputs":[{"type":"uint256","name":"amountWPLS","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountRhino","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeeDistributionUpdated","inputs":[{"type":"uint256","name":"rhinoBurnPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"gelatoBurnPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"solidXBurnPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"genesisPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"plsReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"rhinoReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"solidXReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"eHexReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"pHexReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"gelatoReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"plsxReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"incReflectionPercent","internalType":"uint256","indexed":false},{"type":"uint256","name":"liquidityPercent","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeeParametersUpdated","inputs":[{"type":"uint256","name":"baseBuyFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"baseSellFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"minBuyFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"minSellFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"feeDecayTime","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Launched","inputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidityAdded","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amountRhino","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOther","internalType":"uint256","indexed":false},{"type":"uint256","name":"lpAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidityRemoved","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amountRhino","internalType":"uint256","indexed":false},{"type":"uint256","name":"amountOther","internalType":"uint256","indexed":false},{"type":"uint256","name":"lpAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ParameterUpdated","inputs":[],"anonymous":false},{"type":"event","name":"SwapBackFailed","inputs":[{"type":"string","name":"message","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"SwapBackSuccess","inputs":[{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addLiquidity","inputs":[{"type":"address","name":"otherToken","internalType":"address"},{"type":"address","name":"pairAddress","internalType":"contract IUniswapV2Pair"},{"type":"uint256","name":"amountRhino","internalType":"uint256"},{"type":"uint256","name":"amountOtherToken","internalType":"uint256"},{"type":"uint256","name":"amountRhinoMin","internalType":"uint256"},{"type":"uint256","name":"amountOtherTokenMin","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"addLiquidityETH","inputs":[{"type":"uint256","name":"amountRhino","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"uint256","name":"amountRhinoMin","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"address","name":"pairAddress","internalType":"contract IUniswapV2Pair"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addPair","inputs":[{"type":"address","name":"pair","internalType":"address"},{"type":"address","name":"router","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"holder","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"autoLiquidityReceiver","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"baseBuyFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"baseSellFee","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimDividend","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimLpDividend","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract DividendDistributor"}],"name":"distributor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"eHexReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"emergencyMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"emergencyRemoveLiquidity","inputs":[{"type":"address","name":"pairAddress","internalType":"contract IUniswapV2Pair"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feeDecayTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feeDenominator","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"feesOnNormalTransfers","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"gelatoBurnFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"gelatoReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"genesisFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"genesisReceiver","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCirculatingSupply","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getLpTokenValueInRhino","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getPairs","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalFee","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"bool","name":"selling","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"startTime","internalType":"uint256"}],"name":"holderStartTime","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"incReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isDividendExempt","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isFeeExempt","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"isPair","internalType":"bool"}],"name":"isPair","inputs":[{"type":"address","name":"pairAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTxLimitExempt","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"launch","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"launchedAt","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"liquidityFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"lpBalance","internalType":"uint256"}],"name":"lpBalances","inputs":[{"type":"address","name":"pairAddress","internalType":"address"},{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lpReflectionPercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxTxAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minBuyFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minSellFee","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IUniswapV2Router02"}],"name":"nineinchRouter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"pHexReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pairs","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"plsReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"plsxReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IUniswapV2Router02"}],"name":"pulseRouterV1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IUniswapV2Router02"}],"name":"pulseRouterV2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"pulseV2Pair","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeLiquidity","inputs":[{"type":"uint256","name":"lpAmount","internalType":"uint256"},{"type":"address","name":"pairAddress","internalType":"contract IUniswapV2Pair"},{"type":"uint256","name":"amountRhinoMin","internalType":"uint256"},{"type":"uint256","name":"amountOtherTokenMin","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removePair","inputs":[{"type":"address","name":"pair","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rhinoBurnFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract RhinoCharging"}],"name":"rhinoCharging","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rhinoReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"router","internalType":"address"}],"name":"routerByPair","inputs":[{"type":"address","name":"pairAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setChargingContract","inputs":[{"type":"address","name":"chargingContract","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDistributionCriteria","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"minPeriod","internalType":"uint256"},{"type":"uint256","name":"minDistribution","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDistributor","inputs":[{"type":"address","name":"_newDistributor","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDistributorSettings","inputs":[{"type":"uint256","name":"gas","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setEmergencyMode","inputs":[{"type":"bool","name":"mode","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeDistribution","inputs":[{"type":"uint256[13]","name":"feeParams","internalType":"uint256[13]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeParameters","inputs":[{"type":"uint256","name":"_baseBuyFee","internalType":"uint256"},{"type":"uint256","name":"_baseSellFee","internalType":"uint256"},{"type":"uint256","name":"_minBuyFee","internalType":"uint256"},{"type":"uint256","name":"_minSellFee","internalType":"uint256"},{"type":"uint256","name":"_feeDecayTime","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setGenesisReceiver","inputs":[{"type":"address","name":"_newGenesis","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIsDividendExempt","inputs":[{"type":"address","name":"holder","internalType":"address"},{"type":"bool","name":"exempt","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIsFeeExempt","inputs":[{"type":"address","name":"holder","internalType":"address"},{"type":"bool","name":"exempt","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setIsTxLimitExempt","inputs":[{"type":"address","name":"holder","internalType":"address"},{"type":"bool","name":"exempt","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setLiquidityFeeReceiver","inputs":[{"type":"address","name":"_autoLiquidityReceiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setLpRewardsPercent","inputs":[{"type":"uint256","name":"newLpReflectionPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setSwapBackSettings","inputs":[{"type":"bool","name":"_enabled","internalType":"bool"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTxLimit","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"solidXBurnFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"solidXReflectionFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"swapEnabled","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"swapThreshold","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalPlsLpAdded","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalRhinoBurned","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"userRhinoInLp","internalType":"uint256"}],"name":"userLpRhinoValue","inputs":[{"type":"address","name":"pairAddress","internalType":"address"},{"type":"address","name":"users","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"userTotalRhinoInLp","internalType":"uint256"}],"name":"userTotalLpRhinoValue","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"receive","stateMutability":"payable"}]
              

Contract Creation Code

0x61020080604052346106b1576001600055331561069b5760018054336001600160a01b0319821681179092556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a373a1077a294dde1b09bb078844df40758a5d0f9a27608052738da17db850315a34532108f0f5458fc0401525f660a05273616cb6a245ed4c11216ec58d10b6a2e87271845d60c0527357fde0a71132198bbec939b98976993d8d89d22560e052732b591e99afe9f32eaa6214f7b7629768c40eeb39610100527395b303987a60c71504d99aa1b13b4da07b0790ab61012052732fa878ab3f87cc1c9737fc071108f904c0b0c95d6101405261dead610160526000610180526b033b2e3c9fd0803ce80000006003556b033b2e3c9fd0803ce80000006004556224ea00600f556102586010556109c46011556102586012556102586013556101f46014556101f46015556101f46016556101f46017556101f46018556101f46019556103e8601a556103e8601b556103e8601c556102ee601d556102ee601e556105dc601f556103e8602055610d05602155600160ff1960225416176022557398bf93ebf5c380c0e6ae8e192a7e2ae08edacc026101a05273165c3410fc91ef562c50559f7d2289febed552d96101c05273eb45a3c4aedd0f47f345fb4c8a1802bb5740d7256101e052620186a0602955600160ff19602a541617602a5569d3c21bcecceda1000000602b5561ff0019602c5416602c5563c45a015560e01b815260208160048173165c3410fc91ef562c50559f7d2289febed552d95afa90811561064357600091602091839161067e575b506080516040516364e329cb60e11b81526001600160a01b039182166004820152306024820152938492604492849291165af19081156106435760009161064f575b50602480546001600160a01b0319166001600160a01b03929092169190911790556040516138f78082016001600160401b0381118382101761062d578291615ba8833903906000f0801561064357602880546001600160a01b0319166001600160a01b039283161781553060009081526006602090815260408083206101a05186168452909152808220600019908190556101c051851683528183208190556101e051851683528183208190559254841682529020556024546025549116906801000000000000000081101561062d576001810180602555811015610617577f401968ff42a154441da5f6c4c935ac46b8671f0e062baaa62a7545ba53bb6e4c0180546001600160a01b03199081169092179055602480546001600160a01b039081166000908152600860208181526040808420805460ff1990811660019081179092556101c0518854881687526026855283872080548b1691891691909117905533808752600c8086528488208054841685179055600d8087528589208054851686179055995489168852600e808752858920805485168617905530808a52868a2080548616871790559187528589208054851686179055602880548b168a52868a20805486168717905591895299865284882080548416851790558054891688528488208054841685179055548816875297845282862080548216831790556101605187168652828620805482168317905561018051909616855281852080549096161790945560228054610100600160a81b0319169286901b610100600160a81b03169290921790915560238054909516841790945560035483825260058552828220819055915191825291927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a36040516154af90816106f98239608051818181611e6001528181613d22015261460f015260a051818181613c270152614eca015260c051818181613a710152614d4a015260e051818181613b950152614e4a015261010051818181613b030152614dca0152610120518181816139df0152614cca0152610140518181816139300152614c4a01526101605181612be5015261018051818181612c1d0152614ff201526101a051818181610f0701526114ed01526101c0518181816110ac0152818161280f015261463b01526101e05181818161107a01526125a20152f35b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040513d6000823e3d90fd5b610671915060203d602011610677575b61066981836106b6565b8101906106d9565b386102a6565b503d61065f565b6106959150823d84116106775761066981836106b6565b38610264565b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b601f909101601f19168101906001600160401b0382119082101761062d57604052565b908160209103126106b157516001600160a01b03811681036106b1579056fe6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c806301339c2114612f605780630445b66714612f4257806306fdde0314612eff5780630905f56014612ed9578063095ea7b314612e5a5780630d1554bf14612e31578063180b0d7e14612e1457806318160ddd14612df6578063207a75bf14612db55780632172ab2114612d9757806323b2d78014612d7957806323b872dd14612c8357806326041bf614612c5a5780632b112e4914612bbd5780632b21aca6146129705780632f6ec43a14612952578063313ce567146129365780633190bb8a146128da57806337107377146107575780633f4218e01461289b5780634355855a1461285c5780634ce027fe1461283e5780634f15999a146127f95780635762e264146127a45780635abe6711146127815780635c85974f1461274d5780636083dab61461271457806364d1dd1b146126f6578063654dcf87146126d8578063658d4b7f146126865780636ad5ed21146125ef5780636bb4e2e8146125d15780636d2b49401461258c5780636ddd17131461256957806370a0823114612530578063715018a6146124d3578063749debf21461235757806375619ab51461228957806376312a7a14611cb9578063767eb5ef14611c895780637a7bf3ab14611b495780637ae06e58146115975780637dafe91d146115795780638b42507f1461153a5780638c0b5e221461151c5780638d5396e9146114d75780638da5cb5b146114ae5780638e9b4c671461149057806395d89b411461144b57806398118cb41461142d5780639d1944f5146113b45780639e9fd1c91461135f578063a78dec4014611341578063a9059cbb1461130e578063af6c9c1d14611134578063b5716bf714611116578063b6f3e08714610ead578063b7e4cfeb14610e8f578063b91ac78814610e4b578063be32b3f814610dfd578063bf56b37114610ddf578063bfe1092814610db6578063c8fa742214610d8d578063ca33e64c14610d60578063cbaad20a14610d0e578063cd78f2fc14610c66578063d073a01f14610c48578063d22b98a114610c2a578063d3bf520014610c0c578063d726b22114610bee578063d7ddeb4014610bb9578063dcd2914c14610a3a578063dd48afea14610a1c578063dd62ed3e146109c7578063df20fd491461092e578063e0df99e814610911578063e16aed5114610790578063e4ca6de614610757578063e5e31b1314610718578063eb4122d2146106ba578063edf5cdea1461069c578063efbd96c51461067e578063f0fc6bca14610607578063f2fde38b1461057e578063f708a64f146104305763f84ba65d146103d9575061000e565b3461042d57604036600319011261042d5761042a6103f5613137565b6103fd613163565b9061040661361c565b60018060a01b03168352600d602052604083209060ff801983541691151516179055565b80f35b80fd5b503461042d57604036600319011261042d578061044b613137565b610453613163565b9061045c61361c565b6001600160a01b038116913083141580610569575b1561056457828452600e60205261049781604086209060ff801983541691151516179055565b1561050557506028546001600160a01b031690813b15610501578291604483926040519485938492630a5b654b60e11b845260048401528160248401525af180156104f6576104e557505080f35b816104ef916131ef565b61042d5780f35b6040513d84823e3d90fd5b5050fd5b60018060a01b036028541691835260056020526040832054823b1561056457604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529082908290604490829084905af180156104f6576104e557505080f35b505050fd5b506024546001600160a01b0316831415610471565b503461042d57602036600319011261042d57610598613137565b6105a061361c565b6001600160a01b031680156105f357600180546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b631e4fbdf760e01b82526004829052602482fd5b503461042d578060031936011261042d5761062061409f565b60285481906001600160a01b0316803b1561067b57818091602460405180948193630afbf02f60e11b83523360048401525af180156104f657610666575b506001905580f35b81610670916131ef565b61042d57803861065e565b50fd5b503461042d578060031936011261042d576020601854604051908152f35b503461042d578060031936011261042d576020601254604051908152f35b503461042d578060031936011261042d576106d361409f565b60285481906001600160a01b0316803b1561067b578180916024604051809481936336ad696b60e21b83523360048401525af180156104f65761066657506001905580f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03610743613137565b168152600884522054166040519015158152f35b503461042d57602036600319011261042d576020906040906001600160a01b0361077f613137565b168152600983522054604051908152f35b503461042d57602036600319011261042d576107aa613137565b6107b261361c565b6001600160a01b03166107c68115156133bd565b806001600160601b0360a01b60025416176002558152600c60205260408120600160ff1982541617905560018060a01b03600254168152600d60205260408120600160ff198254161790556004602060018060a01b0360025416604051928380926308aaafe160e31b82525afa9081156104f65782916108f2575b506001600160a01b039081168252600c60209081526040808420805460ff1916600117905560025490516308aaafe160e31b81529283916004918391165afa9081156104f65782916108c3575b506001600160a01b03168152600d60205260408120805460ff1916600117905560008051602061543a8339815191528180a180f35b6108e5915060203d6020116108eb575b6108dd81836131ef565b810190613413565b3861088e565b503d6108d3565b61090b915060203d6020116108eb576108dd81836131ef565b38610841565b503461042d578060031936011261042d5760208054604051908152f35b503461042d57604036600319011261042d57610948613172565b6024359061095461361c565b60ff8019602a54169115151617602a5580602b55156109825760008051602061543a8339815191528180a180f35b60405162461bcd60e51b815260206004820152601a60248201527f4d7573742062652067726561746572207468616e207a65726f2e0000000000006044820152606490fd5b503461042d57604036600319011261042d5760406109e3613137565b916109ec61314d565b9260018060a01b031681526006602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d578060031936011261042d576020601454604051908152f35b503461042d5760a036600319011261042d57604435602435600435608435606435610a6361361c565b848310610b7457808410610b2f578115610aea577fc6395d37de1bf9bd21e069920faf9042852460a35699416fed9792866d3b6e309460a094610aaa6109c48611156135d0565b610ab86109c48211156135d0565b8460105580601155816012558260135583600f556040519485526020850152604084015260608301526080820152a180f35b60405162461bcd60e51b815260206004820152601b60248201527f44656361792074696d65206d75737420626520706f73697469766500000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f53656c6c20666565206d757374206265203e3d206d696e696d756d00000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f42757920666565206d757374206265203e3d206d696e696d756d0000000000006044820152606490fd5b503461042d57604036600319011261042d576020610be6610bd8613137565b610be0613163565b9061357e565b604051908152f35b503461042d578060031936011261042d576020601054604051908152f35b503461042d578060031936011261042d576020602d54604051908152f35b503461042d578060031936011261042d576020601654604051908152f35b503461042d578060031936011261042d576020601154604051908152f35b503461042d57606036600319011261042d5780610c81613137565b610c8961361c565b6028546001600160a01b031690813b156105015760405163335e3cbf60e21b81526001600160a01b039091166004820152602480359082015260448035908201529082908290606490829084905af180156104f657610cf9575b508060008051602061543a83398151915291a180f35b81610d03916131ef565b61042d578038610ce3565b503461042d57602036600319011261042d57610d28613137565b610d3061361c565b60018060a01b03166001600160601b0360a01b602354161760235560008051602061543a8339815191528180a180f35b503461042d578060031936011261042d5760225460405160089190911c6001600160a01b03168152602090f35b503461042d578060031936011261042d576002546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576028546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576020602754604051908152f35b503461042d57602036600319011261042d57610e17613172565b610e1f61361c565b61ff00602c5491151560081b169061ff00191617602c5560008051602061543a8339815191528180a180f35b503461042d57602036600319011261042d576004359060255482101561042d576020610e76836131be565b905460405160039290921b1c6001600160a01b03168152f35b503461042d578060031936011261042d576020601a54604051908152f35b503461042d57604036600319011261042d57610ec7613137565b90610ed061314d565b610ed861361c565b6001600160a01b0383168083526008602052604083205490919060ff166110db576001600160a01b03908116907f000000000000000000000000000000000000000000000000000000000000000016811480156110a9575b8015611077575b15611041576025546801000000000000000081101561102d578394610f65826001610f8994016025556131be565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b818352600860205260408320600160ff19825416179055818352602660205260408320906001600160601b0360a01b825416179055808252600e60205260408220600160ff1982541617905560018060a01b036028541690813b15610501578291604483926040519485938492630a5b654b60e11b845260048401528160248401525af180156104f657610cf957508060008051602061543a83398151915291a180f35b634e487b7160e01b84526041600452602484fd5b60405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b2103937baba32b960911b6044820152606490fd5b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168114610f37565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168114610f30565b60405162461bcd60e51b81526020600482015260136024820152722830b4b91030b63932b0b23c9030b23232b21760691b6044820152606490fd5b503461042d578060031936011261042d576020601554604051908152f35b503461042d57602036600319011261042d5761114e613137565b61115661361c565b6001600160a01b03168082526008602052604082205460ff16156112d357602554829081805b82811061129e575b50156112595760001981019081116112455790610f656111a66111be936131be565b905460039190911b1c6001600160a01b0316916131be565b80825260266020526040822080546001600160a01b0319169055602554801561123157600019016111ee816131be565b81546001600160a01b0360039290921b9190911b191690556025558152600860205260408120805460ff1916905560008051602061543a8339815191528180a180f35b634e487b7160e01b83526031600452602483fd5b634e487b7160e01b84526011600452602484fd5b60405162461bcd60e51b815260206004820152601760248201527f50616972206e6f7420666f756e6420696e2061727261790000000000000000006044820152606490fd5b846112a8826131be565b905460039190911b1c6001600160a01b0316146112c75760010161117c565b92506001905038611184565b60405162461bcd60e51b815260206004820152601360248201527214185a5c88191bd95cc81b9bdd08195e1a5cdd606a1b6044820152606490fd5b503461042d57604036600319011261042d57602061133761132d613137565b60243590336136fa565b6040519015158152f35b503461042d578060031936011261042d576020601c54604051908152f35b503461042d57604036600319011261042d57604061137b613137565b9161138461314d565b9260018060a01b03168152600a602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d57602036600319011261042d57620f42406004356113d561361c565b80602955116113f35760008051602061543a8339815191528180a180f35b60405162461bcd60e51b815260206004820152601260248201527104d61782067617320697320313030303030360741b6044820152606490fd5b503461042d578060031936011261042d576020601f54604051908152f35b503461042d578060031936011261042d575061148c60405161146e6040826131ef565b60058152645248494e4f60d81b60208201526040519182918261310b565b0390f35b503461042d578060031936011261042d576020601d54604051908152f35b503461042d578060031936011261042d576001546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042d578060031936011261042d576020600454604051908152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03611565613137565b168152600d84522054166040519015158152f35b503461042d578060031936011261042d576020601754604051908152f35b503461042d5760e036600319011261042d57806115b2613137565b6115ba61314d565b606435916044356115c961409f565b6201000062ff000019602c541617602c556115e5811515613233565b6001600160a01b03831660008181526008602052604090209092906116159060019060ff905b5416151514613276565b6001600160a01b0383166000908152600a60209081526040808320338452909152902054611ac6575b6116498230336136fa565b506040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b039190911693906020816064818a895af18015611a9e57611aa9575b506116e26020866116b08660018060a01b03166000526026602052604060002090565b5460405163095ea7b360e01b81526001600160a01b039091166004820152602481019190915291829081906044820190565b03818a895af18015611a9e57611a81575b506116fc6132bb565b9086606061171c8660018060a01b03166000526026602052604060002090565b60018060a01b03905416610104604051809b819362e8e33760e81b83523060048401528b60248401528960448401528c6064840152608435608484015260a43560a48401523060c484015260c43560e48401525af1958615611a7457819782998398611a39575b50604085019889526117b0606086019a808c526117aa6117a161402e565b84831115613645565b82613488565b611992575b50505061183b608091611888946117d289516117aa6117a161402e565b611961575b506001600160a01b03861660009081526007602090815260408083203384529091529020546118079088906140c1565b6001600160a01b038716600090815260076020526040902060018060a01b033316600052602052604060002055339061412e565b910190815233600090815260096020526040902054815161185b916140c1565b33600090815260096020526040902055516001600160a01b039092166000908152600a6020526040902090565b33600090815260209182526040808220939093556028546009909252919091205485916001600160a01b0316803b1561195d5760405163d9a91c1d60e01b8152336004820152602481019290925282908290604490829084905af1611940575b50509051915160408051938452602084019190915282015233907f64b83944e79c3ce8d4c297411de637c3e102d064677aac0c163976ebdcd6f50e9080606081015b0390a262ff000019602c5416602c556001815580f35b9061194a916131ef565b836000126119595783386118e8565b8380fd5b8280fd5b61198461198b918a519061197f61197661402e565b82841115613645565b613488565b33306136fa565b50386117d7565b6020916119ad6119d7928c969596519061197f61197661402e565b60405163a9059cbb60e01b8152336004820152602481019190915294859283919082906044820190565b03925af1908115611a2e576118889460809361183b936119ff575b50948193508a92506117b5565b611a209060203d602011611a27575b611a1881836131ef565b8101906133fb565b50386119f2565b503d611a0e565b6040513d8b823e3d90fd5b9198509650611a6191985060603d606011611a6d575b611a5981836131ef565b810190613303565b98919790989638611783565b503d611a4f565b50604051903d90823e3d90fd5b611a999060203d602011611a2757611a1881836131ef565b6116f3565b6040513d89823e3d90fd5b611ac19060203d602011611a2757611a1881836131ef565b61168d565b336000908152600960205260409020611b1090546001600160a01b0385166000908152600a602052604090205b33600090815260209190915260409020549061197f61197661402e565b336000818152600960209081526040808320949094556001600160a01b0387168252600a8152838220928252919091522086905561163e565b503461042d57602036600319011261042d57611b63613137565b611b6b61409f565b602c80546201000062ff00001990911617908190556001600160a01b039190911680835260076020908152604084203360005290529060081c60ff1615611c445780825260076020818152604080852033600081815291845282822054868852948452828720818352845290829020869055905163a9059cbb60e01b81526004810191909152602481019290925290918290604490829086905af180156104f657611c25575b5062ff000019602c5416602c556001815580f35b611c3d9060203d602011611a2757611a1881836131ef565b5038611c11565b60405162461bcd60e51b815260206004820152601d60248201527f456d657267656e6379206d6f6465206973206e6f7420656e61626c65640000006044820152606490fd5b503461042d578060031936011261042d5761148c611ca5613432565b604051918291602083526020830190613181565b503461042d5760a036600319011261042d57600435611cd661314d565b611cde61409f565b6201000062ff000019602c541617602c558115612250576001600160a01b0381166000818152600760209081526040808320338452909152902054909290811161220b578391611d466020836116b08760018060a01b03166000526026602052604060002090565b038187895af18015612200576121e3575b50611d606132bb565b93604051630dfe168160e01b8152602081600481855afa90811561216f5785916121c4575b506001600160a01b0316300361217a5760405163d21220a760e01b8152602081600481855afa90811561216f578591612150575b50945b846040611ddb8460018060a01b03166000526026602052604060002090565b60018060a01b039054169760e4825180998193635d5155ef60e11b835230600484015260018060a01b03169b8c60248401528a6044840152604435606484015260643560848401523060a484015260843560c48401525af1968715611a745781968298612113575b50611e5d604084019780895260608501998a5233306136fa565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169080820361209f57508751813b1561195d578291602483926040519485938492632e1a7d4d60e01b845260048401525af180156104f657612082575b5092611f7f611f9692611eec868080611fce998d5133617530f1611ee661331e565b5061335e565b6001600160a01b0385166000908152600760209081526040808320338452909152902054611f2190889061197f61197661402e565b6001600160a01b03861660008181526007602090815260408083203380855290835281842095909555928252600a8152828220848352815282822054608095909501948552928152600990925290205490519061197f61197661402e565b33600090815260096020526040902055339061412e565b6001600160a01b03919091166000908152600a60209081526040808320338452808352818420859055600983529220549190526140c1565b3360009081526009602052604090208190556028548591906001600160a01b0316803b1561195d5760405163d9a91c1d60e01b8152336004820152602481019290925282908290604490829084905af1612069575b50509051915160408051938452602084019190915282015233907f1dc8bb69df2b8e91fbdcbfcf93d951b3f0000f085a95fe3f7946d6161439245d90806060810161192a565b90612073916131ef565b83600012611959578338612023565b9061208c916131ef565b8660001261209b578638611ec4565b8680fd5b885160405163a9059cbb60e01b8152336004820152602481019190915293959260209250849160449183915af1801561210857611fce94611f9693611f7f926120e9575b50611eec565b6121019060203d602011611a2757611a1881836131ef565b50386120e3565b6040513d8a823e3d90fd5b96509650506040853d604011612148575b81612131604093836131ef565b8101031261209b5786602086519601519638611e43565b3d9150612124565b612169915060203d6020116108eb576108dd81836131ef565b38611db9565b6040513d87823e3d90fd5b604051630dfe168160e01b8152602081600481855afa90811561216f5785916121a5575b5094611dbc565b6121be915060203d6020116108eb576108dd81836131ef565b3861219e565b6121dd915060203d6020116108eb576108dd81836131ef565b38611d85565b6121fb9060203d602011611a2757611a1881836131ef565b611d57565b6040513d86823e3d90fd5b60405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e74204c502062616c616e63650000000000000000006044820152606490fd5b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a5908131408185b5bdd5b9d607a1b6044820152606490fd5b503461042d57602036600319011261042d576122a3613137565b6122ab61361c565b6001600160a01b03166122bf8115156133bd565b602880546001600160a01b03191682178155908252600c60209081526040808420805460ff19908116600190811790925584546001600160a01b039081168752600d85528387208054831684179055855481168752600e855283872080549092169092179055308552600683528185209354166000908152929091529020600019905560008051602061543a8339815191528180a180f35b503461042d576101a036600319011261042d57366101a41161042d5761237b61361c565b80805b600d8210156123a35761239b6001918360051b60040135906140c1565b91019061237e565b612710915003612482577f234e092b0bae58003e83192221fcad7cca23f970cea48f52e9f51b5dc9dee4526101a060043580601c5560243580601e5560443580601d55606435806020556084358060145560a4358060155560c4358060165560e4359081601755610104359283601855610124359485601955610144359687601a55610164359889601b55610184359a8b601f556040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140840152610160830152610180820152a180f35b60405162461bcd60e51b8152602060048201526024808201527f546f74616c2066656573206d75737420657175616c2066656544656e6f6d696e60448201526330ba37b960e11b6064820152608490fd5b503461042d578060031936011261042d576124ec61361c565b600180546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b503461042d57602036600319011261042d576020906040906001600160a01b03612558613137565b168152600583522054604051908152f35b503461042d578060031936011261042d57602060ff602a54166040519015158152f35b503461042d578060031936011261042d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042d578060031936011261042d576020602154604051908152f35b503461042d57602036600319011261042d5761138860043561260f61361c565b806021551161261b5780f35b60405162461bcd60e51b815260206004820152603860248201527f4e6f206d6f7265207468616e2035302070657263656e742063616e206265206460448201527f6972656374656420746f204c502070726f7669646572732e00000000000000006064820152608490fd5b503461042d57604036600319011261042d5761042a6126a3613137565b6126ab613163565b906126b461361c565b60018060a01b03168352600c602052604083209060ff801983541691151516179055565b503461042d578060031936011261042d576020601954604051908152f35b503461042d578060031936011261042d576020601b54604051908152f35b503461042d57602036600319011261042d576020906040906001600160a01b0361273c613137565b168152600b83522054604051908152f35b503461042d57602036600319011261042d5760043561276a61361c565b6107d060035404811061277d5760045580f35b5080fd5b503461042d578060031936011261042d57602060ff602254166040519015158152f35b503461042d57604036600319011261042d5760406127c0613137565b916127c961314d565b9260018060a01b031681526007602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d578060031936011261042d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042d578060031936011261042d576020601e54604051908152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03612887613137565b168152600e84522054166040519015158152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b036128c6613137565b168152600c84522054166040519015158152f35b503461042d57602036600319011261042d576128f4613137565b6128fc61361c565b60228054610100600160a81b03191660089290921b610100600160a81b031691909117905560008051602061543a8339815191528180a180f35b503461042d578060031936011261042d57602060405160128152f35b503461042d578060031936011261042d576020602e54604051908152f35b5060a036600319011261042d576004356084356001600160a01b038116908181036119595760c4916129a061409f565b6201000062ff000019602c541617602c556129bc841515613233565b6129df600160ff61160b8460018060a01b03166000526008602052604060002090565b6001600160a01b0381166000908152600a60209081526040808320338452909152902054612b53575b612a138430336136fa565b50612a1c6132bb565b6001600160a01b038216600090815260266020526040902060609060018060a01b039054166040519586809263f305d71960e01b82523060048301528960248301526044356044830152602435606483015230608483015260643560a483015234905af1928315612b4857612acf95879588908996612b10575b50839260809261183b9260406118889701998a528b606087019b828d938452612ac9612ac061402e565b34831115613645565b34613488565b612ae6575b50506117d289516117aa6117a161402e565b808080612afc612b099551612ac9612ac061402e565b33617530f1611ee661331e565b898b612ad4565b60809297506118889493965061183b9150612b399060603d606011611a6d57611a5981836131ef565b97919890925092509293612a96565b6040513d88823e3d90fd5b336000908152600960205260409020612b8490546001600160a01b0383166000908152600a60205260409020611af3565b336000818152600960209081526040808320949094556001600160a01b0385168252600a81528382209282529190915220859055612a08565b503461042d578060031936011261042d57610be66020916040612c1b60035460018060a01b037f000000000000000000000000000000000000000000000000000000000000000016845260058652828420549061197f61197661402e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168352600585529120549061197f61197661402e565b503461042d578060031936011261042d576023546040516001600160a01b039091168152602090f35b503461042d57606036600319011261042d57611337602091612ca3613137565b612cab61314d565b9060443592612ccc8260018060a01b03166000526006602052604060002090565b33600090815290875260409020548490600101612ceb575b50506136fa565b6001600160a01b038316600090815260066020526040808220338352895290819020548151612d5194919261197f9290612d2590836131ef565b6016825275496e73756666696369656e7420416c6c6f77616e636560501b8b8301525082841115613645565b6001600160a01b03821660009081526006602052604080822033835288529020553883612ce4565b503461042d578060031936011261042d576020601354604051908152f35b503461042d578060031936011261042d576020600f54604051908152f35b503461042d57602036600319011261042d576020906001600160a01b03612dda613137565b16815260268252604060018060a01b0391205416604051908152f35b503461042d578060031936011261042d576020600354604051908152f35b503461042d578060031936011261042d5760206040516127108152f35b503461042d578060031936011261042d576024546040516001600160a01b039091168152602090f35b503461042d57604036600319011261042d57612e74613137565b60406024359233815260066020522060018060a01b0382166000526020528160406000205560405191825260018060a01b0316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b503461042d578060031936011261042d57602060ff602c5460081c166040519015158152f35b503461042d578060031936011261042d575061148c604051612f226040826131ef565b60078152665268696e6f466960c81b60208201526040519182918261310b565b503461042d578060031936011261042d576020602b54604051908152f35b503461042d578060031936011261042d57612f7961361c565b60275461309f57426027556028546002546001600160a01b039081168084526005602052604084205491909216918391833b1561195d57604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529181908390604490829084905af161308f575b6028546001546001600160a01b0390811680845260056020526040842054939450911691823b1561056457604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529082908290604490829084905af161307a575b507f87dcd6626ffde0faf682a10e7b64aff36ea73a5470d5fa6cc7ebd372e4b1900160408051438152426020820152a180f35b81613084916131ef565b61042d578038613047565b613098916131ef565b3881612fe6565b60405162461bcd60e51b815260206004820152601160248201527020b63932b0b23c903630bab731b432b21760791b6044820152606490fd5b60009103126130e357565b600080fd5b60005b8381106130fb5750506000910152565b81810151838201526020016130eb565b6040916020825261312b81518092816020860152602086860191016130e8565b601f01601f1916010190565b600435906001600160a01b03821682036130e357565b602435906001600160a01b03821682036130e357565b6024359081151582036130e357565b6004359081151582036130e357565b906020808351928381520192019060005b81811061319f5750505090565b82516001600160a01b0316845260209384019390920191600101613192565b6025548110156131d957602560005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761321157604052565b634e487b7160e01b600052604160045260246000fd5b6040513d6000823e3d90fd5b1561323a57565b60405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a5908149a1a5b9bc8185b5bdd5b9d60621b6044820152606490fd5b1561327d57565b60405162461bcd60e51b81526020600482015260166024820152752830b4b91034b9903737ba1039bab83837b93a32b21760511b6044820152606490fd5b6040519060e0820182811067ffffffffffffffff82111761321157604052600060c0838281528260208201528260408201528260608201528260808201528260a08201520152565b908160609103126130e3578051916040602083015192015190565b3d15613359573d9067ffffffffffffffff8211613211576040519161334d601f8201601f1916602001846131ef565b82523d6000602084013e565b606090565b1561336557565b60405162461bcd60e51b815260206004820152601060248201526f2826299039b2b732103330b4b632b21760811b6044820152606490fd5b81156133a7570490565b634e487b7160e01b600052601260045260246000fd5b156133c457565b60405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606490fd5b908160209103126130e3575180151581036130e35790565b908160209103126130e357516001600160a01b03811681036130e35790565b604051906025548083528260208101602560005260206000209260005b818110613466575050613464925003836131ef565b565b84546001600160a01b031683526001948501948794506020909301920161344f565b9190820391821161349557565b634e487b7160e01b600052601160045260246000fd5b6010546012546001600160a01b039092166000908152600b602052604090205442811015613544576134dd9042613488565b600f54908181101561353e579061351761352f9261351261353b9661350c61350361402e565b88831115613645565b86613488565b6142c3565b61352a61352261433f565b831515613645565b61339d565b9061197f61197661402e565b90565b50505090565b5060006134dd565b6011546013546001600160a01b039092166000908152600b602052604090205442811015613544576134dd9042613488565b9080156135c757601154905b156135be57601354915b6001600160a01b03166000908152600b602052604090205442811015613544576134dd9042613488565b60125491613594565b6010549061358a565b156135d757565b60405162461bcd60e51b815260206004820152601760248201527f32352050657263656e74206d617820616c6c6f7765642e0000000000000000006044820152606490fd5b6001546001600160a01b0316330361363057565b63118cdaa760e01b6000523360045260246000fd5b1561364d5750565b60405162461bcd60e51b8152908190613669906004830161310b565b0390fd5b61197f61353b9382841115613645565b1561368457565b606460405162461bcd60e51b815260206004820152602060248201527f54686520636f6e7472616374206973206e6f74206c61756e63686564207965746044820152fd5b604051906136d76040836131ef565b6014825273496e73756666696369656e742042616c616e636560601b6020830152565b9190602754158080159061400b575b6137129061367d565b600090602c546137228160ff1690565b918215614003575b508115613ff5575b50613feb5761374283838661441b565b61374b846144b8565b80613f86575b80613f1c575b15613ebb5750613765614525565b6001915b6001600160a01b038416600090815260056020526040902061379590548261378f6136c8565b9161366d565b6001600160a01b0385166000908152600560205260409020556001600160a01b0382166000908152600b602052604090205415613e9b575b6137d78285615259565b15613e95576137e790828561532e565b915b6001600160a01b038216600090815260056020526040902061380d908490546140c1565b6001600160a01b0383166000908152600560205260409020556001600160a01b0384166000908152600e602052604090206138529061384e905b5460ff1690565b1590565b613e0d575b6001600160a01b0382166000908152600e6020526040902061387c9061384e90613847565b613d85575b156138b9575b6040519182526001600160a01b0390811692169060008051602061545a8339815191529080602081015b0390a3600190565b601454613cf3575b601554613c8a575b601654613bf8575b601754613b66575b601854613ad4575b601954613a42575b601a546139b0575b601b541561388757602854613916906001600160a01b03165b6001600160a01b031690565b60295491813b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166004820152602481019390935260008051602061545a833981519152926138b1926000908290604490829084905af1613995575b50915050613887565b806139a460006139aa936131ef565b806130d8565b3861398c565b6028546139c5906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613a2d575b506138f1565b806139a46000613a3c936131ef565b38613a27565b602854613a57906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613abf575b506138e9565b806139a46000613ace936131ef565b38613ab9565b602854613ae9906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613b51575b506138e1565b806139a46000613b60936131ef565b38613b4b565b602854613b7b906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613be3575b506138d9565b806139a46000613bf2936131ef565b38613bdd565b602854613c0d906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613c75575b506138d1565b806139a46000613c84936131ef565b38613c6f565b602854613c9f906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b815230600482015260248101929092526000908290604490829084905af1613cde575b506138c9565b806139a46000613ced936131ef565b38613cd8565b602854613d08906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101929092526000908290604490829084905af1613d70575b506138c1565b806139a46000613d7f936131ef565b38613d6a565b602854613d9a906001600160a01b031661390a565b6001600160a01b03831660009081526005602052604090205490803b156130e357604051630a5b654b60e11b81526001600160a01b038516600482015260248101929092526000908290604490829084905af1613df8575b50613881565b806139a46000613e07936131ef565b38613df2565b602854613e22906001600160a01b031661390a565b6001600160a01b03851660009081526005602052604090205490803b156130e357604051630a5b654b60e11b81526001600160a01b038716600482015260248101929092526000908290604490829084905af1613e80575b50613857565b806139a46000613e8f936131ef565b38613e7a565b916137e9565b6001600160a01b0382166000908152600b602052604090204290556137cd565b600254909290613ed3906001600160a01b031661390a565b803b156130e35760405163f8e5884b60e01b8152426004820152906000908290602490829084905af1613f07575b50613769565b806139a46000613f16936131ef565b38613f01565b50602854600490602090613f38906001600160a01b031661390a565b6040516312c1083d60e01b815292839182905afa908115613f8157600091613f62575b5015613757565b613f7b915060203d602011611a2757611a1881836131ef565b38613f5b565b613227565b50600254600490602090613fa2906001600160a01b031661390a565b604051631732cded60e01b815292839182905afa908115613f8157600091613fcc575b5015613751565b613fe5915060203d602011611a2757611a1881836131ef565b38613fc5565b5061353b9261439b565b60081c60ff16905038613732565b91503861372a565b5060015461371290614025906001600160a01b031661390a565b32149050613709565b6040519061403d6040836131ef565b601e82527f536166654d6174683a207375627472616374696f6e206f766572666c6f7700006020830152565b61407f61407461402e565b612710831115613645565b6127100361271081116134955790565b9061353b9161197f61197661402e565b6002600054146140b0576002600055565b633ee5aeb560e01b60005260046000fd5b908101908181116134955781106140d55790565b60405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606490fd5b51906001600160701b03821682036130e357565b604051630240bc6b60e21b8152916001600160a01b039190911690606083600481855afa908115613227576000938492614266575b50604051630dfe168160e01b815290602082600481875afa918215613227576004956020946141d7946001600160701b0393600091614249575b506001600160a01b03163003614240575016905b60008581526007855260408082206001600160a01b0390931682529185522054906142c3565b91604051938480926318160ddd60e01b82525afa9081156132275760009161420a575b61353b925061352a61352261433f565b90506020823d602011614238575b81614225602093836131ef565b810103126130e35761353b9151906141fa565b3d9150614218565b905016906141b1565b6142609150873d89116108eb576108dd81836131ef565b3861419d565b939091506060843d6060116142bb575b81614283606093836131ef565b8101031261042d576142948461411a565b9060406142a36020870161411a565b95015163ffffffff81160361042d5750929038614163565b3d9150614276565b90811561433857808202918083048203613495576142e1908361339d565b036142e95790565b60405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b6064820152608490fd5b5050600090565b6040519061434e6040836131ef565b601a82527f536166654d6174683a206469766973696f6e206279207a65726f0000000000006020830152565b6127109061438661433f565b500490565b9061353b9161352a61352261433f565b602060008051602061545a8339815191529160018060a01b03169283600052600582526143d38560406000205461197f6119766136c8565b846000526005835260406000205560018060a01b0316938460005260058252614401816040600020546140c1565b8560005260058352604060002055604051908152a3600190565b916004541091821592614495575b508115614472575b501561443957565b60405162461bcd60e51b8152602060048201526011602482015270151608131a5b5a5d08115e18d959591959607a1b6044820152606490fd5b6001600160a01b03166000908152600d602052604090205460ff16905038614431565b6001600160a01b03166000908152600d602052604090205460ff16915038614429565b6024546001600160a01b039182169116141580614518575b80614508575b806144fc575b806144e45790565b50306000526005602052604060002054602b54111590565b5060ff602a54166144dc565b5060ff602c5460101c16156144d6565b5060ff602c5416156144d0565b602c805462ff00ff19166201000117905561453e61508d565b602b5461458f61457f614567614556601554856142c3565b61455e61433f565b50612710900490565b92610120850193845261458461457f601c54836142c3565b61437a565b8552602054906142c3565b60608301908152815180615034575b50825180614fec575b50805180614fcc575b506145ce6145c66145d693602b5490519061408f565b84519061408f565b90519061408f565b90608081019182524760a082019081526145ee615122565b61460a306145fb83615144565b6001600160a01b039091169052565b6146377f00000000000000000000000000000000000000000000000000000000000000006145fb83615151565b83517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b156130e357614693926000928360405180968195829463791ac94760e01b84524291309160048601615175565b03925af19081614fb7575b506148065750505060016146b06151af565b6308c379a014614763575b6146dd575b6146d162ff000019602c5416602c55565b60ff19602c5416602c55565b7fc41a20ad8c23d3903584975786330c6ec73ccfcc657629f10237b792268b0e026040518061475b8160809060208152603460208201527f537761704261636b206661696c656420776974686f757420616e206572726f72604082015273040dacae6e6c2ceca40cce4deda40a0ead8e6cab60631b60608201520190565b0390a16146c0565b61476b6151cd565b80614777575b506146bb565b90507fc41a20ad8c23d3903584975786330c6ec73ccfcc657629f10237b792268b0e026147fd6147e36147f16000946040519283916147dd60208401601b907f537761704261636b206661696c65642077697468206572726f7220000000000081520190565b90615242565b03601f1981018352826131ef565b6040519182918261310b565b0390a138614771565b518190614813904761408f565b60c0820190808252601f54614827916142c3565b6015549061483482614069565b91601c5492836148439161408f565b9160205492836148529161408f565b61485b9161438b565b60e086019081528451601454614870916142c3565b838561487b85614069565b906148859161408f565b9061488f9161408f565b6148989161438b565b610100870190815285516016546148ae916142c3565b84866148b986614069565b906148c39161408f565b906148cd9161408f565b6148d69161438b565b90610140880191825286516017546148ed916142c3565b85876148f887614069565b906149029161408f565b9061490c9161408f565b6149159161438b565b926101608901938452875160185461492c916142c3565b868861493788614069565b906149419161408f565b9061494b9161408f565b6149549161438b565b946101808a01958652885160195461496b916142c3565b878961497684614069565b906149809161408f565b9061498a9161408f565b6149939161438b565b966101a08b019788528951601a546149aa916142c3565b818a6149b585614069565b906149bf9161408f565b906149c99161408f565b6149d29161438b565b986101c08c01998a528a51601b546149e9916142c3565b82826149f486614069565b906149fe9161408f565b90614a089161408f565b614a119161438b565b9a6101e08d019b8c528051601d54614a28916142c3565b8383614a3387614069565b90614a3d9161408f565b90614a479161408f565b614a509161438b565b9c6020019c8d5251601e54614a64916142c3565b92614a6e90614069565b90614a789161408f565b90614a829161408f565b614a8b9161438b565b99604001998a52805180614f6c575b50505180614f1e575b505180614e9e575b505180614e1e575b505180614d9e575b505180614d1e575b505180614c9e575b505180614c1e575b505180614bd0575b505180614b51575b5061475b7fb39214ef4f33ea2d9d329fb67a4f17e7588bf6e00ed15a7967137ba819697a279147614b20575b516040519081529081906020820190565b6000808080614b3c61390a61390a60015460018060a01b031690565b4790617530f150614b4b61331e565b50614b0f565b602854909190614b69906001600160a01b031661390a565b90813b156130e3577fb39214ef4f33ea2d9d329fb67a4f17e7588bf6e00ed15a7967137ba819697a2792600061475b93600460405180948193638ed7ae7360e01b83525af1614bbb575b509150614ae3565b806139a46000614bca936131ef565b38614bb3565b602854614be5906001600160a01b031661390a565b803b156130e35760009060046040518094819363ef7c3a3960e01b83525af115614adb57806139a46000614c18936131ef565b38614adb565b602854614c33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614ad357806139a46000614c98936131ef565b38614ad3565b602854614cb3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614acb57806139a46000614d18936131ef565b38614acb565b602854614d33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614ac357806139a46000614d98936131ef565b38614ac3565b602854614db3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614abb57806139a46000614e18936131ef565b38614abb565b602854614e33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614ab357806139a46000614e98936131ef565b38614ab3565b602854614eb3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015291600091839160249183915af115614aab57806139a46000614f18936131ef565b38614aab565b602854614f33906001600160a01b031661390a565b803b156130e3576000906004604051809481936322366e5b60e01b83525af115614aa357806139a46000614f66936131ef565b38614aa3565b614fb0916000808080614fab95614f9461390a61390a60225460018060a01b039060081c1690565b5af150614f9f61331e565b50602e549051906140c1565b602e55565b3880614a9a565b806139a46000614fc6936131ef565b3861469e565b602354614fe391906001600160a01b0316306136fa565b506145ce6145b0565b615017907f0000000000000000000000000000000000000000000000000000000000000000306136fa565b5061502e615029602d548551906140c1565b602d55565b386145a7565b602854615049906001600160a01b031661390a565b803b156130e35760405163add157b760e01b815260048101929092526000908290602490829084905af11561459e57806139a46000615087936131ef565b3861459e565b60405190610240820182811067ffffffffffffffff821117613211576040526000610220838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e0820152826102008201520152565b6040516060919061513383826131ef565b6002815291601f1901366020840137565b8051156131d95760200190565b8051600110156131d95760400190565b80518210156131d95760209160051b010190565b9060809261519b919695949683526000602084015260a0604084015260a0830190613181565b6001600160a01b0390951660608201520152565b60009060033d116151bc57565b905060046000803e60005160e01c90565b600060443d1061353b576040513d600319016004823e8051913d602484011167ffffffffffffffff84111761523c578282019283519167ffffffffffffffff8311615234573d84016003190185840160200111615234575061353b929101602001906131ef565b949350505050565b92915050565b90615255602092828151948592016130e8565b0190565b6001600160a01b03166000818152600c602052604090205490919060ff16801561530c575b8015615302575b61433857615291613432565b9060005b82518110156152f5576001600160a01b036152b08285615161565b5116841480156152d0575b6152c757600101615295565b50505050600190565b506001600160a01b036152e38285615161565b51166001600160a01b038316146152bb565b5050505060ff6022541690565b5060275415615285565b506001600160a01b0381166000908152600c602052604090205460ff1661527e565b909161353b9261533d816153e9565b156153b857506127106153586153528461354c565b836142c3565b61536061433f565b50049130600052600560205261537b836040600020546140c1565b30600052600560205260406000205560405183815260008051602061545a8339815191526020309360018060a01b031692a361197f61197661402e565b6153c1836153e9565b156153da576153586153d5612710926134ab565b615352565b50612710615358601254615352565b6153f1613432565b9060005b8251811015615431576001600160a01b036154108285615161565b51166001600160a01b03831614615429576001016153f5565b505050600190565b50505060009056fe3e1799d428897e6f54bdb61036ad40e2aa67a45b0181c60fe2f15a9d33a084d6ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212200781aa9a34b72a12eadf9f859af885e76426ae460d6b3d4921e55088389fc11864736f6c634300081a003361020080604052346104de576ec097ce7bc90715b34b9f1000000000600d55738da17db850315a34532108f0f5458fc0401525f66080527357fde0a71132198bbec939b98976993d8d89d22560a052732b591e99afe9f32eaa6214f7b7629768c40eeb3960c05273616cb6a245ed4c11216ec58d10b6a2e87271845d60e0527395b303987a60c71504d99aa1b13b4da07b0790ab61010052732fa878ab3f87cc1c9737fc071108f904c0b0c95d61012081905273a1077a294dde1b09bb078844df40758a5d0f9a2761014052600061016081905261dead610180527398bf93ebf5c380c0e6ae8e192a7e2ae08edacc026101a05273165c3410fc91ef562c50559f7d2289febed552d96101c05273eb45a3c4aedd0f47f345fb4c8a1802bb5740d7256101e05280546001600160a01b031916339081178255600b602052610e107fb45a150941e9acc0e949c765cd6fc535c584751adc18b4520e041b01982b3a7e81905569065a4da25d3016c000007fb45a150941e9acc0e949c765cd6fc535c584751adc18b4520e041b01982b3a7f819055918352604083206004810182905569021e19e0c9bab24000006005919091018190557f4ee405faac7ed4f3475527cbf6d8d73fe9dbfed50251f4bc775cb5c6a092e0bf82905567016345785d8a00007f4ee405faac7ed4f3475527cbf6d8d73fe9dbfed50251f4bc775cb5c6a092e0c0557fab7b31ebad36ae325b34bf886113ee0fcf19ff10dc0bbf0479f4450017c4c846829055640ba43b74007fab7b31ebad36ae325b34bf886113ee0fcf19ff10dc0bbf0479f4450017c4c847557f11eba9f7ca2d7611a87ec5c3ac64a1867ae2955c2f532dfc10ac00f18db512c88290556402540be4007f11eba9f7ca2d7611a87ec5c3ac64a1867ae2955c2f532dfc10ac00f18db512c9557f3219a9a4532885634fabe93b2e08351760135f1319d680e8dfa9e7f7920fb2af8290557f3219a9a4532885634fabe93b2e08351760135f1319d680e8dfa9e7f7920fb2b0557ff5ab9e87c3a56854f35c48db4ecf2541feeb5ff331693de27d8091fc848c1b0f8190557ff5ab9e87c3a56854f35c48db4ecf2541feeb5ff331693de27d8091fc848c1b10919091559190527f26af7d2ac2e3630596c3a7497cd51c525e0a762263838ab856883b3e79c8beaa55670de0b6b3a76400007f26af7d2ac2e3630596c3a7497cd51c525e0a762263838ab856883b3e79c8beab5561341390816104e482396080518181816106f501528181610a6101528181610c7401528181611efc015261210b015260a051818181610a9501528181610ca901528181611f300152612140015260c051818181610ac901528181610cde01528181611f640152612175015260e051818181610afd01528181610d13015281816115b301528181611f9801526121aa015261010051818181610b3101528181610d4901528181611fcc01526121e0015261012051818181610b6501528181610d7f0152818161200001526122160152610140518181816102c40152818161075801528181610a2d01528181610c3e0152818161161001528181611d0301528181611d8601528181611ec8015281816120d501528181613021015261332c01526101605181505061018051816116e701526101a0518181816117f60152612a7201526101c051818181611ab90152612aa601526101e05181818161198b0152612ada0152f35b600080fdfe6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c80630c7993c71461262e57806311ce023d1461261057806312c1083d146125ed57806314b6ca961461207957806315f7e05e14611e6d57806322366e5b14611c4457806328c848e314611c265780633a98ef3914611c0857806342fa818714611bdb57806343ba7c9714611b5d5780634688d33914611b155780634963359d14611ae85780634f15999a14611aa357806368306e4314611a255780636a9a7ffe146119ba5780636d2b49401461197557806376fbfa8c1461192a57806379fe73d8146118df5780637df44647146118705780638377323f146118255780638d5396e9146117e05780638ed7ae731461158157806397a54fd414611563578063add157b71461129e578063b1ae2ada14611280578063cd78f2fc14611225578063cdcf70ec146111c4578063d9a91c1d14610be2578063dab5a5ac146109d2578063e48eb309146109ab578063e67b5593146108fb578063ef7c3a39146106c3578063f5dcb34514610693578063f669d82b14610249578063f9d0b779146101f45763fc7a009a146101b3575061000e565b346101f15760403660031901126101f1576101cc612656565b81546001600160a01b031633036101ed576101ea9060243590612c1f565b80f35b5080fd5b80fd5b50346101f15760403660031901126101f1576040610210612656565b9161021961266c565b9260018060a01b031681526008602052209060018060a01b03166000526020526020604060002054604051908152f35b5060203660031901126101f15761025e612656565b81546001600160a01b031633036101ed576040516370a0823160e01b81523060048201526001600160a01b039190911690602081602481855afa908115610688578391610656575b50826040516102b66060826126e3565b6002815260403660208301377f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166102f582612752565b52836103008261275f565b526001600160a01b036103138234612a50565b16803b1561064757610340918391604051808095819463b6f9de9560e01b83524290309060048501612968565b039134905af1801561064b57610632575b50506040516370a0823160e01b815230600482015290602082602481865afa80156106275784906105f3575b61039b925061039661038d61316f565b8284111561337a565b612a2d565b8254604051630c0586bf60e11b8152906001600160a01b0316602082600481845afa9182156105b35785926105be575b50602060049160405192838092630d769c5d60e31b82525afa9081156105b357859161057c575b50600654151580610573575b15610512579061043d6104ae926104ff9461042361041a61316f565b8584111561337a565b6104996104428561043d6104378683612a2d565b85612e03565b612e78565b888a52600b60205261045b81600160408d2001546131b7565b898b52600b602052600160408c200155888a52600b60205261049361048a600360408d20015492600d54612e03565b600c5490612e78565b906131b7565b878952600b602052600360408a200155612e03565b82845260056020526104c78160016040872001546131b7565b8385526005602052600160408620015582845260056020526104936104f6600360408720015492600d54612e03565b60065490612e78565b9082526005602052600360408320015580f35b505061056090828452600b6020526105318160016040872001546131b7565b838552600b6020526001604086200155828452600b60205261049361048a600360408720015492600d54612e03565b908252600b602052600360408320015580f35b508015156103fe565b90506020813d6020116105ab575b81610597602093836126e3565b810103126105a65751386103f2565b600080fd5b3d915061058a565b6040513d87823e3d90fd5b9091506020813d6020116105eb575b816105da602093836126e3565b810103126105a657519060206103cb565b3d91506105cd565b506020823d60201161061f575b8161060d602093836126e3565b810103126105a65761039b915161037d565b3d9150610600565b6040513d86823e3d90fd5b8161063c916126e3565b610647578238610351565b8280fd5b6040513d84823e3d90fd5b90506020813d602011610680575b81610671602093836126e3565b810103126105a65751386102a6565b3d9150610664565b6040513d85823e3d90fd5b50346101f15760403660031901126101f15760206106bb6106b2612656565b60243590612be7565b604051908152f35b50806003193601126101f15780546001600160a01b031633036101f1576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316908290602081602481865afa90811561064b5782916108c6575b506107c78260405161074a6060826126e3565b6002815260403660208301377f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661078982612752565b52856107948261275f565b526001600160a01b036107a78234612a50565b166040518080958194637ff36ab560e01b83524290309060048501612968565b039134905af18015610688576108a6575b506040516370a0823160e01b8152306004820152602081602481875afa90811561068857839161086d575b50906108149161039661038d61316f565b91803b156101ed57818091602460405180948193630852cd8d60e31b83528860048401525af1801561064b57610858575b5061085282600e546131b7565b600e5580f35b81610862916126e3565b6101ed578138610845565b919250506020813d60201161089e575b8161088a602093836126e3565b810103126105a65751839190610814610803565b3d915061087d565b6108c1903d8085833e6108b981836126e3565b8101906129b3565b6107d8565b9150506020813d6020116108f3575b816108e2602093836126e3565b810103126105a65782905138610737565b3d91506108d5565b50346101f15760403660031901126101f15760243567ffffffffffffffff81116101ed57366023820112156101ed5780600401359061093982612705565b9261094760405194856126e3565b8284526024602085019360051b830101913683116101ed57602401925b82841061098b57602061097986600435612a50565b6040516001600160a01b039091168152f35b83356001600160a01b038116810361064757815260209384019301610964565b50346101f157806003193601126101f157546040516001600160a01b039091168152602090f35b50346101f15760203660031901126101f1576109ec612656565b81549091906001600160a01b0316338190036101ed5760405190610120610a1381846126e3565b60088352601f1901366020840137610a2a82612752565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610a5e8261275f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610a928261276f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610ac68261277f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610afa8261278f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610b2e8261279f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610b62826127af565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610b96826127bf565b52815b8151811015610bde57600190610bce856001600160a01b03610bbb84876127d0565b51168460ff196010541617601055613211565b60ff196010541660105501610b99565b8280f35b50346101f15760403660031901126101f157610bfc612656565b6024359160018060a01b03815416908133036101f15760405191610120610c2381856126e3565b60088452601f190136602085013780610c3b84612752565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169081610c718561275f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031680610ca68661276f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031680610cdb8761277f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031680610d108861278f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169182610d468961279f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169384610d7c8a6127af565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169586610db28b6127bf565b52885260046020526040882060018060a01b038b1660005260205260406000205461117a575b8a15801580611149575b81611116575b15610f77575060405196610dfb886126b0565b60018060a01b038954168852602088015260408701526060860152608085015260a084015260c083015260e08201528160018060a01b038516905b60088110610f23575050509291925b610e8a83610e8560065460018060a01b03855416855260046020526040852060018060a01b0389166000526020526040600020549061039661038d61316f565b6131b7565b6006556001600160a01b0390931692805b8251811015610f1f576001906001600160a01b03610eb982866127d0565b5116835260046020528460408085206000908982526020522055610eee85838060a01b03610ee784886127d0565b5116612be7565b828060a01b03610efe83876127d0565b5116845260046020528260408086206000908a825260205220015501610e9b565b5080f35b6001906001600160a01b03610f3882866130f1565b5116808652826020526040862054818752600260205260408088206000908782526020522055855281602052610f718760408720613145565b01610e36565b610f8b575b50505050505050929192610e45565b60405196610f98886126b0565b60018060a01b038954168852602088015260408701526060860152608085015260a084015260c083015260e08201528160018060a01b038516905b60088110610fe45780808080610f7c565b6001600160a01b03610ff682856130f1565b51168085526001602052604085205481865260026020526040808720600090868252602052205490600181146110e05782875260016020526040872060001982019182116110cc5761104f600195949392604092612682565b868060a01b0391549060031b1c16838952856020526110988161107485858d20612682565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b8389526002602052818920600091878060a01b0316825260205220558552816020526110c660408620613102565b01610fd3565b634e487b7160e01b88526011600452602488fd5b5050908160019286526002602052856040808220600090878252602052205585528160205261111160408620613102565b6110c6565b89546001600160a01b039081168b52600460209081526040808d20928f1660009081529290915290205415159150610de8565b5060018060a01b03895416895260046020526040892060018060a01b038c1660005260205260406000205415610de2565b9996959493929190865b89518110156111b6576001906111a68c6001600160a01b03610bbb848f6127d0565b60ff196010541660105501611184565b509091929394959699610dd8565b50346101f15760403660031901126101f1576111de612656565b6001600160a01b03168152600760205260408120805460243592908310156101f157602061120c8484612682565b905460405160039290921b1c6001600160a01b03168152f35b50346101f15760603660031901126101f15761123f612656565b81546001600160a01b031633036101ed5760018060a01b0316808252600b60205260243560046040842001558152600b602052604435600560408320015580f35b50346101f157806003193601126101f1576020600e54604051908152f35b50346101f15760203660031901126101f15780546004359082906001600160a01b0316338190036101ed576020906064604051809481936323b872dd60e01b83528060048401523060248401528760448401525af1801561068857611536575b508154604051630c0586bf60e11b8152906001600160a01b0316602082600481845afa918215610627578492611502575b50604051630d769c5d60e31b815290602082600481845afa9182156105b35785926114ce575b506006541515806114c5575b1561146c575061043d906114099361137a61041a61316f565b6113918461043d61138b8583612a2d565b84612e03565b86546001600160a01b0316808852600b60205260408820600101546113b79083906131b7565b908852600b60205260016040892001556113f460018060a01b0388541691828952600b60205261049361048a600360408c20015492600d54612e03565b908752600b6020526003604088200155612e03565b81546001600160a01b03168083526005602052604083206001015461142f9083906131b7565b908352600560205260016040842001556104ff60018060a01b038354169182845260056020526104936104f6600360408720015492600d54612e03565b915050808352600b6020526114888260016040862001546131b7565b908352600b602052600160408420015561056060018060a01b0383541691828452600b60205261049361048a600360408720015492600d54612e03565b50811515611361565b9091506020813d6020116114fa575b816114ea602093836126e3565b810103126105a657519038611355565b3d91506114dd565b9091506020813d60201161152e575b8161151e602093836126e3565b810103126105a65751903861132f565b3d9150611511565b6115579060203d60201161155c575b61154f81836126e3565b81019061299b565b6112fe565b503d611545565b50346101f157806003193601126101f1576020600f54604051908152f35b50806003193601126101f15780546001600160a01b031633036101f1576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690602081602481855afa9081156106885783916117ae575b506040516116026060826126e3565b6002815260403660208301377f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661164182612752565b528261164c8261275f565b52836001600160a01b036116608334612a50565b1691823b156101ed5761168c92604051808095819463b6f9de9560e01b83524290309060048501612968565b039134905af180156106275761179a575b506040516370a0823160e01b815230600482015290602082602481865afa8015610627578490611766575b6116d8925061039661038d61316f565b60405163a9059cbb60e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316600482015260248101829052916020908390604490829087905af19182156106885761174392611749575b50600f546131b7565b600f5580f35b6117619060203d60201161155c5761154f81836126e3565b61173a565b506020823d602011611792575b81611780602093836126e3565b810103126105a6576116d891516116c8565b3d9150611773565b836117a7919492946126e3565b913861169d565b90506020813d6020116117d8575b816117c9602093836126e3565b810103126106475751386115f3565b3d91506117bc565b50346101f157806003193601126101f1576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101f15760403660031901126101f1576040602091611844612656565b61184c61266c565b6001600160a01b039182168352600985528383209116825283522054604051908152f35b50346101f15760403660031901126101f15760409061188d612656565b61189561266c565b6001600160a01b039182168352600a6020908152848420919092168352815291902080546001820154600290920154604080519283529382019290925291820152606090f35b0390f35b50346101f15760403660031901126101f15760406020916118fe612656565b61190661266c565b6001600160a01b039182168352600285528383209116825283522054604051908152f35b50346101f15760403660031901126101f1576040602091611949612656565b61195161266c565b6001600160a01b039182168352600385528383209116825283522054604051908152f35b50346101f157806003193601126101f1576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101f15760403660031901126101f1576040906119d7612656565b6119df61266c565b6001600160a01b03918216835260046020908152848420919092168352815291902080546001820154600290920154604080519283529382019290925291820152606090f35b50346101f15760203660031901126101f1576040906001600160a01b03611a4a612656565b168152600b6020522080546118db60018301549260028101549060038101546005600483015492015492604051968796879260a094919796959260c0850198855260208501526040840152606083015260808201520152565b50346101f157806003193601126101f1576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101f15760403660031901126101f15760206106bb611b07612656565b611b0f61266c565b9061288f565b50346101f15760403660031901126101f157611b2f612656565b6001600160a01b03168152600160205260408120805460243592908310156101f157602061120c8484612682565b50346101f15760203660031901126101f1576040906001600160a01b03611b82612656565b16815260056020522080546118db60018301549260028101549060038101546005600483015492015492604051968796879260a094919796959260c0850198855260208501526040840152606083015260808201520152565b50346101f15760403660031901126101f15760206106bb611bfa612656565b611c0261266c565b906127e4565b50346101f157806003193601126101f1576020600c54604051908152f35b50346101f157806003193601126101f1576020600654604051908152f35b50806003193601126101f15780546001600160a01b0316338190036101ed57604051630c0586bf60e11b815290602082600481845afa918215610688578392611e38575b5090602060049260405193848092630d769c5d60e31b82525afa918215610688578392611e04575b50600654151580611dfb575b15611d82576104ae6104ff91611cdc611cd361316f565b8286111561337a565b61043d611cf68261043d611cf08883612a2d565b34612e03565b94611d6c60018060a01b037f00000000000000000000000000000000000000000000000000000000000000001696878952600b602052611d3d81600160408c2001546131b7565b888a52600b602052600160408b200155878952600b60205261049361048a600360408c20015492600d54612e03565b868852600b602052600360408920015534612e03565b50507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316808252600b6020526040822060010154611dc99034906131b7565b818352600b6020526001604084200155808252600b602052610560600360408420015461049361048a34600d54612e03565b50811515611cbc565b9091506020813d602011611e30575b81611e20602093836126e3565b8101031261064757519038611cb0565b3d9150611e13565b91506020823d602011611e65575b81611e53602093836126e3565b81010312610647579051906020611c88565b3d9150611e46565b50346101f15760203660031901126101f157611e87612656565b81549091906001600160a01b0316338190036101ed5760405190610120611eae81846126e3565b60088352601f1901366020840137611ec582612752565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611ef98261275f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611f2d8261276f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611f618261277f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611f958261278f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611fc98261279f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611ffd826127af565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612031826127bf565b52815b8151811015610bde57600190612069856001600160a01b0361205684876127d0565b51168460ff196010541617601055612f05565b60ff196010541660105501612034565b50346101f15760403660031901126101f157612093612656565b81546001600160a01b0316602435338290036125e957604051916101206120ba81856126e3565b60088452601f1901366020850137806120d284612752565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690816121088561275f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168061213d8661276f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316806121728761277f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316806121a78861278f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691826121dd8961279f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693846122138a6127af565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031695866122498b6127bf565b528b52600a60209081526040808d206001600160a01b038d168e529091528b205461259f575b8715801580612572575b81612540575b15612401575060405196612292886126b0565b60018060a01b038c54168852602088015260408701526060860152608085015260a084015260c083015260e08201528460018060a01b038516905b600881106123ac57505050915b600c5484546001600160a01b039081168652600a602090815260408088209285168852919052852054612318918591610e859161039661038d61316f565b600c556001600160a01b0316835b82518110156123a8576001906001600160a01b0361234482866127d0565b51168652600a6020526040862083875260205284604087205561237885838060a01b0361237184886127d0565b511661271d565b828060a01b0361238883876127d0565b51168752600a602052604087208488526020528260408820015501612326565b8480f35b6001906001600160a01b036123c182866130f1565b511680895260076020526040892054818a52600860205260408a20858b5260205260408a2055885260076020526123fb8760408a20613145565b016122cd565b612413575b50505050505050916122da565b60405196612420886126b0565b60018060a01b038c54168852602088015260408701526060860152608085015260a084015260c083015260e08201528460018060a01b038516905b6008811061246c5780808080612406565b6001600160a01b0361247e82856130f1565b51168088526008602052604088208389526020526040882054818952600760205260408920828a52600760205260408a205490600019820191821161252c576124f58b611074856124d6600199989796604096612682565b8a8060a01b0391549060031b1c16948388879552600760205220612682565b828b52600860205260408b2090858060a01b03168b5260205260408a20558852600760205261252660408920613102565b0161245b565b634e487b7160e01b8b52601160045260248bfd5b905060018060a01b038c54168c52600a60205260408c2060018060a01b038c168d5260205260408c205415159061227f565b508b546001600160a01b039081168d52600a60209081526040808f20928e168f529190528c205415612279565b9996959493929190875b89518110156125db576001906125cb8c6001600160a01b03612056848f6127d0565b60ff1960105416601055016125a9565b50909192939495969961226f565b8380fd5b50346101f157806003193601126101f157602060ff601054166040519015158152f35b50346101f157806003193601126101f1576020600d54604051908152f35b50346101f15760403660031901126101f15760206106bb61264d612656565b6024359061271d565b600435906001600160a01b03821682036105a657565b602435906001600160a01b03821682036105a657565b805482101561269a5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b610100810190811067ffffffffffffffff8211176126cd57604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176126cd57604052565b67ffffffffffffffff81116126cd5760051b60200190565b6001600160a01b03166000908152600b602052604090206003015461274f916127469190612e03565b600d5490612e78565b90565b80511561269a5760200190565b80516001101561269a5760400190565b80516002101561269a5760600190565b80516003101561269a5760800190565b80516004101561269a5760a00190565b80516005101561269a5760c00190565b80516006101561269a5760e00190565b80516007101561269a576101000190565b805182101561269a5760209160051b010190565b9060018060a01b0382169182600052600a602052604060002060018060a01b03831660005260205260406000205415612887576000838152600a602090815260408083206001600160a01b03861684529091529020546128439161271d565b91600052600a60205260406000209060018060a01b0316600052602052600160406000200154808211156128805761274f9161039661038d61316f565b5050600090565b505050600090565b9060018060a01b03821691826000526004602052604060002060018060a01b038316600052602052604060002054156128875760008381526004602090815260408083206001600160a01b03861684529091529020546128ee91612be7565b91600052600460205260406000209060018060a01b0316600052602052600160406000200154808211156128805761274f9161039661038d61316f565b906020808351928381520192019060005b8181106129495750505090565b82516001600160a01b031684526020938401939092019160010161293c565b612987606092959493956000835260806020840152608083019061292b565b6001600160a01b0390951660408201520152565b908160209103126105a6575180151581036105a65790565b6020818303126105a65780519067ffffffffffffffff82116105a657019080601f830112156105a65781516129e781612705565b926129f560405194856126e3565b81845260208085019260051b8201019283116105a657602001905b828210612a1d5750505090565b8151815260209182019101612a10565b91908203918211612a3a57565b634e487b7160e01b600052601160045260246000fd5b919091600060405191612a646080846126e3565b6003835260603660208501377f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612aa384612752565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612ad78461275f565b527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612b0b8461276f565b5260009160005b8451811015612bdf576001600160a01b03612b2d82876127d0565b51166000604051809263d06ca61f60e01b8252866004830152604060248301528180612b5d8d604483019061292b565b03915afa60009181612bc4575b50612b7a57506001905b01612b12565b80516000198101908111612a3a57612b91916127d0565b51848111612ba3575b50600190612b74565b9350905060016001600160a01b03612bbb83876127d0565b51169190612b9a565b612bd891923d8091833e6108b981836126e3565b9038612b6a565b509450505050565b6001600160a01b031660009081526005602052604090206003015461274f916127469190612e03565b6000198114612a3a5760010190565b6001600160a01b0381166000818152600760205260409020549390918415612ddc576000949194915a6000935b86811080612dd3575b15612dca57610493612d079287600052600b602052846040600020541015612db5575b876000526007602052612c9e604060002089600052600b60205260406000205490612682565b60018060a01b0391549060031b1c1688600052600960205260408060002060009060018060a01b0384168252602052205489600052600b602052612cec4291600460406000200154906131aa565b109081612d91575b50612d34575b5a9061039661038d61316f565b95612d2c5a9486600052600b6020526040600020612d258154612c10565b9055612c10565b939096612c4c565b876000526007602052612d82612d5d60406000208a600052600b60205260406000205490612682565b90546010805460ff1916600117905560039190911b1c6001600160a01b031687612f05565b60ff1960105416601055612cfa565b612d9c9150876127e4565b88600052600b6020526005604060002001541038612cf4565b87600052600b60205260006040812055612c78565b50505050505050565b50828510612c55565b5092505050565b8115612ded570490565b634e487b7160e01b600052601260045260246000fd5b90811561288057808202918083048203612a3a57612e219083612de3565b03612e295790565b60405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b6064820152608490fd5b9061274f91612ec0604051612e8e6040826126e3565b601a81527f536166654d6174683a206469766973696f6e206279207a65726f000000000000602082015283151561337a565b612de3565b3d15612f00573d9067ffffffffffffffff82116126cd5760405191612ef4601f8201601f1916602001846126e3565b82523d6000602084013e565b606090565b9060018060a01b03821680600052600a602052604060002060018060a01b038316600052602052604060002054156130ec57612f4182846127e4565b9283612f4e575b50505050565b60008281526009602090815260408083206001600160a01b038716808552908352818420429055858452600a8352818420908452909152902060020154612fcc9190612f9b9086906131b7565b6000848152600a602090815260408083206001600160a01b038916845290915290206002810191909155549061271d565b81600052600a602052604060002060018060a01b03841660005260205260016040600020015580600052600b60205261300d836002604060002001546131b7565b6000828152600b60205260409020600201557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316810361307a57506000918291829182916001600160a01b031661c350f15061306f612ec5565b505b38808080612f48565b60405163a9059cbb60e01b81526001600160a01b03929092166004830152602482019290925290602090829060449082906000905af180156130e0576130c1575b50613071565b6130d99060203d60201161155c5761154f81836126e3565b50386130bb565b6040513d6000823e3d90fd5b505050565b90600881101561269a5760051b0190565b8054801561312f5760001901906131198282612682565b81549060018060a01b039060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b90815491680100000000000000008310156126cd578261107491600161316d95018155612682565b565b6040519061317e6040836126e3565b601e82527f536166654d6174683a207375627472616374696f6e206f766572666c6f7700006020830152565b91908201809211612a3a57565b906131c290826131aa565b9081106131cc5790565b60405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606490fd5b9060018060a01b038216806000526004602052604060002060018060a01b038316600052602052604060002054156130ec5761324d828461288f565b92836132595750505050565b60008281526003602090815260408083206001600160a01b038716808552908352818420429055858452600483528184209084529091529020600201546132d791906132a69086906131b7565b60008481526004602090815260408083206001600160a01b0389168452909152902060028101919091555490612be7565b816000526004602052604060002060018060a01b038416600052602052600160406000200155806000526005602052613318836002604060002001546131b7565b6000828152600560205260409020600201557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316810361307a57506000918291829182916001600160a01b031661c350f15061306f612ec5565b156133825750565b6040519062461bcd60e51b8252602060048301528181519182602483015260005b8381106133c55750508160006044809484010152601f80199101168101030190fd5b602082820181015160448784010152859350016133a356fea2646970667358221220fd3149d613836d46067cb5290ed8f39d2c820f9dbc61a79826827dc0afa131c264736f6c634300081a0033

Deployed ByteCode

0x6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c806301339c2114612f605780630445b66714612f4257806306fdde0314612eff5780630905f56014612ed9578063095ea7b314612e5a5780630d1554bf14612e31578063180b0d7e14612e1457806318160ddd14612df6578063207a75bf14612db55780632172ab2114612d9757806323b2d78014612d7957806323b872dd14612c8357806326041bf614612c5a5780632b112e4914612bbd5780632b21aca6146129705780632f6ec43a14612952578063313ce567146129365780633190bb8a146128da57806337107377146107575780633f4218e01461289b5780634355855a1461285c5780634ce027fe1461283e5780634f15999a146127f95780635762e264146127a45780635abe6711146127815780635c85974f1461274d5780636083dab61461271457806364d1dd1b146126f6578063654dcf87146126d8578063658d4b7f146126865780636ad5ed21146125ef5780636bb4e2e8146125d15780636d2b49401461258c5780636ddd17131461256957806370a0823114612530578063715018a6146124d3578063749debf21461235757806375619ab51461228957806376312a7a14611cb9578063767eb5ef14611c895780637a7bf3ab14611b495780637ae06e58146115975780637dafe91d146115795780638b42507f1461153a5780638c0b5e221461151c5780638d5396e9146114d75780638da5cb5b146114ae5780638e9b4c671461149057806395d89b411461144b57806398118cb41461142d5780639d1944f5146113b45780639e9fd1c91461135f578063a78dec4014611341578063a9059cbb1461130e578063af6c9c1d14611134578063b5716bf714611116578063b6f3e08714610ead578063b7e4cfeb14610e8f578063b91ac78814610e4b578063be32b3f814610dfd578063bf56b37114610ddf578063bfe1092814610db6578063c8fa742214610d8d578063ca33e64c14610d60578063cbaad20a14610d0e578063cd78f2fc14610c66578063d073a01f14610c48578063d22b98a114610c2a578063d3bf520014610c0c578063d726b22114610bee578063d7ddeb4014610bb9578063dcd2914c14610a3a578063dd48afea14610a1c578063dd62ed3e146109c7578063df20fd491461092e578063e0df99e814610911578063e16aed5114610790578063e4ca6de614610757578063e5e31b1314610718578063eb4122d2146106ba578063edf5cdea1461069c578063efbd96c51461067e578063f0fc6bca14610607578063f2fde38b1461057e578063f708a64f146104305763f84ba65d146103d9575061000e565b3461042d57604036600319011261042d5761042a6103f5613137565b6103fd613163565b9061040661361c565b60018060a01b03168352600d602052604083209060ff801983541691151516179055565b80f35b80fd5b503461042d57604036600319011261042d578061044b613137565b610453613163565b9061045c61361c565b6001600160a01b038116913083141580610569575b1561056457828452600e60205261049781604086209060ff801983541691151516179055565b1561050557506028546001600160a01b031690813b15610501578291604483926040519485938492630a5b654b60e11b845260048401528160248401525af180156104f6576104e557505080f35b816104ef916131ef565b61042d5780f35b6040513d84823e3d90fd5b5050fd5b60018060a01b036028541691835260056020526040832054823b1561056457604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529082908290604490829084905af180156104f6576104e557505080f35b505050fd5b506024546001600160a01b0316831415610471565b503461042d57602036600319011261042d57610598613137565b6105a061361c565b6001600160a01b031680156105f357600180546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b631e4fbdf760e01b82526004829052602482fd5b503461042d578060031936011261042d5761062061409f565b60285481906001600160a01b0316803b1561067b57818091602460405180948193630afbf02f60e11b83523360048401525af180156104f657610666575b506001905580f35b81610670916131ef565b61042d57803861065e565b50fd5b503461042d578060031936011261042d576020601854604051908152f35b503461042d578060031936011261042d576020601254604051908152f35b503461042d578060031936011261042d576106d361409f565b60285481906001600160a01b0316803b1561067b578180916024604051809481936336ad696b60e21b83523360048401525af180156104f65761066657506001905580f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03610743613137565b168152600884522054166040519015158152f35b503461042d57602036600319011261042d576020906040906001600160a01b0361077f613137565b168152600983522054604051908152f35b503461042d57602036600319011261042d576107aa613137565b6107b261361c565b6001600160a01b03166107c68115156133bd565b806001600160601b0360a01b60025416176002558152600c60205260408120600160ff1982541617905560018060a01b03600254168152600d60205260408120600160ff198254161790556004602060018060a01b0360025416604051928380926308aaafe160e31b82525afa9081156104f65782916108f2575b506001600160a01b039081168252600c60209081526040808420805460ff1916600117905560025490516308aaafe160e31b81529283916004918391165afa9081156104f65782916108c3575b506001600160a01b03168152600d60205260408120805460ff1916600117905560008051602061543a8339815191528180a180f35b6108e5915060203d6020116108eb575b6108dd81836131ef565b810190613413565b3861088e565b503d6108d3565b61090b915060203d6020116108eb576108dd81836131ef565b38610841565b503461042d578060031936011261042d5760208054604051908152f35b503461042d57604036600319011261042d57610948613172565b6024359061095461361c565b60ff8019602a54169115151617602a5580602b55156109825760008051602061543a8339815191528180a180f35b60405162461bcd60e51b815260206004820152601a60248201527f4d7573742062652067726561746572207468616e207a65726f2e0000000000006044820152606490fd5b503461042d57604036600319011261042d5760406109e3613137565b916109ec61314d565b9260018060a01b031681526006602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d578060031936011261042d576020601454604051908152f35b503461042d5760a036600319011261042d57604435602435600435608435606435610a6361361c565b848310610b7457808410610b2f578115610aea577fc6395d37de1bf9bd21e069920faf9042852460a35699416fed9792866d3b6e309460a094610aaa6109c48611156135d0565b610ab86109c48211156135d0565b8460105580601155816012558260135583600f556040519485526020850152604084015260608301526080820152a180f35b60405162461bcd60e51b815260206004820152601b60248201527f44656361792074696d65206d75737420626520706f73697469766500000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f53656c6c20666565206d757374206265203e3d206d696e696d756d00000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f42757920666565206d757374206265203e3d206d696e696d756d0000000000006044820152606490fd5b503461042d57604036600319011261042d576020610be6610bd8613137565b610be0613163565b9061357e565b604051908152f35b503461042d578060031936011261042d576020601054604051908152f35b503461042d578060031936011261042d576020602d54604051908152f35b503461042d578060031936011261042d576020601654604051908152f35b503461042d578060031936011261042d576020601154604051908152f35b503461042d57606036600319011261042d5780610c81613137565b610c8961361c565b6028546001600160a01b031690813b156105015760405163335e3cbf60e21b81526001600160a01b039091166004820152602480359082015260448035908201529082908290606490829084905af180156104f657610cf9575b508060008051602061543a83398151915291a180f35b81610d03916131ef565b61042d578038610ce3565b503461042d57602036600319011261042d57610d28613137565b610d3061361c565b60018060a01b03166001600160601b0360a01b602354161760235560008051602061543a8339815191528180a180f35b503461042d578060031936011261042d5760225460405160089190911c6001600160a01b03168152602090f35b503461042d578060031936011261042d576002546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576028546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576020602754604051908152f35b503461042d57602036600319011261042d57610e17613172565b610e1f61361c565b61ff00602c5491151560081b169061ff00191617602c5560008051602061543a8339815191528180a180f35b503461042d57602036600319011261042d576004359060255482101561042d576020610e76836131be565b905460405160039290921b1c6001600160a01b03168152f35b503461042d578060031936011261042d576020601a54604051908152f35b503461042d57604036600319011261042d57610ec7613137565b90610ed061314d565b610ed861361c565b6001600160a01b0383168083526008602052604083205490919060ff166110db576001600160a01b03908116907f00000000000000000000000098bf93ebf5c380c0e6ae8e192a7e2ae08edacc0216811480156110a9575b8015611077575b15611041576025546801000000000000000081101561102d578394610f65826001610f8994016025556131be565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b818352600860205260408320600160ff19825416179055818352602660205260408320906001600160601b0360a01b825416179055808252600e60205260408220600160ff1982541617905560018060a01b036028541690813b15610501578291604483926040519485938492630a5b654b60e11b845260048401528160248401525af180156104f657610cf957508060008051602061543a83398151915291a180f35b634e487b7160e01b84526041600452602484fd5b60405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b2103937baba32b960911b6044820152606490fd5b507f000000000000000000000000eb45a3c4aedd0f47f345fb4c8a1802bb5740d7256001600160a01b03168114610f37565b507f000000000000000000000000165c3410fc91ef562c50559f7d2289febed552d96001600160a01b03168114610f30565b60405162461bcd60e51b81526020600482015260136024820152722830b4b91030b63932b0b23c9030b23232b21760691b6044820152606490fd5b503461042d578060031936011261042d576020601554604051908152f35b503461042d57602036600319011261042d5761114e613137565b61115661361c565b6001600160a01b03168082526008602052604082205460ff16156112d357602554829081805b82811061129e575b50156112595760001981019081116112455790610f656111a66111be936131be565b905460039190911b1c6001600160a01b0316916131be565b80825260266020526040822080546001600160a01b0319169055602554801561123157600019016111ee816131be565b81546001600160a01b0360039290921b9190911b191690556025558152600860205260408120805460ff1916905560008051602061543a8339815191528180a180f35b634e487b7160e01b83526031600452602483fd5b634e487b7160e01b84526011600452602484fd5b60405162461bcd60e51b815260206004820152601760248201527f50616972206e6f7420666f756e6420696e2061727261790000000000000000006044820152606490fd5b846112a8826131be565b905460039190911b1c6001600160a01b0316146112c75760010161117c565b92506001905038611184565b60405162461bcd60e51b815260206004820152601360248201527214185a5c88191bd95cc81b9bdd08195e1a5cdd606a1b6044820152606490fd5b503461042d57604036600319011261042d57602061133761132d613137565b60243590336136fa565b6040519015158152f35b503461042d578060031936011261042d576020601c54604051908152f35b503461042d57604036600319011261042d57604061137b613137565b9161138461314d565b9260018060a01b03168152600a602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d57602036600319011261042d57620f42406004356113d561361c565b80602955116113f35760008051602061543a8339815191528180a180f35b60405162461bcd60e51b815260206004820152601260248201527104d61782067617320697320313030303030360741b6044820152606490fd5b503461042d578060031936011261042d576020601f54604051908152f35b503461042d578060031936011261042d575061148c60405161146e6040826131ef565b60058152645248494e4f60d81b60208201526040519182918261310b565b0390f35b503461042d578060031936011261042d576020601d54604051908152f35b503461042d578060031936011261042d576001546040516001600160a01b039091168152602090f35b503461042d578060031936011261042d576040517f00000000000000000000000098bf93ebf5c380c0e6ae8e192a7e2ae08edacc026001600160a01b03168152602090f35b503461042d578060031936011261042d576020600454604051908152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03611565613137565b168152600d84522054166040519015158152f35b503461042d578060031936011261042d576020601754604051908152f35b503461042d5760e036600319011261042d57806115b2613137565b6115ba61314d565b606435916044356115c961409f565b6201000062ff000019602c541617602c556115e5811515613233565b6001600160a01b03831660008181526008602052604090209092906116159060019060ff905b5416151514613276565b6001600160a01b0383166000908152600a60209081526040808320338452909152902054611ac6575b6116498230336136fa565b506040516323b872dd60e01b8152336004820152306024820152604481018690526001600160a01b039190911693906020816064818a895af18015611a9e57611aa9575b506116e26020866116b08660018060a01b03166000526026602052604060002090565b5460405163095ea7b360e01b81526001600160a01b039091166004820152602481019190915291829081906044820190565b03818a895af18015611a9e57611a81575b506116fc6132bb565b9086606061171c8660018060a01b03166000526026602052604060002090565b60018060a01b03905416610104604051809b819362e8e33760e81b83523060048401528b60248401528960448401528c6064840152608435608484015260a43560a48401523060c484015260c43560e48401525af1958615611a7457819782998398611a39575b50604085019889526117b0606086019a808c526117aa6117a161402e565b84831115613645565b82613488565b611992575b50505061183b608091611888946117d289516117aa6117a161402e565b611961575b506001600160a01b03861660009081526007602090815260408083203384529091529020546118079088906140c1565b6001600160a01b038716600090815260076020526040902060018060a01b033316600052602052604060002055339061412e565b910190815233600090815260096020526040902054815161185b916140c1565b33600090815260096020526040902055516001600160a01b039092166000908152600a6020526040902090565b33600090815260209182526040808220939093556028546009909252919091205485916001600160a01b0316803b1561195d5760405163d9a91c1d60e01b8152336004820152602481019290925282908290604490829084905af1611940575b50509051915160408051938452602084019190915282015233907f64b83944e79c3ce8d4c297411de637c3e102d064677aac0c163976ebdcd6f50e9080606081015b0390a262ff000019602c5416602c556001815580f35b9061194a916131ef565b836000126119595783386118e8565b8380fd5b8280fd5b61198461198b918a519061197f61197661402e565b82841115613645565b613488565b33306136fa565b50386117d7565b6020916119ad6119d7928c969596519061197f61197661402e565b60405163a9059cbb60e01b8152336004820152602481019190915294859283919082906044820190565b03925af1908115611a2e576118889460809361183b936119ff575b50948193508a92506117b5565b611a209060203d602011611a27575b611a1881836131ef565b8101906133fb565b50386119f2565b503d611a0e565b6040513d8b823e3d90fd5b9198509650611a6191985060603d606011611a6d575b611a5981836131ef565b810190613303565b98919790989638611783565b503d611a4f565b50604051903d90823e3d90fd5b611a999060203d602011611a2757611a1881836131ef565b6116f3565b6040513d89823e3d90fd5b611ac19060203d602011611a2757611a1881836131ef565b61168d565b336000908152600960205260409020611b1090546001600160a01b0385166000908152600a602052604090205b33600090815260209190915260409020549061197f61197661402e565b336000818152600960209081526040808320949094556001600160a01b0387168252600a8152838220928252919091522086905561163e565b503461042d57602036600319011261042d57611b63613137565b611b6b61409f565b602c80546201000062ff00001990911617908190556001600160a01b039190911680835260076020908152604084203360005290529060081c60ff1615611c445780825260076020818152604080852033600081815291845282822054868852948452828720818352845290829020869055905163a9059cbb60e01b81526004810191909152602481019290925290918290604490829086905af180156104f657611c25575b5062ff000019602c5416602c556001815580f35b611c3d9060203d602011611a2757611a1881836131ef565b5038611c11565b60405162461bcd60e51b815260206004820152601d60248201527f456d657267656e6379206d6f6465206973206e6f7420656e61626c65640000006044820152606490fd5b503461042d578060031936011261042d5761148c611ca5613432565b604051918291602083526020830190613181565b503461042d5760a036600319011261042d57600435611cd661314d565b611cde61409f565b6201000062ff000019602c541617602c558115612250576001600160a01b0381166000818152600760209081526040808320338452909152902054909290811161220b578391611d466020836116b08760018060a01b03166000526026602052604060002090565b038187895af18015612200576121e3575b50611d606132bb565b93604051630dfe168160e01b8152602081600481855afa90811561216f5785916121c4575b506001600160a01b0316300361217a5760405163d21220a760e01b8152602081600481855afa90811561216f578591612150575b50945b846040611ddb8460018060a01b03166000526026602052604060002090565b60018060a01b039054169760e4825180998193635d5155ef60e11b835230600484015260018060a01b03169b8c60248401528a6044840152604435606484015260643560848401523060a484015260843560c48401525af1968715611a745781968298612113575b50611e5d604084019780895260608501998a5233306136fa565b507f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a276001600160a01b03169080820361209f57508751813b1561195d578291602483926040519485938492632e1a7d4d60e01b845260048401525af180156104f657612082575b5092611f7f611f9692611eec868080611fce998d5133617530f1611ee661331e565b5061335e565b6001600160a01b0385166000908152600760209081526040808320338452909152902054611f2190889061197f61197661402e565b6001600160a01b03861660008181526007602090815260408083203380855290835281842095909555928252600a8152828220848352815282822054608095909501948552928152600990925290205490519061197f61197661402e565b33600090815260096020526040902055339061412e565b6001600160a01b03919091166000908152600a60209081526040808320338452808352818420859055600983529220549190526140c1565b3360009081526009602052604090208190556028548591906001600160a01b0316803b1561195d5760405163d9a91c1d60e01b8152336004820152602481019290925282908290604490829084905af1612069575b50509051915160408051938452602084019190915282015233907f1dc8bb69df2b8e91fbdcbfcf93d951b3f0000f085a95fe3f7946d6161439245d90806060810161192a565b90612073916131ef565b83600012611959578338612023565b9061208c916131ef565b8660001261209b578638611ec4565b8680fd5b885160405163a9059cbb60e01b8152336004820152602481019190915293959260209250849160449183915af1801561210857611fce94611f9693611f7f926120e9575b50611eec565b6121019060203d602011611a2757611a1881836131ef565b50386120e3565b6040513d8a823e3d90fd5b96509650506040853d604011612148575b81612131604093836131ef565b8101031261209b5786602086519601519638611e43565b3d9150612124565b612169915060203d6020116108eb576108dd81836131ef565b38611db9565b6040513d87823e3d90fd5b604051630dfe168160e01b8152602081600481855afa90811561216f5785916121a5575b5094611dbc565b6121be915060203d6020116108eb576108dd81836131ef565b3861219e565b6121dd915060203d6020116108eb576108dd81836131ef565b38611d85565b6121fb9060203d602011611a2757611a1881836131ef565b611d57565b6040513d86823e3d90fd5b60405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e74204c502062616c616e63650000000000000000006044820152606490fd5b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a5908131408185b5bdd5b9d607a1b6044820152606490fd5b503461042d57602036600319011261042d576122a3613137565b6122ab61361c565b6001600160a01b03166122bf8115156133bd565b602880546001600160a01b03191682178155908252600c60209081526040808420805460ff19908116600190811790925584546001600160a01b039081168752600d85528387208054831684179055855481168752600e855283872080549092169092179055308552600683528185209354166000908152929091529020600019905560008051602061543a8339815191528180a180f35b503461042d576101a036600319011261042d57366101a41161042d5761237b61361c565b80805b600d8210156123a35761239b6001918360051b60040135906140c1565b91019061237e565b612710915003612482577f234e092b0bae58003e83192221fcad7cca23f970cea48f52e9f51b5dc9dee4526101a060043580601c5560243580601e5560443580601d55606435806020556084358060145560a4358060155560c4358060165560e4359081601755610104359283601855610124359485601955610144359687601a55610164359889601b55610184359a8b601f556040519c8d5260208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140840152610160830152610180820152a180f35b60405162461bcd60e51b8152602060048201526024808201527f546f74616c2066656573206d75737420657175616c2066656544656e6f6d696e60448201526330ba37b960e11b6064820152608490fd5b503461042d578060031936011261042d576124ec61361c565b600180546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b503461042d57602036600319011261042d576020906040906001600160a01b03612558613137565b168152600583522054604051908152f35b503461042d578060031936011261042d57602060ff602a54166040519015158152f35b503461042d578060031936011261042d576040517f000000000000000000000000eb45a3c4aedd0f47f345fb4c8a1802bb5740d7256001600160a01b03168152602090f35b503461042d578060031936011261042d576020602154604051908152f35b503461042d57602036600319011261042d5761138860043561260f61361c565b806021551161261b5780f35b60405162461bcd60e51b815260206004820152603860248201527f4e6f206d6f7265207468616e2035302070657263656e742063616e206265206460448201527f6972656374656420746f204c502070726f7669646572732e00000000000000006064820152608490fd5b503461042d57604036600319011261042d5761042a6126a3613137565b6126ab613163565b906126b461361c565b60018060a01b03168352600c602052604083209060ff801983541691151516179055565b503461042d578060031936011261042d576020601954604051908152f35b503461042d578060031936011261042d576020601b54604051908152f35b503461042d57602036600319011261042d576020906040906001600160a01b0361273c613137565b168152600b83522054604051908152f35b503461042d57602036600319011261042d5760043561276a61361c565b6107d060035404811061277d5760045580f35b5080fd5b503461042d578060031936011261042d57602060ff602254166040519015158152f35b503461042d57604036600319011261042d5760406127c0613137565b916127c961314d565b9260018060a01b031681526007602052209060018060a01b03166000526020526020604060002054604051908152f35b503461042d578060031936011261042d576040517f000000000000000000000000165c3410fc91ef562c50559f7d2289febed552d96001600160a01b03168152602090f35b503461042d578060031936011261042d576020601e54604051908152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b03612887613137565b168152600e84522054166040519015158152f35b503461042d57602036600319011261042d5760209060ff906040906001600160a01b036128c6613137565b168152600c84522054166040519015158152f35b503461042d57602036600319011261042d576128f4613137565b6128fc61361c565b60228054610100600160a81b03191660089290921b610100600160a81b031691909117905560008051602061543a8339815191528180a180f35b503461042d578060031936011261042d57602060405160128152f35b503461042d578060031936011261042d576020602e54604051908152f35b5060a036600319011261042d576004356084356001600160a01b038116908181036119595760c4916129a061409f565b6201000062ff000019602c541617602c556129bc841515613233565b6129df600160ff61160b8460018060a01b03166000526008602052604060002090565b6001600160a01b0381166000908152600a60209081526040808320338452909152902054612b53575b612a138430336136fa565b50612a1c6132bb565b6001600160a01b038216600090815260266020526040902060609060018060a01b039054166040519586809263f305d71960e01b82523060048301528960248301526044356044830152602435606483015230608483015260643560a483015234905af1928315612b4857612acf95879588908996612b10575b50839260809261183b9260406118889701998a528b606087019b828d938452612ac9612ac061402e565b34831115613645565b34613488565b612ae6575b50506117d289516117aa6117a161402e565b808080612afc612b099551612ac9612ac061402e565b33617530f1611ee661331e565b898b612ad4565b60809297506118889493965061183b9150612b399060603d606011611a6d57611a5981836131ef565b97919890925092509293612a96565b6040513d88823e3d90fd5b336000908152600960205260409020612b8490546001600160a01b0383166000908152600a60205260409020611af3565b336000818152600960209081526040808320949094556001600160a01b0385168252600a81528382209282529190915220859055612a08565b503461042d578060031936011261042d57610be66020916040612c1b60035460018060a01b037f000000000000000000000000000000000000000000000000000000000000dead16845260058652828420549061197f61197661402e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168352600585529120549061197f61197661402e565b503461042d578060031936011261042d576023546040516001600160a01b039091168152602090f35b503461042d57606036600319011261042d57611337602091612ca3613137565b612cab61314d565b9060443592612ccc8260018060a01b03166000526006602052604060002090565b33600090815290875260409020548490600101612ceb575b50506136fa565b6001600160a01b038316600090815260066020526040808220338352895290819020548151612d5194919261197f9290612d2590836131ef565b6016825275496e73756666696369656e7420416c6c6f77616e636560501b8b8301525082841115613645565b6001600160a01b03821660009081526006602052604080822033835288529020553883612ce4565b503461042d578060031936011261042d576020601354604051908152f35b503461042d578060031936011261042d576020600f54604051908152f35b503461042d57602036600319011261042d576020906001600160a01b03612dda613137565b16815260268252604060018060a01b0391205416604051908152f35b503461042d578060031936011261042d576020600354604051908152f35b503461042d578060031936011261042d5760206040516127108152f35b503461042d578060031936011261042d576024546040516001600160a01b039091168152602090f35b503461042d57604036600319011261042d57612e74613137565b60406024359233815260066020522060018060a01b0382166000526020528160406000205560405191825260018060a01b0316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b503461042d578060031936011261042d57602060ff602c5460081c166040519015158152f35b503461042d578060031936011261042d575061148c604051612f226040826131ef565b60078152665268696e6f466960c81b60208201526040519182918261310b565b503461042d578060031936011261042d576020602b54604051908152f35b503461042d578060031936011261042d57612f7961361c565b60275461309f57426027556028546002546001600160a01b039081168084526005602052604084205491909216918391833b1561195d57604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529181908390604490829084905af161308f575b6028546001546001600160a01b0390811680845260056020526040842054939450911691823b1561056457604051630a5b654b60e11b81526001600160a01b0392909216600483015260248201529082908290604490829084905af161307a575b507f87dcd6626ffde0faf682a10e7b64aff36ea73a5470d5fa6cc7ebd372e4b1900160408051438152426020820152a180f35b81613084916131ef565b61042d578038613047565b613098916131ef565b3881612fe6565b60405162461bcd60e51b815260206004820152601160248201527020b63932b0b23c903630bab731b432b21760791b6044820152606490fd5b60009103126130e357565b600080fd5b60005b8381106130fb5750506000910152565b81810151838201526020016130eb565b6040916020825261312b81518092816020860152602086860191016130e8565b601f01601f1916010190565b600435906001600160a01b03821682036130e357565b602435906001600160a01b03821682036130e357565b6024359081151582036130e357565b6004359081151582036130e357565b906020808351928381520192019060005b81811061319f5750505090565b82516001600160a01b0316845260209384019390920191600101613192565b6025548110156131d957602560005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761321157604052565b634e487b7160e01b600052604160045260246000fd5b6040513d6000823e3d90fd5b1561323a57565b60405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a5908149a1a5b9bc8185b5bdd5b9d60621b6044820152606490fd5b1561327d57565b60405162461bcd60e51b81526020600482015260166024820152752830b4b91034b9903737ba1039bab83837b93a32b21760511b6044820152606490fd5b6040519060e0820182811067ffffffffffffffff82111761321157604052600060c0838281528260208201528260408201528260608201528260808201528260a08201520152565b908160609103126130e3578051916040602083015192015190565b3d15613359573d9067ffffffffffffffff8211613211576040519161334d601f8201601f1916602001846131ef565b82523d6000602084013e565b606090565b1561336557565b60405162461bcd60e51b815260206004820152601060248201526f2826299039b2b732103330b4b632b21760811b6044820152606490fd5b81156133a7570490565b634e487b7160e01b600052601260045260246000fd5b156133c457565b60405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606490fd5b908160209103126130e3575180151581036130e35790565b908160209103126130e357516001600160a01b03811681036130e35790565b604051906025548083528260208101602560005260206000209260005b818110613466575050613464925003836131ef565b565b84546001600160a01b031683526001948501948794506020909301920161344f565b9190820391821161349557565b634e487b7160e01b600052601160045260246000fd5b6010546012546001600160a01b039092166000908152600b602052604090205442811015613544576134dd9042613488565b600f54908181101561353e579061351761352f9261351261353b9661350c61350361402e565b88831115613645565b86613488565b6142c3565b61352a61352261433f565b831515613645565b61339d565b9061197f61197661402e565b90565b50505090565b5060006134dd565b6011546013546001600160a01b039092166000908152600b602052604090205442811015613544576134dd9042613488565b9080156135c757601154905b156135be57601354915b6001600160a01b03166000908152600b602052604090205442811015613544576134dd9042613488565b60125491613594565b6010549061358a565b156135d757565b60405162461bcd60e51b815260206004820152601760248201527f32352050657263656e74206d617820616c6c6f7765642e0000000000000000006044820152606490fd5b6001546001600160a01b0316330361363057565b63118cdaa760e01b6000523360045260246000fd5b1561364d5750565b60405162461bcd60e51b8152908190613669906004830161310b565b0390fd5b61197f61353b9382841115613645565b1561368457565b606460405162461bcd60e51b815260206004820152602060248201527f54686520636f6e7472616374206973206e6f74206c61756e63686564207965746044820152fd5b604051906136d76040836131ef565b6014825273496e73756666696369656e742042616c616e636560601b6020830152565b9190602754158080159061400b575b6137129061367d565b600090602c546137228160ff1690565b918215614003575b508115613ff5575b50613feb5761374283838661441b565b61374b846144b8565b80613f86575b80613f1c575b15613ebb5750613765614525565b6001915b6001600160a01b038416600090815260056020526040902061379590548261378f6136c8565b9161366d565b6001600160a01b0385166000908152600560205260409020556001600160a01b0382166000908152600b602052604090205415613e9b575b6137d78285615259565b15613e95576137e790828561532e565b915b6001600160a01b038216600090815260056020526040902061380d908490546140c1565b6001600160a01b0383166000908152600560205260409020556001600160a01b0384166000908152600e602052604090206138529061384e905b5460ff1690565b1590565b613e0d575b6001600160a01b0382166000908152600e6020526040902061387c9061384e90613847565b613d85575b156138b9575b6040519182526001600160a01b0390811692169060008051602061545a8339815191529080602081015b0390a3600190565b601454613cf3575b601554613c8a575b601654613bf8575b601754613b66575b601854613ad4575b601954613a42575b601a546139b0575b601b541561388757602854613916906001600160a01b03165b6001600160a01b031690565b60295491813b156130e357604051637e3d004d60e11b81527f0000000000000000000000002fa878ab3f87cc1c9737fc071108f904c0b0c95d6001600160a01b03166004820152602481019390935260008051602061545a833981519152926138b1926000908290604490829084905af1613995575b50915050613887565b806139a460006139aa936131ef565b806130d8565b3861398c565b6028546139c5906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000095b303987a60c71504d99aa1b13b4da07b0790ab6001600160a01b0316600482015260248101929092526000908290604490829084905af1613a2d575b506138f1565b806139a46000613a3c936131ef565b38613a27565b602854613a57906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f000000000000000000000000616cb6a245ed4c11216ec58d10b6a2e87271845d6001600160a01b0316600482015260248101929092526000908290604490829084905af1613abf575b506138e9565b806139a46000613ace936131ef565b38613ab9565b602854613ae9906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f0000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb396001600160a01b0316600482015260248101929092526000908290604490829084905af1613b51575b506138e1565b806139a46000613b60936131ef565b38613b4b565b602854613b7b906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f00000000000000000000000057fde0a71132198bbec939b98976993d8d89d2256001600160a01b0316600482015260248101929092526000908290604490829084905af1613be3575b506138d9565b806139a46000613bf2936131ef565b38613bdd565b602854613c0d906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f0000000000000000000000008da17db850315a34532108f0f5458fc0401525f66001600160a01b0316600482015260248101929092526000908290604490829084905af1613c75575b506138d1565b806139a46000613c84936131ef565b38613c6f565b602854613c9f906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b815230600482015260248101929092526000908290604490829084905af1613cde575b506138c9565b806139a46000613ced936131ef565b38613cd8565b602854613d08906001600160a01b031661390a565b60295490803b156130e357604051637e3d004d60e11b81527f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a276001600160a01b0316600482015260248101929092526000908290604490829084905af1613d70575b506138c1565b806139a46000613d7f936131ef565b38613d6a565b602854613d9a906001600160a01b031661390a565b6001600160a01b03831660009081526005602052604090205490803b156130e357604051630a5b654b60e11b81526001600160a01b038516600482015260248101929092526000908290604490829084905af1613df8575b50613881565b806139a46000613e07936131ef565b38613df2565b602854613e22906001600160a01b031661390a565b6001600160a01b03851660009081526005602052604090205490803b156130e357604051630a5b654b60e11b81526001600160a01b038716600482015260248101929092526000908290604490829084905af1613e80575b50613857565b806139a46000613e8f936131ef565b38613e7a565b916137e9565b6001600160a01b0382166000908152600b602052604090204290556137cd565b600254909290613ed3906001600160a01b031661390a565b803b156130e35760405163f8e5884b60e01b8152426004820152906000908290602490829084905af1613f07575b50613769565b806139a46000613f16936131ef565b38613f01565b50602854600490602090613f38906001600160a01b031661390a565b6040516312c1083d60e01b815292839182905afa908115613f8157600091613f62575b5015613757565b613f7b915060203d602011611a2757611a1881836131ef565b38613f5b565b613227565b50600254600490602090613fa2906001600160a01b031661390a565b604051631732cded60e01b815292839182905afa908115613f8157600091613fcc575b5015613751565b613fe5915060203d602011611a2757611a1881836131ef565b38613fc5565b5061353b9261439b565b60081c60ff16905038613732565b91503861372a565b5060015461371290614025906001600160a01b031661390a565b32149050613709565b6040519061403d6040836131ef565b601e82527f536166654d6174683a207375627472616374696f6e206f766572666c6f7700006020830152565b61407f61407461402e565b612710831115613645565b6127100361271081116134955790565b9061353b9161197f61197661402e565b6002600054146140b0576002600055565b633ee5aeb560e01b60005260046000fd5b908101908181116134955781106140d55790565b60405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f7700000000006044820152606490fd5b51906001600160701b03821682036130e357565b604051630240bc6b60e21b8152916001600160a01b039190911690606083600481855afa908115613227576000938492614266575b50604051630dfe168160e01b815290602082600481875afa918215613227576004956020946141d7946001600160701b0393600091614249575b506001600160a01b03163003614240575016905b60008581526007855260408082206001600160a01b0390931682529185522054906142c3565b91604051938480926318160ddd60e01b82525afa9081156132275760009161420a575b61353b925061352a61352261433f565b90506020823d602011614238575b81614225602093836131ef565b810103126130e35761353b9151906141fa565b3d9150614218565b905016906141b1565b6142609150873d89116108eb576108dd81836131ef565b3861419d565b939091506060843d6060116142bb575b81614283606093836131ef565b8101031261042d576142948461411a565b9060406142a36020870161411a565b95015163ffffffff81160361042d5750929038614163565b3d9150614276565b90811561433857808202918083048203613495576142e1908361339d565b036142e95790565b60405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b6064820152608490fd5b5050600090565b6040519061434e6040836131ef565b601a82527f536166654d6174683a206469766973696f6e206279207a65726f0000000000006020830152565b6127109061438661433f565b500490565b9061353b9161352a61352261433f565b602060008051602061545a8339815191529160018060a01b03169283600052600582526143d38560406000205461197f6119766136c8565b846000526005835260406000205560018060a01b0316938460005260058252614401816040600020546140c1565b8560005260058352604060002055604051908152a3600190565b916004541091821592614495575b508115614472575b501561443957565b60405162461bcd60e51b8152602060048201526011602482015270151608131a5b5a5d08115e18d959591959607a1b6044820152606490fd5b6001600160a01b03166000908152600d602052604090205460ff16905038614431565b6001600160a01b03166000908152600d602052604090205460ff16915038614429565b6024546001600160a01b039182169116141580614518575b80614508575b806144fc575b806144e45790565b50306000526005602052604060002054602b54111590565b5060ff602a54166144dc565b5060ff602c5460101c16156144d6565b5060ff602c5416156144d0565b602c805462ff00ff19166201000117905561453e61508d565b602b5461458f61457f614567614556601554856142c3565b61455e61433f565b50612710900490565b92610120850193845261458461457f601c54836142c3565b61437a565b8552602054906142c3565b60608301908152815180615034575b50825180614fec575b50805180614fcc575b506145ce6145c66145d693602b5490519061408f565b84519061408f565b90519061408f565b90608081019182524760a082019081526145ee615122565b61460a306145fb83615144565b6001600160a01b039091169052565b6146377f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a276145fb83615151565b83517f000000000000000000000000165c3410fc91ef562c50559f7d2289febed552d96001600160a01b031691823b156130e357614693926000928360405180968195829463791ac94760e01b84524291309160048601615175565b03925af19081614fb7575b506148065750505060016146b06151af565b6308c379a014614763575b6146dd575b6146d162ff000019602c5416602c55565b60ff19602c5416602c55565b7fc41a20ad8c23d3903584975786330c6ec73ccfcc657629f10237b792268b0e026040518061475b8160809060208152603460208201527f537761704261636b206661696c656420776974686f757420616e206572726f72604082015273040dacae6e6c2ceca40cce4deda40a0ead8e6cab60631b60608201520190565b0390a16146c0565b61476b6151cd565b80614777575b506146bb565b90507fc41a20ad8c23d3903584975786330c6ec73ccfcc657629f10237b792268b0e026147fd6147e36147f16000946040519283916147dd60208401601b907f537761704261636b206661696c65642077697468206572726f7220000000000081520190565b90615242565b03601f1981018352826131ef565b6040519182918261310b565b0390a138614771565b518190614813904761408f565b60c0820190808252601f54614827916142c3565b6015549061483482614069565b91601c5492836148439161408f565b9160205492836148529161408f565b61485b9161438b565b60e086019081528451601454614870916142c3565b838561487b85614069565b906148859161408f565b9061488f9161408f565b6148989161438b565b610100870190815285516016546148ae916142c3565b84866148b986614069565b906148c39161408f565b906148cd9161408f565b6148d69161438b565b90610140880191825286516017546148ed916142c3565b85876148f887614069565b906149029161408f565b9061490c9161408f565b6149159161438b565b926101608901938452875160185461492c916142c3565b868861493788614069565b906149419161408f565b9061494b9161408f565b6149549161438b565b946101808a01958652885160195461496b916142c3565b878961497684614069565b906149809161408f565b9061498a9161408f565b6149939161438b565b966101a08b019788528951601a546149aa916142c3565b818a6149b585614069565b906149bf9161408f565b906149c99161408f565b6149d29161438b565b986101c08c01998a528a51601b546149e9916142c3565b82826149f486614069565b906149fe9161408f565b90614a089161408f565b614a119161438b565b9a6101e08d019b8c528051601d54614a28916142c3565b8383614a3387614069565b90614a3d9161408f565b90614a479161408f565b614a509161438b565b9c6020019c8d5251601e54614a64916142c3565b92614a6e90614069565b90614a789161408f565b90614a829161408f565b614a8b9161438b565b99604001998a52805180614f6c575b50505180614f1e575b505180614e9e575b505180614e1e575b505180614d9e575b505180614d1e575b505180614c9e575b505180614c1e575b505180614bd0575b505180614b51575b5061475b7fb39214ef4f33ea2d9d329fb67a4f17e7588bf6e00ed15a7967137ba819697a279147614b20575b516040519081529081906020820190565b6000808080614b3c61390a61390a60015460018060a01b031690565b4790617530f150614b4b61331e565b50614b0f565b602854909190614b69906001600160a01b031661390a565b90813b156130e3577fb39214ef4f33ea2d9d329fb67a4f17e7588bf6e00ed15a7967137ba819697a2792600061475b93600460405180948193638ed7ae7360e01b83525af1614bbb575b509150614ae3565b806139a46000614bca936131ef565b38614bb3565b602854614be5906001600160a01b031661390a565b803b156130e35760009060046040518094819363ef7c3a3960e01b83525af115614adb57806139a46000614c18936131ef565b38614adb565b602854614c33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f0000000000000000000000002fa878ab3f87cc1c9737fc071108f904c0b0c95d6001600160a01b0316600482015291600091839160249183915af115614ad357806139a46000614c98936131ef565b38614ad3565b602854614cb3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000095b303987a60c71504d99aa1b13b4da07b0790ab6001600160a01b0316600482015291600091839160249183915af115614acb57806139a46000614d18936131ef565b38614acb565b602854614d33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f000000000000000000000000616cb6a245ed4c11216ec58d10b6a2e87271845d6001600160a01b0316600482015291600091839160249183915af115614ac357806139a46000614d98936131ef565b38614ac3565b602854614db3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f0000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb396001600160a01b0316600482015291600091839160249183915af115614abb57806139a46000614e18936131ef565b38614abb565b602854614e33906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f00000000000000000000000057fde0a71132198bbec939b98976993d8d89d2256001600160a01b0316600482015291600091839160249183915af115614ab357806139a46000614e98936131ef565b38614ab3565b602854614eb3906001600160a01b031661390a565b90813b156130e35760405163f669d82b60e01b81527f0000000000000000000000008da17db850315a34532108f0f5458fc0401525f66001600160a01b0316600482015291600091839160249183915af115614aab57806139a46000614f18936131ef565b38614aab565b602854614f33906001600160a01b031661390a565b803b156130e3576000906004604051809481936322366e5b60e01b83525af115614aa357806139a46000614f66936131ef565b38614aa3565b614fb0916000808080614fab95614f9461390a61390a60225460018060a01b039060081c1690565b5af150614f9f61331e565b50602e549051906140c1565b602e55565b3880614a9a565b806139a46000614fc6936131ef565b3861469e565b602354614fe391906001600160a01b0316306136fa565b506145ce6145b0565b615017907f0000000000000000000000000000000000000000000000000000000000000000306136fa565b5061502e615029602d548551906140c1565b602d55565b386145a7565b602854615049906001600160a01b031661390a565b803b156130e35760405163add157b760e01b815260048101929092526000908290602490829084905af11561459e57806139a46000615087936131ef565b3861459e565b60405190610240820182811067ffffffffffffffff821117613211576040526000610220838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e08201528261010082015282610120820152826101408201528261016082015282610180820152826101a0820152826101c0820152826101e0820152826102008201520152565b6040516060919061513383826131ef565b6002815291601f1901366020840137565b8051156131d95760200190565b8051600110156131d95760400190565b80518210156131d95760209160051b010190565b9060809261519b919695949683526000602084015260a0604084015260a0830190613181565b6001600160a01b0390951660608201520152565b60009060033d116151bc57565b905060046000803e60005160e01c90565b600060443d1061353b576040513d600319016004823e8051913d602484011167ffffffffffffffff84111761523c578282019283519167ffffffffffffffff8311615234573d84016003190185840160200111615234575061353b929101602001906131ef565b949350505050565b92915050565b90615255602092828151948592016130e8565b0190565b6001600160a01b03166000818152600c602052604090205490919060ff16801561530c575b8015615302575b61433857615291613432565b9060005b82518110156152f5576001600160a01b036152b08285615161565b5116841480156152d0575b6152c757600101615295565b50505050600190565b506001600160a01b036152e38285615161565b51166001600160a01b038316146152bb565b5050505060ff6022541690565b5060275415615285565b506001600160a01b0381166000908152600c602052604090205460ff1661527e565b909161353b9261533d816153e9565b156153b857506127106153586153528461354c565b836142c3565b61536061433f565b50049130600052600560205261537b836040600020546140c1565b30600052600560205260406000205560405183815260008051602061545a8339815191526020309360018060a01b031692a361197f61197661402e565b6153c1836153e9565b156153da576153586153d5612710926134ab565b615352565b50612710615358601254615352565b6153f1613432565b9060005b8251811015615431576001600160a01b036154108285615161565b51166001600160a01b03831614615429576001016153f5565b505050600190565b50505060009056fe3e1799d428897e6f54bdb61036ad40e2aa67a45b0181c60fe2f15a9d33a084d6ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212200781aa9a34b72a12eadf9f859af885e76426ae460d6b3d4921e55088389fc11864736f6c634300081a0033