Contract 0x938a797ce6cb5333903e1994fb19010d3d1985a2

Contract Overview

Balance:
0 BNB
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xac014919be8d6fac5753c0e342cd5d5393426042ee2fb43936b3dd12c432c2daDeploy New Asset109220782021-07-26 7:54:3664 days 43 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747269
0x841a1eb6c857cdc8f526e5f4c946db99e77416cfc0faaa459b0bd48aa6ff9461Deploy New Asset108430222021-07-23 12:54:5566 days 19 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747305
0x6f7f89cce1e104d97e86ed7063900a3866be4c0405857e6bdcb5083c260ca899Deploy New Asset108429742021-07-23 12:52:3166 days 19 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746339
0xa636ee224b1e8ff56fcc44e5df2abcb184d6afb6f33e5e9d1fc86eb3d06e419aDeploy New Asset108402612021-07-23 10:36:4966 days 22 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746339
0x18bd42a20b18b3301d05b2f6847f41dbe83c720c627693d6bbe98f5bf6a2c807Deploy New Asset108396482021-07-23 10:06:1066 days 22 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746339
0x4bd77c615b3e182b7afafbf027f4f30b57efca1132a112d021d4abcab339117cDeploy New Asset108382652021-07-23 8:56:1066 days 23 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747317
0x95b0b71e0a75c57f186f5757e0a5409f0709734a21641e82ab908aa6304f014cDeploy New Asset108380602021-07-23 8:45:5566 days 23 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747317
0x708f0e119a4039a0df7c0da8cae21cb9f85bfa43844dc0b9d5985cb466de3ec9Deploy New Asset108378642021-07-23 8:36:0767 days 2 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747317
0x78688444a7f1333caf70e28622e0cd92d6b97a9aec549a3703351c40751e8334Deploy New Asset108378372021-07-23 8:34:4667 days 3 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05690055
0xc200ee4cab9dc4bd5b8820c7e200a6f53c4aeff5b78c795d6d0b74dce3e65d39Deploy New Asset108378142021-07-23 8:33:3767 days 4 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747245
0x290da65eebdc91502469a8f2fa18549c52d44be01c370b4ab399aaba5c53fe88Deploy New Asset108377922021-07-23 8:32:3167 days 5 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747245
0xaaab7345f1e5d3c90222d5128002cf7a38b9e4d23a72c2cb86fb7e6924f04e8bDeploy New Asset108369622021-07-23 7:51:0167 days 47 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05690091
0x07926c42f6bab943618300b53c88711dd14f5a6678479cb0d309b1bdb7ae85e1Deploy New Asset108367502021-07-23 7:40:2567 days 58 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747257
0x31375316b72d9cdedf3a1073aab25f3e3d7fd7d2ec5ce02973983defbcc8b88dDeploy New Asset108166872021-07-22 14:55:3567 days 17 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746279
0xb6e8df610713b283bdc2c4665eea39adda6853cbb04f3d92e4acdacc5b9bfcf4Deploy New Asset108162882021-07-22 14:35:3867 days 18 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746255
0x8726329d4b1381335d65cd48e2b587fb65dd85aa8dcbcee1f06a8d3e384342f1Deploy New Asset108159542021-07-22 14:18:5667 days 18 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746255
0xad6c2db4dfab349a858ac6895a40f358488e75bb6d54c8a749af0551acb761d1Deploy New Asset108152522021-07-22 13:43:5067 days 18 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746279
0x375feb403b3e2b46d6449eda0eb2a9ff5b2ac60a7237543b116c229b71998df0Deploy New Asset108139842021-07-22 12:40:2667 days 19 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746267
0xc89886fcfd560059be0dd139a7c3070c70b770fd53ede9236793561bc9b5977aDeploy New Asset108127322021-07-22 11:37:5067 days 21 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746315
0x83ffaaa46a31d0e79856a33aa593740f7e5a5b2bdd39a0fd91ec61f5e12d61a1Deploy New Asset108121472021-07-22 11:08:2967 days 21 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746315
0x06248b8c6a3d0b36837d3a3693c34b78d36ce73f5008ca917572faa4a3c0748dDeploy New Asset108095002021-07-22 8:56:0067 days 23 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747281
0x1d91e9388060064a792b62ded2141d3aa5177cd8b1b02fdd09b41dac3826a7b7Deploy New Asset108093822021-07-22 8:50:0667 days 23 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747281
0x2855538d928779792ae3a19729484887b06af4f5830b0739b43d42b79489dc5aDeploy New Asset108093672021-07-22 8:49:2167 days 23 hrs ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05152534
0x751217f11ee711884a5ae35573707af1f22e75e55371fe8cd00d24dfbf938280Deploy New Asset108088442021-07-22 8:23:1268 days 15 mins ago0xbdc0389aa5f6a7e858434c29d5eda973dfdea166 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05747281
0xb1a3608b39488a892ddefd684f159e29f97243fab3eeb2c421282eb046f1e84dDeploy New Asset107885142021-07-21 15:19:1868 days 17 hrs ago0xb248f0045493e9a43365441176ff1076d7463376 IN  0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB0.05746279
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x6ca4ae9c2c0e7296adefa7fe0174e31d89a0a79855c1a61a5d28d7c2b0d5cbe9109262162021-07-26 11:21:3063 days 21 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x6ca4ae9c2c0e7296adefa7fe0174e31d89a0a79855c1a61a5d28d7c2b0d5cbe9109262162021-07-26 11:21:3063 days 21 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xa51388d56c57753d85ad50ef953ae8286ae93cd927ab12ca76398850dc5997bd109252152021-07-26 10:31:2763 days 22 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xa51388d56c57753d85ad50ef953ae8286ae93cd927ab12ca76398850dc5997bd109252152021-07-26 10:31:2763 days 22 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xe19bff4b78f120748736a1efe9e9342025033e8fc74e3b3ad66e6844e7cb04a5109251152021-07-26 10:26:2763 days 22 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xe19bff4b78f120748736a1efe9e9342025033e8fc74e3b3ad66e6844e7cb04a5109251152021-07-26 10:26:2763 days 22 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x68affa7b0b7c1e2f6ee2adb969f2071848ea56e90ebcb2083440c471949a6a0d109247862021-07-26 10:10:0063 days 22 hrs ago 0x17d795e0be6d8d9efbaac958ee12bb7ddb99814f 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x0289b89191b26204d456a01e09ead64142409ba9befb1ac68d05547a59702805109247582021-07-26 10:08:3663 days 22 hrs ago 0x17d795e0be6d8d9efbaac958ee12bb7ddb99814f 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xcb5a2125fde2b7e2c5a3a61d145983aefd258e574f50b29dbc9a940d753035fb109247362021-07-26 10:07:3063 days 22 hrs ago 0x17d795e0be6d8d9efbaac958ee12bb7ddb99814f 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x06b893fc73dcb4ff9c8dc0b204b4db15f7035f8e8de0310f4f767a43be3ea009109237152021-07-26 9:16:2763 days 23 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x06b893fc73dcb4ff9c8dc0b204b4db15f7035f8e8de0310f4f767a43be3ea009109237152021-07-26 9:16:2763 days 23 hrs ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x4b668a140ce22908b16ef8b6f2d0db88be544455ee4c43bcf74c856279194373109227152021-07-26 8:26:2764 days 12 mins ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x4b668a140ce22908b16ef8b6f2d0db88be544455ee4c43bcf74c856279194373109227152021-07-26 8:26:2764 days 12 mins ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x087398dfd6649fdbdf2154a1f72ad43f834637945f9f4c72afed955f315c97be109226972021-07-26 8:25:3364 days 12 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x087398dfd6649fdbdf2154a1f72ad43f834637945f9f4c72afed955f315c97be109226972021-07-26 8:25:3364 days 12 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x087398dfd6649fdbdf2154a1f72ad43f834637945f9f4c72afed955f315c97be109226972021-07-26 8:25:3364 days 12 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x087398dfd6649fdbdf2154a1f72ad43f834637945f9f4c72afed955f315c97be109226972021-07-26 8:25:3364 days 12 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x9cf50b430945a6d8b43e33bf4b672ac9f726b6065365fbc0d7cbdaffd1a5b381109226162021-07-26 8:21:3064 days 17 mins ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x9cf50b430945a6d8b43e33bf4b672ac9f726b6065365fbc0d7cbdaffd1a5b381109226162021-07-26 8:21:3064 days 17 mins ago 0xc3afd0e38cdea0e97dc33b037694d374f37048f6 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x1ff630b8c1ca4112d73af190092cdda7524be4b03f891bd2339705acea04f6da109225262021-07-26 8:17:0064 days 21 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0x1ff630b8c1ca4112d73af190092cdda7524be4b03f891bd2339705acea04f6da109225262021-07-26 8:17:0064 days 21 mins ago 0xa5f8f4ac222b74ffe46b38f068adf97107fd3969 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xc8d9f57f60254ec2600af729e1e475f74720a2119ced0979292a95477bbdb562109224242021-07-26 8:11:5464 days 26 mins ago 0x7f8250162e707b425db89d14b90de3eefba55602 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xc8d9f57f60254ec2600af729e1e475f74720a2119ced0979292a95477bbdb562109224242021-07-26 8:11:5464 days 26 mins ago 0x7f8250162e707b425db89d14b90de3eefba55602 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xc8d9f57f60254ec2600af729e1e475f74720a2119ced0979292a95477bbdb562109224242021-07-26 8:11:5464 days 26 mins ago 0x7f8250162e707b425db89d14b90de3eefba55602 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
0xc8d9f57f60254ec2600af729e1e475f74720a2119ced0979292a95477bbdb562109224242021-07-26 8:11:5464 days 26 mins ago 0x7f8250162e707b425db89d14b90de3eefba55602 0x938a797ce6cb5333903e1994fb19010d3d1985a20 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AssetFactory

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 150 runs

Other Settings:
default evmVersion
File 1 of 23 : AssetFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./lib/AssetLib.sol";
import "./lib/AssetLib2.sol";

import "./interfaces/IAssetDeployCode.sol";
import "./interfaces/IAssetFactory.sol";
import "./interfaces/IAsset.sol";
import "./interfaces/IStaking.sol";

contract AssetFactory is AccessControl, ReentrancyGuard, IAssetFactory {
    using EnumerableSet for EnumerableSet.AddressSet;

    // public
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    address public deployCodeContract;
    address public oracle;
    address public override defaultDexRouter;
    address public override defaultDexFactory;
    address public override weth;
    address public zVault;

    address[] public allAssets;

    mapping(address => bool) public isTokenDefaultWhitelisted;
    mapping(address => address) public override notDefaultDexRouterToken;
    mapping(address => address) public override notDefaultDexFactoryToken;
    mapping(address => bool) public override isAddressDexRouter;
    address[] public defaultTokenWhitelist;

    // private
    mapping(address => uint256) private _defaultWhitelistIndexes;
    EnumerableSet.AddressSet private notDefaultDexTokensSet;

    event NewAssetDeploy(
        address newAsset,
        string name,
        string symbol,
        uint256 imeStartTimestamp,
        uint256 imeEndTimestamp,
        address[] tokensInAsset,
        uint256[] tokensDistribution
    );
    event WhitelistChange(address indexed token, bool newValue);

    modifier onlyManagerOrAdmin {
        address sender = _msgSender();
        require(
            hasRole(MANAGER_ROLE, sender) || hasRole(DEFAULT_ADMIN_ROLE, sender),
            "Access error"
        );
        _;
    }

    constructor(
        address _deployCodeContract,
        address _defaultDexRouter,
        address _defaultDexFactory
    ) {
        deployCodeContract = _deployCodeContract;
        defaultDexRouter = _defaultDexRouter;
        defaultDexFactory = _defaultDexFactory;
        address _weth = AssetLib.getWethFromDex(_defaultDexRouter);
        require(_weth != address(0), "Wrong dex");
        weth = _weth;

        isAddressDexRouter[_defaultDexRouter] = true;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MANAGER_ROLE, _msgSender());
    }

    function deployNewAsset(
        string memory name,
        string memory symbol,
        uint256[2] memory imeTimeParameters,
        address[] memory tokensInAsset,
        uint256[] memory tokensDistribution
    ) external virtual onlyManagerOrAdmin returns (address) {
        address _zVault = zVault;
        require(oracle != address(0), "Oracle not found");
        require(_zVault != address(0), "Oracle not found");

        IAsset assetInst = _deployAsset(
            name,
            symbol,
            imeTimeParameters,
            tokensInAsset,
            tokensDistribution
        );

        IStaking(_zVault).createPool(address(assetInst));

        emit NewAssetDeploy(
            address(assetInst),
            name,
            symbol,
            imeTimeParameters[0],
            imeTimeParameters[1],
            tokensInAsset,
            tokensDistribution
        );

        return address(assetInst);
    }

    function changeIsTokenWhitelisted(address token, bool value)
        external
        onlyManagerOrAdmin
        nonReentrant
    {
        AssetLib.changeWhitelist(
            token,
            value,
            address(this),
            defaultTokenWhitelist,
            isTokenDefaultWhitelisted,
            _defaultWhitelistIndexes
        );

        emit WhitelistChange(token, value);
    }

    function changeOracle(address newOracle) external onlyManagerOrAdmin {
        require(oracle == address(0) && newOracle != address(0), "Bad use");
        oracle = newOracle;
    }

    function changeZVault(address newZVault) external onlyManagerOrAdmin {
        require(zVault == address(0) && newZVault != address(0), "Bad use");
        zVault = newZVault;
    }

    function addNotDefaultDexToken(
        address token,
        address dexRouter,
        address dexFactory
    ) external onlyManagerOrAdmin {
        address _weth = AssetLib.getWethFromDex(dexRouter);
        require(_weth != address(0) && _weth == weth, "Wrong dex router");

        isAddressDexRouter[dexRouter] = true;

        notDefaultDexTokensSet.add(token);
        notDefaultDexRouterToken[token] = dexRouter;
        notDefaultDexFactoryToken[token] = dexFactory;

        address[] memory temp = new address[](1);
        temp[0] = token;
        AssetLib.checkIfTokensHavePair(temp, address(this));
    }

    function removeNotDefaultDexToken(address token) external onlyManagerOrAdmin {
        notDefaultDexTokensSet.remove(token);
        delete notDefaultDexRouterToken[token];
        delete notDefaultDexFactoryToken[token];
    }

    function allAssetsLen() external view returns (uint256) {
        return allAssets.length;
    }

    function defaultTokenWhitelistLen() external view returns (uint256) {
        return defaultTokenWhitelist.length;
    }

    function notDefaultDexTokensSetLen() external view returns (uint256) {
        return notDefaultDexTokensSet.length();
    }

    function getNotDefaultDexTokensSet(uint256 index) external view returns (address) {
        return notDefaultDexTokensSet.at(index);
    }

    function _deployAsset(
        string memory name,
        string memory symbol,
        uint256[2] memory imeTimeParameters,
        address[] memory tokensInAsset,
        uint256[] memory tokensDistribution
    ) internal returns (IAsset assetInst) {
        (bool success, bytes memory data) = deployCodeContract.delegatecall(
            abi.encodeWithSelector(IAssetDeployCode.newAsset.selector, bytes32(allAssets.length))
        );
        require(success == true, "Deploy failed");

        assetInst = IAsset(abi.decode(data, (address)));
        assetInst.__Asset_init(
            [name, symbol],
            [oracle, zVault],
            imeTimeParameters,
            defaultTokenWhitelist,
            tokensInAsset,
            tokensDistribution,
            weth
        );

        allAssets.push(address(assetInst));
    }
}

File 2 of 23 : IAsset.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAsset {
    function __Asset_init(
        string[2] memory nameSymbol,
        address[2] memory oracleAndZVault,
        uint256[2] memory imeTimeInfo,
        address[] calldata _tokenWhitelist,
        address[] calldata _tokensInAsset,
        uint256[] calldata _tokensDistribution,
        address weth_
    ) external;

    function mint(address tokenToPay, uint256 amount) external payable returns (uint256);

    function redeem(uint256 amount, address currencyToPay) external returns (uint256);
}

File 3 of 23 : IAssetDeployCode.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAssetDeployCode {
    function newAsset(bytes32 salt) external returns (address);
}

File 4 of 23 : IAssetFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IAssetFactory {
    function notDefaultDexRouterToken(address) external view returns (address);

    function notDefaultDexFactoryToken(address) external view returns (address);

    function defaultDexRouter() external view returns (address);

    function defaultDexFactory() external view returns (address);

    function weth() external view returns (address);

    function isAddressDexRouter(address) external view returns (bool);
}

File 5 of 23 : IOracle.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IOracle {
    function getData(address[] calldata tokens)
        external
        view
        returns (bool[] memory isValidValue, uint256[] memory tokensPrices);

    function uploadData(address[] calldata tokens, uint256[] calldata values) external;

    function getTimestampsOfLastUploads(address[] calldata tokens)
        external
        view
        returns (uint256[] memory timestamps);
}

File 6 of 23 : IStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IStaking {
    function stakeStart(
        address token,
        uint256 amount,
        uint8 timeIntervalIndex
    ) external;

    function stakeEnd(uint256 stakeIndex) external;

    function claimDividends(uint256 stakeIndex, uint256 maxDepth) external;

    function createPool(address token) external;

    function inputBnb() external payable;

    function treasuryWithdraw() external;
}

File 7 of 23 : AssetLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

import "../pancake-swap/interfaces/IPancakeRouter02.sol";
import "../pancake-swap/interfaces/IPancakeRouter02BNB.sol";
import "../pancake-swap/interfaces/IPancakeFactory.sol";
import "../pancake-swap/interfaces/IWETH.sol";

import "../interfaces/IOracle.sol";
import "../interfaces/IAssetFactory.sol";

library AssetLib {
    function calculateUserWeight(
        address user,
        address[] memory tokens,
        uint256[] memory tokenPricesInIme,
        mapping(address => mapping(address => uint256)) storage userEnters
    ) external view returns (uint256) {
        uint256 totalUserWeight;
        for (uint256 i = 0; i < tokens.length; ++i) {
            uint256 decimals_;
            if (tokens[i] == address(0)) {
                decimals_ = 18;
            } else {
                decimals_ = IERC20Metadata(tokens[i]).decimals();
            }
            totalUserWeight +=
                (userEnters[user][tokens[i]] * tokenPricesInIme[i]) /
                (10**decimals_);
        }
        return totalUserWeight;
    }

    function checkIfTokensHavePair(address[] memory tokens, address assetFactory) public view {
        address defaultDexFactory = IAssetFactory(assetFactory).defaultDexFactory();
        address defaultDexRouter = IAssetFactory(assetFactory).defaultDexRouter();

        for (uint256 i = 0; i < tokens.length; ++i) {
            address dexFactory = IAssetFactory(assetFactory).notDefaultDexFactoryToken(tokens[i]);
            address dexRouter;
            address weth;
            if (dexFactory != address(0)) {
                dexRouter = IAssetFactory(assetFactory).notDefaultDexRouterToken(tokens[i]);
            } else {
                dexFactory = defaultDexFactory;
                dexRouter = defaultDexRouter;
            }
            weth = getWethFromDex(dexRouter);

            if (tokens[i] == weth) {
                continue;
            }
            bool isValid = checkIfAddressIsToken(tokens[i]);
            require(isValid == true, "Address is not token");

            address pair = IPancakeFactory(dexFactory).getPair(tokens[i], weth);
            require(pair != address(0), "Not have eth pair");
        }
    }

    function checkIfAddressIsToken(address token) public view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(token)
        }
        if (size == 0) {
            return false;
        }
        try IERC20Metadata(token).decimals() returns (uint8) {
            return true;
        } catch (bytes memory) {
            return false;
        }
    }

    function initTokenToBuyInfo(
        address[] memory tokensToBuy,
        uint256 totalWeight,
        mapping(address => uint256) storage tokensDistribution,
        IOracle oracle
    ) external view returns (uint256[][5] memory, uint256[] memory) {
        /*
        tokenToBuyInfo
        0 - tokens to buy amounts
        1 - actual number to buy (tokens to buy amounts - tokensInAssetNow)
        2 - actual weight to buy
        3 - tokens decimals
        4 - is in asset already
         */
        uint256[][5] memory tokenToBuyInfo;
        for (uint256 i = 0; i < tokenToBuyInfo.length; ++i) {
            tokenToBuyInfo[i] = new uint256[](tokensToBuy.length);
        }

        (bool[] memory isValidValue, uint256[] memory tokensPrices) = oracle.getData(tokensToBuy);
        for (uint256 i = 0; i < tokensToBuy.length; ++i) {
            require(isValidValue[i] == true, "Oracle price error");

            tokenToBuyInfo[3][i] = IERC20Metadata(tokensToBuy[i]).decimals();

            uint256 tokenWeight = (tokensDistribution[tokensToBuy[i]] * totalWeight) / 1e4;
            tokenToBuyInfo[0][i] = (tokenWeight * (10**tokenToBuyInfo[3][i])) / tokensPrices[i];
        }

        return (tokenToBuyInfo, tokensPrices);
    }

    function initTokenToSellInfo(
        address[] memory tokensOld,
        IOracle oracle,
        mapping(address => uint256) storage totalTokenAmount
    ) external view returns (uint256[][3] memory, uint256) {
        uint256[][3] memory tokensOldInfo;
        for (uint256 i = 0; i < tokensOldInfo.length; ++i) {
            tokensOldInfo[i] = new uint256[](tokensOld.length);
        }

        (bool[] memory isValidValue, uint256[] memory tokensPrices) = oracle.getData(tokensOld);
        uint256 oldWeight;
        for (uint256 i = 0; i < tokensOld.length; ++i) {
            tokensOldInfo[0][i] = totalTokenAmount[tokensOld[i]];
            tokensOldInfo[2][i] = IERC20Metadata(tokensOld[i]).decimals();
            require(isValidValue[i] == true, "Oracle error");
            oldWeight += (tokensOldInfo[0][i] * tokensPrices[i]) / (10**tokensOldInfo[2][i]);
        }
        require(oldWeight != 0, "No value in asset");

        return (tokensOldInfo, oldWeight);
    }

    function checkAndWriteDistribution(
        address[] memory newTokensInAsset,
        uint256[] memory distribution,
        address[] memory oldTokens,
        mapping(address => uint256) storage tokensDistribution
    ) external {
        require(newTokensInAsset.length == distribution.length, "Input error");
        require(newTokensInAsset.length > 0, "Len error");
        uint256 totalPerc;
        for (uint256 i = 0; i < newTokensInAsset.length; ++i) {
            require(newTokensInAsset[i] != address(0), "Wrong token");
            require(distribution[i] > 0, "Zero distribution");
            for (uint256 j = i + 1; j < newTokensInAsset.length; ++j) {
                require(newTokensInAsset[i] != newTokensInAsset[j], "Input error");
            }
            tokensDistribution[newTokensInAsset[i]] = distribution[i];
            totalPerc += distribution[i];
        }
        require(totalPerc == 1e4, "Perc error");

        for (uint256 i = 0; i < oldTokens.length; ++i) {
            bool isFound = false;
            for (uint256 j = 0; j < newTokensInAsset.length && isFound == false; ++j) {
                if (newTokensInAsset[j] == oldTokens[i]) {
                    isFound = true;
                }
            }

            if (isFound == false) {
                tokensDistribution[oldTokens[i]] = 0;
            }
        }
    }

    function withdrawFromYForOwner(
        address[] memory tokensInAsset,
        uint256[] memory tokenAmounts,
        address sender,
        mapping(address => uint256) storage yVaultAmount,
        mapping(address => uint256) storage yVaultAmountInStaking
    ) external {
        require(tokenAmounts.length == tokensInAsset.length, "Invalid input");
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 yAmount = yVaultAmount[tokensInAsset[i]];
            require(yAmount >= tokenAmounts[i], "Not enough y balance");
            yAmount -= tokenAmounts[i];
            yVaultAmount[tokensInAsset[i]] = yAmount;
            yVaultAmountInStaking[tokensInAsset[i]] += tokenAmounts[i];

            safeTransfer(tokensInAsset[i], sender, tokenAmounts[i]);
        }
    }

    function checkAndWriteWhitelist(
        address[] memory tokenWhitelist,
        address assetFactory,
        mapping(address => uint256) storage _whitelistIndexes,
        mapping(address => bool) storage isTokenWhitelisted
    ) external {
        checkIfTokensHavePair(tokenWhitelist, assetFactory);
        for (uint256 i = 0; i < tokenWhitelist.length; ++i) {
            require(tokenWhitelist[i] != address(0), "No zero address");
            for (uint256 j = 0; j < i; ++j) {
                require(tokenWhitelist[i] != tokenWhitelist[j], "Whitelist error");
            }
            _whitelistIndexes[tokenWhitelist[i]] = i;
            isTokenWhitelisted[tokenWhitelist[i]] = true;
        }
    }

    function changeWhitelist(
        address token,
        bool value,
        address assetFactory,
        address[] storage tokenWhitelist,
        mapping(address => bool) storage isTokenWhitelisted,
        mapping(address => uint256) storage whitelistIndexes
    ) external {
        require(token != address(0), "Token error");

        bool oldValue = isTokenWhitelisted[token];
        if (value == false && oldValue == true) {
            uint256 index = whitelistIndexes[token];
            uint256 len = tokenWhitelist.length;
            if (index < len - 1) {
                address newToken = tokenWhitelist[len - 1];
                tokenWhitelist[index] = newToken;
                whitelistIndexes[newToken] = index;
            }
            tokenWhitelist.pop();
        } else if (value == true && oldValue == false) {
            address[] memory temp = new address[](1);
            temp[0] = token;
            checkIfTokensHavePair(temp, assetFactory);
            whitelistIndexes[token] = tokenWhitelist.length;
            tokenWhitelist.push(token);
        } else {
            revert("Wrong value");
        }

        isTokenWhitelisted[token] = value;
    }

    function sellTokensInAssetNow(
        address[] memory tokensInAssetNow,
        uint256[][3] memory tokensInAssetNowInfo,
        address weth,
        address assetFactory,
        mapping(address => uint256) storage totalTokenAmount
    ) external returns (uint256 availableWeth) {
        for (uint256 i = 0; i < tokensInAssetNow.length; ++i) {
            {
                address temp = tokensInAssetNow[i];
                if (totalTokenAmount[temp] == 0) {
                    totalTokenAmount[temp] = tokensInAssetNowInfo[0][i];
                }
            }

            if (tokensInAssetNowInfo[1][i] == 0) continue;

            if (tokensInAssetNow[i] == address(0)) {
                IWETH(weth).deposit{value: tokensInAssetNowInfo[1][i]}();
                availableWeth += tokensInAssetNowInfo[1][i];
            } else if (tokensInAssetNow[i] == address(weth)) {
                availableWeth += tokensInAssetNowInfo[1][i];
            } else if (tokensInAssetNow[i] != address(weth)) {
                availableWeth += safeSwap(
                    [tokensInAssetNow[i], weth],
                    tokensInAssetNowInfo[1][i],
                    assetFactory,
                    0
                );
            }
            {
                address temp = tokensInAssetNow[i];
                totalTokenAmount[temp] -= tokensInAssetNowInfo[1][i];
            }
        }
    }

    function buyTokensInAssetRebase(
        address[] memory tokensToBuy,
        uint256[][5] memory tokenToBuyInfo,
        uint256[2] memory tokenToBuyInfoGlobals,
        address weth,
        address assetFactory,
        uint256 availableWeth,
        mapping(address => uint256) storage totalTokenAmount
    ) external returns (uint256[] memory outputAmounts) {
        outputAmounts = new uint256[](tokensToBuy.length);
        if (tokenToBuyInfoGlobals[0] == 0 || availableWeth == 0) {
            return outputAmounts;
        }
        uint256 restWeth = availableWeth;
        for (uint256 i = 0; i < tokensToBuy.length && tokenToBuyInfoGlobals[1] > 0; ++i) {
            uint256 wethToSpend;
            // if actual weight to buy = 0
            if (tokenToBuyInfo[2][i] == 0) {
                continue;
            }
            if (tokenToBuyInfoGlobals[1] > 1) {
                wethToSpend = (availableWeth * tokenToBuyInfo[2][i]) / tokenToBuyInfoGlobals[0];
            } else {
                wethToSpend = restWeth;
            }
            require(wethToSpend > 0 && wethToSpend <= restWeth, "Internal error");

            restWeth -= wethToSpend;
            --tokenToBuyInfoGlobals[1];

            outputAmounts[i] = safeSwap([weth, tokensToBuy[i]], wethToSpend, assetFactory, 1);

            {
                address temp = tokensToBuy[i];
                totalTokenAmount[temp] += outputAmounts[i];
            }
        }

        require(restWeth == 0, "Internal error");

        return outputAmounts;
    }

    function transferTokenAndSwapToWeth(
        address tokenToPay,
        uint256 amount,
        address sender,
        address weth,
        address assetFactory
    ) external returns (address, uint256) {
        tokenToPay = transferFromToGoodToken(tokenToPay, sender, amount, weth);
        uint256 totalWeth;
        if (tokenToPay == weth) {
            totalWeth = amount;
        } else {
            totalWeth = safeSwap([tokenToPay, weth], amount, assetFactory, 0);
        }

        return (tokenToPay, totalWeth);
    }

    function transferFromToGoodToken(
        address token,
        address user,
        uint256 amount,
        address weth
    ) public returns (address) {
        if (token == address(0)) {
            require(msg.value == amount, "Value error");
            token = weth;
            IWETH(weth).deposit{value: amount}();
        } else {
            require(msg.value == 0, "Value error");
            AssetLib.safeTransferFrom(token, user, amount);
        }
        return token;
    }

    function checkCurrency(
        address currency,
        address weth,
        mapping(address => bool) storage isTokenWhitelisted
    ) external view {
        address currencyToCheck;
        if (currency == address(0)) {
            currencyToCheck = weth;
        } else {
            currencyToCheck = currency;
        }
        require(isTokenWhitelisted[currencyToCheck], "Not allowed currency");
    }

    function buyTokensMint(
        uint256 totalWeth,
        address[] memory tokensInAsset,
        address[2] memory wethAndAssetFactory,
        mapping(address => uint256) storage tokensDistribution,
        mapping(address => uint256) storage totalTokenAmount
    ) external returns (uint256[] memory buyAmounts, uint256[] memory oldDistribution) {
        buyAmounts = new uint256[](tokensInAsset.length);
        oldDistribution = new uint256[](tokensInAsset.length);
        uint256 restWeth = totalWeth;
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 wethToThisToken;
            if (i < tokensInAsset.length - 1) {
                wethToThisToken = (totalWeth * tokensDistribution[tokensInAsset[i]]) / 1e4;
            } else {
                wethToThisToken = restWeth;
            }
            require(wethToThisToken > 0 && wethToThisToken <= restWeth, "Internal error");

            restWeth -= wethToThisToken;

            oldDistribution[i] = totalTokenAmount[tokensInAsset[i]];

            buyAmounts[i] = safeSwap(
                [wethAndAssetFactory[0], tokensInAsset[i]],
                wethToThisToken,
                wethAndAssetFactory[1],
                1
            );

            totalTokenAmount[tokensInAsset[i]] = oldDistribution[i] + buyAmounts[i];
        }
    }

    function getMintAmount(
        address[] memory tokensInAsset,
        uint256[] memory buyAmounts,
        uint256[] memory oldDistribution,
        uint256 totalSupply,
        uint256 decimals,
        IOracle oracle
    ) public view returns (uint256 mintAmount) {
        uint256 totalPriceInAsset;
        uint256 totalPriceUser;
        (bool[] memory isValidValue, uint256[] memory tokensPrices) = oracle.getData(tokensInAsset);
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            require(isValidValue[i] == true, "Oracle error");
            uint256 decimalsToken = IERC20Metadata(tokensInAsset[i]).decimals();
            totalPriceInAsset += (oldDistribution[i] * tokensPrices[i]) / (10**decimalsToken);
            totalPriceUser += (buyAmounts[i] * tokensPrices[i]) / (10**decimalsToken);
        }

        if (totalPriceInAsset == 0 || totalSupply == 0) {
            return 10**decimals;
        } else {
            return (totalSupply * totalPriceUser) / totalPriceInAsset;
        }
    }

    function safeSwap(
        address[2] memory path,
        uint256 amount,
        address assetFactory,
        uint256 forWhatTokenDex
    ) public returns (uint256) {
        if (path[0] == path[1]) {
            return amount;
        }

        address dexRouter = getTokenDexRouter(assetFactory, path[forWhatTokenDex]);
        checkAllowance(path[0], dexRouter, amount);

        address[] memory _path = new address[](2);
        _path[0] = path[0];
        _path[1] = path[1];
        uint256[] memory amounts =
            IPancakeRouter02(dexRouter).swapExactTokensForTokens(
                amount,
                0,
                _path,
                address(this),
                // solhint-disable-next-line not-rely-on-time
                block.timestamp
            );

        return amounts[1];
    }

    function redeemAndTransfer(
        uint256[2] memory amountAndTotalSupply,
        address[4] memory userCurrencyToPayWethFactory,
        mapping(address => uint256) storage totalTokenAmount,
        address[] memory tokensInAsset,
        uint256[] memory feePercentages
    )
        public
        returns (
            uint256 feeTotal,
            uint256[] memory inputAmounts,
            uint256 outputAmountTotal
        )
    {
        inputAmounts = new uint256[](tokensInAsset.length);
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            inputAmounts[i] =
                (totalTokenAmount[tokensInAsset[i]] * amountAndTotalSupply[0]) /
                amountAndTotalSupply[1];

            uint256 outputAmount =
                swapToCurrency(
                    tokensInAsset[i],
                    userCurrencyToPayWethFactory[1],
                    inputAmounts[i],
                    [userCurrencyToPayWethFactory[2], userCurrencyToPayWethFactory[3]]
                );

            uint256 fee = (outputAmount * feePercentages[i]) / 1e4;
            outputAmountTotal += outputAmount - fee;
            feeTotal += fee;

            totalTokenAmount[tokensInAsset[i]] -= inputAmounts[i];
        }

        if (userCurrencyToPayWethFactory[1] == address(0)) {
            IWETH(userCurrencyToPayWethFactory[2]).withdraw(outputAmountTotal);
            safeTransfer(address(0), userCurrencyToPayWethFactory[0], outputAmountTotal);
        } else {
            safeTransfer(
                userCurrencyToPayWethFactory[1],
                userCurrencyToPayWethFactory[0],
                outputAmountTotal
            );
        }
    }

    function initTokenInfoFromWhitelist(
        address[] memory tokensWhitelist,
        mapping(address => uint256) storage tokenEntersIme
    ) external view returns (uint256[][3] memory tokensIncomeAmounts) {
        tokensIncomeAmounts[0] = new uint256[](tokensWhitelist.length);
        tokensIncomeAmounts[1] = new uint256[](tokensWhitelist.length);
        tokensIncomeAmounts[2] = new uint256[](tokensWhitelist.length);
        for (uint256 i = 0; i < tokensWhitelist.length; ++i) {
            tokensIncomeAmounts[0][i] = tokenEntersIme[tokensWhitelist[i]];
            tokensIncomeAmounts[2][i] = IERC20Metadata(tokensWhitelist[i]).decimals();
        }
    }

    function calculateXYAfterIme(
        address[] memory tokensInAsset,
        mapping(address => uint256) storage totalTokenAmount,
        mapping(address => uint256) storage xVaultAmount,
        mapping(address => uint256) storage yVaultAmount
    ) external {
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 amountTotal = totalTokenAmount[tokensInAsset[i]];
            uint256 amountToX = (amountTotal * 2000) / 1e4;

            xVaultAmount[tokensInAsset[i]] = amountToX;
            yVaultAmount[tokensInAsset[i]] = amountTotal - amountToX;
        }
    }

    function depositToY(
        address[] memory tokensInAsset,
        uint256[] memory tokenAmountsOfY,
        address[] memory tokensOfDividends,
        uint256[] memory amountOfDividends,
        address sender,
        address assetFactory,
        address weth,
        mapping(address => uint256) storage yVaultAmountInStaking,
        mapping(address => uint256) storage yVaultAmount
    ) external returns (uint256) {
        require(tokensInAsset.length == tokenAmountsOfY.length, "Input error 1");
        require(tokensOfDividends.length == amountOfDividends.length, "Input error 2");

        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 amountInStaking = yVaultAmountInStaking[tokensInAsset[i]];
            require(amountInStaking >= tokenAmountsOfY[i], "Trying to send more");
            amountInStaking -= tokenAmountsOfY[i];
            yVaultAmountInStaking[tokensInAsset[i]] = amountInStaking;
            yVaultAmount[tokensInAsset[i]] += tokenAmountsOfY[i];

            safeTransferFrom(tokensInAsset[i], sender, tokenAmountsOfY[i]);
        }

        uint256 totalWeth;
        for (uint256 i = 0; i < tokensOfDividends.length; ++i) {
            safeTransferFrom(tokensOfDividends[i], sender, amountOfDividends[i]);
            totalWeth += safeSwap(
                [tokensOfDividends[i], weth],
                amountOfDividends[i],
                assetFactory,
                0
            );
        }
        return totalWeth;
    }

    function proceedIme(
        address[] memory tokens,
        IOracle oracle,
        mapping(address => uint256) storage tokenEntersIme
    ) external view returns (uint256, uint256[] memory) {
        (bool[] memory isValidValue, uint256[] memory tokensPrices) = oracle.getData(tokens);

        uint256 totalWeight;
        for (uint256 i = 0; i < tokens.length; ++i) {
            require(isValidValue[i] == true, "Not valid oracle values");
            uint256 decimals_ = IERC20Metadata(tokens[i]).decimals();
            totalWeight += (tokenEntersIme[tokens[i]] * tokensPrices[i]) / (10**decimals_);
        }

        return (totalWeight, tokensPrices);
    }

    function getFeePercentagesRedeem(
        address[] memory tokensInAsset,
        mapping(address => uint256) storage totalTokenAmount,
        mapping(address => uint256) storage xVaultAmount
    ) external view returns (uint256[] memory feePercentages) {
        feePercentages = new uint256[](tokensInAsset.length);

        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 totalAmount = totalTokenAmount[tokensInAsset[i]];
            uint256 xAmount = xVaultAmount[tokensInAsset[i]];

            if (xAmount >= (1500 * totalAmount) / 1e4) {
                feePercentages[i] = 200;
            } else if (
                xAmount < (1500 * totalAmount) / 1e4 && xAmount >= (500 * totalAmount) / 1e4
            ) {
                uint256 xAmountPertcentage = (xAmount * 1e4) / totalAmount;
                feePercentages[i] = 600 - (400 * (xAmountPertcentage - 500)) / 1000;
            } else {
                revert("xAmount percentage error");
            }
        }
    }

    function swapToCurrency(
        address inputCurrency,
        address outputCurrency,
        uint256 amount,
        address[2] memory wethAndAssetFactory
    ) internal returns (uint256) {
        require(inputCurrency != address(0), "Internal error");
        if (inputCurrency != outputCurrency) {
            uint256 outputAmount;
            if (outputCurrency == wethAndAssetFactory[0] || outputCurrency == address(0)) {
                outputAmount = safeSwap(
                    [inputCurrency, wethAndAssetFactory[0]],
                    amount,
                    wethAndAssetFactory[1],
                    0
                );
            } else {
                outputAmount = safeSwap(
                    [inputCurrency, wethAndAssetFactory[0]],
                    amount,
                    wethAndAssetFactory[1],
                    0
                );
                outputAmount = safeSwap(
                    [wethAndAssetFactory[0], outputCurrency],
                    outputAmount,
                    wethAndAssetFactory[1],
                    1
                );
            }
            return outputAmount;
        } else {
            return amount;
        }
    }

    function safeTransferFrom(
        address token,
        address from,
        uint256 amount
    ) internal {
        if (token == address(0)) {
            require(msg.value == amount, "Value error");
        } else {
            require(IERC20(token).transferFrom(from, address(this), amount), "TransferFrom failed");
        }
    }

    function safeTransfer(
        address token,
        address to,
        uint256 amount
    ) public {
        if (to == address(this)) {
            return;
        }
        if (token == address(0)) {
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, ) = to.call{value: amount}(new bytes(0));
            require(success, "Transfer eth failed");
        } else {
            require(IERC20(token).transfer(to, amount), "Transfer token failed");
        }
    }

    function checkAllowance(
        address token,
        address to,
        uint256 amount
    ) public {
        uint256 allowance = IERC20(token).allowance(address(this), to);

        if (amount > allowance) {
            IERC20(token).approve(to, type(uint256).max);
        }
    }

    function getWethFromDex(address dexRouter) public view returns (address) {
        try IPancakeRouter02(dexRouter).WETH() returns (address weth) {
            return weth;
        } catch (bytes memory) {}

        try IPancakeRouter02BNB(dexRouter).WBNB() returns (address weth) {
            return weth;
        } catch (bytes memory) {
            return address(0);
        }
    }

    function getTokenDexRouter(address factory, address token) public view returns (address) {
        address customDexRouter = IAssetFactory(factory).notDefaultDexRouterToken(token);

        if (customDexRouter != address(0)) {
            return customDexRouter;
        } else {
            return IAssetFactory(factory).defaultDexRouter();
        }
    }
}

File 8 of 23 : AssetLib2.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

import "../pancake-swap/interfaces/IPancakeRouter02.sol";
import "../pancake-swap/interfaces/IPancakeRouter02BNB.sol";
import "../pancake-swap/interfaces/IWETH.sol";

import "../interfaces/IOracle.sol";

import "./AssetLib.sol";

library AssetLib2 {
    function calculateBuyAmountOut(
        uint256 amount,
        address currencyIn,
        address[] memory tokensInAsset,
        address[3] memory wethAssetFactoryAndOracle,
        uint256[2] memory totalSupplyAndDecimals,
        mapping(address => uint256) storage tokensDistribution,
        mapping(address => uint256) storage totalTokenAmount
    ) external view returns (uint256) {
        if (amount == 0) {
            return 0;
        }
        address[] memory path = new address[](2);
        if (currencyIn == address(0)) {
            currencyIn = wethAssetFactoryAndOracle[0];
        }
        if (currencyIn != wethAssetFactoryAndOracle[0]) {
            path[0] = currencyIn;
            path[1] = wethAssetFactoryAndOracle[0];
            address dexRouter =
                AssetLib.getTokenDexRouter(wethAssetFactoryAndOracle[1], currencyIn);
            try IPancakeRouter02(dexRouter).getAmountsOut(amount, path) returns (
                uint256[] memory amounts
            ) {
                amount = amounts[1];
            } catch (bytes memory) {
                amount = 0;
            }
        }
        if (amount == 0) {
            return 0;
        }
        amount -= (amount * 50) / 1e4;
        uint256 restAmount = amount;
        uint256[][2] memory buyAmountsAndDistribution;
        buyAmountsAndDistribution[0] = new uint256[](tokensInAsset.length);
        buyAmountsAndDistribution[1] = new uint256[](tokensInAsset.length);
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 wethToThisToken;
            buyAmountsAndDistribution[1][i] = totalTokenAmount[tokensInAsset[i]];
            if (i < tokensInAsset.length - 1) {
                wethToThisToken = (amount * tokensDistribution[tokensInAsset[i]]) / 1e4;
            } else {
                wethToThisToken = restAmount;
            }
            restAmount -= wethToThisToken;

            if (tokensInAsset[i] != wethAssetFactoryAndOracle[0]) {
                path[0] = wethAssetFactoryAndOracle[0];
                path[1] = tokensInAsset[i];
                address dexRouter =
                    AssetLib.getTokenDexRouter(wethAssetFactoryAndOracle[1], tokensInAsset[i]);
                try IPancakeRouter02(dexRouter).getAmountsOut(wethToThisToken, path) returns (
                    uint256[] memory amounts
                ) {
                    buyAmountsAndDistribution[0][i] = amounts[1];
                } catch (bytes memory) {
                    buyAmountsAndDistribution[0][i] = 0;
                }
            } else {
                buyAmountsAndDistribution[0][i] = wethToThisToken;
            }
        }

        return
            AssetLib.getMintAmount(
                tokensInAsset,
                buyAmountsAndDistribution[0],
                buyAmountsAndDistribution[1],
                totalSupplyAndDecimals[0],
                totalSupplyAndDecimals[1],
                IOracle(wethAssetFactoryAndOracle[2])
            );
    }

    function calculateSellAmountOut(
        uint256[2] memory amountAndTotalSupply,
        address currencyToPay,
        address[] memory tokensInAsset,
        address[2] memory wethAndAssetFactory,
        mapping(address => uint256) storage totalTokenAmount,
        mapping(address => uint256) storage xVaultAmount
    ) external view returns (uint256) {
        if (amountAndTotalSupply[0] == 0 || amountAndTotalSupply[1] == 0) {
            return 0;
        }
        if (currencyToPay == address(0)) {
            currencyToPay = wethAndAssetFactory[0];
        }
        uint256[] memory feePercentages =
            AssetLib.getFeePercentagesRedeem(tokensInAsset, totalTokenAmount, xVaultAmount);

        address[] memory path = new address[](2);
        uint256 outputAmountTotal;
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 inputAmount =
                (totalTokenAmount[tokensInAsset[i]] * amountAndTotalSupply[0]) /
                    amountAndTotalSupply[1];

            if (inputAmount == 0) {
                continue;
            }

            uint256 outputAmount;
            if (tokensInAsset[i] != currencyToPay) {
                if (
                    currencyToPay == wethAndAssetFactory[0] ||
                    tokensInAsset[i] == wethAndAssetFactory[0]
                ) {
                    address dexRouter;
                    if (tokensInAsset[i] != wethAndAssetFactory[0]) {
                        dexRouter = AssetLib.getTokenDexRouter(
                            wethAndAssetFactory[1],
                            tokensInAsset[i]
                        );
                    } else {
                        dexRouter = AssetLib.getTokenDexRouter(
                            wethAndAssetFactory[1],
                            currencyToPay
                        );
                    }
                    path[0] = tokensInAsset[i];
                    path[1] = currencyToPay;
                    try IPancakeRouter02(dexRouter).getAmountsOut(inputAmount, path) returns (
                        uint256[] memory amounts
                    ) {
                        outputAmount = amounts[1];
                    } catch (bytes memory) {
                        outputAmount = 0;
                    }
                } else {
                    address dexRouter =
                        AssetLib.getTokenDexRouter(wethAndAssetFactory[1], tokensInAsset[i]);
                    path[0] = tokensInAsset[i];
                    path[1] = wethAndAssetFactory[0];
                    try IPancakeRouter02(dexRouter).getAmountsOut(inputAmount, path) returns (
                        uint256[] memory amounts
                    ) {
                        outputAmount = amounts[1];
                    } catch (bytes memory) {
                        outputAmount = 0;
                        continue;
                    }

                    dexRouter = AssetLib.getTokenDexRouter(wethAndAssetFactory[1], currencyToPay);
                    path[0] = wethAndAssetFactory[0];
                    path[1] = currencyToPay;
                    try IPancakeRouter02(dexRouter).getAmountsOut(outputAmount, path) returns (
                        uint256[] memory amounts
                    ) {
                        outputAmount = amounts[1];
                    } catch (bytes memory) {
                        outputAmount = 0;
                    }
                }
            } else {
                outputAmount = inputAmount;
            }

            uint256 fee = (outputAmount * feePercentages[i]) / 1e4;
            outputAmountTotal += outputAmount - fee;
        }

        return outputAmountTotal;
    }

    function xyDistributionAfterMint(
        address[] memory tokensInAsset,
        uint256[] memory buyAmounts,
        uint256[] memory oldDistribution,
        mapping(address => uint256) storage xVaultAmount,
        mapping(address => uint256) storage yVaultAmount
    ) external {
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 totalAmount = buyAmounts[i] + oldDistribution[i];
            uint256 maxAmountInX = (totalAmount * 2000) / 1e4;

            uint256 amountInXOld = xVaultAmount[tokensInAsset[i]];
            uint256 restAmountToDistribute = buyAmounts[i];
            if (amountInXOld < maxAmountInX) {
                amountInXOld += restAmountToDistribute;
                if (amountInXOld > maxAmountInX) {
                    uint256 delta = amountInXOld - maxAmountInX;
                    amountInXOld = maxAmountInX;
                    restAmountToDistribute = delta;
                } else {
                    restAmountToDistribute = 0;
                }
            }

            if (restAmountToDistribute > 0) {
                yVaultAmount[tokensInAsset[i]] += restAmountToDistribute;
            }

            xVaultAmount[tokensInAsset[i]] = amountInXOld;
        }
    }

    function xyDistributionAfterRedeem(
        mapping(address => uint256) storage totalTokenAmount,
        bool isAllowedAutoXYRebalace,
        mapping(address => uint256) storage xVaultAmount,
        mapping(address => uint256) storage yVaultAmount,
        address[] memory tokensInAsset,
        uint256[] memory sellAmounts
    ) public {
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 totalAmount = totalTokenAmount[tokensInAsset[i]];
            uint256 xStopAmount = (totalAmount * 500) / 1e4;
            uint256 xAmountMax = (totalAmount * 2000) / 1e4;

            uint256 xAmount = xVaultAmount[tokensInAsset[i]];
            if (isAllowedAutoXYRebalace == true) {
                uint256 yAmount = yVaultAmount[tokensInAsset[i]];
                require(
                    xAmount + yAmount >= sellAmounts[i] &&
                        xAmount + yAmount - sellAmounts[i] >= xStopAmount,
                    "Not enough XY"
                );
                if (xAmount >= sellAmounts[i] && xAmount - sellAmounts[i] >= xStopAmount) {
                    xAmount -= sellAmounts[i];
                } else {
                    xAmount += yAmount;
                    xAmount -= sellAmounts[i];
                    if (xAmount > xAmountMax) {
                        uint256 delta = xAmount - xAmountMax;
                        yAmount = delta;
                        xAmount = xAmountMax;

                        yVaultAmount[tokensInAsset[i]] = yAmount;
                    }
                }
            } else {
                require(
                    xAmount >= sellAmounts[i] && xAmount - sellAmounts[i] >= xStopAmount,
                    "Not enough X"
                );
                xAmount -= sellAmounts[i];
            }
            xVaultAmount[tokensInAsset[i]] = xAmount;
        }
    }

    function xyDistributionAfterRebase(
        address[] memory tokensInAssetNow,
        uint256[] memory tokensInAssetNowSellAmounts,
        address[] memory tokensToBuy,
        uint256[] memory tokenToBuyAmounts,
        mapping(address => uint256) storage xVaultAmount,
        mapping(address => uint256) storage yVaultAmount,
        mapping(address => uint256) storage totalTokenAmount
    ) external {
        for (uint256 i = 0; i < tokensInAssetNow.length; ++i) {
            uint256 xAmount = xVaultAmount[tokensInAssetNow[i]];
            uint256 yAmount = yVaultAmount[tokensInAssetNow[i]];

            require(
                xAmount + yAmount >= tokensInAssetNowSellAmounts[i],
                "Not enought value in asset"
            );
            if (tokensInAssetNowSellAmounts[i] > yAmount) {
                xAmount -= tokensInAssetNowSellAmounts[i] - yAmount;
                yAmount = 0;
                xVaultAmount[tokensInAssetNow[i]] = xAmount;
                yVaultAmount[tokensInAssetNow[i]] = yAmount;
            } else {
                yAmount -= tokensInAssetNowSellAmounts[i];
                yVaultAmount[tokensInAssetNow[i]] = yAmount;
            }
        }

        for (uint256 i = 0; i < tokensToBuy.length; ++i) {
            uint256 xAmount = xVaultAmount[tokensToBuy[i]];
            uint256 yAmount = yVaultAmount[tokensToBuy[i]];
            uint256 xMaxAmount = (totalTokenAmount[tokensToBuy[i]] * 2000) / 1e4;

            xAmount += tokenToBuyAmounts[i];
            if (xAmount > xMaxAmount) {
                yAmount += xAmount - xMaxAmount;
                xAmount = xMaxAmount;
                xVaultAmount[tokensToBuy[i]] = xAmount;
                yVaultAmount[tokensToBuy[i]] = yAmount;
            } else {
                xVaultAmount[tokensToBuy[i]] = xAmount;
            }
        }
    }

    function xyRebalance(
        uint256 xPercentage,
        address[] memory tokensInAsset,
        mapping(address => uint256) storage xVaultAmount,
        mapping(address => uint256) storage yVaultAmount,
        mapping(address => uint256) storage totalTokenAmount
    ) external {
        for (uint256 i = 0; i < tokensInAsset.length; ++i) {
            uint256 totalAmount = totalTokenAmount[tokensInAsset[i]];
            uint256 xAmount = xVaultAmount[tokensInAsset[i]];
            uint256 yAmount = yVaultAmount[tokensInAsset[i]];
            uint256 xAmountDesired = (totalAmount * xPercentage) / 1e4;

            if (xAmount > xAmountDesired) {
                yAmount += xAmount - xAmountDesired;
                xAmount = xAmountDesired;
                xVaultAmount[tokensInAsset[i]] = xAmount;
                yVaultAmount[tokensInAsset[i]] = yAmount;
            } else if (xAmount < xAmountDesired) {
                uint256 delta = xAmountDesired - xAmount;
                require(yAmount >= delta, "Not enough value in Y");
                xAmount += delta;
                yAmount -= delta;
            } else {
                continue;
            }
            xVaultAmount[tokensInAsset[i]] = xAmount;
            yVaultAmount[tokensInAsset[i]] = yAmount;
        }
    }

    function swapTokensDex(
        address dexRouter,
        address[] memory path,
        uint256 amount
    ) external returns (uint256, bool) {
        try
            IPancakeRouter02(dexRouter).swapExactTokensForETH(
                amount,
                0,
                path,
                address(this),
                block.timestamp
            )
        returns (uint256[] memory amounts) {
            return (amounts[1], true);
        } catch (bytes memory) {}

        try
            IPancakeRouter02BNB(dexRouter).swapExactTokensForBNB(
                amount,
                0,
                path,
                address(this),
                block.timestamp
            )
        returns (uint256[] memory amounts) {
            return (amounts[1], true);
        } catch (bytes memory) {
            return (0, false);
        }
    }

    function addLiquidityETH(
        address dexRouter,
        address token,
        uint256 amount
    )
        external
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        AssetLib.checkAllowance(token, dexRouter, amount);
        try
            IPancakeRouter02(dexRouter).addLiquidityETH(
                token,
                amount,
                0,
                0,
                address(this),
                block.timestamp
            )
        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) {
            return (amountToken, amountETH, liquidity);
        } catch (bytes memory) {}

        try
            IPancakeRouter02BNB(dexRouter).addLiquidityBNB(
                token,
                amount,
                0,
                0,
                address(this),
                block.timestamp
            )
        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) {
            return (amountToken, amountETH, liquidity);
        } catch Error(string memory reason) {
            revert(reason);
        }
        /* catch (bytes memory) {
            revert("Wriong dex router");
        } */
    }

    function removeLiquidityBNB(
        address dexRouter,
        address token,
        address goodToken,
        uint256 amount
    )
        external
        returns (
            uint256,
            uint256,
            bool
        )
    {
        AssetLib.checkAllowance(token, dexRouter, amount);
        try
            IPancakeRouter02(dexRouter).removeLiquidityETH(
                goodToken,
                amount,
                0,
                0,
                address(this),
                block.timestamp
            )
        returns (uint256 amountToken, uint256 amountETH) {
            return (amountToken, amountETH, true);
        } catch Error(string memory reason) {
            if (compareStrings(reason, string("revert")) == 0) {
                return (0, 0, false);
            }
        }

        try
            IPancakeRouter02BNB(dexRouter).removeLiquidityBNB(
                goodToken,
                amount,
                0,
                0,
                address(this),
                block.timestamp
            )
        returns (uint256 amountToken, uint256 amountETH) {
            return (amountToken, amountETH, true);
        } catch (bytes memory) {
            return (0, 0, false);
        }
        /* catch (bytes memory) {
            revert("Wriong dex router");
        } */
    }

    function compareStrings(string memory _a, string memory _b) internal pure returns (int256) {
        bytes memory a = bytes(_a);
        bytes memory b = bytes(_b);
        uint256 minLength = a.length;
        if (b.length < minLength) minLength = b.length;
        //@todo unroll the loop into increments of 32 and do full 32 byte comparisons
        for (uint256 i = 0; i < minLength; i++)
            if (a[i] < b[i]) return -1;
            else if (a[i] > b[i]) return 1;
        if (a.length < b.length) return -1;
        else if (a.length > b.length) return 1;
        else return 0;
    }

    function fillInformationInSellAndBuyTokens(
        address[] memory tokensInAssetNow,
        uint256[][3] memory tokensInAssetNowInfo,
        address[] memory tokensToBuy,
        uint256[][5] memory tokenToBuyInfo,
        uint256[] memory tokensPrices
    )
        external
        pure
        returns (
            uint256[][3] memory,
            uint256[][5] memory,
            uint256[2] memory
        )
    {
        for (uint256 i = 0; i < tokensInAssetNow.length; ++i) {
            bool isFound = false;
            for (uint256 j = 0; j < tokensToBuy.length && isFound == false; ++j) {
                if (tokensInAssetNow[i] == tokensToBuy[j]) {
                    isFound = true;
                    // mark that we found that token in asset already
                    tokenToBuyInfo[4][j] = 1;

                    if (tokenToBuyInfo[0][j] >= tokensInAssetNowInfo[0][i]) {
                        // if need to buy more than asset already have

                        // amount to sell = 0 (already 0)
                        //tokensInAssetNowInfo[1][i] = 0;

                        // actual amount to buy = (total amount to buy) - (amount in asset already)
                        tokenToBuyInfo[1][j] = tokenToBuyInfo[0][j] - tokensInAssetNowInfo[0][i];
                    } else {
                        // if need to buy less than asset already have

                        // amount to sell = (amount in asset already) - (total amount to buy)
                        tokensInAssetNowInfo[1][i] =
                            tokensInAssetNowInfo[0][i] -
                            tokenToBuyInfo[0][j];

                        // actual amount to buy = 0 (already 0)
                        //tokenToBuyInfo[1][j] = 0;
                    }
                }
            }

            // if we don't find token in _tokensToBuy than we need to sell it all
            if (isFound == false) {
                tokensInAssetNowInfo[1][i] = tokensInAssetNowInfo[0][i];
            }
        }

        // tokenToBuyInfoGlobals info
        // 0 - total weight to buy
        // 1 - number of true tokens to buy
        uint256[2] memory tokenToBuyInfoGlobals;
        for (uint256 i = 0; i < tokensToBuy.length; ++i) {
            if (tokenToBuyInfo[4][i] == 0) {
                // if no found in asset yet

                // actual weight to buy = (amount to buy) * (token price) / decimals
                tokenToBuyInfo[2][i] =
                    (tokenToBuyInfo[0][i] * tokensPrices[i]) /
                    (10**tokenToBuyInfo[3][i]);
            } else if (tokenToBuyInfo[1][i] != 0) {
                // if found in asset and amount to buy != 0

                // actual weight to buy = (actual amount to buy) * (token price) / decimals
                tokenToBuyInfo[2][i] =
                    (tokenToBuyInfo[1][i] * tokensPrices[i]) /
                    (10**tokenToBuyInfo[3][i]);
            } else {
                // if found in asset and amount to buy = 0
                continue;
            }
            // increase total weight
            tokenToBuyInfoGlobals[0] += tokenToBuyInfo[2][i];
            // increase number of true tokens to buy
            ++tokenToBuyInfoGlobals[1];
        }

        return (tokensInAssetNowInfo, tokenToBuyInfo, tokenToBuyInfoGlobals);
    }
}

File 9 of 23 : IPancakeFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IPancakeFactory {
    event PairCreated(
        address indexed token0,
        address indexed token1,
        address pair,
        uint256
    );

    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(uint256) external view returns (address pair);

    function allPairsLength() external view returns (uint256);

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

    function setFeeTo(address) external;

    function setFeeToSetter(address) external;
}

File 10 of 23 : IPancakeRouter01.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IPancakeRouter01 {
    function factory() external view returns (address);

    function WETH() external view returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountETH);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);
}

File 11 of 23 : IPancakeRouter01BNB.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IPancakeRouter01BNB {
    function factory() external view returns (address);

    function WBNB() external view returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityBNB(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountBNB,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityBNB(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountBNB);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityBNBWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountBNB);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactBNBForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactBNB(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForBNB(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapBNBForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);
}

File 12 of 23 : IPancakeRouter02.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IPancakeRouter01.sol";

interface IPancakeRouter02 is IPancakeRouter01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

File 13 of 23 : IPancakeRouter02BNB.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IPancakeRouter01BNB.sol";

interface IPancakeRouter02BNB is IPancakeRouter01BNB {
    function removeLiquidityBNBSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountBNB);

    function removeLiquidityBNBWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountBNBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountBNB);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactBNBForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForBNBSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

File 14 of 23 : IWETH.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IWETH {
    function deposit() external payable;

    function transfer(address to, uint256 value) external returns (bool);

    function withdraw(uint256) external;
}

File 15 of 23 : AccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    function hasRole(bytes32 role, address account) external view returns (bool);
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
    function grantRole(bytes32 role, address account) external;
    function revokeRole(bytes32 role, address account) external;
    function renounceRole(bytes32 role, address account) external;
}

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping (address => bool) members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId
            || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if(!hasRole(role, account)) {
            revert(string(abi.encodePacked(
                "AccessControl: account ",
                Strings.toHexString(uint160(account), 20),
                " is missing role ",
                Strings.toHexString(uint256(role), 32)
            )));
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 16 of 23 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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 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;

    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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 17 of 23 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

File 18 of 23 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 19 of 23 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/*
 * @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) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 20 of 23 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = alphabet[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

}

File 21 of 23 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 22 of 23 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

File 23 of 23 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 150
  },
  "evmVersion": "istanbul",
  "libraries": {
    "/wsl-files/work/git/eth/ydragon-contracts/contracts/lib/AssetLib.sol": {
      "AssetLib": "0x6c573ac78fA7A162fDC36B0278D91Fcf5E99bEB0"
    },
    "/wsl-files/work/git/eth/ydragon-contracts/contracts/lib/AssetLib2.sol": {
      "AssetLib2": "0x135aac689139b021c1C639e7A61068277C04b0A8"
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_deployCodeContract","type":"address"},{"internalType":"address","name":"_defaultDexRouter","type":"address"},{"internalType":"address","name":"_defaultDexFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAsset","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"uint256","name":"imeStartTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"imeEndTimestamp","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"tokensInAsset","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"tokensDistribution","type":"uint256[]"}],"name":"NewAssetDeploy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"newValue","type":"bool"}],"name":"WhitelistChange","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"dexRouter","type":"address"},{"internalType":"address","name":"dexFactory","type":"address"}],"name":"addNotDefaultDexToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allAssetsLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"changeIsTokenWhitelisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOracle","type":"address"}],"name":"changeOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newZVault","type":"address"}],"name":"changeZVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultDexFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultDexRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"defaultTokenWhitelist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultTokenWhitelistLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployCodeContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256[2]","name":"imeTimeParameters","type":"uint256[2]"},{"internalType":"address[]","name":"tokensInAsset","type":"address[]"},{"internalType":"uint256[]","name":"tokensDistribution","type":"uint256[]"}],"name":"deployNewAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getNotDefaultDexTokensSet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAddressDexRouter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTokenDefaultWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"notDefaultDexFactoryToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"notDefaultDexRouterToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"notDefaultDexTokensSetLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeNotDefaultDexToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b50604051620020bf380380620020bf8339810160408190526200003491620002bf565b60018055600280546001600160a01b038581166001600160a01b0319928316179092556004805485841690831681178255600580549486169490931693909317909155604051636c8489e360e01b815290810191909152600090736c573ac78fa7a162fdc36b0278d91fcf5e99beb090636c8489e39060240160206040518083038186803b158015620000c657600080fd5b505af4158015620000db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010191906200029b565b90506001600160a01b0381166200014a5760405162461bcd60e51b81526020600482015260096024820152680aee4dedcce40c8caf60bb1b604482015260640160405180910390fd5b600680546001600160a01b0319166001600160a01b038381169190911790915583166000908152600c60205260408120805460ff191660011790556200019890620001923390565b620001ce565b620001c47f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0833620001ce565b5050505062000308565b620001da8282620001de565b5050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16620001da576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556200023a3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b80516001600160a01b03811681146200029657600080fd5b919050565b600060208284031215620002ad578081fd5b620002b8826200027e565b9392505050565b600080600060608486031215620002d4578182fd5b620002df846200027e565b9250620002ef602085016200027e565b9150620002ff604085016200027e565b90509250925092565b611da780620003186000396000f3fe608060405234801561001057600080fd5b50600436106101ba5760003560e01c80637bd09520116100f4578063b0d98e71116100a2578063d8f7af0c11610071578063d8f7af0c1461040b578063dd9ab7921461041e578063ec87621c14610426578063ed553e521461043b57600080fd5b8063b0d98e71146103bf578063d547741f146103d2578063d578192c146103e5578063d59da131146103f857600080fd5b80637bd09520146103455780637dc0d1d014610358578063841364f11461036b57806391d148541461037e57806393d8ee8f14610391578063a1ca33bd146103a4578063a217fddf146103b757600080fd5b80633fc8cef31161016c578063578e5c221161013b578063578e5c22146102e95780635b20b02a146102fc5780635cc5888a1461030f578063629424f41461032257600080fd5b80633fc8cef3146102a857806341f8fafe146102bb57806347c421b5146102ce57806348c0d96f146102e157600080fd5b806301ffc9a7146101bf57806313d20a42146101e757806315bc26971461020a578063171dee371461024b578063248a9ca31461025d5780632f2ff15d1461028057806336568abe14610295575b600080fd5b6101d26101cd366004611753565b610464565b60405190151581526020015b60405180910390f35b6101d26101f5366004611659565b600c6020526000908152604090205460ff1681565b610233610218366004611659565b600b602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101de565b6008545b6040519081526020016101de565b61024f61026b366004611717565b60009081526020819052604090206001015490565b61029361028e36600461172f565b61049b565b005b6102936102a336600461172f565b6104c6565b600654610233906001600160a01b031681565b6102936102c9366004611659565b610549565b6102936102dc366004611659565b6105d8565b61024f610698565b6102336102f7366004611717565b6106a9565b61029361030a366004611659565b6106d3565b61029361031d366004611691565b610793565b6101d2610330366004611659565b60096020526000908152604090205460ff1681565b6102936103533660046116db565b610a0c565b600354610233906001600160a01b031681565b610233610379366004611717565b610b86565b6101d261038c36600461172f565b610b96565b600554610233906001600160a01b031681565b6102336103b236600461177b565b610bbf565b61024f600081565b600754610233906001600160a01b031681565b6102936103e036600461172f565b610d8b565b600454610233906001600160a01b031681565b610233610406366004611717565b610db1565b600254610233906001600160a01b031681565b600d5461024f565b61024f600080516020611d5283398151915281565b610233610449366004611659565b600a602052600090815260409020546001600160a01b031681565b60006001600160e01b03198216637965db0b60e01b148061049557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000828152602081905260409020600101546104b78133610dbe565b6104c18383610e22565b505050565b6001600160a01b038116331461053b5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b6105458282610ea6565b5050565b33610562600080516020611d5283398151915282610b96565b806105735750610573600082610b96565b61058f5760405162461bcd60e51b815260040161053290611bd8565b61059a600f83610f0b565b50506001600160a01b03166000908152600a6020908152604080832080546001600160a01b0319908116909155600b90925290912080549091169055565b336105f1600080516020611d5283398151915282610b96565b806106025750610602600082610b96565b61061e5760405162461bcd60e51b815260040161053290611bd8565b6003546001600160a01b031615801561063f57506001600160a01b03821615155b6106755760405162461bcd60e51b81526020600482015260076024820152664261642075736560c81b6044820152606401610532565b50600380546001600160a01b0319166001600160a01b0392909216919091179055565b60006106a4600f610f27565b905090565b600881815481106106b957600080fd5b6000918252602090912001546001600160a01b0316905081565b336106ec600080516020611d5283398151915282610b96565b806106fd57506106fd600082610b96565b6107195760405162461bcd60e51b815260040161053290611bd8565b6007546001600160a01b031615801561073a57506001600160a01b03821615155b6107705760405162461bcd60e51b81526020600482015260076024820152664261642075736560c81b6044820152606401610532565b50600780546001600160a01b0319166001600160a01b0392909216919091179055565b336107ac600080516020611d5283398151915282610b96565b806107bd57506107bd600082610b96565b6107d95760405162461bcd60e51b815260040161053290611bd8565b604051636c8489e360e01b81526001600160a01b0384166004820152600090736c573ac78fa7a162fdc36b0278d91fcf5e99beb090636c8489e39060240160206040518083038186803b15801561082f57600080fd5b505af4158015610843573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108679190611675565b90506001600160a01b0381161580159061088e57506006546001600160a01b038281169116145b6108cd5760405162461bcd60e51b815260206004820152601060248201526f2bb937b733903232bc103937baba32b960811b6044820152606401610532565b6001600160a01b0384166000908152600c60205260409020805460ff191660011790556108fb600f86610f31565b506001600160a01b038581166000908152600a6020908152604080832080546001600160a01b03199081168a871617909155600b8352818420805490911694881694909417909355825160018082528185019094529192908281019080368337019050509050858160008151811061098357634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015260405163046e032360e21b8152736c573ac78fa7a162fdc36b0278d91fcf5e99beb0906311b80c8c906109d49084903090600401611aa5565b60006040518083038186803b1580156109ec57600080fd5b505af4158015610a00573d6000803e3d6000fd5b50505050505050505050565b33610a25600080516020611d5283398151915282610b96565b80610a365750610a36600082610b96565b610a525760405162461bcd60e51b815260040161053290611bd8565b60026001541415610aa55760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610532565b600260015560405163656160f560e01b81526001600160a01b03841660048201528215156024820152306044820152600d606482015260096084820152600e60a4820152736c573ac78fa7a162fdc36b0278d91fcf5e99beb09063656160f59060c40160006040518083038186803b158015610b2057600080fd5b505af4158015610b34573d6000803e3d6000fd5b50505050826001600160a01b03167f3f50d04114467f3ca25c50f1540e2864f8a852c0380bbbbb603ebfda10b5baf283604051610b75911515815260200190565b60405180910390a250506001805550565b600d81815481106106b957600080fd5b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b600033610bda600080516020611d5283398151915282610b96565b80610beb5750610beb600082610b96565b610c075760405162461bcd60e51b815260040161053290611bd8565b6007546003546001600160a01b039182169116610c595760405162461bcd60e51b815260206004820152601060248201526f13dc9858db19481b9bdd08199bdd5b9960821b6044820152606401610532565b6001600160a01b038116610ca25760405162461bcd60e51b815260206004820152601060248201526f13dc9858db19481b9bdd08199bdd5b9960821b6044820152606401610532565b6000610cb18989898989610f46565b604051634824fce960e11b81526001600160a01b03808316600483015291925090831690639049f9d290602401600060405180830381600087803b158015610cf857600080fd5b505af1158015610d0c573d6000803e3d6000fd5b505050507f77385131d5e50e3148358fee0babb8b6c33931370bc5533b7adbb4a3dbeb4831818a8a8a600060028110610d5557634e487b7160e01b600052603260045260246000fd5b60200201518b600160200201518b8b604051610d779796959493929190611a2d565b60405180910390a198975050505050505050565b600082815260208190526040902060010154610da78133610dbe565b6104c18383610ea6565b6000610495600f83611131565b610dc88282610b96565b61054557610de0816001600160a01b0316601461113d565b610deb83602061113d565b604051602001610dfc9291906119be565b60408051601f198184030181529082905262461bcd60e51b825261053291600401611bc5565b610e2c8282610b96565b610545576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610e623390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610eb08282610b96565b15610545576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000610f20836001600160a01b03841661131f565b9392505050565b6000610495825490565b6000610f20836001600160a01b038416611436565b600254600854604080516024808201939093528151808203909301835260440181526020820180516001600160e01b031663eaf94f7560e01b17905251600092839283926001600160a01b0390921691610fa091906119a2565b600060405180830381855af49150503d8060008114610fdb576040519150601f19603f3d011682016040523d82523d6000602084013e610fe0565b606091505b5090925090506001821515146110285760405162461bcd60e51b815260206004820152600d60248201526c11195c1b1bde4819985a5b1959609a1b6044820152606401610532565b8080602001905181019061103c9190611675565b6040805180820182528a815260208082018b9052825180840184526003546001600160a01b0390811682526007548116928201929092526006549351633c7c6e2360e21b81529497508188169463f1f1b88c946110a894938d92600d928e928e92911690600401611b02565b600060405180830381600087803b1580156110c257600080fd5b505af11580156110d6573d6000803e3d6000fd5b5050600880546001810182556000919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30180546001600160a01b0319166001600160a01b038716179055509298975050505050505050565b6000610f208383611485565b6060600061114c836002611c94565b611157906002611c7c565b67ffffffffffffffff81111561117d57634e487b7160e01b600052604160045260246000fd5b6040519080825280601f01601f1916602001820160405280156111a7576020820181803683370190505b509050600360fc1b816000815181106111d057634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061120d57634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a9053506000611231846002611c94565b61123c906001611c7c565b90505b60018111156112d0576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061127e57634e487b7160e01b600052603260045260246000fd5b1a60f81b8282815181106112a257634e487b7160e01b600052603260045260246000fd5b60200101906001600160f81b031916908160001a90535060049490941c936112c981611cf6565b905061123f565b508315610f205760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610532565b6000818152600183016020526040812054801561142c576000611343600183611cb3565b855490915060009061135790600190611cb3565b9050600086600001828154811061137e57634e487b7160e01b600052603260045260246000fd5b90600052602060002001549050808760000184815481106113af57634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910192909255828152600189019091526040902084905586548790806113f057634e487b7160e01b600052603160045260246000fd5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610495565b6000915050610495565b600081815260018301602052604081205461147d57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610495565b506000610495565b815460009082106114e35760405162461bcd60e51b815260206004820152602260248201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604482015261647360f01b6064820152608401610532565b82600001828154811061150657634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b600082601f830112611529578081fd5b8135602061153e61153983611c58565b611c27565b80838252828201915082860187848660051b890101111561155d578586fd5b855b8581101561158457813561157281611d39565b8452928401929084019060010161155f565b5090979650505050505050565b600082601f8301126115a1578081fd5b813560206115b161153983611c58565b80838252828201915082860187848660051b89010111156115d0578586fd5b855b85811015611584578135845292840192908401906001016115d2565b600082601f8301126115fe578081fd5b813567ffffffffffffffff81111561161857611618611d23565b61162b601f8201601f1916602001611c27565b81815284602083860101111561163f578283fd5b816020850160208301379081016020019190915292915050565b60006020828403121561166a578081fd5b8135610f2081611d39565b600060208284031215611686578081fd5b8151610f2081611d39565b6000806000606084860312156116a5578182fd5b83356116b081611d39565b925060208401356116c081611d39565b915060408401356116d081611d39565b809150509250925092565b600080604083850312156116ed578182fd5b82356116f881611d39565b91506020830135801515811461170c578182fd5b809150509250929050565b600060208284031215611728578081fd5b5035919050565b60008060408385031215611741578182fd5b82359150602083013561170c81611d39565b600060208284031215611764578081fd5b81356001600160e01b031981168114610f20578182fd5b600080600080600060c08688031215611792578081fd5b853567ffffffffffffffff808211156117a9578283fd5b6117b589838a016115ee565b96506020915081880135818111156117cb578384fd5b6117d78a828b016115ee565b96505088605f8901126117e8578283fd5b6117f0611bfe565b8060408a0160808b018c811115611805578687fd5b865b600281101561182457823585529386019391860191600101611807565b50919750503592505080821115611839578283fd5b61184589838a01611519565b935060a088013591508082111561185a578283fd5b5061186788828901611591565b9150509295509295909350565b8060005b60028110156118a05781516001600160a01b0316845260209384019390910190600101611878565b50505050565b6000815180845260208085019450808401835b838110156118de5781516001600160a01b0316875295820195908201906001016118b9565b509495945050505050565b6000815480845260208085019450838352808320835b838110156118de5781546001600160a01b0316875295820195600191820191016118ff565b8060005b60028110156118a0578151845260209384019390910190600101611928565b6000815180845260208085019450808401835b838110156118de5781518752958201959082019060010161195a565b6000815180845261198e816020860160208601611cca565b601f01601f19169290920160200192915050565b600082516119b4818460208701611cca565b9190910192915050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516119f0816017850160208801611cca565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351611a21816028840160208801611cca565b01602801949350505050565b6001600160a01b038816815260e060208201819052600090611a5190830189611976565b8281036040840152611a638189611976565b905086606084015285608084015282810360a0840152611a8381866118a6565b905082810360c0840152611a978185611947565b9a9950505050505050505050565b604080825283519082018190526000906020906060840190828701845b82811015611ae75781516001600160a01b031684529284019290840190600101611ac2565b5050506001600160a01b039490941692019190915250919050565b610120808252600090820161016083018a835b6002811015611b495761011f19868403018452611b33838351611976565b6020948501949093509190910190600101611b15565b5050611b58602085018b611874565b611b65606085018a611924565b83810360a0850152611b7781896118e9565b91505082810360c0840152611b8c81876118a6565b905082810360e0840152611ba08186611947565b915050611bb96101008301846001600160a01b03169052565b98975050505050505050565b602081526000610f206020830184611976565b6020808252600c908201526b20b1b1b2b9b99032b93937b960a11b604082015260600190565b6040805190810167ffffffffffffffff81118282101715611c2157611c21611d23565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611c5057611c50611d23565b604052919050565b600067ffffffffffffffff821115611c7257611c72611d23565b5060051b60200190565b60008219821115611c8f57611c8f611d0d565b500190565b6000816000190483118215151615611cae57611cae611d0d565b500290565b600082821015611cc557611cc5611d0d565b500390565b60005b83811015611ce5578181015183820152602001611ccd565b838111156118a05750506000910152565b600081611d0557611d05611d0d565b506000190190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114611d4e57600080fd5b5056fe241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08a2646970667358221220c6aa16a1db560dd6c8b45e3c0309a61f79ddeaaf7327963a62462322d108cff264736f6c63430008040033000000000000000000000000b3233b39b2c6f39af72062f422f23e79d656180c0000000000000000000000002f377dedf6e09907d821ec4fbd51f13826539f43000000000000000000000000fc7bc32889460a4fe25f18b9997d4b88b8db048d

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

000000000000000000000000b3233b39b2c6f39af72062f422f23e79d656180c0000000000000000000000002f377dedf6e09907d821ec4fbd51f13826539f43000000000000000000000000fc7bc32889460a4fe25f18b9997d4b88b8db048d
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000b3233b39b2c6f39af72062f422f23e79d656180c
Arg [1] : 0000000000000000000000002f377dedf6e09907d821ec4fbd51f13826539f43
Arg [2] : 000000000000000000000000fc7bc32889460a4fe25f18b9997d4b88b8db048d


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading