ETH Price: $3,442.26 (-1.11%)
Gas: 9 Gwei

Contract

0x8149276f275EEFAc110D74AFE8AFECEaeC7d1593
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Change Router Ow...149070832022-06-05 4:32:36774 days ago1654403556IN
0x8149276f...aeC7d1593
0 ETH0.0011422840
Add Approved Tok...149070802022-06-05 4:32:23774 days ago1654403543IN
0x8149276f...aeC7d1593
0 ETH0.0022176440
Add SS Contract149070742022-06-05 4:30:50774 days ago1654403450IN
0x8149276f...aeC7d1593
0 ETH0.0028019640
Add Dispenser Co...149070722022-06-05 4:30:35774 days ago1654403435IN
0x8149276f...aeC7d1593
0 ETH0.0028019240
Add Fixed Rate C...149070692022-06-05 4:29:16774 days ago1654403356IN
0x8149276f...aeC7d1593
0 ETH0.0028002840
Add Factory149070672022-06-05 4:28:41774 days ago1654403321IN
0x8149276f...aeC7d1593
0 ETH0.0019145640
0x60806040149070222022-06-05 4:16:04774 days ago1654402564IN
 Create: FactoryRouter
0 ETH0.1701892440

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
151098532022-07-09 17:50:40739 days ago1657389040
0x8149276f...aeC7d1593
 Contract Creation0 ETH
149234832022-06-07 23:15:27771 days ago1654643727
0x8149276f...aeC7d1593
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FactoryRouter

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 18 : FactoryRouter.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import "./balancer/BFactory.sol";
import "../interfaces/IFactory.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IFixedRateExchange.sol";
import "../interfaces/IPool.sol";
import "../interfaces/IDispenser.sol";
import "../utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract FactoryRouter is BFactory, IFactoryRouter {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;
    address public routerOwner;
    address public factory;
    address public fixedRate;
    uint256 public minVestingPeriodInBlocks = 2426000;

    uint256 public swapOceanFee = 1e15; //0.1%
    uint256 public swapNonOceanFee = 2e15;  // 0.2%
    uint256 public consumeFee = 3e16; // 0.03 DT
    uint256 public providerFee = 0; // 0%
    address[] public approvedTokens;
    address[] public ssContracts;
    address[] public fixedrates;
    address[] public dispensers;
    // mapping(address => bool) public approvedTokens;
    // mapping(address => bool) public ssContracts;
    // mapping(address => bool) public fixedPrice;
    // mapping(address => bool) public dispenser;

    event NewPool(address indexed poolAddress, bool isOcean);
    event VestingPeriodChanges(address indexed caller, uint256 minVestingPeriodInBlocks);
    event RouterChanged(address indexed caller, address indexed newRouter);
    event FactoryContractChanged(
        address indexed caller,
        address indexed contractAddress
    );
    event TokenAdded(address indexed caller, address indexed token);
    event TokenRemoved(address indexed caller, address indexed token);
    event SSContractAdded(
        address indexed caller,
        address indexed contractAddress
    );
    event SSContractRemoved(
        address indexed caller,
        address indexed contractAddress
    );
    event FixedRateContractAdded(
        address indexed caller,
        address indexed contractAddress
    );
    event FixedRateContractRemoved(
        address indexed caller,
        address indexed contractAddress
    );
    event DispenserContractAdded(
        address indexed caller,
        address indexed contractAddress
    );
    event DispenserContractRemoved(
        address indexed caller,
        address indexed contractAddress
    );

    event OPCFeeChanged(address indexed caller, uint256 newSwapOceanFee,
        uint256 newSwapNonOceanFee, uint256 newConsumeFee, uint256 newProviderFee);

    modifier onlyRouterOwner() {
        require(routerOwner == msg.sender, "OceanRouter: NOT OWNER");
        _;
    }

    event OPCCollectorChanged(address indexed caller, address indexed _newOpcCollector);

    constructor(
        address _routerOwner,
        address _oceanToken,
        address _bpoolTemplate,
        address _opcCollector,
        address[] memory _preCreatedPools
    ) BFactory(_bpoolTemplate, _opcCollector, _preCreatedPools) {
        require(
            _routerOwner != address(0),
            "FactoryRouter: Invalid router owner"
        );
        require(
            _opcCollector != address(0),
            "FactoryRouter: Invalid opcCollector"
        );
        require(
            _oceanToken != address(0),
            "FactoryRouter: Invalid Ocean Token address"
        );
        routerOwner = _routerOwner;
        opcCollector = _opcCollector;
        _addApprovedToken(_oceanToken);
    }

    function changeRouterOwner(address _routerOwner) external onlyRouterOwner {
        require(_routerOwner != address(0), "Invalid new router owner");
        routerOwner = _routerOwner;
        emit RouterChanged(msg.sender, _routerOwner);
    }

    /**
     * @dev addApprovedToken
     *      Adds a token to the list of tokens with reduced fees
     *  @param tokenAddress address Token to be added
     */
    function addApprovedToken(address tokenAddress) external onlyRouterOwner {
        _addApprovedToken(tokenAddress);
    }
    
    function _addApprovedToken(address tokenAddress) internal {
        if(!isApprovedToken(tokenAddress)){
            approvedTokens.push(tokenAddress);
            emit TokenAdded(msg.sender, tokenAddress);
        }
    }

    /**
     * @dev removeApprovedToken
     *      Removes a token if exists from the list of tokens with reduced fees
     *  @param tokenAddress address Token to be removed
     */
    function removeApprovedToken(address tokenAddress)
        external
        onlyRouterOwner
    {
        require(
            tokenAddress != address(0),
            "FactoryRouter: Invalid Ocean Token address"
        );
        uint256 i;
        for (i = 0; i < approvedTokens.length; i++) {
            if(approvedTokens[i] == tokenAddress) break;
        }
        if(i < approvedTokens.length){
            approvedTokens[i] = approvedTokens[approvedTokens.length -1];
            approvedTokens.pop();
            emit TokenRemoved(msg.sender, tokenAddress);
        }
    }
    /**
     * @dev isApprovedToken
     *      Returns true if token exists in the list of tokens with reduced fees
     *  @param tokenAddress address Token to be checked
     */
    function isApprovedToken(address tokenAddress) public view returns(bool) {
        for (uint256 i = 0; i < approvedTokens.length; i++) {
            if(approvedTokens[i] == tokenAddress) return true;
        }
        return false;
    }
    /**
     * @dev getApprovedTokens
     *      Returns the list of tokens with reduced fees
     */
    function getApprovedTokens() public view returns(address[] memory) {
        return(approvedTokens);
    }


     /**
     * @dev addSSContract
     *      Adds a token to the list of ssContracts
     *  @param _ssContract address Contract to be added
     */

    function addSSContract(address _ssContract) external onlyRouterOwner {
        require(
            _ssContract != address(0),
            "FactoryRouter: Invalid _ssContract address"
        );
        if(!isSSContract(_ssContract)){
            ssContracts.push(_ssContract);
            emit SSContractAdded(msg.sender, _ssContract);
        }
    }
    /**
     * @dev removeSSContract
     *      Removes a token if exists from the list of ssContracts
     *  @param _ssContract address Contract to be removed
     */

    function removeSSContract(address _ssContract) external onlyRouterOwner {
        require(
            _ssContract != address(0),
            "FactoryRouter: Invalid _ssContract address"
        );
        uint256 i;
        for (i = 0; i < ssContracts.length; i++) {
            if(ssContracts[i] == _ssContract) break;
        }
        if(i < ssContracts.length){
            // it's in the array
            ssContracts[i] = ssContracts[ssContracts.length -1];
            ssContracts.pop();
            emit SSContractRemoved(msg.sender, _ssContract);
        }
    }

    /**
     * @dev isSSContract
     *      Returns true if token exists in the list of ssContracts
     *  @param _ssContract  address Contract to be checked
     */
    function isSSContract(address _ssContract) public view returns(bool) {
        for (uint256 i = 0; i < ssContracts.length; i++) {
            if(ssContracts[i] == _ssContract) return true;
        }
        return false;
    }
    /**
     * @dev getSSContracts
     *      Returns the list of ssContracts
     */
    function getSSContracts() public view returns(address[] memory) {
        return(ssContracts);
    }

    function addFactory(address _factory) external onlyRouterOwner {
        require(
            _factory != address(0),
            "FactoryRouter: Invalid _factory address"
        );
        require(factory == address(0), "FACTORY ALREADY SET");
        factory = _factory;
        emit FactoryContractChanged(msg.sender, _factory);
    }


    /**
     * @dev addFixedRateContract
     *      Adds an address to the list of fixed rate contracts
     *  @param _fixedRate address Contract to be added
     */
    function addFixedRateContract(address _fixedRate) external onlyRouterOwner {
        require(
            _fixedRate != address(0),
            "FactoryRouter: Invalid _fixedRate address"
        );
        if(!isFixedRateContract(_fixedRate)){
            fixedrates.push(_fixedRate);
            emit FixedRateContractAdded(msg.sender, _fixedRate);
        }
    }
     /**
     * @dev removeFixedRateContract
     *      Removes an address from the list of fixed rate contracts
     *  @param _fixedRate address Contract to be removed
     */
    function removeFixedRateContract(address _fixedRate)
        external
        onlyRouterOwner
    {
        require(
            _fixedRate != address(0),
            "FactoryRouter: Invalid _fixedRate address"
        );
        uint256 i;
        for (i = 0; i < fixedrates.length; i++) {
            if(fixedrates[i] == _fixedRate) break;
        }
        if(i < fixedrates.length){
            // it's in the array
            fixedrates[i] = fixedrates[fixedrates.length -1];
            fixedrates.pop();
            emit FixedRateContractRemoved(msg.sender, _fixedRate);
        }
    }
    /**
     * @dev isFixedRateContract
     *      Removes true if address exists in the list of fixed rate contracts
     *  @param _fixedRate address Contract to be checked
     */
    function isFixedRateContract(address _fixedRate) public view returns(bool) {
        for (uint256 i = 0; i < fixedrates.length; i++) {
            if(fixedrates[i] == _fixedRate) return true;
        }
        return false;
    }
    /**
     * @dev getFixedRatesContracts
     *      Returns the list of fixed rate contracts
     */
    function getFixedRatesContracts() public view returns(address[] memory) {
        return(fixedrates);
    }

    /**
     * @dev addDispenserContract
     *      Adds an address to the list of dispensers
     *  @param _dispenser address Contract to be added
     */
    function addDispenserContract(address _dispenser) external onlyRouterOwner {
        require(
            _dispenser != address(0),
            "FactoryRouter: Invalid _dispenser address"
        );
          if(!isDispenserContract(_dispenser)){
            dispensers.push(_dispenser);
            emit DispenserContractAdded(msg.sender, _dispenser);
        }
    }

    /**
     * @dev removeDispenserContract
     *      Removes an address from the list of dispensers
     *  @param _dispenser address Contract to be removed
     */
    function removeDispenserContract(address _dispenser)
        external
        onlyRouterOwner
    {
        require(
            _dispenser != address(0),
            "FactoryRouter: Invalid _dispenser address"
        );
        uint256 i;
        for (i = 0; i < dispensers.length; i++) {
            if(dispensers[i] == _dispenser) break;
        }
        if(i < dispensers.length){
            // it's in the array
            dispensers[i] = dispensers[dispensers.length -1];
            dispensers.pop();
            emit DispenserContractRemoved(msg.sender, _dispenser);
        }
    }
    /**
     * @dev isDispenserContract
     *      Returns true if address exists in the list of dispensers
     *  @param _dispenser  address Contract to be checked
     */
    function isDispenserContract(address _dispenser) public view returns(bool) {
        for (uint256 i = 0; i < dispensers.length; i++) {
            if(dispensers[i] == _dispenser) return true;
        }
        return false;
    }
    /**
     * @dev getDispensersContracts
     *      Returns the list of fixed rate contracts
     */
    function getDispensersContracts() public view returns(address[] memory) {
        return(dispensers);
    }

    /**
     * @dev getOPCFee
     *      Gets OP Community Fees for a particular token
     * @param baseToken  address token to be checked
     */
    function getOPCFee(address baseToken) public view returns (uint256) {
        if (isApprovedToken(baseToken)) {
            return swapOceanFee;
        } else return swapNonOceanFee;
    }

    /**
     * @dev getOPCFees
     *      Gets OP Community Fees for approved tokens and non approved tokens
     */
    function getOPCFees() public view returns (uint256,uint256) {
        return (swapOceanFee, swapNonOceanFee);
    }

    /**
     * @dev getConsumeFee
     *      Gets OP Community Fee cuts for consume fees
     */
    function getOPCConsumeFee() public view returns (uint256) {
        return consumeFee;
    }

    /**
     * @dev getOPCProviderFee
     *      Gets OP Community Fee cuts for provider fees
     */
    function getOPCProviderFee() public view returns (uint256) {
        return providerFee;
    }


    /**
     * @dev updateOPCFee
     *      Updates OP Community Fees
     * @param _newSwapOceanFee Amount charged for swapping with ocean approved tokens
     * @param _newSwapNonOceanFee Amount charged for swapping with non ocean approved tokens
     * @param _newConsumeFee Amount charged from consumeFees
     * @param _newProviderFee Amount charged for providerFees
     */
    function updateOPCFee(uint256 _newSwapOceanFee, uint256 _newSwapNonOceanFee,
        uint256 _newConsumeFee, uint256 _newProviderFee) external onlyRouterOwner {

        swapOceanFee = _newSwapOceanFee;
        swapNonOceanFee = _newSwapNonOceanFee;
        consumeFee = _newConsumeFee;
        providerFee = _newProviderFee;
        emit OPCFeeChanged(msg.sender, _newSwapOceanFee, _newSwapNonOceanFee, _newConsumeFee, _newProviderFee);
    }

    /*
     * @dev getMinVestingPeriod
     *      Returns current minVestingPeriodInBlocks
       @return minVestingPeriodInBlocks
     */
    function getMinVestingPeriod() public view returns (uint256) {
        return minVestingPeriodInBlocks;
    }
    /*
     * @dev updateMinVestingPeriod
     *      Set new minVestingPeriodInBlocks
     * @param _newPeriod
     */
    function updateMinVestingPeriod(uint256 _newPeriod) external onlyRouterOwner {
        minVestingPeriodInBlocks = _newPeriod;
        emit VestingPeriodChanges(msg.sender, _newPeriod);
    }
    /**
     * @dev Deploys a new `OceanPool` on Ocean Friendly Fork modified for 1SS.
     This function cannot be called directly, but ONLY through the ERC20DT contract from a ERC20DEployer role

      ssContract address
     tokens [datatokenAddress, baseTokenAddress]
     publisherAddress user which will be assigned the vested amount.
     * @param tokens precreated parameter
     * @param ssParams params for the ssContract. 
     *                     [0]  = rate (wei)
     *                     [1]  = baseToken decimals
     *                     [2]  = vesting amount (wei)
     *                     [3]  = vested blocks
     *                     [4]  = initial liquidity in baseToken for pool creation
     * @param swapFees swapFees (swapFee, swapMarketFee), swapOceanFee will be set automatically later
     *                     [0] = swapFee for LP Providers
     *                     [1] = swapFee for marketplace runner
      
      .
     * @param addresses refers to an array of addresses passed by user
     *                     [0]  = side staking contract address
     *                     [1]  = baseToken address for pool creation(OCEAN or other)
     *                     [2]  = baseTokenSender user which will provide the baseToken amount for initial liquidity
     *                     [3]  = publisherAddress user which will be assigned the vested amount
     *                     [4]  = marketFeeCollector marketFeeCollector address
                           [5]  = poolTemplateAddress
       
        @return pool address
     */
    function deployPool(
        address[2] calldata tokens,
        // [datatokenAddress, baseTokenAddress]
        uint256[] calldata ssParams,
        uint256[] calldata swapFees,
        address[] calldata addresses
    )
        external
        returns (
            //[controller,baseTokenAddress,baseTokenSender,publisherAddress, marketFeeCollector,poolTemplateAddress]

            address
        )
    {
        require(
            IFactory(factory).erc20List(msg.sender),
            "FACTORY ROUTER: NOT ORIGINAL ERC20 TEMPLATE"
        );
        require(isSSContract(addresses[0]),
            "FACTORY ROUTER: invalid ssContract"
        );
        require(ssParams[1] > 0, "Wrong decimals");

        // we pull baseToken for creating initial pool and send it to the controller (ssContract)
        _pullUnderlying(tokens[1],addresses[2], addresses[0], ssParams[4]);
        
        address pool = newBPool(tokens, ssParams, swapFees, addresses);
        require(pool != address(0), "FAILED TO DEPLOY POOL");
        if (isApprovedToken(tokens[1])) emit NewPool(pool, true);
        else emit NewPool(pool, false);
        return pool;
    }

    function _getLength(IERC20[] memory array) internal pure returns (uint256) {
        return array.length;
    }

    /**
     * @dev deployFixedRate
     *      Creates a new FixedRateExchange setup.
     * As for deployPool, this function cannot be called directly,
     * but ONLY through the ERC20DT contract from a ERC20DEployer role
     * @param fixedPriceAddress fixedPriceAddress
     * @param addresses array of addresses [baseToken,owner,marketFeeCollector]
     * @param uints array of uints [baseTokenDecimals,datatokenDecimals, fixedRate, marketFee, withMint]
       @return exchangeId
     */

    function deployFixedRate(
        address fixedPriceAddress,
        address[] calldata addresses,
        uint256[] calldata uints
    ) external returns (bytes32 exchangeId) {
        require(
            IFactory(factory).erc20List(msg.sender),
            "FACTORY ROUTER: NOT ORIGINAL ERC20 TEMPLATE"
        );

        require(isFixedRateContract(fixedPriceAddress),
            "FACTORY ROUTER: Invalid FixedPriceContract"
        );

        exchangeId = IFixedRateExchange(fixedPriceAddress).createWithDecimals(
            msg.sender,
            addresses,
            uints
        );
    }

    /**
     * @dev deployDispenser
     *      Activates a new Dispenser
     * As for deployPool, this function cannot be called directly,
     * but ONLY through the ERC20DT contract from a ERC20DEployer role
     * @param _dispenser dispenser contract address
     * @param datatoken refers to datatoken address.
     * @param maxTokens - max tokens to dispense
     * @param maxBalance - max balance of requester.
     * @param owner - owner
     * @param allowedSwapper - if !=0, only this address can request DTs
     */

    function deployDispenser(
        address _dispenser,
        address datatoken,
        uint256 maxTokens,
        uint256 maxBalance,
        address owner,
        address allowedSwapper
    ) external {
        require(
            IFactory(factory).erc20List(msg.sender),
            "FACTORY ROUTER: NOT ORIGINAL ERC20 TEMPLATE"
        );

        require(isDispenserContract(_dispenser),
            "FACTORY ROUTER: Invalid DispenserContract"
        );
        IDispenser(_dispenser).create(
            datatoken,
            maxTokens,
            maxBalance,
            owner,
            allowedSwapper
        );
    }

     /**
     * @dev addPoolTemplate
     *      Adds an address to the list of pools templates
     *  @param poolTemplate address Contract to be added
     */
    function addPoolTemplate(address poolTemplate) external onlyRouterOwner {
        _addPoolTemplate(poolTemplate);
    }
     /**
     * @dev removePoolTemplate
     *      Removes an address from the list of pool templates
     *  @param poolTemplate address Contract to be removed
     */
    function removePoolTemplate(address poolTemplate) external onlyRouterOwner {
        _removePoolTemplate(poolTemplate);
    }

    // If you need to buy multiple DT (let's say for a compute job which has multiple datasets),
    // you have to send one transaction for each DT that you want to buy.

    // Perks:

    // one single call to buy multiple DT for multiple assets (better UX, better gas optimization)

    // require tokenIn approvals for router from user. (except for dispenser operations)
    function buyDTBatch(Operations[] calldata _operations) external {
        // TODO: to avoid DOS attack, we set a limit to maximum orders (50?)
        require(_operations.length <= 50, "FactoryRouter: Too Many Operations");
        for (uint256 i = 0; i < _operations.length; i++) {
            // address[] memory tokenInOutMarket = new address[](3);
            address[3] memory tokenInOutMarket = [
                _operations[i].tokenIn,
                _operations[i].tokenOut,
                _operations[i].marketFeeAddress
            ];
            uint256[4] memory amountsInOutMaxFee = [
                _operations[i].amountsIn,
                _operations[i].amountsOut,
                _operations[i].maxPrice,
                _operations[i].swapMarketFee
            ];

            // tokenInOutMarket[0] =
            if (_operations[i].operation == operationType.SwapExactIn) {
                // Get amountIn from user to router
                _pullUnderlying(_operations[i].tokenIn,msg.sender,
                    address(this),
                    _operations[i].amountsIn);
                // we approve pool to pull token from router
                IERC20(_operations[i].tokenIn).safeIncreaseAllowance(
                    _operations[i].source,
                    _operations[i].amountsIn
                );

                // Perform swap
                (uint256 amountReceived, ) = IPool(_operations[i].source)
                    .swapExactAmountIn(tokenInOutMarket, amountsInOutMaxFee);
                // transfer token swapped to user

                IERC20(_operations[i].tokenOut).safeTransfer(
                    msg.sender,
                    amountReceived
                );
            } else if (_operations[i].operation == operationType.SwapExactOut) {
                // calculate how much amount In we need for exact Out
                uint256 amountIn;
                (amountIn, , , , ) = IPool(_operations[i].source)
                    .getAmountInExactOut(
                        _operations[i].tokenIn,
                        _operations[i].tokenOut,
                        _operations[i].amountsOut,
                        _operations[i].swapMarketFee
                    );
                // pull amount In from user
                _pullUnderlying(_operations[i].tokenIn,msg.sender,
                    address(this),
                    amountIn);
                // we approve pool to pull token from router
                IERC20(_operations[i].tokenIn).safeIncreaseAllowance(
                    _operations[i].source,
                    amountIn
                );
                // perform swap
                (uint tokenAmountIn,) = IPool(_operations[i].source).swapExactAmountOut(
                    tokenInOutMarket,
                    amountsInOutMaxFee
                );
                require(tokenAmountIn <= amountsInOutMaxFee[0], 'TOO MANY TOKENS IN');
                // send amount out back to user
                IERC20(_operations[i].tokenOut).safeTransfer(
                    msg.sender,
                    _operations[i].amountsOut
                );
            } else if (_operations[i].operation == operationType.FixedRate) {
                // get datatoken address
                (, address datatoken, , , , , , , , , , ) = IFixedRateExchange(
                    _operations[i].source
                ).getExchange(_operations[i].exchangeIds);
                // get tokenIn amount required for dt out
                (uint256 baseTokenAmount, , , ) = IFixedRateExchange(
                    _operations[i].source
                ).calcBaseInGivenOutDT(
                        _operations[i].exchangeIds,
                        _operations[i].amountsOut,
                        _operations[i].swapMarketFee
                    );

                // pull tokenIn amount
                _pullUnderlying(_operations[i].tokenIn,msg.sender,
                    address(this),
                    baseTokenAmount);
                // we approve pool to pull token from router
                IERC20(_operations[i].tokenIn).safeIncreaseAllowance(
                    _operations[i].source,
                    baseTokenAmount
                );
                // perform swap
                IFixedRateExchange(_operations[i].source).buyDT(
                    _operations[i].exchangeIds,
                    _operations[i].amountsOut,
                    _operations[i].amountsIn,
                    _operations[i].marketFeeAddress,
                    _operations[i].swapMarketFee
                );
                // send dt out to user
                IERC20(datatoken).safeTransfer(
                    msg.sender,
                    _operations[i].amountsOut
                );
            } else {
                IDispenser(_operations[i].source).dispense(
                    _operations[i].tokenOut,
                    _operations[i].amountsOut,
                    msg.sender
                );
            }
        }
    }

    // require pool[].baseToken (for each pool) approvals for router from user.
    function stakeBatch(Stakes[] calldata _stakes) external {
        // TODO: to avoid DOS attack, we set a limit to maximum orders (50?)
        require(_stakes.length <= 50, "FactoryRouter: Too Many Operations");
        for (uint256 i = 0; i < _stakes.length; i++) {
            address baseToken = IPool(_stakes[i].poolAddress).getBaseTokenAddress();
            _pullUnderlying(baseToken,msg.sender,
                    address(this),
                    _stakes[i].tokenAmountIn);
            uint256 balanceBefore = IERC20(_stakes[i].poolAddress).balanceOf(address(this));
            // we approve pool to pull token from router
            IERC20(baseToken).safeIncreaseAllowance(
                    _stakes[i].poolAddress,
                    _stakes[i].tokenAmountIn);
            //now stake
            uint poolAmountOut = IPool(_stakes[i].poolAddress).joinswapExternAmountIn(
                _stakes[i].tokenAmountIn, _stakes[i].minPoolAmountOut
                );
            require(poolAmountOut >=  _stakes[i].minPoolAmountOut,'NOT ENOUGH LP');
            uint256 balanceAfter = IERC20(_stakes[i].poolAddress).balanceOf(address(this));
            //send LP shares to user
            IERC20(_stakes[i].poolAddress).safeTransfer(
                    msg.sender,
                    balanceAfter.sub(balanceBefore)
                );
        }
    }
    
    function _pullUnderlying(
        address erc20,
        address from,
        address to,
        uint256 amount
    ) internal {
        uint256 balanceBefore = IERC20(erc20).balanceOf(to);
        IERC20(erc20).safeTransferFrom(from, to, amount);
        require(IERC20(erc20).balanceOf(to) >= balanceBefore.add(amount),
                    "Transfer amount is too low");
    }

    function getPoolTemplates() public view override(BFactory, IFactoryRouter) returns (address[] memory) {
        return BFactory.getPoolTemplates();
    }

    function isPoolTemplate(address poolTemplate) public view override(BFactory, IFactoryRouter)
        returns (bool) {
        return BFactory.isPoolTemplate(poolTemplate);
    }


    /*
     * @dev updateOPCCollector
     *      Set new opcCollector
     * @param opcCollector
     */
    function updateOPCCollector(address _opcCollector) external onlyRouterOwner {
        require(_opcCollector != address(0), "New opcCollector cannot be ZERO_ADDR");
        opcCollector = _opcCollector;
        emit OPCCollectorChanged(msg.sender, _opcCollector);
    }
    /*
      * @dev getOPCCollector
      * getter for opcCollector
    */
    function getOPCCollector() view public returns (address) {
        return opcCollector;
    }
}

File 2 of 18 : BFactory.sol
pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import "./BPool.sol";
import "./BConst.sol";
import "../../utils/Deployer.sol";
import "../../interfaces/ISideStaking.sol";
import "../../interfaces/IERC20.sol";

/*
 * @title BFactory contract
 * @author Ocean Protocol (with code from Balancer Labs)
 *
 * @dev Ocean implementation of Balancer BPool Factory
 *      BFactory deploys BPool proxy contracts.
 *      New BPool proxy contracts are links to the template contract's bytecode.
 *      Proxy contract functionality is based on Ocean Protocol custom
 *        implementation of ERC1167 standard.
 */
contract BFactory is BConst, Deployer {
    address public opcCollector;

    // mapping(address => bool) internal poolTemplates;
    address[] public poolTemplates;

    event BPoolCreated(
        address indexed newBPoolAddress,
        address indexed registeredBy,
        address indexed datatokenAddress,
        address baseTokenAddress,
        address bpoolTemplateAddress,
        address ssAddress
    );

    event PoolTemplateAdded(
        address indexed caller,
        address indexed contractAddress
    );
    event PoolTemplateRemoved(
        address indexed caller,
        address indexed contractAddress
    );

    /* @dev Called on contract deployment. Cannot be called with zero address.
       @param _bpoolTemplate -- address of a deployed BPool contract. 
       @param _preCreatedPools list of pre-created pools. 
                          It can be only used in case of migration from an old factory contract.
    */
    constructor(
        address _bpoolTemplate,
        address _opcCollector,
        address[] memory _preCreatedPools
    ) {
        require(
            _bpoolTemplate != address(0),
            "BFactory: invalid bpool template zero address"
        );
        require(_opcCollector != address(0), "BFactory: zero address");

        opcCollector = _opcCollector;
        _addPoolTemplate(_bpoolTemplate);

        if (_preCreatedPools.length > 0) {
            for (uint256 i = 0; i < _preCreatedPools.length; i++) {
                emit BPoolCreated(
                    _preCreatedPools[i],
                    msg.sender,
                    address(0),
                    address(0),
                    address(0),
                    address(0)
                );
            }
        }
    }

    /** 
     * @dev Deploys new BPool proxy contract. 
       Template contract address could not be a zero address. 

     * @param tokens [datatokenAddress, baseTokenAddress]
     * publisherAddress user which will be assigned the vested amount.
     * @param ssParams params for the ssContract. 
     * @param swapFees swapFees (swapFee, swapMarketFee), swapOceanFee will be set automatically later
       marketFeeCollector marketFeeCollector address
       @param addresses // array of addresses passed by the user
       [controller,baseTokenAddress,baseTokenSender,publisherAddress, marketFeeCollector,poolTemplate address]
      @return bpool address of a new proxy BPool contract 
     */

    function newBPool(
        address[2] memory tokens,
        uint256[] memory ssParams,
        uint256[] memory swapFees,
        address[] memory addresses
    ) internal returns (address bpool) {
        require(isPoolTemplate(addresses[5]), "BFactory: Wrong Pool Template");
        address[1] memory feeCollectors = [addresses[4]];

        bpool = deploy(addresses[5]);

        require(bpool != address(0), "BFactory: invalid bpool zero address");
        BPool bpoolInstance = BPool(bpool);

        require(
            bpoolInstance.initialize(
                addresses[0], // ss is the pool controller
                address(this),
                swapFees,
                false,
                false,
                tokens,
                feeCollectors
            ),
            "ERR_INITIALIZE_BPOOL"
        );

        //  emit BPoolCreated(bpool, msg.sender,datatokenAddress,baseTokenAddress,bpoolTemplate,controller);

        // requires approval first from baseTokenSender
        require(
            ISideStaking(addresses[0]).newDatatokenCreated(
                tokens[0],
                tokens[1],
                bpool,
                addresses[3], //publisherAddress
                ssParams
            ),
            "ERR_INITIALIZE_SIDESTAKING"
        );

        return bpool;
    }

    /**
     * @dev _addPoolTemplate
     *      Adds an address to the list of pools templates
     *  @param poolTemplate address Contract to be added
     */
    function _addPoolTemplate(address poolTemplate) internal {
        require(
            poolTemplate != address(0),
            "FactoryRouter: Invalid poolTemplate address"
        );
        if (!isPoolTemplate(poolTemplate)) {
            poolTemplates.push(poolTemplate);
            emit PoolTemplateAdded(msg.sender, poolTemplate);
        }
    }

    /**
     * @dev _removeFixedRateContract
     *      Removes an address from the list of pool templates
     *  @param poolTemplate address Contract to be removed
     */
    function _removePoolTemplate(address poolTemplate) internal {
        uint256 i;
        for (i = 0; i < poolTemplates.length; i++) {
            if (poolTemplates[i] == poolTemplate) break;
        }
        if (i < poolTemplates.length) {
            // it's in the array
            for (uint256 c = i; c < poolTemplates.length - 1; c++) {
                poolTemplates[c] = poolTemplates[c + 1];
            }
            poolTemplates.pop();
            emit PoolTemplateRemoved(msg.sender, poolTemplate);
        }
    }

    /**
     * @dev isPoolTemplate
     *      Removes true if address exists in the list of templates
     *  @param poolTemplate address Contract to be checked
     */
    function isPoolTemplate(address poolTemplate) public view virtual returns (bool) {
        for (uint256 i = 0; i < poolTemplates.length; i++) {
            if (poolTemplates[i] == poolTemplate) return true;
        }
        return false;
    }

    /**
     * @dev getPoolTemplates
     *      Returns the list of pool templates
     */
    function getPoolTemplates() public view virtual returns (address[] memory) {
        return (poolTemplates);
    }
}

File 3 of 18 : IFactory.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface IFactory {
    function createToken(
        uint256 _templateIndex,
        string[] calldata strings,
        address[] calldata addresses,
        uint256[] calldata uints,
        bytes[] calldata bytess
    ) external returns (address token);

    function erc721List(address ERC721address) external returns (address);

    function erc20List(address erc20dt) external view returns(bool);


    struct NftCreateData{
        string name;
        string symbol;
        uint256 templateIndex;
        string tokenURI;
        bool transferable;
        address owner;
    }
    struct ErcCreateData{
        uint256 templateIndex;
        string[] strings;
        address[] addresses;
        uint256[] uints;
        bytes[] bytess;
    }

    struct PoolData{
        uint256[] ssParams;
        uint256[] swapFees;
        address[] addresses;
    }

    struct FixedData{
        address fixedPriceAddress;
        address[] addresses;
        uint256[] uints;
    }

    struct DispenserData{
        address dispenserAddress;
        uint256 maxTokens;
        uint256 maxBalance;
        bool withMint;
        address allowedSwapper;
    }
    
    function createNftWithErc20(
        NftCreateData calldata _NftCreateData,
        ErcCreateData calldata _ErcCreateData
    ) external returns (address , address);

    function createNftWithErc20WithPool(
        NftCreateData calldata _NftCreateData,
        ErcCreateData calldata _ErcCreateData,
        PoolData calldata _PoolData
    ) external returns (address, address , address);

    
    function createNftWithErc20WithFixedRate(
         NftCreateData calldata _NftCreateData,
        ErcCreateData calldata _ErcCreateData,
        FixedData calldata _FixedData
    ) external returns (address, address , bytes32 );

    
    function createNftWithErc20WithDispenser(
        NftCreateData calldata _NftCreateData,
        ErcCreateData calldata _ErcCreateData,
        DispenserData calldata _DispenserData
    ) external returns (address, address);
}

File 4 of 18 : IERC20.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0


/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    function decimals() external view returns (uint8);
    /**
     * @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 5 of 18 : IFixedRateExchange.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface IFixedRateExchange {
    function createWithDecimals(
        address datatoken,
        address[] calldata addresses, // [baseToken,owner,marketFeeCollector]
        uint256[] calldata uints // [baseTokenDecimals,datatokenDecimals, fixedRate, marketFee]
    ) external returns (bytes32 exchangeId);

    function buyDT(bytes32 exchangeId, uint256 datatokenAmount,
        uint256 maxBaseTokenAmount, address consumeMarketAddress, uint256 consumeMarketSwapFeeAmount) external;
    function sellDT(bytes32 exchangeId, uint256 datatokenAmount,
        uint256 minBaseTokenAmount, address consumeMarketAddress, uint256 consumeMarketSwapFeeAmount) external;

    function getAllowedSwapper(bytes32 exchangeId) external view returns (address allowedSwapper);
    function getExchange(bytes32 exchangeId)
        external
        view
        returns (
            address exchangeOwner,
            address datatoken,
            uint256 dtDecimals,
            address baseToken,
            uint256 btDecimals,
            uint256 fixedRate,
            bool active,
            uint256 dtSupply,
            uint256 btSupply,
            uint256 dtBalance,
            uint256 btBalance,
            bool withMint
            //address allowedSwapper
        );

    function getFeesInfo(bytes32 exchangeId)
        external
        view
        returns (
            uint256 marketFee,
            address marketFeeCollector,
            uint256 opcFee,
            uint256 marketFeeAvailable,
            uint256 oceanFeeAvailable
        );

    function isActive(bytes32 exchangeId) external view returns (bool);

    function calcBaseInGivenOutDT(bytes32 exchangeId, uint256 datatokenAmount, uint256 consumeMarketSwapFeeAmount)
        external
        view
        returns (
            uint256 baseTokenAmount,
            uint256 oceanFeeAmount,
            uint256 publishMarketFeeAmount,
            uint256 consumeMarketFeeAmount
        );
    function calcBaseOutGivenInDT(bytes32 exchangeId, uint256 datatokenAmount, uint256 consumeMarketSwapFeeAmount)
        external
        view
        returns (
            uint256 baseTokenAmount,
            uint256 oceanFeeAmount,
            uint256 publishMarketFeeAmount,
            uint256 consumeMarketFeeAmount
        );
    function updateMarketFee(bytes32 exchangeId, uint256 _newMarketFee) external;
    function updateMarketFeeCollector(bytes32 exchangeId, address _newMarketCollector) external;
    function setAllowedSwapper(bytes32 exchangeId, address newAllowedSwapper) external;
    function getId() pure external returns (uint8);
    function collectBT(bytes32 exchangeId, uint256 amount) external;
    function collectDT(bytes32 exchangeId, uint256 amount) external;
}

File 6 of 18 : IPool.sol
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface IPool {
    function getDatatokenAddress() external view returns (address);

    function getBaseTokenAddress() external view returns (address);

    function getController() external view returns (address);

    function setup(
        address datatokenAddress,
        uint256 datatokenAmount,
        uint256 datatokennWeight,
        address baseTokenAddress,
        uint256 baseTokenAmount,
        uint256 baseTokenWeight
    ) external;

    function swapExactAmountIn(
        address[3] calldata tokenInOutMarket, //[tokenIn,tokenOut,marketFeeAddress]
        uint256[4] calldata amountsInOutMaxFee //[tokenAmountIn,minAmountOut,maxPrice,_swapMarketFee]
    ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter);

    function swapExactAmountOut(
        address[3] calldata tokenInOutMarket, // [tokenIn,tokenOut,marketFeeAddress]
        uint256[4] calldata amountsInOutMaxFee // [maxAmountIn,tokenAmountOut,maxPrice,_swapMarketFee]
    ) external returns (uint256 tokenAmountIn, uint256 spotPriceAfter);

    function getAmountInExactOut(
        address tokenIn,
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 _consumeMarketSwapFee
    ) external view returns (uint256, uint256, uint256, uint256, uint256);

    function getAmountOutExactIn(
        address tokenIn,
        address tokenOut,
        uint256 tokenAmountIn,
        uint256 _consumeMarketSwapFee
    ) external view returns (uint256, uint256, uint256, uint256, uint256);

    function setSwapFee(uint256 swapFee) external;
    function getId() pure external returns (uint8);

    function exitswapPoolAmountIn(
        uint256 poolAmountIn,
        uint256 minAmountOut
    ) external returns (uint256 tokenAmountOut);
    
    function joinswapExternAmountIn(
        uint256 tokenAmountIn,
        uint256 minPoolAmountOut
    ) external returns (uint256 poolAmountOut);
}

File 7 of 18 : IDispenser.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface IDispenser {
    
    function status(address datatoken)
    external
    view
    returns (
        bool active,
        address owner,
        bool isMinter,
        uint256 maxTokens,
        uint256 maxBalance,
        uint256 balance,
        address allowedSwapper
    );
    
    function create(
        address datatoken,uint256 maxTokens, uint256 maxBalance, address owner, address allowedSwapper) external;
    function activate(address datatoken,uint256 maxTokens, uint256 maxBalance) external;
    
    function deactivate(address datatoken) external;
    
    function dispense(address datatoken, uint256 amount, address destination) external payable;
    
    function ownerWithdraw(address datatoken) external;
    function setAllowedSwapper(address datatoken, address newAllowedSwapper) external;
    function getId() pure external returns (uint8);
    
}

File 8 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.12;

import "../interfaces/IERC20.sol";
import "./ERC721/Address.sol";

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 9 of 18 : SafeMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 10 of 18 : BPool.sol
pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import "./BToken.sol";
import "./BMath.sol";
import "../../interfaces/IPool.sol";
import "../../interfaces/ISideStaking.sol";
import "../../utils/SafeERC20.sol";


/**
 * @title BPool
 *
 * @dev Used by the (Ocean version) BFactory contract as a bytecode reference to
 *      deploy new BPools.
 *
 * This contract is a friendly fork of Balancer [1]
 *  [1] https://github.com/balancer-labs/balancer-core/contracts/.

 * All fees are expressed in wei.  Examples:
 *  (1e17 = 10 % , 1e16 = 1% , 1e15 = 0.1%, 1e14 = 0.01%)
 */
contract BPool is BMath, BToken, IPool {
    using SafeERC20 for IERC20;
    struct Record {
        bool bound; // is token bound to pool
        uint256 index; // private
        uint256 denorm; // denormalized weight
        uint256 balance;
    }

    event LOG_SWAP(
        address indexed caller,
        address indexed tokenIn,
        address indexed tokenOut,
        uint256 tokenAmountIn,
        uint256 tokenAmountOut,
        uint256 timestamp,
        uint256 inBalance,
        uint256 outBalance,
        uint256 newSpotPrice
    );

    event LOG_JOIN(
        address indexed caller,
        address indexed tokenIn,
        uint256 tokenAmountIn,
        uint256 timestamp
    );
    event LOG_SETUP(
        address indexed caller,
        address indexed baseToken,
        uint256 baseTokenAmountIn,
        uint256 baseTokenWeight,
        address indexed datatoken,
        uint256 datatokenAmountIn,
        uint256 datatokenWeight
    );

    event LOG_EXIT(
        address indexed caller,
        address indexed tokenOut,
        uint256 tokenAmountOut,
        uint256 timestamp
    );

    event LOG_CALL(
        bytes4 indexed sig,
        address indexed caller,
        uint256 timestamp,
        bytes data
    );

    event LOG_BPT(uint256 bptAmount);
    event LOG_BPT_SS(uint256 bptAmount); //emitted for SS contract

    event OPCFee(
        address caller,
        address OPCWallet,
        address token,
        uint256 amount
    );
    event SwapFeeChanged(address caller, uint256 amount);
    event PublishMarketFee(
        address caller,
        address marketAddress,
        address token,
        uint256 amount
    );
    // emited for fees sent to consumeMarket
    event ConsumeMarketFee(address to, address token, uint256 amount);
    event SWAP_FEES(uint LPFeeAmount, uint oceanFeeAmount, uint marketFeeAmount,
        uint consumeMarketFeeAmount, address tokenFeeAddress);
    //emitted for every change done by publisherMarket
    event PublishMarketFeeChanged(address caller, address newMarketCollector, uint256 swapFee);
    event Gulped(address token, uint256 oldBalance, uint256 newBalance);
    modifier _lock_() {
        require(!_mutex, "ERR_REENTRY");
        _mutex = true;
        _;
        _mutex = false;
    }

    modifier _viewlock_() {
        require(!_mutex, "ERR_REENTRY");
        _;
    }

    bool private _mutex;

    address private _controller; // has CONTROL role
    bool private _publicSwap; // true if PUBLIC can call SWAP functions

    //address public _publishMarketCollector;
    address public _publishMarketCollector;
    // `setSwapFee` and `finalize` require CONTROL
    // `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
    bool private _finalized;

    address[] private _tokens;
    mapping(address => Record) private _records;
    uint256 private _totalWeight;
    ISideStaking ssContract;

    //-----------------------------------------------------------------------
    //Proxy contract functionality: begin
    bool private initialized;

    /**
     * @dev getId
     *      Return template id in case we need different ABIs. 
     *      If you construct your own template, please make sure to change the hardcoded value
     */
    function getId() pure public returns (uint8) {
        return 1;
    }

    function isInitialized() external view returns (bool) {
        return initialized;
    }

    // Called prior to contract initialization (e.g creating new BPool instance)
    // Calls private _initialize function. Only if contract is not initialized.
    function initialize(
        address controller,
        address factory,
        uint256[] calldata swapFees,
        bool publicSwap,
        bool finalized,
        address[2] calldata tokens,
        address[1] calldata feeCollectors
    ) external returns (bool) {
        require(!initialized, "ERR_ALREADY_INITIALIZED");
        require(controller != address(0), "ERR_INVALID_CONTROLLER_ADDRESS");
        require(factory != address(0), "ERR_INVALID_FACTORY_ADDRESS");
        require(swapFees[0] >= MIN_FEE, "ERR_MIN_FEE");
        require(swapFees[0] <= MAX_FEE, "ERR_MAX_FEE");
        require(swapFees[1] == 0 || swapFees[1]>= MIN_FEE, "ERR_MIN_FEE");
        require(swapFees[1] <= MAX_FEE, "ERR_MAX_FEE");
        return
            _initialize(
                controller,
                factory,
                swapFees,
                publicSwap,
                finalized,
                tokens,
                feeCollectors
            );
    }

    // Private function called on contract initialization.
    function _initialize(
        address controller,
        address factory,
        uint256[] memory swapFees,
        bool publicSwap,
        bool finalized,
        address[2] memory tokens,
        address[1] memory feeCollectors
    ) private returns (bool) {
        _controller = controller;
        router = factory;
        _swapFee = swapFees[0];
        emit SwapFeeChanged(msg.sender, _swapFee);
        _swapPublishMarketFee = swapFees[1];
        _publicSwap = publicSwap;
        _finalized = finalized;
        _datatokenAddress = tokens[0];
        _baseTokenAddress = tokens[1];
        _publishMarketCollector = feeCollectors[0];
        emit PublishMarketFeeChanged(msg.sender, _publishMarketCollector, _swapPublishMarketFee);
        initialized = true;
        ssContract = ISideStaking(_controller);
        return initialized;
    }

    
    /**
     * @dev setup
     *      Initial setup of the pool
     *      Can be called only by the controller
     * @param datatokenAddress datatokenAddress
     * @param datatokenAmount how many datatokens in the initial reserve
     * @param datatokenWeight datatoken weight (hardcoded in deployer at 50%)
     * @param baseTokenAddress base token
     * @param baseTokenAmount how many basetokens in the initial reserve
     * @param baseTokenWeight base weight (hardcoded in deployer at 50%)
     */
    function setup(
        address datatokenAddress,
        uint256 datatokenAmount,
        uint256 datatokenWeight,
        address baseTokenAddress,
        uint256 baseTokenAmount,
        uint256 baseTokenWeight
    ) external _lock_ {
        require(msg.sender == _controller, "ERR_INVALID_CONTROLLER");
        require(
            datatokenAddress == _datatokenAddress,
            "ERR_INVALID_DATATOKEN_ADDRESS"
        );
        require(
            baseTokenAddress == _baseTokenAddress,
            "ERR_INVALID_baseToken_ADDRESS"
        );
        // other inputs will be validated prior
        // calling the below functions
        // bind datatoken
        bind(datatokenAddress, datatokenAmount, datatokenWeight);
        emit LOG_JOIN(
            msg.sender,
            datatokenAddress,
            datatokenAmount,
            block.timestamp
        );

        // bind baseToken
        bind(baseTokenAddress, baseTokenAmount, baseTokenWeight);
        emit LOG_JOIN(
            msg.sender,
            baseTokenAddress,
            baseTokenAmount,
            block.timestamp
        );
        // finalize
        finalize();
        emit LOG_SETUP(
            msg.sender,
            baseTokenAddress,
            baseTokenAmount,
            baseTokenWeight,
            datatokenAddress,
            datatokenAmount,
            datatokenWeight
        );
    }

    //Proxy contract functionality: end
    //-----------------------------------------------------------------------
    /**
     * @dev isPublicSwap
     *      Returns true if swapping is allowed
     */
    function isPublicSwap() external view returns (bool) {
        return _publicSwap;
    }
    /**
     * @dev isFinalized
     *      Returns true if pool is finalized
     */
    function isFinalized() external view returns (bool) {
        return _finalized;
    }

    /**
     * @dev isBound
     *      Returns true if token is bound
     * @param t token to be checked
     */
    function isBound(address t) external view returns (bool) {
        return _records[t].bound;
    }

    function _checkBound(address token) internal view {
        require(_records[token].bound, "ERR_NOT_BOUND");
    }

    /**
     * @dev getNumTokens
     *      Returns number of tokens bounded to pool
     */
    function getNumTokens() external view returns (uint256) {
        return _tokens.length;
    }

    /**
     * @dev getCurrentTokens
     *      Returns tokens bounded to pool, before the pool is finalized
     */
    function getCurrentTokens()
        external
        view
        _viewlock_
        returns (address[] memory tokens)
    {
        return _tokens;
    }

    /**
     * @dev getFinalTokens
     *      Returns tokens bounded to pool, after the pool was finalized
     */
    function getFinalTokens()
        public
        view
        _viewlock_
        returns (address[] memory tokens)
    {
        require(_finalized, "ERR_NOT_FINALIZED");
        return _tokens;
    }

    /**
     * @dev collectOPC
     *      Collects and send all OPC Fees to _opcCollector.
     *      This funtion can be called by anyone, because fees are being sent to _opcCollector
     */
    function collectOPC() external {
        address[] memory tokens = getFinalTokens();
        for (uint256 i = 0; i < tokens.length; i++) {
            uint256 amount = communityFees[tokens[i]];
            communityFees[tokens[i]] = 0;
            address _opcCollector = IFactoryRouter(router).getOPCCollector();
            emit OPCFee(msg.sender, _opcCollector, tokens[i], amount);
            IERC20(tokens[i]).safeTransfer(_opcCollector, amount);
        }
    }

    /**
     * @dev getCurrentOPCFees
     *      Get the current amount of fees which can be withdrawned by OPC
     * @return address[] - array of tokens addresses
     *         uint256[] - array of amounts
     */
    function getCurrentOPCFees()
        public
        view
        returns (address[] memory, uint256[] memory)
    {
        address[] memory poolTokens = getFinalTokens();
        address[] memory tokens = new address[](poolTokens.length);
        uint256[] memory amounts = new uint256[](poolTokens.length);
        for (uint256 i = 0; i < poolTokens.length; i++) {
            tokens[i] = poolTokens[i];
            amounts[i] = communityFees[poolTokens[i]];
        }
        return (tokens, amounts);
    }

    /**
     * @dev getCurrentMarketFees
     *      Get the current amount of fees which can be withdrawned by _publishMarketCollector
     * @return address[] - array of tokens addresses
     *         uint256[] - array of amounts
     */
    function getCurrentMarketFees()
        public
        view
        returns (address[] memory, uint256[] memory)
    {
        address[] memory poolTokens = getFinalTokens();
        address[] memory tokens = new address[](poolTokens.length);
        uint256[] memory amounts = new uint256[](poolTokens.length);
        for (uint256 i = 0; i < poolTokens.length; i++) {
            tokens[i] = poolTokens[i];
            amounts[i] = publishMarketFees[poolTokens[i]];
        }
        return (tokens, amounts);
    }

    /**
     * @dev collectMarketFee
     *      Collects and send all Market Fees to _publishMarketCollector.
     *      This function can be called by anyone, because fees are being sent to _publishMarketCollector
     */
    function collectMarketFee() external {
        address[] memory tokens = getFinalTokens();
        for (uint256 i = 0; i < tokens.length; i++) {
            uint256 amount = publishMarketFees[tokens[i]];
            publishMarketFees[tokens[i]] = 0;
            emit PublishMarketFee(
                msg.sender,
                _publishMarketCollector,
                tokens[i],
                amount
            );
            IERC20(tokens[i]).safeTransfer(_publishMarketCollector, amount);
        }
    }

    /**
     * @dev updatePublishMarketFee
     *      Set _newCollector as _publishMarketCollector
     * @param _newCollector new _publishMarketCollector
     * @param _newSwapFee new swapFee
     */
    function updatePublishMarketFee(address _newCollector, uint256 _newSwapFee) external {
        require(_publishMarketCollector == msg.sender, "ONLY MARKET COLLECTOR");
        require(_newCollector != address(0), "Invalid _newCollector address");
        require(_newSwapFee ==0 || _newSwapFee >= MIN_FEE, "ERR_MIN_FEE");
        require(_newSwapFee <= MAX_FEE, "ERR_MAX_FEE");
        _publishMarketCollector = _newCollector;
        _swapPublishMarketFee = _newSwapFee;
        emit PublishMarketFeeChanged(msg.sender, _publishMarketCollector, _swapPublishMarketFee);
    }

    /**
     * @dev getDenormalizedWeight
     *      Returns denormalized weight of a token
     * @param token token to be checked
     */
    function getDenormalizedWeight(address token)
        external
        view
        _viewlock_
        returns (uint256)
    {
        _checkBound(token);
        return _records[token].denorm;
    }

     /**
     * @dev getTotalDenormalizedWeight
     *      Returns total denormalized weught of the pool
     */
    function getTotalDenormalizedWeight()
        external
        view
        _viewlock_
        returns (uint256)
    {
        return _totalWeight;
    }

    /**
     * @dev getNormalizedWeight
     *      Returns normalized weight of a token
     * @param token token to be checked
     */
    
    function getNormalizedWeight(address token)
        external
        view
        _viewlock_
        returns (uint256)
    {
        _checkBound(token);
        uint256 denorm = _records[token].denorm;
        return bdiv(denorm, _totalWeight);
    }


    /**
     * @dev getBalance
     *      Returns the current token reserve amount
     * @param token token to be checked
     */
    function getBalance(address token)
        external
        view
        _viewlock_
        returns (uint256)
    {
        _checkBound(token);
        return _records[token].balance;
    }

    /**
     * @dev getSwapFee
     *      Returns the current Liquidity Providers swap fee
     */
    function getSwapFee() external view returns (uint256) {
        return _swapFee;
    }

    /**
     * @dev getMarketFee
     *      Returns the current fee of publishingMarket
     */
    function getMarketFee() external view returns (uint256) {
        return _swapPublishMarketFee;
    }

    /**
     * @dev getController
     *      Returns the current controller address (ssBot)
     */
    function getController() external view returns (address) {
        return _controller;
    }

    /**
     * @dev getDatatokenAddress
     *      Returns the current datatoken address
     */
    function getDatatokenAddress() external view returns (address) {
        return _datatokenAddress;
    }

    /**
     * @dev getBaseTokenAddress
     *      Returns the current baseToken address
     */
    function getBaseTokenAddress() external view returns (address) {
        return _baseTokenAddress;
    }


    /**
     * @dev setSwapFee
     *      Allows controller to change the swapFee
     * @param swapFee new swap fee (max 1e17 = 10 % , 1e16 = 1% , 1e15 = 0.1%, 1e14 = 0.01%)
     */
    function setSwapFee(uint256 swapFee) public {
        require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
        require(swapFee >= MIN_FEE, "ERR_MIN_FEE");
        require(swapFee <= MAX_FEE, "ERR_MAX_FEE");
        _swapFee = swapFee;
        emit SwapFeeChanged(msg.sender, swapFee);
    }

    /**
     * @dev finalize
     *      Finalize pool. After this,new tokens cannot be bound
     */
    function finalize() internal {
        _finalized = true;
        _publicSwap = true;

        _mintPoolShare(INIT_POOL_SUPPLY);
        _pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
    }

    /**
     * @dev bind
     *      Bind a new token to the pool.
     * @param token token address
     * @param balance initial reserve
     * @param denorm denormalized weight
     */
    function bind(
        address token,
        uint256 balance,
        uint256 denorm
    ) internal {
        require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
        require(!_records[token].bound, "ERR_IS_BOUND");
        require(!_finalized, "ERR_IS_FINALIZED");

        require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");

        _records[token] = Record({
            bound: true,
            index: _tokens.length,
            denorm: 0, // balance and denorm will be validated
            balance: 0 // and set by `rebind`
        });
        _tokens.push(token);
        rebind(token, balance, denorm);
    }

    /**
     * @dev rebind
     *      Update pool reserves & weight after a token bind
     * @param token token address
     * @param balance initial reserve
     * @param denorm denormalized weight
     */
    function rebind(
        address token,
        uint256 balance,
        uint256 denorm
    ) internal {
        require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
        require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
        require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");

        // Adjust the denorm and totalWeight
        uint256 oldWeight = _records[token].denorm;
        if (denorm > oldWeight) {
            _totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
            require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
        } else if (denorm < oldWeight) {
            _totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
        }
        _records[token].denorm = denorm;

        // Adjust the balance record and actual token balance
        uint256 oldBalance = _records[token].balance;
        _records[token].balance = balance;
        if (balance > oldBalance) {
            _pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
        } else if (balance < oldBalance) {
            // In this case liquidity is being withdrawn, we don't have EXIT_FEES
            uint256 tokenBalanceWithdrawn = bsub(oldBalance, balance);
            _pushUnderlying(
                token,
                msg.sender,
                tokenBalanceWithdrawn
            );
        }
    }

    /**
     * @dev getSpotPrice
     *      Return the spot price of swapping tokenIn to tokenOut
     * @param tokenIn in token
     * @param tokenOut out token
     * @param _consumeMarketSwapFee consume market swap fee 
     */
    function getSpotPrice(
        address tokenIn,
        address tokenOut,
        uint256 _consumeMarketSwapFee
    ) external view _viewlock_ returns (uint256 spotPrice) {
        _checkBound(tokenIn);
        _checkBound(tokenOut);
        Record storage inRecord = _records[tokenIn];
        Record storage outRecord = _records[tokenOut];
        return
            calcSpotPrice(
                inRecord.balance,
                inRecord.denorm,
                outRecord.balance,
                outRecord.denorm,
                _consumeMarketSwapFee
            );
    }

    // view function used for batch buy. useful for frontend
     /**
     * @dev getAmountInExactOut
     *      How many tokensIn do you need in order to get exact tokenAmountOut.
            Returns: tokenAmountIn, LPFee, opcFee , publishMarketSwapFee, consumeMarketSwapFee
     * @param tokenIn token to be swaped
     * @param tokenOut token to get
     * @param tokenAmountOut exact amount of tokenOut
     * @param _consumeMarketSwapFee consume market swap fee
     */

    function getAmountInExactOut(
        address tokenIn,
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 _consumeMarketSwapFee
    )
        external
        view
        returns (
            // _viewlock_
            uint256 tokenAmountIn, uint lpFeeAmount, 
            uint oceanFeeAmount, 
            uint publishMarketSwapFeeAmount,
            uint consumeMarketSwapFeeAmount
        )
    {
        _checkBound(tokenIn);
        _checkBound(tokenOut);
        uint256[4] memory data = [
            _records[tokenIn].balance,
            _records[tokenIn].denorm,
            _records[tokenOut].balance,
            _records[tokenOut].denorm
        ];
        uint tokenAmountInBalance;
        swapfees memory _swapfees;
        (tokenAmountIn, tokenAmountInBalance, _swapfees) =        
            calcInGivenOut(
                data,
                tokenAmountOut,
                // tokenIn,
                _consumeMarketSwapFee
            );
        return(tokenAmountIn, _swapfees.LPFee, _swapfees.oceanFeeAmount, 
        _swapfees.publishMarketFeeAmount, _swapfees.consumeMarketFee);

    }

    // view function useful for frontend
    /**
     * @dev getAmountOutExactIn
     *      How many tokensOut you will get for a exact tokenAmountIn
            Returns: tokenAmountOut, LPFee, opcFee ,  publishMarketSwapFee, consumeMarketSwapFee
     * @param tokenIn token to be swaped
     * @param tokenOut token to get
     * @param tokenAmountOut exact amount of tokenOut
     * @param _consumeMarketSwapFee consume market swap fee
     */
    function getAmountOutExactIn(
        address tokenIn,
        address tokenOut,
        uint256 tokenAmountIn,
        uint256 _consumeMarketSwapFee
    )
        external
        view
        returns (
            //  _viewlock_
            uint256 tokenAmountOut,
            uint lpFeeAmount, 
            uint oceanFeeAmount, 
            uint publishMarketSwapFeeAmount,
            uint consumeMarketSwapFeeAmount
        )
    {
        _checkBound(tokenIn);
        _checkBound(tokenOut);
        uint256[4] memory data = [
            _records[tokenIn].balance,
            _records[tokenIn].denorm,
            _records[tokenOut].balance,
            _records[tokenOut].denorm
        ];
        uint balanceInToAdd;
        swapfees memory _swapfees;
         (tokenAmountOut, balanceInToAdd, _swapfees) =        
            calcOutGivenIn(
                data,
                tokenAmountIn,
               // tokenIn,
                _consumeMarketSwapFee
            );
        return(tokenAmountOut, _swapfees.LPFee, 
        _swapfees.oceanFeeAmount, _swapfees.publishMarketFeeAmount, _swapfees.consumeMarketFee);
    }


    /**
     * @dev swapExactAmountIn
     *      Swaps an exact amount of tokensIn to get a mimum amount of tokenOut
     * @param tokenInOutMarket array of addreses: [tokenIn, tokenOut, consumeMarketFeeAddress]
     * @param amountsInOutMaxFee array of ints: [tokenAmountIn, minAmountOut, maxPrice, consumeMarketSwapFee]
     */
    function swapExactAmountIn(
        address[3] calldata tokenInOutMarket, 
        uint256[4] calldata amountsInOutMaxFee
    ) external _lock_ returns (uint256 tokenAmountOut, uint256 spotPriceAfter) {
        require(_finalized, "ERR_NOT_FINALIZED");
        require(tokenInOutMarket[0] != tokenInOutMarket[1], 'Cannot swap same token');
        _checkBound(tokenInOutMarket[0]);
        _checkBound(tokenInOutMarket[1]);
        Record storage inRecord = _records[address(tokenInOutMarket[0])];
        Record storage outRecord = _records[address(tokenInOutMarket[1])];
        require(amountsInOutMaxFee[3] ==0 || amountsInOutMaxFee[3] >= MIN_FEE,'ConsumeSwapFee too low');
        require(amountsInOutMaxFee[3] <= MAX_FEE,'ConsumeSwapFee too high');
        require(
            amountsInOutMaxFee[0] <= bmul(inRecord.balance, MAX_IN_RATIO),
            "ERR_MAX_IN_RATIO"
        );

        uint256 spotPriceBefore = calcSpotPrice(
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm,
            amountsInOutMaxFee[3]
        );

        require(
            spotPriceBefore <= amountsInOutMaxFee[2],
            "ERR_BAD_LIMIT_PRICE"
        );
        uint256 balanceInToAdd;
        uint256[4] memory data = [
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm
        ];
        swapfees memory _swapfees;
        (tokenAmountOut, balanceInToAdd, _swapfees) = calcOutGivenIn(
            data,
            amountsInOutMaxFee[0],
           // tokenInOutMarket[0],
            amountsInOutMaxFee[3]
        );
        // update balances
        communityFees[tokenInOutMarket[0]] = badd(communityFees[tokenInOutMarket[0]],_swapfees.oceanFeeAmount);
        publishMarketFees[tokenInOutMarket[0]] = 
        badd(publishMarketFees[tokenInOutMarket[0]],_swapfees.publishMarketFeeAmount);
        emit SWAP_FEES(_swapfees.LPFee, _swapfees.oceanFeeAmount,
        _swapfees.publishMarketFeeAmount,_swapfees.consumeMarketFee, tokenInOutMarket[0]);
        require(tokenAmountOut >= amountsInOutMaxFee[1], "ERR_LIMIT_OUT");

        inRecord.balance = badd(inRecord.balance, balanceInToAdd);
        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);

        spotPriceAfter = calcSpotPrice(
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm,
            amountsInOutMaxFee[3]
        );

        require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
        require(spotPriceAfter <= amountsInOutMaxFee[2], "ERR_LIMIT_PRICE");

        require(
            spotPriceBefore <= bdiv(amountsInOutMaxFee[0], tokenAmountOut),
            "ERR_MATH_APPROX"
        );

        emit LOG_SWAP(
            msg.sender,
            tokenInOutMarket[0],
            tokenInOutMarket[1],
            amountsInOutMaxFee[0],
            tokenAmountOut,
            block.timestamp,
            inRecord.balance,
            outRecord.balance,
            spotPriceAfter

        );

        _pullUnderlying(tokenInOutMarket[0], msg.sender, amountsInOutMaxFee[0]);
        uint256 consumeMarketFeeAmount = bsub(
            amountsInOutMaxFee[0],
            bmul(amountsInOutMaxFee[0], bsub(BONE, amountsInOutMaxFee[3]))
        );
        if (amountsInOutMaxFee[3] > 0) {
            IERC20(tokenInOutMarket[0]).safeTransfer(
                tokenInOutMarket[2],
                consumeMarketFeeAmount
            );
            emit ConsumeMarketFee(
                tokenInOutMarket[2],
                tokenInOutMarket[0],
                consumeMarketFeeAmount
            );
        }
        _pushUnderlying(tokenInOutMarket[1], msg.sender, tokenAmountOut);

        return (tokenAmountOut, spotPriceAfter); //returning spot price 0 because there is no public spotPrice
    }


    /**
     * @dev swapExactAmountOut
     *      Swaps a maximum  maxAmountIn of tokensIn to get an exact amount of tokenOut
     * @param tokenInOutMarket array of addreses: [tokenIn, tokenOut, consumeMarketFeeAddress]
     * @param amountsInOutMaxFee array of ints: [maxAmountIn,tokenAmountOut,maxPrice, consumeMarketSwapFee]
     */
    function swapExactAmountOut(
        address[3] calldata tokenInOutMarket,
        uint256[4] calldata amountsInOutMaxFee
    ) external _lock_ returns (uint256 tokenAmountIn, uint256 spotPriceAfter) {
        require(_finalized, "ERR_NOT_FINALIZED");
        require(tokenInOutMarket[0] != tokenInOutMarket[1], 'Cannot swap same token');
        require(amountsInOutMaxFee[3] ==0 || amountsInOutMaxFee[3] >= MIN_FEE,'ConsumeSwapFee too low');
        require(amountsInOutMaxFee[3] <= MAX_FEE,'ConsumeSwapFee too high');
        _checkBound(tokenInOutMarket[0]);
        _checkBound(tokenInOutMarket[1]);
        Record storage inRecord = _records[address(tokenInOutMarket[0])];
        Record storage outRecord = _records[address(tokenInOutMarket[1])];

        require(
            amountsInOutMaxFee[1] <= bmul(outRecord.balance, MAX_OUT_RATIO),
            "ERR_MAX_OUT_RATIO"
        );

        uint256 spotPriceBefore = calcSpotPrice(
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm,
            amountsInOutMaxFee[3]
        );

        require(
            spotPriceBefore <= amountsInOutMaxFee[2],
            "ERR_BAD_LIMIT_PRICE"
        );
        // this is the amount we are going to register in balances
        // (only takes account of swapFee, not OPC and market fee,
        //in order to not affect price during following swaps, fee wtihdrawl etc)
        uint256 balanceToAdd;
        uint256[4] memory data = [
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm
        ];
        swapfees memory _swapfees;
        (tokenAmountIn, balanceToAdd,
        _swapfees) = calcInGivenOut(
            data,
            amountsInOutMaxFee[1],
            //tokenInOutMarket[0],
            amountsInOutMaxFee[3]
        );
        communityFees[tokenInOutMarket[0]] = badd(communityFees[tokenInOutMarket[0]],_swapfees.oceanFeeAmount);
        publishMarketFees[tokenInOutMarket[0]] 
        = badd(publishMarketFees[tokenInOutMarket[0]],_swapfees.publishMarketFeeAmount);
        emit SWAP_FEES(_swapfees.LPFee, _swapfees.oceanFeeAmount,
        _swapfees.publishMarketFeeAmount,_swapfees.consumeMarketFee, tokenInOutMarket[0]);
        require(tokenAmountIn <= amountsInOutMaxFee[0], "ERR_LIMIT_IN");

        inRecord.balance = badd(inRecord.balance, balanceToAdd);
        outRecord.balance = bsub(outRecord.balance, amountsInOutMaxFee[1]);

        spotPriceAfter = calcSpotPrice(
            inRecord.balance,
            inRecord.denorm,
            outRecord.balance,
            outRecord.denorm,
            amountsInOutMaxFee[3]
        );

        require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
        require(spotPriceAfter <= amountsInOutMaxFee[2], "ERR_LIMIT_PRICE");
        require(
            spotPriceBefore <= bdiv(tokenAmountIn, amountsInOutMaxFee[1]),
            "ERR_MATH_APPROX"
        );

        emit LOG_SWAP(
            msg.sender,
            tokenInOutMarket[0],
            tokenInOutMarket[1],
            tokenAmountIn,
            amountsInOutMaxFee[1],
            block.timestamp,
            inRecord.balance,
            outRecord.balance,
            spotPriceAfter
        );
        _pullUnderlying(tokenInOutMarket[0], msg.sender, tokenAmountIn);
        uint256 consumeMarketFeeAmount = bsub(
            tokenAmountIn,
            bmul(tokenAmountIn, bsub(BONE, amountsInOutMaxFee[3]))
        );
        if (amountsInOutMaxFee[3] > 0) {
            IERC20(tokenInOutMarket[0]).safeTransfer(
                tokenInOutMarket[2],// market address
                consumeMarketFeeAmount
            );
            emit ConsumeMarketFee(
                tokenInOutMarket[2], // to (market address)
                tokenInOutMarket[0], // token
                consumeMarketFeeAmount
            );
        }
        _pushUnderlying(tokenInOutMarket[1], msg.sender, amountsInOutMaxFee[1]);
        return (tokenAmountIn, spotPriceAfter);
    }

    /**
     * @dev joinswapExternAmountIn
     *      Single side add liquidity to the pool,
     *      expecting a minPoolAmountOut of shares for spending tokenAmountIn basetokens
     * @param tokenAmountIn exact number of base tokens to spend
     * @param minPoolAmountOut minimum of pool shares expectex
     */
    function joinswapExternAmountIn(
        uint256 tokenAmountIn,
        uint256 minPoolAmountOut
    ) external _lock_ returns (uint256 poolAmountOut) {
        //tokenIn = _baseTokenAddress;
        require(_finalized, "ERR_NOT_FINALIZED");
        _checkBound(_baseTokenAddress);
        require(
            tokenAmountIn <= bmul(_records[_baseTokenAddress].balance, MAX_IN_RATIO),
            "ERR_MAX_IN_RATIO"
        );
        //ask ssContract
        Record storage inRecord = _records[_baseTokenAddress];

        poolAmountOut = calcPoolOutGivenSingleIn(
            inRecord.balance,
            _totalSupply,
            tokenAmountIn
        );

        require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");

        inRecord.balance = badd(inRecord.balance, tokenAmountIn);
        emit LOG_JOIN(msg.sender, _baseTokenAddress, tokenAmountIn, block.timestamp);
        emit LOG_BPT(poolAmountOut);

        

        //ask the ssContract to stake as well
        //calculate how much should the 1ss stake
        Record storage ssInRecord = _records[_datatokenAddress];
        uint256 ssAmountIn = calcSingleInGivenPoolOut(
            ssInRecord.balance,
            _totalSupply,
            poolAmountOut
        );
        if (ssContract.canStake(_datatokenAddress, ssAmountIn)) {
            
            //call 1ss to approve
            ssContract.Stake(_datatokenAddress, ssAmountIn);
            // follow the same path
            ssInRecord.balance = badd(ssInRecord.balance, ssAmountIn);
            emit LOG_JOIN(
                _controller,
                _datatokenAddress,
                ssAmountIn,
                block.timestamp
            );
            emit LOG_BPT_SS(poolAmountOut);
            _mintPoolShare(poolAmountOut);
            _pushPoolShare(_controller, poolAmountOut);
            _pullUnderlying(_datatokenAddress, _controller, ssAmountIn);
            
        }
        _mintPoolShare(poolAmountOut);
        _pushPoolShare(msg.sender, poolAmountOut);
        _pullUnderlying(_baseTokenAddress, msg.sender, tokenAmountIn);
        return poolAmountOut;
    }

    
    /**
     * @dev exitswapPoolAmountIn
     *      Single side remove liquidity from the pool,
     *      expecting a minAmountOut of basetokens for spending poolAmountIn pool shares
     * @param poolAmountIn exact number of pool shares to spend
     * @param minAmountOut minimum amount of basetokens expected
     */
    function exitswapPoolAmountIn(
        uint256 poolAmountIn,
        uint256 minAmountOut
    ) external _lock_ returns (uint256 tokenAmountOut) {
        //tokenOut = _baseTokenAddress;
        require(_finalized, "ERR_NOT_FINALIZED");
        _checkBound(_baseTokenAddress);

        Record storage outRecord = _records[_baseTokenAddress];

        tokenAmountOut = calcSingleOutGivenPoolIn(
            outRecord.balance,
            _totalSupply,
            poolAmountIn
        );
        require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");

        require(
            tokenAmountOut <= bmul(_records[_baseTokenAddress].balance, MAX_OUT_RATIO),
            "ERR_MAX_OUT_RATIO"
        );

        outRecord.balance = bsub(outRecord.balance, tokenAmountOut);

        //uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
        emit LOG_EXIT(msg.sender, _baseTokenAddress, tokenAmountOut, block.timestamp);
        emit LOG_BPT(poolAmountIn);

        //ask the ssContract to unstake as well
        //calculate how much should the 1ss unstake
        
        if (
            ssContract.canUnStake(_datatokenAddress, poolAmountIn)
        ) {
            Record storage ssOutRecord = _records[_datatokenAddress];
            uint256 ssAmountOut = calcSingleOutGivenPoolIn(
                ssOutRecord.balance,
                _totalSupply,
                poolAmountIn
            );

            ssOutRecord.balance = bsub(ssOutRecord.balance, ssAmountOut);
            //exitFee = bmul(poolAmountIn, EXIT_FEE);
            emit LOG_EXIT(
                _controller,
                _datatokenAddress,
                ssAmountOut,
                block.timestamp
            );
            _pullPoolShare(_controller, poolAmountIn);
            //_burnPoolShare(bsub(poolAmountIn, exitFee));
            _burnPoolShare(poolAmountIn);
            //_pushPoolShare(_factory, exitFee);
            _pushUnderlying(_datatokenAddress, _controller, ssAmountOut);
            //call unstake on 1ss to do cleanup on their side
            ssContract.UnStake(
                _datatokenAddress,
                ssAmountOut,
                poolAmountIn
            );
            emit LOG_BPT_SS(poolAmountIn);
        }
        
        _pullPoolShare(msg.sender, poolAmountIn);
        //_burnPoolShare(bsub(poolAmountIn, exitFee));
        _burnPoolShare(poolAmountIn);
        //_pushPoolShare(_factory, exitFee);
        _pushUnderlying(_baseTokenAddress, msg.sender, tokenAmountOut);
        return tokenAmountOut;
    }

    

    /**
     * @dev calcSingleOutPoolIn
     *      Returns expected amount of tokenOut for removing exact poolAmountIn pool shares from the pool
     * @param tokenOut tokenOut
     * @param poolAmountIn amount of shares spent
     */
    function calcSingleOutPoolIn(address tokenOut, uint256 poolAmountIn)
        external
        view
        returns (uint256 tokenAmountOut)
    {
        Record memory outRecord = _records[tokenOut];

        tokenAmountOut = calcSingleOutGivenPoolIn(
            outRecord.balance,
            _totalSupply,
            poolAmountIn
        );

        return tokenAmountOut;
    }

    /**
     * @dev calcPoolInSingleOut
     *      Returns number of poolshares needed to withdraw exact tokenAmountOut tokens
     * @param tokenOut tokenOut
     * @param tokenAmountOut expected amount of tokensOut
     */
    function calcPoolInSingleOut(address tokenOut, uint256 tokenAmountOut)
        external
        view
        returns (uint256 poolAmountIn)
    {
        Record memory outRecord = _records[tokenOut];

        poolAmountIn = calcPoolInGivenSingleOut(
            outRecord.balance,
            _totalSupply,
            tokenAmountOut
        );
        return poolAmountIn;
    }

    /**
     * @dev calcSingleInPoolOut
     *      Returns number of tokens to be staked to the pool in order to get an exact number of poolshares
     * @param tokenIn tokenIn
     * @param poolAmountOut expected amount of pool shares
     */
    function calcSingleInPoolOut(address tokenIn, uint256 poolAmountOut)
        external
        view
        returns (uint256 tokenAmountIn)
    {
        Record memory inRecord = _records[tokenIn];

        tokenAmountIn = calcSingleInGivenPoolOut(
            inRecord.balance,
            _totalSupply,
            poolAmountOut
        );

        return tokenAmountIn;
    }

    /**
     * @dev calcPoolOutSingleIn
     *      Returns number of poolshares obtain by staking exact tokenAmountIn tokens
     * @param tokenIn tokenIn
     * @param tokenAmountIn exact number of tokens staked
     */
    function calcPoolOutSingleIn(address tokenIn, uint256 tokenAmountIn)
        external
        view
        returns (uint256 poolAmountOut)
    {
        Record memory inRecord = _records[tokenIn];

        poolAmountOut = calcPoolOutGivenSingleIn(
            inRecord.balance,
            _totalSupply,
            tokenAmountIn
        );

        return poolAmountOut;
    }


    // Internal functions below

    // ==
    // 'Underlying' token-manipulation functions make external calls but are NOT locked
    // You must `_lock_` or otherwise ensure reentry-safety
    function _pullUnderlying(
        address erc20,
        address from,
        uint256 amount
    ) internal {
        uint256 balanceBefore = IERC20(erc20).balanceOf(address(this));
        IERC20(erc20).safeTransferFrom(from, address(this), amount);
        require(IERC20(erc20).balanceOf(address(this)) >= balanceBefore + amount,
                    "Transfer amount is too low");
        //require(xfer, "ERR_ERC20_FALSE");
    }

    function _pushUnderlying(
        address erc20,
        address to,
        uint256 amount
    ) internal {
        IERC20(erc20).safeTransfer(to, amount);
        //require(xfer, "ERR_ERC20_FALSE");
    }

    function _pullPoolShare(address from, uint256 amount) internal {
        _pull(from, amount);
    }

    function _pushPoolShare(address to, uint256 amount) internal {
        _push(to, amount);
    }

    function _mintPoolShare(uint256 amount) internal {
        _mint(amount);
    }

    function _burnPoolShare(uint256 amount) internal {
        _burn(amount);
    }

    // Absorb any tokens that have been sent to this contract into the pool
    function gulp(address token)
        external
        _lock_
    {
        require(_records[token].bound, "ERR_NOT_BOUND");
        uint256 oldBalance = _records[token].balance;
        _records[token].balance = IERC20(token).balanceOf(address(this));
        emit Gulped(token,oldBalance, _records[token].balance);
    }
}

File 11 of 18 : BConst.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

contract BConst {
    uint public constant BONE              = 1e18;

    uint public constant MIN_BOUND_TOKENS  = 2;
    uint public constant MAX_BOUND_TOKENS  = 2;

    uint public constant MIN_FEE           = BONE / 1e4;
    uint public constant MAX_FEE           = BONE / 10;
    uint public constant EXIT_FEE          = 0;

    uint public constant MIN_WEIGHT        = BONE;
    uint public constant MAX_WEIGHT        = BONE * 50;
    uint public constant MAX_TOTAL_WEIGHT  = BONE * 50;
    uint public constant MIN_BALANCE       = BONE / 1e12;

    uint public constant INIT_POOL_SUPPLY  = BONE * 100;

    uint public constant MIN_BPOW_BASE     = 1 wei;
    uint public constant MAX_BPOW_BASE     = (2 * BONE) - 1 wei;
    uint public constant BPOW_PRECISION    = BONE / 1e10;

    uint public constant MAX_IN_RATIO      = BONE / 2;
    uint public constant MAX_OUT_RATIO     = (BONE / 2) + 1 wei;
}

File 12 of 18 : Deployer.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

/**
 * @title Deployer Contract
 * @author Ocean Protocol Team
 *
 * @dev Contract Deployer
 *      This contract allowes factory contract 
 *      to deploy new contract instances using
 *      the same library pattern in solidity.
 *      the logic it self is deployed only once, but
 *      executed in the context of the new storage 
 *      contract (new contract instance)
 */
contract Deployer {
    event InstanceDeployed(address instance);
    
    // /**
    //  * @dev deploy
    //  *      deploy new contract instance 
    //  * @param _logic the logic contract address
    //  * @return  address of the new instance
    //  */
    function deploy(
        address _logic
    ) 
      internal 
      returns (address instance) 
    {
        bytes20 targetBytes = bytes20(_logic);
        // solhint-disable-next-line max-line-length
        // Follows OpenZeppelin Implementation https://github.com/OpenZeppelin/openzeppelin-sdk/blob/71c9ad77e0326db079e6a643eca8568ab316d4a9/packages/lib/contracts/upgradeability/ProxyFactory.sol
        // solhint-disable-next-line max-line-length
        // Adapted from https://github.com/optionality/clone-factory/blob/32782f82dfc5a00d103a7e61a17a5dedbd1e8e9d/contracts/CloneFactory.sol
        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
          let clone := mload(0x40)
          mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
          mstore(add(clone, 0x14), targetBytes)
          mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
          instance := create(0, clone, 0x37)
        }
        emit InstanceDeployed(address(instance));
    }
}

File 13 of 18 : ISideStaking.sol
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface ISideStaking {


    function newDatatokenCreated(
        address datatokenAddress,
        address baseTokenAddress,
        address poolAddress,
        address publisherAddress,
        uint256[] calldata ssParams
    ) external returns (bool);

    function getDatatokenCirculatingSupply(address datatokenAddress)
        external
        view
        returns (uint256);

    function getPublisherAddress(address datatokenAddress)
        external
        view
        returns (address);

    function getBaseTokenAddress(address datatokenAddress)
        external
        view
        returns (address);

    function getPoolAddress(address datatokenAddress)
        external
        view
        returns (address);

    function getBaseTokenBalance(address datatokenAddress)
        external
        view
        returns (uint256);

    function getDatatokenBalance(address datatokenAddress)
        external
        view
        returns (uint256);

    function getvestingEndBlock(address datatokenAddress)
        external
        view
        returns (uint256);

    function getvestingAmount(address datatokenAddress)
        external
        view
        returns (uint256);

    function getvestingLastBlock(address datatokenAddress)
        external
        view
        returns (uint256);

    function getvestingAmountSoFar(address datatokenAddress)
        external
        view
        returns (uint256);



    function canStake(
        address datatokenAddress,
        uint256 amount
    ) external view returns (bool);

    function Stake(
        address datatokenAddress,
        uint256 amount
    ) external;

    function canUnStake(
        address datatokenAddress,
        uint256 amount
    ) external view returns (bool);

    function UnStake(
        address datatokenAddress,
        uint256 amount,
        uint256 poolAmountIn
    ) external;

    function getId() pure external returns (uint8);

  
}

File 14 of 18 : BToken.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import './BNum.sol';
// import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '../../interfaces/IERC20.sol';
// Highly opinionated token implementation

// interface IERC20 {
//     event Approval(address indexed src, address indexed dst, uint amt);
//     event Transfer(address indexed src, address indexed dst, uint amt);

//     function totalSupply() external view returns (uint);
//     function balanceOf(address whom) external view returns (uint);
//     function allowance(address src, address dst) external view returns (uint);

//     function approve(address dst, uint amt) external returns (bool);
//     function transfer(address dst, uint amt) external returns (bool);
//     function transferFrom(
//         address src, address dst, uint amt
//     ) external returns (bool);
// }

contract BTokenBase is BNum {

    mapping(address => uint)                   internal _balance;
    mapping(address => mapping(address=>uint)) internal _allowance;
    uint internal _totalSupply;

    event Approval(address indexed src, address indexed dst, uint amt);
    event Transfer(address indexed src, address indexed dst, uint amt);

    function _mint(uint amt) internal {
        _balance[address(this)] = badd(_balance[address(this)], amt);
        _totalSupply = badd(_totalSupply, amt);
        emit Transfer(address(0), address(this), amt);
    }

    function _burn(uint amt) internal {
        require(
            _balance[address(this)] >= amt, 
            'ERR_INSUFFICIENT_BAL'
        );
        _balance[address(this)] = bsub(_balance[address(this)], amt);
        _totalSupply = bsub(_totalSupply, amt);
        emit Transfer(address(this), address(0), amt);
    }

    function _move(address src, address dst, uint amt) internal {
        require(_balance[src] >= amt, 'ERR_INSUFFICIENT_BAL');
        _balance[src] = bsub(_balance[src], amt);
        _balance[dst] = badd(_balance[dst], amt);
        emit Transfer(src, dst, amt);
    }

    function _push(address to, uint amt) internal {
        _move(address(this), to, amt);
    }

    function _pull(address from, uint amt) internal {
        _move(from, address(this), amt);
    }
}

contract BToken is BTokenBase {

    function name() external view returns (string memory) {
        return 'Ocean Pool Token';
    }

    function symbol() external view returns (string memory) {
        return 'OPT';
    }

    function decimals() external view returns(uint8) {
        return 18;
    }

    function allowance(address src, address dst) external view returns (uint256) {
        return _allowance[src][dst];
    }

    function balanceOf(address whom) external view returns (uint) {
        return _balance[whom];
    }

    function totalSupply() public view returns (uint) {
        return _totalSupply;
    }

    function approve(address dst, uint amt) external returns (bool) {
        _allowance[msg.sender][dst] = amt;
        emit Approval(msg.sender, dst, amt);
        return true;
    }

    function increaseApproval(address dst, uint amt) external returns (bool) {
        _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
        return true;
    }

    function decreaseApproval(address dst, uint amt) external returns (bool) {
        uint oldValue = _allowance[msg.sender][dst];
        if (amt > oldValue) {
            _allowance[msg.sender][dst] = 0;
        } else {
            _allowance[msg.sender][dst] = bsub(oldValue, amt);
        }
        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
        return true;
    }

    function transfer(address dst, uint amt) external returns (bool) {
        _move(msg.sender, dst, amt);
        return true;
    }

    function transferFrom(
        address src, 
        address dst, 
        uint amt
    ) 
    external
    returns (bool) 
    {
        require(
            msg.sender == src || amt <= _allowance[src][msg.sender], 
            'ERR_BTOKEN_BAD_CALLER'
        );
        _move(src, dst, amt);
        if (msg.sender != src && _allowance[src][msg.sender] != uint256(int(-1)) ) {
            _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
            emit Approval(src, msg.sender, _allowance[src][msg.sender]);
        }
        return true;
    }
}

File 15 of 18 : BMath.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import './BNum.sol';


import "../../interfaces/IFactoryRouter.sol";

contract BMath is BConst, BNum {

   // uint public _swapMarketFee;
    uint public _swapPublishMarketFee;
    uint internal _swapFee;
  
    address public router; // BFactory address to push token exitFee to

    address internal _datatokenAddress; //datatoken address
    address internal _baseTokenAddress; //base token address
    mapping(address => uint) public communityFees;

     mapping(address => uint) public publishMarketFees;
   // mapping(address => uint) public marketFees;


    function getOPCFee() public view returns (uint) {
        return IFactoryRouter(router).getOPCFee(_baseTokenAddress);
    }
    
    struct swapfees{
        uint256 LPFee;
        uint256 oceanFeeAmount;
        uint256 publishMarketFeeAmount;
        uint256 consumeMarketFee;
    }
    /**********************************************************************************************
    // calcSpotPrice                                                                             //
    // sP = spotPrice                                                                            //
    // bI = tokenBalanceIn                ( bI / wI )         1                                  //
    // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //
    // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //
    // wO = tokenWeightOut                                                                       //
    // sF = swapFee                                                                              //
    **********************************************************************************************/
    function calcSpotPrice(
        uint tokenBalanceIn,
        uint tokenWeightIn,
        uint tokenBalanceOut,
        uint tokenWeightOut,
        uint _swapMarketFee
    )
        internal view
        returns (uint spotPrice)
        
    {   
       

        uint numer = bdiv(tokenBalanceIn, tokenWeightIn);
        uint denom = bdiv(tokenBalanceOut, tokenWeightOut);
        uint ratio = bdiv(numer, denom);
        uint scale = bdiv(BONE, bsub(BONE, _swapFee+getOPCFee()+_swapPublishMarketFee+_swapMarketFee));
      
        return  (spotPrice = bmul(ratio, scale));
    }

    
    //    data = [
    //         inRecord.balance,
    //         inRecord.denorm,
    //         outRecord.balance,
    //         outRecord.denorm
    //     ];
    function calcOutGivenIn(
        uint[4] memory data,
        uint tokenAmountIn,
        //address tokenInAddress,
        uint256 _consumeMarketSwapFee

    )
        public view
        returns (uint tokenAmountOut, uint balanceInToAdd, swapfees memory _swapfees)
    {
        uint weightRatio = bdiv(data[1], data[3]);

        _swapfees.oceanFeeAmount =  bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, getOPCFee())));

        
        _swapfees.publishMarketFeeAmount =  bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _swapPublishMarketFee)));
        

        _swapfees.LPFee = bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _swapFee)));
        _swapfees.consumeMarketFee = bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _consumeMarketSwapFee)));
        uint totalFee =_swapFee+getOPCFee()+_swapPublishMarketFee+_consumeMarketSwapFee;

        uint adjustedIn = bsub(BONE, totalFee);
        
        adjustedIn = bmul(tokenAmountIn, adjustedIn);
         
        uint y = bdiv(data[0], badd(data[0], adjustedIn));
        uint foo = bpow(y, weightRatio);
        uint bar = bsub(BONE, foo);
        

        tokenAmountOut = bmul(data[2], bar);
       
        return (tokenAmountOut, bsub(tokenAmountIn,(_swapfees.oceanFeeAmount+_swapfees.publishMarketFeeAmount+_swapfees.consumeMarketFee)), _swapfees);
        
    }

     
    function calcInGivenOut(
        uint[4] memory data,
        uint tokenAmountOut,
        uint _consumeMarketSwapFee
    )
        public view 
        returns (uint tokenAmountIn, uint tokenAmountInBalance, swapfees memory _swapfees)
    {
        uint weightRatio = bdiv(data[3], data[1]);
        uint diff = bsub(data[2], tokenAmountOut);
        uint y = bdiv(data[2], diff);
        uint foo = bpow(y, weightRatio);
        foo = bsub(foo, BONE);
        uint totalFee =_swapFee+getOPCFee()+_consumeMarketSwapFee+_swapPublishMarketFee;
        
        
        tokenAmountIn = bdiv(bmul(data[0], foo), bsub(BONE, totalFee));
        _swapfees.oceanFeeAmount =  bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, getOPCFee())));
        
     
        _swapfees.publishMarketFeeAmount =  bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _swapPublishMarketFee)));

     
        _swapfees.LPFee = bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _swapFee)));
        _swapfees.consumeMarketFee = bsub(tokenAmountIn, bmul(tokenAmountIn, bsub(BONE, _consumeMarketSwapFee)));
        
      
        tokenAmountInBalance = bsub(tokenAmountIn,(_swapfees.oceanFeeAmount+_swapfees.publishMarketFeeAmount+_swapfees.consumeMarketFee));
      
        
        return (tokenAmountIn, tokenAmountInBalance,_swapfees);
    }

    function calcPoolOutGivenSingleIn(
        uint tokenBalanceIn,
        uint poolSupply,
        uint tokenAmountIn
       
    )
        internal pure
        returns (uint poolAmountOut)
    {
        uint tokenAmountInAfterFee = bmul(tokenAmountIn, BONE);
        uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
        uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
        uint poolRatio = bsub(tokenInRatio,BONE);
        uint newPoolSupply = bmul(poolRatio, poolSupply);
        require(newPoolSupply >= 2, 'ERR_TOKEN_AMOUNT_IN_TOO_LOW'); 
        newPoolSupply = newPoolSupply/2;
        return newPoolSupply;
    }

    function calcSingleInGivenPoolOut(
        uint tokenBalanceIn,
        uint poolSupply,
        uint poolAmountOut
    )
        internal pure
        returns (uint tokenAmountIn)
    {
        uint newPoolSupply = badd(poolSupply, poolAmountOut);
        uint poolRatio = bdiv(newPoolSupply, poolSupply);
        uint tokenInRatio = bsub(poolRatio, BONE);
        uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
        require(newTokenBalanceIn >= 1, 'ERR_POOL_AMOUNT_OUT_TOO_LOW'); 
        newTokenBalanceIn = newTokenBalanceIn * 2;
        return newTokenBalanceIn;
    }

    function calcSingleOutGivenPoolIn(
        uint tokenSupply,
        uint poolSupply,
        uint poolAmountIn
    )
        internal pure
        returns (uint tokenAmountOut)
    {
        require(poolAmountIn >= 1, 'ERR_POOL_AMOUNT_IN_TOO_LOW'); 
        poolAmountIn = poolAmountIn * 2;
        uint newPoolSupply = bsub(poolSupply, poolAmountIn);
        uint poolRatio = bdiv(newPoolSupply, poolSupply);
        uint tokenOutRatio = bsub(BONE,poolRatio);
        uint newTokenBalanceOut = bmul(tokenOutRatio, tokenSupply);
        return newTokenBalanceOut;
    }

    function calcPoolInGivenSingleOut(
        uint tokenBalanceOut,
        uint poolSupply,
        uint tokenAmountOut
    )
        internal pure
        returns (uint poolAmountIn)
    {
        uint newTokenBalanceOut = bsub(
            tokenBalanceOut, 
            tokenAmountOut
        );
        uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
        uint poolRatio = bsub(BONE,tokenOutRatio);
        uint newPoolSupply = bmul(poolRatio, poolSupply);
        require(newPoolSupply >= 2, 'ERR_TOKEN_AMOUNT_OUT_TOO_LOW'); 
        newPoolSupply = newPoolSupply/2;
        return newPoolSupply;
    }


    

}

File 16 of 18 : BNum.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.12;
// Copyright Balancer, BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

import './BConst.sol';

contract BNum is BConst {

    function btoi(uint a)
        internal pure 
        returns (uint)
    {
        return a / BONE;
    }

    function bfloor(uint a)
        internal pure
        returns (uint)
    {
        return btoi(a) * BONE;
    }

    function badd(uint a, uint b)
        internal pure
        returns (uint)
    {
        uint c = a + b;
        require(c >= a, 'ERR_ADD_OVERFLOW');
        return c;
    }

    function bsub(uint a, uint b)
        internal pure
        returns (uint)
    {
        (uint c, bool flag) = bsubSign(a, b);
        require(!flag, 'ERR_SUB_UNDERFLOW');
        return c;
    }

    function bsubSign(uint a, uint b)
        internal pure
        returns (uint, bool)
    {
        if (a >= b) {
            return (a - b, false);
        } else {
            return (b - a, true);
        }
    }

    function bmul(uint a, uint b)
        internal pure
        returns (uint)
    {
        uint c0 = a * b;
        require(a == 0 || c0 / a == b, 'ERR_MUL_OVERFLOW');
        uint c1 = c0 + (BONE / 2);
        require(c1 >= c0, 'ERR_MUL_OVERFLOW');
        uint c2 = c1 / BONE;
        return c2;
    }

    function bdiv(uint a, uint b)
        internal pure
        returns (uint)
    {
        require(b != 0, 'ERR_DIV_ZERO');
        uint c0 = a * BONE;
        require(a == 0 || c0 / a == BONE, 'ERR_DIV_INTERNAL'); // bmul overflow
        uint c1 = c0 + (b / 2);
        require(c1 >= c0, 'ERR_DIV_INTERNAL'); //  badd require
        uint c2 = c1 / b;
        return c2;
    }

    // DSMath.wpow
    function bpowi(uint a, uint n)
        internal pure
        returns (uint)
    {
        uint b = a;
        uint z = n % 2 != 0 ? b : BONE;

        for (n /= 2; n != 0; n /= 2) {
            b = bmul(b, b);

            if (n % 2 != 0) {
                z = bmul(z, b);
            }
        }
        return z;
    }

    // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
    // Use `bpowi` for `b^e` and `bpowK` for k iterations
    // of approximation of b^0.w
    function bpow(uint base, uint exp)
        internal pure
        returns (uint)
    {
        require(base >= MIN_BPOW_BASE, 'ERR_BPOW_BASE_TOO_LOW');
        require(base <= MAX_BPOW_BASE, 'ERR_BPOW_BASE_TOO_HIGH');

        uint whole = bfloor(exp);
        uint remain = bsub(exp, whole);

        uint wholePow = bpowi(base, btoi(whole));

        if (remain == 0) {
            return wholePow;
        }

        uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
        return bmul(wholePow, partialResult);
    }

    function bpowApprox(uint base, uint exp, uint precision)
        internal pure
        returns (uint)
    {
        // term 0:
        uint a = exp;
        (uint x, bool xneg) = bsubSign(base, BONE);
        uint term = BONE;
        uint sum = term;
        bool negative = false;


        // term(k) = numer / denom 
        //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
        // each iteration, multiply previous term by (a-(k-1)) * x / k
        // continue until term is less than precision
        for (uint i = 1; term >= precision; i++) {
            uint bigK = i * BONE;
            (uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
            term = bmul(term, bmul(c, x));
            term = bdiv(term, bigK);
            if (term == 0) break;

            if (xneg) negative = !negative;
            if (cneg) negative = !negative;
            if (negative) {
                sum = bsub(sum, term);
            } else {
                sum = badd(sum, term);
            }
        }

        return sum;
    }

}

File 17 of 18 : IFactoryRouter.sol
pragma solidity 0.8.12;
// Copyright BigchainDB GmbH and Ocean Protocol contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0

interface IFactoryRouter {
    function deployPool(
        address[2] calldata tokens, // [datatokenAddress, baseTokenAddress]
        uint256[] calldata ssParams,
        uint256[] calldata swapFees,
        address[] calldata addresses
    ) external returns (address);

    function deployFixedRate(
        address fixedPriceAddress,
        address[] calldata addresses,
        uint256[] calldata uints
    ) external returns (bytes32 exchangeId);

    function getOPCFee(address baseToken) external view returns (uint256);
    function getOPCFees() external view returns (uint256,uint256);
    function getOPCConsumeFee() external view returns (uint256);
    function getOPCProviderFee() external view returns (uint256);

    function getMinVestingPeriod() external view returns (uint256);
    function deployDispenser(
        address _dispenser,
        address datatoken,
        uint256 maxTokens,
        uint256 maxBalance,
        address owner,
        address allowedSwapper
    ) external;

    function isApprovedToken(address) external view returns(bool);
    function getApprovedTokens() external view returns(address[] memory);
    function isSSContract(address) external view returns(bool);
    function getSSContracts() external view returns(address[] memory);
    function isFixedRateContract(address) external view returns(bool);
    function getFixedRatesContracts() external view returns(address[] memory);
    function isDispenserContract(address) external view returns(bool);
    function getDispensersContracts() external view returns(address[] memory);
    function isPoolTemplate(address) external view returns(bool);
    function getPoolTemplates() external view returns(address[] memory);

    struct Stakes {
        address poolAddress;
        uint256 tokenAmountIn;
        uint256 minPoolAmountOut;
    }
    function stakeBatch(Stakes[] calldata) external;

    enum operationType {
        SwapExactIn,
        SwapExactOut,
        FixedRate,
        Dispenser
    }

    struct Operations {
        bytes32 exchangeIds; // used for fixedRate or dispenser
        address source; // pool, dispenser or fixed rate address
        operationType operation; // type of operation: enum operationType
        address tokenIn; // token in address, only for pools
        uint256 amountsIn; // ExactAmount In for swapExactIn operation, maxAmount In for swapExactOut
        address tokenOut; // token out address, only for pools
        uint256 amountsOut; // minAmountOut for swapExactIn or exactAmountOut for swapExactOut
        uint256 maxPrice; // maxPrice, only for pools
        uint256 swapMarketFee;
        address marketFeeAddress;
    }
    function buyDTBatch(Operations[] calldata) external;
    function updateOPCCollector(address _opcCollector) external;
    function getOPCCollector() view external returns (address);
}

File 18 of 18 : Address.sol
pragma solidity 0.8.12;
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }


}


// File @openzeppelin/contracts/utils/[email protected]

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_routerOwner","type":"address"},{"internalType":"address","name":"_oceanToken","type":"address"},{"internalType":"address","name":"_bpoolTemplate","type":"address"},{"internalType":"address","name":"_opcCollector","type":"address"},{"internalType":"address[]","name":"_preCreatedPools","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newBPoolAddress","type":"address"},{"indexed":true,"internalType":"address","name":"registeredBy","type":"address"},{"indexed":true,"internalType":"address","name":"datatokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"baseTokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"bpoolTemplateAddress","type":"address"},{"indexed":false,"internalType":"address","name":"ssAddress","type":"address"}],"name":"BPoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"DispenserContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"DispenserContractRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"FactoryContractChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"FixedRateContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"FixedRateContractRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"instance","type":"address"}],"name":"InstanceDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"isOcean","type":"bool"}],"name":"NewPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"_newOpcCollector","type":"address"}],"name":"OPCCollectorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSwapOceanFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSwapNonOceanFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newConsumeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newProviderFee","type":"uint256"}],"name":"OPCFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"PoolTemplateAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"PoolTemplateRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"newRouter","type":"address"}],"name":"RouterChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"SSContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"SSContractRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"minVestingPeriodInBlocks","type":"uint256"}],"name":"VestingPeriodChanges","type":"event"},{"inputs":[],"name":"BONE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BPOW_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXIT_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INIT_POOL_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BOUND_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BPOW_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_IN_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_OUT_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TOTAL_WEIGHT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_WEIGHT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_BALANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_BOUND_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_BPOW_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_WEIGHT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"addApprovedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_dispenser","type":"address"}],"name":"addDispenserContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_factory","type":"address"}],"name":"addFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fixedRate","type":"address"}],"name":"addFixedRateContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolTemplate","type":"address"}],"name":"addPoolTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_ssContract","type":"address"}],"name":"addSSContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"approvedTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"exchangeIds","type":"bytes32"},{"internalType":"address","name":"source","type":"address"},{"internalType":"enum IFactoryRouter.operationType","name":"operation","type":"uint8"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountsIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountsOut","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"swapMarketFee","type":"uint256"},{"internalType":"address","name":"marketFeeAddress","type":"address"}],"internalType":"struct IFactoryRouter.Operations[]","name":"_operations","type":"tuple[]"}],"name":"buyDTBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_routerOwner","type":"address"}],"name":"changeRouterOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consumeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_dispenser","type":"address"},{"internalType":"address","name":"datatoken","type":"address"},{"internalType":"uint256","name":"maxTokens","type":"uint256"},{"internalType":"uint256","name":"maxBalance","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"allowedSwapper","type":"address"}],"name":"deployDispenser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fixedPriceAddress","type":"address"},{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256[]","name":"uints","type":"uint256[]"}],"name":"deployFixedRate","outputs":[{"internalType":"bytes32","name":"exchangeId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[2]","name":"tokens","type":"address[2]"},{"internalType":"uint256[]","name":"ssParams","type":"uint256[]"},{"internalType":"uint256[]","name":"swapFees","type":"uint256[]"},{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"deployPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"dispensers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fixedRate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fixedrates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getApprovedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDispensersContracts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFixedRatesContracts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinVestingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOPCCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOPCConsumeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseToken","type":"address"}],"name":"getOPCFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOPCFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOPCProviderFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolTemplates","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSSContracts","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"isApprovedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_dispenser","type":"address"}],"name":"isDispenserContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_fixedRate","type":"address"}],"name":"isFixedRateContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolTemplate","type":"address"}],"name":"isPoolTemplate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ssContract","type":"address"}],"name":"isSSContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minVestingPeriodInBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"opcCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolTemplates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"providerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"removeApprovedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_dispenser","type":"address"}],"name":"removeDispenserContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fixedRate","type":"address"}],"name":"removeFixedRateContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolTemplate","type":"address"}],"name":"removePoolTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_ssContract","type":"address"}],"name":"removeSSContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ssContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"}],"internalType":"struct IFactoryRouter.Stakes[]","name":"_stakes","type":"tuple[]"}],"name":"stakeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapNonOceanFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapOceanFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPeriod","type":"uint256"}],"name":"updateMinVestingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_opcCollector","type":"address"}],"name":"updateOPCCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newSwapOceanFee","type":"uint256"},{"internalType":"uint256","name":"_newSwapNonOceanFee","type":"uint256"},{"internalType":"uint256","name":"_newConsumeFee","type":"uint256"},{"internalType":"uint256","name":"_newProviderFee","type":"uint256"}],"name":"updateOPCFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526225049060055566038d7ea4c6800060065566071afd498d0000600755666a94d74f43000060085560006009553480156200003e57600080fd5b5060405162004ee938038062004ee983398101604081905262000061916200062d565b8282826001600160a01b038316620000d65760405162461bcd60e51b815260206004820152602d60248201527f42466163746f72793a20696e76616c69642062706f6f6c2074656d706c61746560448201526c207a65726f206164647265737360981b60648201526084015b60405180910390fd5b6001600160a01b0382166200012e5760405162461bcd60e51b815260206004820152601660248201527f42466163746f72793a207a65726f2061646472657373000000000000000000006044820152606401620000cd565b600080546001600160a01b0319166001600160a01b03841617905562000154836200037f565b805115620002035760005b8151811015620002015760006001600160a01b0316336001600160a01b03168383815181106200019357620001936200074d565b602090810291909101810151604080516000808252938101849052908101929092526001600160a01b0316907fbb6121447d29a04cc37bfd93cdf6a9dd08229f2d941116c1528f04698c4e790e9060600160405180910390a480620001f88162000763565b9150506200015f565b505b5050506001600160a01b0385166200026a5760405162461bcd60e51b815260206004820152602360248201527f466163746f7279526f757465723a20496e76616c696420726f75746572206f776044820152623732b960e91b6064820152608401620000cd565b6001600160a01b038216620002ce5760405162461bcd60e51b815260206004820152602360248201527f466163746f7279526f757465723a20496e76616c6964206f7063436f6c6c65636044820152623a37b960e91b6064820152608401620000cd565b6001600160a01b038416620003395760405162461bcd60e51b815260206004820152602a60248201527f466163746f7279526f757465723a20496e76616c6964204f6365616e20546f6b604482015269656e206164647265737360b01b6064820152608401620000cd565b600280546001600160a01b038088166001600160a01b0319928316179092556000805492851692909116919091179055620003748462000476565b50505050506200078d565b6001600160a01b038116620003eb5760405162461bcd60e51b815260206004820152602b60248201527f466163746f7279526f757465723a20496e76616c696420706f6f6c54656d706c60448201526a617465206164647265737360a81b6064820152608401620000cd565b620003f68162000501565b620004735760018054808201825560009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b038416908117909155604051909133917fc3674c32cbec94fe266aab7ef71f65b9bf77e1ed501d4f2bece8752f5352e9ac9190a35b50565b62000481816200051f565b6200047357600a805460018101825560009182527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319166001600160a01b038416908117909155604051909133917fdffbd9ded1c09446f09377de547142dcce7dc541c8b0b028142b1eba7026b9e79190a350565b600062000519826200059160201b620030361760201c565b92915050565b6000805b600a548110156200058857826001600160a01b0316600a82815481106200054e576200054e6200074d565b6000918252602090912001546001600160a01b03161415620005735750600192915050565b806200057f8162000763565b91505062000523565b50600092915050565b6000805b6001548110156200058857826001600160a01b031660018281548110620005c057620005c06200074d565b6000918252602090912001546001600160a01b03161415620005e55750600192915050565b80620005f18162000763565b91505062000595565b80516001600160a01b03811681146200061257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600080600080600060a086880312156200064657600080fd5b6200065186620005fa565b9450602062000662818801620005fa565b94506200067260408801620005fa565b93506200068260608801620005fa565b60808801519093506001600160401b0380821115620006a057600080fd5b818901915089601f830112620006b557600080fd5b815181811115620006ca57620006ca62000617565b8060051b604051601f19603f83011681018181108582111715620006f257620006f262000617565b60405291825284820192508381018501918c8311156200071157600080fd5b938501935b828510156200073a576200072a85620005fa565b8452938501939285019262000716565b8096505050505050509295509295909350565b634e487b7160e01b600052603260045260246000fd5b60006000198214156200078657634e487b7160e01b600052601160045260246000fd5b5060010190565b61474c806200079d6000396000f3fe608060405234801561001057600080fd5b50600436106103e65760003560e01c8063858ab5961161020a578063c45a015511610125578063e183fb3f116100b8578063e525f99c11610087578063e525f99c14610782578063ec09302114610795578063ecdda5881461079d578063ee3bc635146107b0578063fca24bc6146107c357600080fd5b8063e183fb3f1461075d578063e193faad14610766578063e2bdeefa14610779578063e4a28a521461041b57600080fd5b8063cd10534b116100f4578063cd10534b14610709578063d629a00a1461071c578063dce2d0df1461072f578063de9a95a71461074a57600080fd5b8063c45a0155146106dc578063c6580d12146106ef578063c6e983d9146106f7578063cb870cbf1461070057600080fd5b8063b7b800a41161019d578063bc694ea21161016c578063bc694ea2146106ae578063bfa04b85146106b6578063c07c00fe146106c9578063c36596a6146104bd57600080fd5b8063b7b800a41461065d578063b8421e2b1461068b578063ba019dab1461069e578063bc063e1a146106a657600080fd5b80639f2c010a116101d95780639f2c010a1461064a578063b0e0d1361461065d578063b19aaac614610665578063b66806fb1461067857600080fd5b8063858ab5961461061f578063867378c5146106325780639381cd2b1461063a578063992e2a921461064257600080fd5b8063475030c0116103055780636cdf90a1116102985780637cbf85bf116102675780637cbf85bf146105cb5780637d28354d146105de578063802d1422146105e657806382449375146105f95780638552730a1461060c57600080fd5b80636cdf90a114610595578063737e5ca0146105a85780637521aff9146105b057806376c7a3c7146105c357600080fd5b80635705987a116102d45780635705987a1461055f5780636afc0c5f146105725780636c45e8811461057a5780636c9fb6121461058257600080fd5b8063475030c01461052857806347e140941461053157806350cbbe7614610544578063510f34651461055757600080fd5b80631dafede01161037d5780632d5ad3d51161034c5780632d5ad3d5146104e7578063335b7fa2146104fa578063415792081461050d57806346104ea81461052057600080fd5b80631dafede0146104aa578063218b5382146104bd578063241c7a6d146104cc57806329ce1ec5146104d457600080fd5b806316592614116103b9578063165926141461046957806316d9cb3d1461047e578063189d00ca146104915780631a81876d1461049957600080fd5b806303814238146103eb57806309a3bbe41461041b57806315c25dd51461043157806315d4c9eb14610454575b600080fd5b6000546103fe906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6104236107d6565b604051908152602001610412565b61044461043f366004613cc2565b6107ec565b6040519015158152602001610412565b61045c610856565b6040516104129190613cdf565b61047c610477366004613cc2565b6108b8565b005b61047c61048c366004613cc2565b610a60565b610423610b39565b6000546001600160a01b03166103fe565b6103fe6104b8366004613d2c565b610b50565b610423670de0b6b3a764000081565b61045c610b7a565b61047c6104e2366004613cc2565b610b89565b6104446104f5366004613cc2565b610cb4565b6103fe610508366004613d2c565b610d15565b61047c61051b366004613cc2565b610d25565b61045c610dfc565b61042360055481565b61047c61053f366004613cc2565b610e5c565b6103fe610552366004613d2c565b610ffa565b600554610423565b61047c61056d366004613cc2565b61100a565b61045c6111eb565b600954610423565b61047c610590366004613cc2565b61124b565b6104446105a3366004613cc2565b611323565b61045c611384565b61047c6105be366004613d2c565b6113e4565b61042361144b565b6103fe6105d9366004613d2c565b61145f565b600854610423565b6004546103fe906001600160a01b031681565b610423610607366004613d91565b61146f565b6002546103fe906001600160a01b031681565b61047c61062d366004613e14565b6115e1565b6104236119e5565b6104236119fc565b610423611a0f565b61047c610658366004613cc2565b611a2d565b610423600281565b61047c610673366004613cc2565b611bcb565b6103fe610686366004613d2c565b611bfe565b610423610699366004613cc2565b611c0e565b610423600181565b610423611c33565b610423611c46565b61047c6106c4366004613e89565b611c65565b6104446106d7366004613cc2565b611cf3565b6003546103fe906001600160a01b031681565b610423600081565b61042360095481565b61042360075481565b61047c610717366004613cc2565b611d04565b61047c61072a366004613ebb565b611d37565b60065460075460408051928352602083019190915201610412565b61047c610758366004613cc2565b611ea7565b61042360065481565b61047c610774366004613cc2565b611f7f565b61042360085481565b61047c610790366004613cc2565b611fb2565b61042361207e565b61047c6107ab366004613f2b565b612091565b6103fe6107be366004613f8f565b612c33565b6104446107d1366004613cc2565b612fd5565b6107e9670de0b6b3a76400006032614055565b81565b6000805b600b5481101561084d57826001600160a01b0316600b828154811061081757610817614074565b6000918252602090912001546001600160a01b0316141561083b5750600192915050565b806108458161408a565b9150506107f0565b50600092915050565b6060600d8054806020026020016040519081016040528092919081815260200182805480156108ae57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610890575b5050505050905090565b6002546001600160a01b031633146108eb5760405162461bcd60e51b81526004016108e2906140a5565b60405180910390fd5b6001600160a01b0381166109115760405162461bcd60e51b81526004016108e2906140d5565b60005b600d5481101561096d57816001600160a01b0316600d828154811061093b5761093b614074565b6000918252602090912001546001600160a01b0316141561095b5761096d565b806109658161408a565b915050610914565b600d54811015610a5c57600d80546109879060019061411e565b8154811061099757610997614074565b600091825260209091200154600d80546001600160a01b0390921691839081106109c3576109c3614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600d805480610a0257610a02614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917f63fe522dcdc5f006279afe8840a87398bb3d0dbb906b848866195859621908b39190a35b5050565b6002546001600160a01b03163314610a8a5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610ab05760405162461bcd60e51b81526004016108e29061414b565b610ab981611323565b610b3657600c805460018101825560009182527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b038416908117909155604051909133917f45ccb9b9d6b112eb7a38daf6a23bda8b4c8449d5cf64a034975a5beb8cdd37b39190a35b50565b6107e96402540be400670de0b6b3a7640000614194565b600a8181548110610b6057600080fd5b6000918252602090912001546001600160a01b0316905081565b6060610b84613097565b905090565b6002546001600160a01b03163314610bb35760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610c195760405162461bcd60e51b815260206004820152602760248201527f466163746f7279526f757465723a20496e76616c6964205f666163746f7279206044820152666164647265737360c81b60648201526084016108e2565b6003546001600160a01b031615610c685760405162461bcd60e51b8152602060048201526013602482015272119050d513d49648105314915051164814d155606a1b60448201526064016108e2565b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f1f869f1ddaa0e2e3652252b3394c05c805f54a0d5f7fb6975f785ae4f91e587190600090a350565b6000805b600a5481101561084d57826001600160a01b0316600a8281548110610cdf57610cdf614074565b6000918252602090912001546001600160a01b03161415610d035750600192915050565b80610d0d8161408a565b915050610cb8565b600d8181548110610b6057600080fd5b6002546001600160a01b03163314610d4f5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610db15760405162461bcd60e51b8152602060048201526024808201527f4e6577206f7063436f6c6c6563746f722063616e6e6f74206265205a45524f5f60448201526320a2222960e11b60648201526084016108e2565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917fbadc0056fc46a437b2d0a4e9ae17a4f964e18972b5f2455b98b7331c1401a80f9190a350565b6060600c8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b03163314610e865760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610eac5760405162461bcd60e51b81526004016108e2906141b6565b60005b600b54811015610f0857816001600160a01b0316600b8281548110610ed657610ed6614074565b6000918252602090912001546001600160a01b03161415610ef657610f08565b80610f008161408a565b915050610eaf565b600b54811015610a5c57600b8054610f229060019061411e565b81548110610f3257610f32614074565b600091825260209091200154600b80546001600160a01b039092169183908110610f5e57610f5e614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b805480610f9d57610f9d614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917f7df5d17d768be6563796784637c80bc9204e2af0bc4ba8630f5aa2fed96ea4879190a35050565b60018181548110610b6057600080fd5b6002546001600160a01b031633146110345760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b03811661109d5760405162461bcd60e51b815260206004820152602a60248201527f466163746f7279526f757465723a20496e76616c6964204f6365616e20546f6b604482015269656e206164647265737360b01b60648201526084016108e2565b60005b600a548110156110f957816001600160a01b0316600a82815481106110c7576110c7614074565b6000918252602090912001546001600160a01b031614156110e7576110f9565b806110f18161408a565b9150506110a0565b600a54811015610a5c57600a80546111139060019061411e565b8154811061112357611123614074565b600091825260209091200154600a80546001600160a01b03909216918390811061114f5761114f614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600a80548061118e5761118e614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917fbbe55b1ff108e23e5ff1a6f5d36946eec15ec0ca0ded2bfed4cdcf697ca904609190a35050565b6060600a8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b031633146112755760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b03811661129b5760405162461bcd60e51b81526004016108e2906141b6565b6112a4816107ec565b610b3657600b805460018101825560009182527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b038416908117909155604051909133917fcef3ce01f85c030161b431114e41e463297b68f169c1013c526b1cbc5747e5449190a350565b6000805b600c5481101561084d57826001600160a01b0316600c828154811061134e5761134e614074565b6000918252602090912001546001600160a01b031614156113725750600192915050565b8061137c8161408a565b915050611327565b6060600b8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b0316331461140e5760405162461bcd60e51b81526004016108e2906140a5565b600581905560405181815233907f5ee73342e684c0b848562c301fa2dc6b0146b266cfa12d4219fb04b316f7a4759060200160405180910390a250565b6107e9612710670de0b6b3a7640000614194565b600b8181548110610b6057600080fd5b60035460405163226e761560e01b81523360048201526000916001600160a01b03169063226e761590602401602060405180830381865afa1580156114b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114dc9190614210565b6114f85760405162461bcd60e51b81526004016108e29061422b565b61150186611323565b6115605760405162461bcd60e51b815260206004820152602a60248201527f464143544f525920524f555445523a20496e76616c696420466978656450726960448201526918d950dbdb9d1c9858dd60b21b60648201526084016108e2565b60405163012c327560e01b81526001600160a01b0387169063012c3275906115949033908990899089908990600401614276565b6020604051808303816000875af11580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190614305565b9695505050505050565b60328111156116025760405162461bcd60e51b81526004016108e29061431e565b60005b818110156119e057600083838381811061162157611621614074565b6116379260206060909202019081019150613cc2565b6001600160a01b0316634df947d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611674573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116989190614360565b90506116c18133308787878181106116b2576116b2614074565b905060600201602001356130f7565b60008484848181106116d5576116d5614074565b6116eb9260206060909202019081019150613cc2565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611731573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117559190614305565b90506117b785858581811061176c5761176c614074565b6117829260206060909202019081019150613cc2565b86868681811061179457611794614074565b90506060020160200135846001600160a01b03166132459092919063ffffffff16565b60008585858181106117cb576117cb614074565b6117e19260206060909202019081019150613cc2565b6001600160a01b0316638329ab3387878781811061180157611801614074565b9050606002016020013588888881811061181d5761181d614074565b905060600201604001356040518363ffffffff1660e01b815260040161184d929190918252602082015260400190565b6020604051808303816000875af115801561186c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118909190614305565b90508585858181106118a4576118a4614074565b905060600201604001358110156118ed5760405162461bcd60e51b815260206004820152600d60248201526c04e4f5420454e4f554748204c5609c1b60448201526064016108e2565b600086868681811061190157611901614074565b6119179260206060909202019081019150613cc2565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190614305565b90506119c9336119918386613330565b8989898181106119a3576119a3614074565b6119b99260206060909202019081019150613cc2565b6001600160a01b03169190613343565b5050505080806119d89061408a565b915050611605565b505050565b6107e964e8d4a51000670de0b6b3a7640000614194565b6107e9670de0b6b3a76400006064614055565b611a226002670de0b6b3a7640000614194565b6107e990600161437d565b6002546001600160a01b03163314611a575760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116611a7d5760405162461bcd60e51b81526004016108e29061414b565b60005b600c54811015611ad957816001600160a01b0316600c8281548110611aa757611aa7614074565b6000918252602090912001546001600160a01b03161415611ac757611ad9565b80611ad18161408a565b915050611a80565b600c54811015610a5c57600c8054611af39060019061411e565b81548110611b0357611b03614074565b600091825260209091200154600c80546001600160a01b039092169183908110611b2f57611b2f614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600c805480611b6e57611b6e614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917ff1ec0697329a2e8de8cf055335547acc40fbace8712fc77aeea1ea2afbcf7e3b9190a35050565b6002546001600160a01b03163314611bf55760405162461bcd60e51b81526004016108e2906140a5565b610b3681613373565b600c8181548110610b6057600080fd5b6000611c1982610cb4565b15611c2657505060065490565b505060075490565b919050565b6107e9600a670de0b6b3a7640000614194565b6001611c5b670de0b6b3a76400006002614055565b6107e9919061411e565b6002546001600160a01b03163314611c8f5760405162461bcd60e51b81526004016108e2906140a5565b600684905560078390556008829055600981905560408051858152602081018590529081018390526060810182905233907fe6171cf0506862c0a0185f8c21dca979859d4eb63186eff63f1b3dd14926be7b9060800160405180910390a250505050565b6000611cfe82613036565b92915050565b6002546001600160a01b03163314611d2e5760405162461bcd60e51b81526004016108e2906140a5565b610b3681613464565b60035460405163226e761560e01b81523360048201526001600160a01b039091169063226e761590602401602060405180830381865afa158015611d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da39190614210565b611dbf5760405162461bcd60e51b81526004016108e29061422b565b611dc886612fd5565b611e265760405162461bcd60e51b815260206004820152602960248201527f464143544f525920524f555445523a20496e76616c69642044697370656e73656044820152681c90dbdb9d1c9858dd60ba1b60648201526084016108e2565b6040516324ce291760e01b81526001600160a01b0386811660048301526024820186905260448201859052838116606483015282811660848301528716906324ce29179060a401600060405180830381600087803b158015611e8757600080fd5b505af1158015611e9b573d6000803e3d6000fd5b50505050505050505050565b6002546001600160a01b03163314611ed15760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116611ef75760405162461bcd60e51b81526004016108e2906140d5565b611f0081612fd5565b610b3657600d805460018101825560009182527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b0319166001600160a01b038416908117909155604051909133917f78be9d87a7b2c8707b044dbf5b04c3f747b692c55ec34dcd1d7357736478789c9190a350565b6002546001600160a01b03163314611fa95760405162461bcd60e51b81526004016108e2906140a5565b610b36816134ec565b6002546001600160a01b03163314611fdc5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b0381166120325760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964206e657720726f75746572206f776e6572000000000000000060448201526064016108e2565b600280546001600160a01b0319166001600160a01b03831690811790915560405133907fc736654a613824c69968e0ec25ac1a428ccd49e15c28e97b5dfd2c6059757e2a90600090a350565b6107e96002670de0b6b3a7640000614194565b60328111156120b25760405162461bcd60e51b81526004016108e29061431e565b60005b818110156119e057600060405180606001604052808585858181106120dc576120dc614074565b9050610140020160600160208101906120f59190613cc2565b6001600160a01b0316815260200185858581811061211557612115614074565b9050610140020160a001602081019061212e9190613cc2565b6001600160a01b0316815260200185858581811061214e5761214e614074565b905061014002016101200160208101906121689190613cc2565b6001600160a01b03166001600160a01b031681525090506000604051806080016040528086868681811061219e5761219e614074565b905061014002016080013581526020018686868181106121c0576121c0614074565b9050610140020160c0013581526020018686868181106121e2576121e2614074565b9050610140020160e00135815260200186868681811061220457612204614074565b90506101400201610100013581525090506000600381111561222857612228614395565b85858581811061223a5761223a614074565b90506101400201604001602081019061225391906143ab565b600381111561226457612264614395565b1415612417576122bc85858581811061227f5761227f614074565b9050610140020160600160208101906122989190613cc2565b33308888888181106122ac576122ac614074565b90506101400201608001356130f7565b6123428585858181106122d1576122d1614074565b9050610140020160200160208101906122ea9190613cc2565b8686868181106122fc576122fc614074565b905061014002016080013587878781811061231957612319614074565b9050610140020160600160208101906123329190613cc2565b6001600160a01b03169190613245565b600085858581811061235657612356614074565b90506101400201602001602081019061236f9190613cc2565b6001600160a01b031663c421a3fc84846040518363ffffffff1660e01b815260040161239c9291906143cc565b60408051808303816000875af11580156123ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123de9190614431565b50905061241133828888888181106123f8576123f8614074565b9050610140020160a00160208101906119b99190613cc2565b50612c1e565b600185858581811061242b5761242b614074565b90506101400201604001602081019061244491906143ab565b600381111561245557612455614395565b141561274757600085858581811061246f5761246f614074565b9050610140020160200160208101906124889190613cc2565b6001600160a01b031663caa011488787878181106124a8576124a8614074565b9050610140020160600160208101906124c19190613cc2565b8888888181106124d3576124d3614074565b9050610140020160a00160208101906124ec9190613cc2565b8989898181106124fe576124fe614074565b9050610140020160c001358a8a8a81811061251b5761251b614074565b6040516001600160e01b031960e089901b1681526001600160a01b039687166004820152959094166024860152506044840191909152610100610140909202010135606482015260840160a060405180830381865afa158015612582573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a69190614455565b509293506125e69250889150879050868181106125c5576125c5614074565b9050610140020160600160208101906125de9190613cc2565b3330846130f7565b6126278686868181106125fb576125fb614074565b9050610140020160200160208101906126149190613cc2565b8288888881811061231957612319614074565b600086868681811061263b5761263b614074565b9050610140020160200160208101906126549190613cc2565b6001600160a01b031663c98a59cf85856040518363ffffffff1660e01b81526004016126819291906143cc565b60408051808303816000875af115801561269f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c39190614431565b50835190915081111561270d5760405162461bcd60e51b81526020600482015260126024820152712a27a79026a0a72c902a27a5a2a7299024a760711b60448201526064016108e2565b6127403388888881811061272357612723614074565b9050610140020160c001358989898181106123f8576123f8614074565b5050612c1e565b600285858581811061275b5761275b614074565b90506101400201604001602081019061277491906143ab565b600381111561278557612785614395565b1415612b3557600085858581811061279f5761279f614074565b9050610140020160200160208101906127b89190613cc2565b6001600160a01b0316634c87087d8787878181106127d8576127d8614074565b90506101400201600001356040518263ffffffff1660e01b815260040161280191815260200190565b61018060405180830381865afa15801561281f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128439190614495565b50505050505050505050915050600086868681811061286457612864614074565b90506101400201602001602081019061287d9190613cc2565b6001600160a01b031663dd1bc96a88888881811061289d5761289d614074565b90506101400201600001358989898181106128ba576128ba614074565b9050610140020160c001358a8a8a8181106128d7576128d7614074565b6040516001600160e01b031960e088901b16815260048101959095526024850193909352506101006101409092020101356044820152606401608060405180830381865afa15801561292d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129519190614549565b505050905061296b8787878181106125c5576125c5614074565b6129ac87878781811061298057612980614074565b9050610140020160200160208101906129999190613cc2565b8289898981811061231957612319614074565b8686868181106129be576129be614074565b9050610140020160200160208101906129d79190613cc2565b6001600160a01b03166368c4b7e98888888181106129f7576129f7614074565b9050610140020160000135898989818110612a1457612a14614074565b9050610140020160c001358a8a8a818110612a3157612a31614074565b90506101400201608001358b8b8b818110612a4e57612a4e614074565b90506101400201610120016020810190612a689190613cc2565b8c8c8c818110612a7a57612a7a614074565b6040516001600160e01b031960e08a901b168152600481019790975260248701959095525060448501929092526001600160a01b03166064840152610100610140909202010135608482015260a401600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b5050505061274033888888818110612b1157612b11614074565b9050610140020160c00135846001600160a01b03166133439092919063ffffffff16565b848484818110612b4757612b47614074565b905061014002016020016020810190612b609190613cc2565b6001600160a01b0316631d746d83868686818110612b8057612b80614074565b9050610140020160a0016020810190612b999190613cc2565b878787818110612bab57612bab614074565b60405160e086901b6001600160e01b03191681526001600160a01b03949094166004850152610140029190910160c00135602483015250336044820152606401600060405180830381600087803b158015612c0557600080fd5b505af1158015612c19573d6000803e3d6000fd5b505050505b50508080612c2b9061408a565b9150506120b5565b60035460405163226e761560e01b81523360048201526000916001600160a01b03169063226e761590602401602060405180830381865afa158015612c7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ca09190614210565b612cbc5760405162461bcd60e51b81526004016108e29061422b565b612ce783836000818110612cd257612cd2614074565b905060200201602081019061043f9190613cc2565b612d3e5760405162461bcd60e51b815260206004820152602260248201527f464143544f525920524f555445523a20696e76616c6964207373436f6e74726160448201526118dd60f21b60648201526084016108e2565b600087876001818110612d5357612d53614074565b9050602002013511612d985760405162461bcd60e51b815260206004820152600e60248201526d57726f6e6720646563696d616c7360901b60448201526064016108e2565b612e1a612dab60408a0160208b01613cc2565b84846002818110612dbe57612dbe614074565b9050602002016020810190612dd39190613cc2565b85856000818110612de657612de6614074565b9050602002016020810190612dfb9190613cc2565b8a8a6004818110612e0e57612e0e614074565b905060200201356130f7565b604080518082018252600091612edd91908b90600290839083908082843760009201919091525050604080516020808d0282810182019093528c82529092508c918c9182919085019084908082843760009201919091525050604080516020808d0282810182019093528c82529093508c92508b91829185019084908082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061365b92505050565b90506001600160a01b038116612f2d5760405162461bcd60e51b8152602060048201526015602482015274119052531151081513c81111541313d6481413d3d3605a1b60448201526064016108e2565b612f406104f560408b0160208c01613cc2565b15612f8957604051600181526001600160a01b038216907f90279d7790471e260411db76079630dfaa838fc987d29ae7aa7dc1bcd773ef2e9060200160405180910390a2612fc9565b604051600081526001600160a01b038216907f90279d7790471e260411db76079630dfaa838fc987d29ae7aa7dc1bcd773ef2e9060200160405180910390a25b98975050505050505050565b6000805b600d5481101561084d57826001600160a01b0316600d828154811061300057613000614074565b6000918252602090912001546001600160a01b031614156130245750600192915050565b8061302e8161408a565b915050612fd9565b6000805b60015481101561084d57826001600160a01b03166001828154811061306157613061614074565b6000918252602090912001546001600160a01b031614156130855750600192915050565b8061308f8161408a565b91505061303a565b606060018054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908616906370a0823190602401602060405180830381865afa158015613141573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131659190614305565b905061317c6001600160a01b03861685858561398f565b61318681836139c7565b6040516370a0823160e01b81526001600160a01b0385811660048301528716906370a0823190602401602060405180830381865afa1580156131cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f09190614305565b101561323e5760405162461bcd60e51b815260206004820152601a60248201527f5472616e7366657220616d6f756e7420697320746f6f206c6f7700000000000060448201526064016108e2565b5050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015613296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ba9190614305565b6132c4919061437d565b6040516001600160a01b03851660248201526044810182905290915061332a90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526139d3565b50505050565b600061333c828461411e565b9392505050565b6040516001600160a01b0383166024820152604481018290526119e090849063a9059cbb60e01b906064016132f3565b6001600160a01b0381166133dd5760405162461bcd60e51b815260206004820152602b60248201527f466163746f7279526f757465723a20496e76616c696420706f6f6c54656d706c60448201526a617465206164647265737360a81b60648201526084016108e2565b6133e681611cf3565b610b365760018054808201825560009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b038416908117909155604051909133917fc3674c32cbec94fe266aab7ef71f65b9bf77e1ed501d4f2bece8752f5352e9ac9190a350565b61346d81610cb4565b610b3657600a805460018101825560009182527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319166001600160a01b038416908117909155604051909133917fdffbd9ded1c09446f09377de547142dcce7dc541c8b0b028142b1eba7026b9e79190a350565b60005b60015481101561354857816001600160a01b03166001828154811061351657613516614074565b6000918252602090912001546001600160a01b0316141561353657613548565b806135408161408a565b9150506134ef565b600154811015610a5c57805b60018054613562919061411e565b8110156135ec576001613575828261437d565b8154811061358557613585614074565b600091825260209091200154600180546001600160a01b0390921691839081106135b1576135b1614074565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055806135e48161408a565b915050613554565b5060018054806135fe576135fe614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917fc73fd5d40669565e257cbcd153784617ef26acfcc9fead31e68c0dce3ce82fb29190a35050565b60006136808260058151811061367357613673614074565b6020026020010151611cf3565b6136cc5760405162461bcd60e51b815260206004820152601d60248201527f42466163746f72793a2057726f6e6720506f6f6c2054656d706c61746500000060448201526064016108e2565b60006040518060200160405280846004815181106136ec576136ec614074565b60200260200101516001600160a01b03166001600160a01b0316815250905061372e8360058151811061372157613721614074565b6020026020010151613aa5565b91506001600160a01b0382166137925760405162461bcd60e51b8152602060048201526024808201527f42466163746f72793a20696e76616c69642062706f6f6c207a65726f206164646044820152637265737360e01b60648201526084016108e2565b6000829050806001600160a01b031663284e97a9856000815181106137b9576137b9614074565b602002602001015130886000808d896040518863ffffffff1660e01b81526004016137ea97969594939291906145e6565b6020604051808303816000875af1158015613809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061382d9190614210565b6138705760405162461bcd60e51b815260206004820152601460248201527311549497d253925512505312569157d09413d3d360621b60448201526064016108e2565b8360008151811061388357613883614074565b60200260200101516001600160a01b03166383b87e5a886000600281106138ac576138ac614074565b6020020151896001602002015186886003815181106138cd576138cd614074565b60200260200101518b6040518663ffffffff1660e01b81526004016138f695949392919061465f565b6020604051808303816000875af1158015613915573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139399190614210565b6139855760405162461bcd60e51b815260206004820152601a60248201527f4552525f494e495449414c495a455f534944455354414b494e4700000000000060448201526064016108e2565b5050949350505050565b6040516001600160a01b038085166024830152831660448201526064810182905261332a9085906323b872dd60e01b906084016132f3565b600061333c828461437d565b6000613a28826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b359092919063ffffffff16565b8051909150156119e05780806020019051810190613a469190614210565b6119e05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108e2565b6000808260601b9050604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528160148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f06040516001600160a01b03821681529093507f117c72e6c25f0a072e36e148df71468ce2f3dbe7defec5b2c257a6e3eb65278c915060200160405180910390a150919050565b6060613b448484600085613b4c565b949350505050565b606082471015613bad5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108e2565b843b613bfb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108e2565b600080866001600160a01b03168587604051613c1791906146c7565b60006040518083038185875af1925050503d8060008114613c54576040519150601f19603f3d011682016040523d82523d6000602084013e613c59565b606091505b5091509150613c69828286613c74565b979650505050505050565b60608315613c8357508161333c565b825115613c935782518084602001fd5b8160405162461bcd60e51b81526004016108e291906146e3565b6001600160a01b0381168114610b3657600080fd5b600060208284031215613cd457600080fd5b813561333c81613cad565b6020808252825182820181905260009190848201906040850190845b81811015613d205783516001600160a01b031683529284019291840191600101613cfb565b50909695505050505050565b600060208284031215613d3e57600080fd5b5035919050565b60008083601f840112613d5757600080fd5b50813567ffffffffffffffff811115613d6f57600080fd5b6020830191508360208260051b8501011115613d8a57600080fd5b9250929050565b600080600080600060608688031215613da957600080fd5b8535613db481613cad565b9450602086013567ffffffffffffffff80821115613dd157600080fd5b613ddd89838a01613d45565b90965094506040880135915080821115613df657600080fd5b50613e0388828901613d45565b969995985093965092949392505050565b60008060208385031215613e2757600080fd5b823567ffffffffffffffff80821115613e3f57600080fd5b818501915085601f830112613e5357600080fd5b813581811115613e6257600080fd5b866020606083028501011115613e7757600080fd5b60209290920196919550909350505050565b60008060008060808587031215613e9f57600080fd5b5050823594602084013594506040840135936060013592509050565b60008060008060008060c08789031215613ed457600080fd5b8635613edf81613cad565b95506020870135613eef81613cad565b945060408701359350606087013592506080870135613f0d81613cad565b915060a0870135613f1d81613cad565b809150509295509295509295565b60008060208385031215613f3e57600080fd5b823567ffffffffffffffff80821115613f5657600080fd5b818501915085601f830112613f6a57600080fd5b813581811115613f7957600080fd5b86602061014083028501011115613e7757600080fd5b600080600080600080600060a0888a031215613faa57600080fd5b6040880189811115613fbb57600080fd5b8897503567ffffffffffffffff80821115613fd557600080fd5b613fe18b838c01613d45565b909850965060608a0135915080821115613ffa57600080fd5b6140068b838c01613d45565b909650945060808a013591508082111561401f57600080fd5b5061402c8a828b01613d45565b989b979a50959850939692959293505050565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561406f5761406f61403f565b500290565b634e487b7160e01b600052603260045260246000fd5b600060001982141561409e5761409e61403f565b5060010190565b60208082526016908201527527b1b2b0b72937baba32b91d102727aa1027aba722a960511b604082015260600190565b60208082526029908201527f466163746f7279526f757465723a20496e76616c6964205f64697370656e736560408201526872206164647265737360b81b606082015260800190565b6000828210156141305761413061403f565b500390565b634e487b7160e01b600052603160045260246000fd5b60208082526029908201527f466163746f7279526f757465723a20496e76616c6964205f666978656452617460408201526865206164647265737360b81b606082015260800190565b6000826141b157634e487b7160e01b600052601260045260246000fd5b500490565b6020808252602a908201527f466163746f7279526f757465723a20496e76616c6964205f7373436f6e7472616040820152696374206164647265737360b01b606082015260800190565b80518015158114611c2e57600080fd5b60006020828403121561422257600080fd5b61333c82614200565b6020808252602b908201527f464143544f525920524f555445523a204e4f54204f524947494e414c2045524360408201526a32302054454d504c41544560a81b606082015260800190565b6001600160a01b03868116825260606020808401829052908301869052600091879160808501845b898110156142c55784356142b181613cad565b84168252938201939082019060010161429e565b5085810360408701528681526001600160fb1b038711156142e557600080fd5b8660051b9350838883830137600093010191825250979650505050505050565b60006020828403121561431757600080fd5b5051919050565b60208082526022908201527f466163746f7279526f757465723a20546f6f204d616e79204f7065726174696f6040820152616e7360f01b606082015260800190565b60006020828403121561437257600080fd5b815161333c81613cad565b600082198211156143905761439061403f565b500190565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156143bd57600080fd5b81356004811061333c57600080fd5b60e08101818460005b60038110156143fd5781516001600160a01b03168352602092830192909101906001016143d5565b505050606082018360005b6004811015614427578151835260209283019290910190600101614408565b5050509392505050565b6000806040838503121561444457600080fd5b505080516020909101519092909150565b600080600080600060a0868803121561446d57600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b6000806000806000806000806000806000806101808d8f0312156144b857600080fd5b8c516144c381613cad565b60208e0151909c506144d481613cad565b60408e015160608f0151919c509a506144ec81613cad565b60808e015160a08f0151919a509850965061450960c08e01614200565b955060e08d015194506101008d015193506101208d015192506101408d015191506145376101608e01614200565b90509295989b509295989b509295989b565b6000806000806080858703121561455f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b600081518084526020808501945080840160005b838110156145af57815187529582019590820190600101614593565b509495945050505050565b8060005b600181101561332a5781516001600160a01b03168452602093840193909101906001016145be565b600061010060018060a01b03808b1684526020818b16818601528260408601526146128386018b61457f565b93508815156060860152871515608086015260a0850192508660005b600281101561464d57815184168552938201939082019060010161462e565b5050505050612fc960e08301846145ba565b6001600160a01b038681168252858116602083015284811660408301528316606082015260a060808201819052600090613c699083018461457f565b60005b838110156146b657818101518382015260200161469e565b8381111561332a5750506000910152565b600082516146d981846020870161469b565b9190910192915050565b602081526000825180602084015261470281604085016020870161469b565b601f01601f1916919091016040019291505056fea2646970667358221220c67e8fca36a392ffc78255f877c99882c98a625746868858035b2cf86a93d1ba64736f6c634300080c0033000000000000000000000000c7ec1970b09224b317c52d92f37f5e1e4ff6b687000000000000000000000000967da4048cd07ab37855c090aaf366e4ce1b9f480000000000000000000000008dac419d5d81af8c8e795d0d73f64d5220e28cd800000000000000000000000049e35cd2bae043abd9074b6e5a649a5adeb05c3300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103e65760003560e01c8063858ab5961161020a578063c45a015511610125578063e183fb3f116100b8578063e525f99c11610087578063e525f99c14610782578063ec09302114610795578063ecdda5881461079d578063ee3bc635146107b0578063fca24bc6146107c357600080fd5b8063e183fb3f1461075d578063e193faad14610766578063e2bdeefa14610779578063e4a28a521461041b57600080fd5b8063cd10534b116100f4578063cd10534b14610709578063d629a00a1461071c578063dce2d0df1461072f578063de9a95a71461074a57600080fd5b8063c45a0155146106dc578063c6580d12146106ef578063c6e983d9146106f7578063cb870cbf1461070057600080fd5b8063b7b800a41161019d578063bc694ea21161016c578063bc694ea2146106ae578063bfa04b85146106b6578063c07c00fe146106c9578063c36596a6146104bd57600080fd5b8063b7b800a41461065d578063b8421e2b1461068b578063ba019dab1461069e578063bc063e1a146106a657600080fd5b80639f2c010a116101d95780639f2c010a1461064a578063b0e0d1361461065d578063b19aaac614610665578063b66806fb1461067857600080fd5b8063858ab5961461061f578063867378c5146106325780639381cd2b1461063a578063992e2a921461064257600080fd5b8063475030c0116103055780636cdf90a1116102985780637cbf85bf116102675780637cbf85bf146105cb5780637d28354d146105de578063802d1422146105e657806382449375146105f95780638552730a1461060c57600080fd5b80636cdf90a114610595578063737e5ca0146105a85780637521aff9146105b057806376c7a3c7146105c357600080fd5b80635705987a116102d45780635705987a1461055f5780636afc0c5f146105725780636c45e8811461057a5780636c9fb6121461058257600080fd5b8063475030c01461052857806347e140941461053157806350cbbe7614610544578063510f34651461055757600080fd5b80631dafede01161037d5780632d5ad3d51161034c5780632d5ad3d5146104e7578063335b7fa2146104fa578063415792081461050d57806346104ea81461052057600080fd5b80631dafede0146104aa578063218b5382146104bd578063241c7a6d146104cc57806329ce1ec5146104d457600080fd5b806316592614116103b9578063165926141461046957806316d9cb3d1461047e578063189d00ca146104915780631a81876d1461049957600080fd5b806303814238146103eb57806309a3bbe41461041b57806315c25dd51461043157806315d4c9eb14610454575b600080fd5b6000546103fe906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6104236107d6565b604051908152602001610412565b61044461043f366004613cc2565b6107ec565b6040519015158152602001610412565b61045c610856565b6040516104129190613cdf565b61047c610477366004613cc2565b6108b8565b005b61047c61048c366004613cc2565b610a60565b610423610b39565b6000546001600160a01b03166103fe565b6103fe6104b8366004613d2c565b610b50565b610423670de0b6b3a764000081565b61045c610b7a565b61047c6104e2366004613cc2565b610b89565b6104446104f5366004613cc2565b610cb4565b6103fe610508366004613d2c565b610d15565b61047c61051b366004613cc2565b610d25565b61045c610dfc565b61042360055481565b61047c61053f366004613cc2565b610e5c565b6103fe610552366004613d2c565b610ffa565b600554610423565b61047c61056d366004613cc2565b61100a565b61045c6111eb565b600954610423565b61047c610590366004613cc2565b61124b565b6104446105a3366004613cc2565b611323565b61045c611384565b61047c6105be366004613d2c565b6113e4565b61042361144b565b6103fe6105d9366004613d2c565b61145f565b600854610423565b6004546103fe906001600160a01b031681565b610423610607366004613d91565b61146f565b6002546103fe906001600160a01b031681565b61047c61062d366004613e14565b6115e1565b6104236119e5565b6104236119fc565b610423611a0f565b61047c610658366004613cc2565b611a2d565b610423600281565b61047c610673366004613cc2565b611bcb565b6103fe610686366004613d2c565b611bfe565b610423610699366004613cc2565b611c0e565b610423600181565b610423611c33565b610423611c46565b61047c6106c4366004613e89565b611c65565b6104446106d7366004613cc2565b611cf3565b6003546103fe906001600160a01b031681565b610423600081565b61042360095481565b61042360075481565b61047c610717366004613cc2565b611d04565b61047c61072a366004613ebb565b611d37565b60065460075460408051928352602083019190915201610412565b61047c610758366004613cc2565b611ea7565b61042360065481565b61047c610774366004613cc2565b611f7f565b61042360085481565b61047c610790366004613cc2565b611fb2565b61042361207e565b61047c6107ab366004613f2b565b612091565b6103fe6107be366004613f8f565b612c33565b6104446107d1366004613cc2565b612fd5565b6107e9670de0b6b3a76400006032614055565b81565b6000805b600b5481101561084d57826001600160a01b0316600b828154811061081757610817614074565b6000918252602090912001546001600160a01b0316141561083b5750600192915050565b806108458161408a565b9150506107f0565b50600092915050565b6060600d8054806020026020016040519081016040528092919081815260200182805480156108ae57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610890575b5050505050905090565b6002546001600160a01b031633146108eb5760405162461bcd60e51b81526004016108e2906140a5565b60405180910390fd5b6001600160a01b0381166109115760405162461bcd60e51b81526004016108e2906140d5565b60005b600d5481101561096d57816001600160a01b0316600d828154811061093b5761093b614074565b6000918252602090912001546001600160a01b0316141561095b5761096d565b806109658161408a565b915050610914565b600d54811015610a5c57600d80546109879060019061411e565b8154811061099757610997614074565b600091825260209091200154600d80546001600160a01b0390921691839081106109c3576109c3614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600d805480610a0257610a02614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917f63fe522dcdc5f006279afe8840a87398bb3d0dbb906b848866195859621908b39190a35b5050565b6002546001600160a01b03163314610a8a5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610ab05760405162461bcd60e51b81526004016108e29061414b565b610ab981611323565b610b3657600c805460018101825560009182527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b038416908117909155604051909133917f45ccb9b9d6b112eb7a38daf6a23bda8b4c8449d5cf64a034975a5beb8cdd37b39190a35b50565b6107e96402540be400670de0b6b3a7640000614194565b600a8181548110610b6057600080fd5b6000918252602090912001546001600160a01b0316905081565b6060610b84613097565b905090565b6002546001600160a01b03163314610bb35760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610c195760405162461bcd60e51b815260206004820152602760248201527f466163746f7279526f757465723a20496e76616c6964205f666163746f7279206044820152666164647265737360c81b60648201526084016108e2565b6003546001600160a01b031615610c685760405162461bcd60e51b8152602060048201526013602482015272119050d513d49648105314915051164814d155606a1b60448201526064016108e2565b600380546001600160a01b0319166001600160a01b03831690811790915560405133907f1f869f1ddaa0e2e3652252b3394c05c805f54a0d5f7fb6975f785ae4f91e587190600090a350565b6000805b600a5481101561084d57826001600160a01b0316600a8281548110610cdf57610cdf614074565b6000918252602090912001546001600160a01b03161415610d035750600192915050565b80610d0d8161408a565b915050610cb8565b600d8181548110610b6057600080fd5b6002546001600160a01b03163314610d4f5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610db15760405162461bcd60e51b8152602060048201526024808201527f4e6577206f7063436f6c6c6563746f722063616e6e6f74206265205a45524f5f60448201526320a2222960e11b60648201526084016108e2565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917fbadc0056fc46a437b2d0a4e9ae17a4f964e18972b5f2455b98b7331c1401a80f9190a350565b6060600c8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b03163314610e865760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116610eac5760405162461bcd60e51b81526004016108e2906141b6565b60005b600b54811015610f0857816001600160a01b0316600b8281548110610ed657610ed6614074565b6000918252602090912001546001600160a01b03161415610ef657610f08565b80610f008161408a565b915050610eaf565b600b54811015610a5c57600b8054610f229060019061411e565b81548110610f3257610f32614074565b600091825260209091200154600b80546001600160a01b039092169183908110610f5e57610f5e614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b805480610f9d57610f9d614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917f7df5d17d768be6563796784637c80bc9204e2af0bc4ba8630f5aa2fed96ea4879190a35050565b60018181548110610b6057600080fd5b6002546001600160a01b031633146110345760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b03811661109d5760405162461bcd60e51b815260206004820152602a60248201527f466163746f7279526f757465723a20496e76616c6964204f6365616e20546f6b604482015269656e206164647265737360b01b60648201526084016108e2565b60005b600a548110156110f957816001600160a01b0316600a82815481106110c7576110c7614074565b6000918252602090912001546001600160a01b031614156110e7576110f9565b806110f18161408a565b9150506110a0565b600a54811015610a5c57600a80546111139060019061411e565b8154811061112357611123614074565b600091825260209091200154600a80546001600160a01b03909216918390811061114f5761114f614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600a80548061118e5761118e614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917fbbe55b1ff108e23e5ff1a6f5d36946eec15ec0ca0ded2bfed4cdcf697ca904609190a35050565b6060600a8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b031633146112755760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b03811661129b5760405162461bcd60e51b81526004016108e2906141b6565b6112a4816107ec565b610b3657600b805460018101825560009182527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b038416908117909155604051909133917fcef3ce01f85c030161b431114e41e463297b68f169c1013c526b1cbc5747e5449190a350565b6000805b600c5481101561084d57826001600160a01b0316600c828154811061134e5761134e614074565b6000918252602090912001546001600160a01b031614156113725750600192915050565b8061137c8161408a565b915050611327565b6060600b8054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6002546001600160a01b0316331461140e5760405162461bcd60e51b81526004016108e2906140a5565b600581905560405181815233907f5ee73342e684c0b848562c301fa2dc6b0146b266cfa12d4219fb04b316f7a4759060200160405180910390a250565b6107e9612710670de0b6b3a7640000614194565b600b8181548110610b6057600080fd5b60035460405163226e761560e01b81523360048201526000916001600160a01b03169063226e761590602401602060405180830381865afa1580156114b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114dc9190614210565b6114f85760405162461bcd60e51b81526004016108e29061422b565b61150186611323565b6115605760405162461bcd60e51b815260206004820152602a60248201527f464143544f525920524f555445523a20496e76616c696420466978656450726960448201526918d950dbdb9d1c9858dd60b21b60648201526084016108e2565b60405163012c327560e01b81526001600160a01b0387169063012c3275906115949033908990899089908990600401614276565b6020604051808303816000875af11580156115b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d79190614305565b9695505050505050565b60328111156116025760405162461bcd60e51b81526004016108e29061431e565b60005b818110156119e057600083838381811061162157611621614074565b6116379260206060909202019081019150613cc2565b6001600160a01b0316634df947d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611674573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116989190614360565b90506116c18133308787878181106116b2576116b2614074565b905060600201602001356130f7565b60008484848181106116d5576116d5614074565b6116eb9260206060909202019081019150613cc2565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa158015611731573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117559190614305565b90506117b785858581811061176c5761176c614074565b6117829260206060909202019081019150613cc2565b86868681811061179457611794614074565b90506060020160200135846001600160a01b03166132459092919063ffffffff16565b60008585858181106117cb576117cb614074565b6117e19260206060909202019081019150613cc2565b6001600160a01b0316638329ab3387878781811061180157611801614074565b9050606002016020013588888881811061181d5761181d614074565b905060600201604001356040518363ffffffff1660e01b815260040161184d929190918252602082015260400190565b6020604051808303816000875af115801561186c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118909190614305565b90508585858181106118a4576118a4614074565b905060600201604001358110156118ed5760405162461bcd60e51b815260206004820152600d60248201526c04e4f5420454e4f554748204c5609c1b60448201526064016108e2565b600086868681811061190157611901614074565b6119179260206060909202019081019150613cc2565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190614305565b90506119c9336119918386613330565b8989898181106119a3576119a3614074565b6119b99260206060909202019081019150613cc2565b6001600160a01b03169190613343565b5050505080806119d89061408a565b915050611605565b505050565b6107e964e8d4a51000670de0b6b3a7640000614194565b6107e9670de0b6b3a76400006064614055565b611a226002670de0b6b3a7640000614194565b6107e990600161437d565b6002546001600160a01b03163314611a575760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116611a7d5760405162461bcd60e51b81526004016108e29061414b565b60005b600c54811015611ad957816001600160a01b0316600c8281548110611aa757611aa7614074565b6000918252602090912001546001600160a01b03161415611ac757611ad9565b80611ad18161408a565b915050611a80565b600c54811015610a5c57600c8054611af39060019061411e565b81548110611b0357611b03614074565b600091825260209091200154600c80546001600160a01b039092169183908110611b2f57611b2f614074565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600c805480611b6e57611b6e614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917ff1ec0697329a2e8de8cf055335547acc40fbace8712fc77aeea1ea2afbcf7e3b9190a35050565b6002546001600160a01b03163314611bf55760405162461bcd60e51b81526004016108e2906140a5565b610b3681613373565b600c8181548110610b6057600080fd5b6000611c1982610cb4565b15611c2657505060065490565b505060075490565b919050565b6107e9600a670de0b6b3a7640000614194565b6001611c5b670de0b6b3a76400006002614055565b6107e9919061411e565b6002546001600160a01b03163314611c8f5760405162461bcd60e51b81526004016108e2906140a5565b600684905560078390556008829055600981905560408051858152602081018590529081018390526060810182905233907fe6171cf0506862c0a0185f8c21dca979859d4eb63186eff63f1b3dd14926be7b9060800160405180910390a250505050565b6000611cfe82613036565b92915050565b6002546001600160a01b03163314611d2e5760405162461bcd60e51b81526004016108e2906140a5565b610b3681613464565b60035460405163226e761560e01b81523360048201526001600160a01b039091169063226e761590602401602060405180830381865afa158015611d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da39190614210565b611dbf5760405162461bcd60e51b81526004016108e29061422b565b611dc886612fd5565b611e265760405162461bcd60e51b815260206004820152602960248201527f464143544f525920524f555445523a20496e76616c69642044697370656e73656044820152681c90dbdb9d1c9858dd60ba1b60648201526084016108e2565b6040516324ce291760e01b81526001600160a01b0386811660048301526024820186905260448201859052838116606483015282811660848301528716906324ce29179060a401600060405180830381600087803b158015611e8757600080fd5b505af1158015611e9b573d6000803e3d6000fd5b50505050505050505050565b6002546001600160a01b03163314611ed15760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b038116611ef75760405162461bcd60e51b81526004016108e2906140d5565b611f0081612fd5565b610b3657600d805460018101825560009182527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b0319166001600160a01b038416908117909155604051909133917f78be9d87a7b2c8707b044dbf5b04c3f747b692c55ec34dcd1d7357736478789c9190a350565b6002546001600160a01b03163314611fa95760405162461bcd60e51b81526004016108e2906140a5565b610b36816134ec565b6002546001600160a01b03163314611fdc5760405162461bcd60e51b81526004016108e2906140a5565b6001600160a01b0381166120325760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964206e657720726f75746572206f776e6572000000000000000060448201526064016108e2565b600280546001600160a01b0319166001600160a01b03831690811790915560405133907fc736654a613824c69968e0ec25ac1a428ccd49e15c28e97b5dfd2c6059757e2a90600090a350565b6107e96002670de0b6b3a7640000614194565b60328111156120b25760405162461bcd60e51b81526004016108e29061431e565b60005b818110156119e057600060405180606001604052808585858181106120dc576120dc614074565b9050610140020160600160208101906120f59190613cc2565b6001600160a01b0316815260200185858581811061211557612115614074565b9050610140020160a001602081019061212e9190613cc2565b6001600160a01b0316815260200185858581811061214e5761214e614074565b905061014002016101200160208101906121689190613cc2565b6001600160a01b03166001600160a01b031681525090506000604051806080016040528086868681811061219e5761219e614074565b905061014002016080013581526020018686868181106121c0576121c0614074565b9050610140020160c0013581526020018686868181106121e2576121e2614074565b9050610140020160e00135815260200186868681811061220457612204614074565b90506101400201610100013581525090506000600381111561222857612228614395565b85858581811061223a5761223a614074565b90506101400201604001602081019061225391906143ab565b600381111561226457612264614395565b1415612417576122bc85858581811061227f5761227f614074565b9050610140020160600160208101906122989190613cc2565b33308888888181106122ac576122ac614074565b90506101400201608001356130f7565b6123428585858181106122d1576122d1614074565b9050610140020160200160208101906122ea9190613cc2565b8686868181106122fc576122fc614074565b905061014002016080013587878781811061231957612319614074565b9050610140020160600160208101906123329190613cc2565b6001600160a01b03169190613245565b600085858581811061235657612356614074565b90506101400201602001602081019061236f9190613cc2565b6001600160a01b031663c421a3fc84846040518363ffffffff1660e01b815260040161239c9291906143cc565b60408051808303816000875af11580156123ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123de9190614431565b50905061241133828888888181106123f8576123f8614074565b9050610140020160a00160208101906119b99190613cc2565b50612c1e565b600185858581811061242b5761242b614074565b90506101400201604001602081019061244491906143ab565b600381111561245557612455614395565b141561274757600085858581811061246f5761246f614074565b9050610140020160200160208101906124889190613cc2565b6001600160a01b031663caa011488787878181106124a8576124a8614074565b9050610140020160600160208101906124c19190613cc2565b8888888181106124d3576124d3614074565b9050610140020160a00160208101906124ec9190613cc2565b8989898181106124fe576124fe614074565b9050610140020160c001358a8a8a81811061251b5761251b614074565b6040516001600160e01b031960e089901b1681526001600160a01b039687166004820152959094166024860152506044840191909152610100610140909202010135606482015260840160a060405180830381865afa158015612582573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a69190614455565b509293506125e69250889150879050868181106125c5576125c5614074565b9050610140020160600160208101906125de9190613cc2565b3330846130f7565b6126278686868181106125fb576125fb614074565b9050610140020160200160208101906126149190613cc2565b8288888881811061231957612319614074565b600086868681811061263b5761263b614074565b9050610140020160200160208101906126549190613cc2565b6001600160a01b031663c98a59cf85856040518363ffffffff1660e01b81526004016126819291906143cc565b60408051808303816000875af115801561269f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c39190614431565b50835190915081111561270d5760405162461bcd60e51b81526020600482015260126024820152712a27a79026a0a72c902a27a5a2a7299024a760711b60448201526064016108e2565b6127403388888881811061272357612723614074565b9050610140020160c001358989898181106123f8576123f8614074565b5050612c1e565b600285858581811061275b5761275b614074565b90506101400201604001602081019061277491906143ab565b600381111561278557612785614395565b1415612b3557600085858581811061279f5761279f614074565b9050610140020160200160208101906127b89190613cc2565b6001600160a01b0316634c87087d8787878181106127d8576127d8614074565b90506101400201600001356040518263ffffffff1660e01b815260040161280191815260200190565b61018060405180830381865afa15801561281f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128439190614495565b50505050505050505050915050600086868681811061286457612864614074565b90506101400201602001602081019061287d9190613cc2565b6001600160a01b031663dd1bc96a88888881811061289d5761289d614074565b90506101400201600001358989898181106128ba576128ba614074565b9050610140020160c001358a8a8a8181106128d7576128d7614074565b6040516001600160e01b031960e088901b16815260048101959095526024850193909352506101006101409092020101356044820152606401608060405180830381865afa15801561292d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129519190614549565b505050905061296b8787878181106125c5576125c5614074565b6129ac87878781811061298057612980614074565b9050610140020160200160208101906129999190613cc2565b8289898981811061231957612319614074565b8686868181106129be576129be614074565b9050610140020160200160208101906129d79190613cc2565b6001600160a01b03166368c4b7e98888888181106129f7576129f7614074565b9050610140020160000135898989818110612a1457612a14614074565b9050610140020160c001358a8a8a818110612a3157612a31614074565b90506101400201608001358b8b8b818110612a4e57612a4e614074565b90506101400201610120016020810190612a689190613cc2565b8c8c8c818110612a7a57612a7a614074565b6040516001600160e01b031960e08a901b168152600481019790975260248701959095525060448501929092526001600160a01b03166064840152610100610140909202010135608482015260a401600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b5050505061274033888888818110612b1157612b11614074565b9050610140020160c00135846001600160a01b03166133439092919063ffffffff16565b848484818110612b4757612b47614074565b905061014002016020016020810190612b609190613cc2565b6001600160a01b0316631d746d83868686818110612b8057612b80614074565b9050610140020160a0016020810190612b999190613cc2565b878787818110612bab57612bab614074565b60405160e086901b6001600160e01b03191681526001600160a01b03949094166004850152610140029190910160c00135602483015250336044820152606401600060405180830381600087803b158015612c0557600080fd5b505af1158015612c19573d6000803e3d6000fd5b505050505b50508080612c2b9061408a565b9150506120b5565b60035460405163226e761560e01b81523360048201526000916001600160a01b03169063226e761590602401602060405180830381865afa158015612c7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ca09190614210565b612cbc5760405162461bcd60e51b81526004016108e29061422b565b612ce783836000818110612cd257612cd2614074565b905060200201602081019061043f9190613cc2565b612d3e5760405162461bcd60e51b815260206004820152602260248201527f464143544f525920524f555445523a20696e76616c6964207373436f6e74726160448201526118dd60f21b60648201526084016108e2565b600087876001818110612d5357612d53614074565b9050602002013511612d985760405162461bcd60e51b815260206004820152600e60248201526d57726f6e6720646563696d616c7360901b60448201526064016108e2565b612e1a612dab60408a0160208b01613cc2565b84846002818110612dbe57612dbe614074565b9050602002016020810190612dd39190613cc2565b85856000818110612de657612de6614074565b9050602002016020810190612dfb9190613cc2565b8a8a6004818110612e0e57612e0e614074565b905060200201356130f7565b604080518082018252600091612edd91908b90600290839083908082843760009201919091525050604080516020808d0282810182019093528c82529092508c918c9182919085019084908082843760009201919091525050604080516020808d0282810182019093528c82529093508c92508b91829185019084908082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a91829185019084908082843760009201919091525061365b92505050565b90506001600160a01b038116612f2d5760405162461bcd60e51b8152602060048201526015602482015274119052531151081513c81111541313d6481413d3d3605a1b60448201526064016108e2565b612f406104f560408b0160208c01613cc2565b15612f8957604051600181526001600160a01b038216907f90279d7790471e260411db76079630dfaa838fc987d29ae7aa7dc1bcd773ef2e9060200160405180910390a2612fc9565b604051600081526001600160a01b038216907f90279d7790471e260411db76079630dfaa838fc987d29ae7aa7dc1bcd773ef2e9060200160405180910390a25b98975050505050505050565b6000805b600d5481101561084d57826001600160a01b0316600d828154811061300057613000614074565b6000918252602090912001546001600160a01b031614156130245750600192915050565b8061302e8161408a565b915050612fd9565b6000805b60015481101561084d57826001600160a01b03166001828154811061306157613061614074565b6000918252602090912001546001600160a01b031614156130855750600192915050565b8061308f8161408a565b91505061303a565b606060018054806020026020016040519081016040528092919081815260200182805480156108ae576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610890575050505050905090565b6040516370a0823160e01b81526001600160a01b038381166004830152600091908616906370a0823190602401602060405180830381865afa158015613141573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131659190614305565b905061317c6001600160a01b03861685858561398f565b61318681836139c7565b6040516370a0823160e01b81526001600160a01b0385811660048301528716906370a0823190602401602060405180830381865afa1580156131cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131f09190614305565b101561323e5760405162461bcd60e51b815260206004820152601a60248201527f5472616e7366657220616d6f756e7420697320746f6f206c6f7700000000000060448201526064016108e2565b5050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015613296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ba9190614305565b6132c4919061437d565b6040516001600160a01b03851660248201526044810182905290915061332a90859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526139d3565b50505050565b600061333c828461411e565b9392505050565b6040516001600160a01b0383166024820152604481018290526119e090849063a9059cbb60e01b906064016132f3565b6001600160a01b0381166133dd5760405162461bcd60e51b815260206004820152602b60248201527f466163746f7279526f757465723a20496e76616c696420706f6f6c54656d706c60448201526a617465206164647265737360a81b60648201526084016108e2565b6133e681611cf3565b610b365760018054808201825560009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b038416908117909155604051909133917fc3674c32cbec94fe266aab7ef71f65b9bf77e1ed501d4f2bece8752f5352e9ac9190a350565b61346d81610cb4565b610b3657600a805460018101825560009182527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a80180546001600160a01b0319166001600160a01b038416908117909155604051909133917fdffbd9ded1c09446f09377de547142dcce7dc541c8b0b028142b1eba7026b9e79190a350565b60005b60015481101561354857816001600160a01b03166001828154811061351657613516614074565b6000918252602090912001546001600160a01b0316141561353657613548565b806135408161408a565b9150506134ef565b600154811015610a5c57805b60018054613562919061411e565b8110156135ec576001613575828261437d565b8154811061358557613585614074565b600091825260209091200154600180546001600160a01b0390921691839081106135b1576135b1614074565b600091825260209091200180546001600160a01b0319166001600160a01b0392909216919091179055806135e48161408a565b915050613554565b5060018054806135fe576135fe614135565b600082815260208120820160001990810180546001600160a01b03191690559091019091556040516001600160a01b0384169133917fc73fd5d40669565e257cbcd153784617ef26acfcc9fead31e68c0dce3ce82fb29190a35050565b60006136808260058151811061367357613673614074565b6020026020010151611cf3565b6136cc5760405162461bcd60e51b815260206004820152601d60248201527f42466163746f72793a2057726f6e6720506f6f6c2054656d706c61746500000060448201526064016108e2565b60006040518060200160405280846004815181106136ec576136ec614074565b60200260200101516001600160a01b03166001600160a01b0316815250905061372e8360058151811061372157613721614074565b6020026020010151613aa5565b91506001600160a01b0382166137925760405162461bcd60e51b8152602060048201526024808201527f42466163746f72793a20696e76616c69642062706f6f6c207a65726f206164646044820152637265737360e01b60648201526084016108e2565b6000829050806001600160a01b031663284e97a9856000815181106137b9576137b9614074565b602002602001015130886000808d896040518863ffffffff1660e01b81526004016137ea97969594939291906145e6565b6020604051808303816000875af1158015613809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061382d9190614210565b6138705760405162461bcd60e51b815260206004820152601460248201527311549497d253925512505312569157d09413d3d360621b60448201526064016108e2565b8360008151811061388357613883614074565b60200260200101516001600160a01b03166383b87e5a886000600281106138ac576138ac614074565b6020020151896001602002015186886003815181106138cd576138cd614074565b60200260200101518b6040518663ffffffff1660e01b81526004016138f695949392919061465f565b6020604051808303816000875af1158015613915573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139399190614210565b6139855760405162461bcd60e51b815260206004820152601a60248201527f4552525f494e495449414c495a455f534944455354414b494e4700000000000060448201526064016108e2565b5050949350505050565b6040516001600160a01b038085166024830152831660448201526064810182905261332a9085906323b872dd60e01b906084016132f3565b600061333c828461437d565b6000613a28826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b359092919063ffffffff16565b8051909150156119e05780806020019051810190613a469190614210565b6119e05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108e2565b6000808260601b9050604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528160148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f06040516001600160a01b03821681529093507f117c72e6c25f0a072e36e148df71468ce2f3dbe7defec5b2c257a6e3eb65278c915060200160405180910390a150919050565b6060613b448484600085613b4c565b949350505050565b606082471015613bad5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108e2565b843b613bfb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108e2565b600080866001600160a01b03168587604051613c1791906146c7565b60006040518083038185875af1925050503d8060008114613c54576040519150601f19603f3d011682016040523d82523d6000602084013e613c59565b606091505b5091509150613c69828286613c74565b979650505050505050565b60608315613c8357508161333c565b825115613c935782518084602001fd5b8160405162461bcd60e51b81526004016108e291906146e3565b6001600160a01b0381168114610b3657600080fd5b600060208284031215613cd457600080fd5b813561333c81613cad565b6020808252825182820181905260009190848201906040850190845b81811015613d205783516001600160a01b031683529284019291840191600101613cfb565b50909695505050505050565b600060208284031215613d3e57600080fd5b5035919050565b60008083601f840112613d5757600080fd5b50813567ffffffffffffffff811115613d6f57600080fd5b6020830191508360208260051b8501011115613d8a57600080fd5b9250929050565b600080600080600060608688031215613da957600080fd5b8535613db481613cad565b9450602086013567ffffffffffffffff80821115613dd157600080fd5b613ddd89838a01613d45565b90965094506040880135915080821115613df657600080fd5b50613e0388828901613d45565b969995985093965092949392505050565b60008060208385031215613e2757600080fd5b823567ffffffffffffffff80821115613e3f57600080fd5b818501915085601f830112613e5357600080fd5b813581811115613e6257600080fd5b866020606083028501011115613e7757600080fd5b60209290920196919550909350505050565b60008060008060808587031215613e9f57600080fd5b5050823594602084013594506040840135936060013592509050565b60008060008060008060c08789031215613ed457600080fd5b8635613edf81613cad565b95506020870135613eef81613cad565b945060408701359350606087013592506080870135613f0d81613cad565b915060a0870135613f1d81613cad565b809150509295509295509295565b60008060208385031215613f3e57600080fd5b823567ffffffffffffffff80821115613f5657600080fd5b818501915085601f830112613f6a57600080fd5b813581811115613f7957600080fd5b86602061014083028501011115613e7757600080fd5b600080600080600080600060a0888a031215613faa57600080fd5b6040880189811115613fbb57600080fd5b8897503567ffffffffffffffff80821115613fd557600080fd5b613fe18b838c01613d45565b909850965060608a0135915080821115613ffa57600080fd5b6140068b838c01613d45565b909650945060808a013591508082111561401f57600080fd5b5061402c8a828b01613d45565b989b979a50959850939692959293505050565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561406f5761406f61403f565b500290565b634e487b7160e01b600052603260045260246000fd5b600060001982141561409e5761409e61403f565b5060010190565b60208082526016908201527527b1b2b0b72937baba32b91d102727aa1027aba722a960511b604082015260600190565b60208082526029908201527f466163746f7279526f757465723a20496e76616c6964205f64697370656e736560408201526872206164647265737360b81b606082015260800190565b6000828210156141305761413061403f565b500390565b634e487b7160e01b600052603160045260246000fd5b60208082526029908201527f466163746f7279526f757465723a20496e76616c6964205f666978656452617460408201526865206164647265737360b81b606082015260800190565b6000826141b157634e487b7160e01b600052601260045260246000fd5b500490565b6020808252602a908201527f466163746f7279526f757465723a20496e76616c6964205f7373436f6e7472616040820152696374206164647265737360b01b606082015260800190565b80518015158114611c2e57600080fd5b60006020828403121561422257600080fd5b61333c82614200565b6020808252602b908201527f464143544f525920524f555445523a204e4f54204f524947494e414c2045524360408201526a32302054454d504c41544560a81b606082015260800190565b6001600160a01b03868116825260606020808401829052908301869052600091879160808501845b898110156142c55784356142b181613cad565b84168252938201939082019060010161429e565b5085810360408701528681526001600160fb1b038711156142e557600080fd5b8660051b9350838883830137600093010191825250979650505050505050565b60006020828403121561431757600080fd5b5051919050565b60208082526022908201527f466163746f7279526f757465723a20546f6f204d616e79204f7065726174696f6040820152616e7360f01b606082015260800190565b60006020828403121561437257600080fd5b815161333c81613cad565b600082198211156143905761439061403f565b500190565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156143bd57600080fd5b81356004811061333c57600080fd5b60e08101818460005b60038110156143fd5781516001600160a01b03168352602092830192909101906001016143d5565b505050606082018360005b6004811015614427578151835260209283019290910190600101614408565b5050509392505050565b6000806040838503121561444457600080fd5b505080516020909101519092909150565b600080600080600060a0868803121561446d57600080fd5b5050835160208501516040860151606087015160809097015192989197509594509092509050565b6000806000806000806000806000806000806101808d8f0312156144b857600080fd5b8c516144c381613cad565b60208e0151909c506144d481613cad565b60408e015160608f0151919c509a506144ec81613cad565b60808e015160a08f0151919a509850965061450960c08e01614200565b955060e08d015194506101008d015193506101208d015192506101408d015191506145376101608e01614200565b90509295989b509295989b509295989b565b6000806000806080858703121561455f57600080fd5b505082516020840151604085015160609095015191969095509092509050565b600081518084526020808501945080840160005b838110156145af57815187529582019590820190600101614593565b509495945050505050565b8060005b600181101561332a5781516001600160a01b03168452602093840193909101906001016145be565b600061010060018060a01b03808b1684526020818b16818601528260408601526146128386018b61457f565b93508815156060860152871515608086015260a0850192508660005b600281101561464d57815184168552938201939082019060010161462e565b5050505050612fc960e08301846145ba565b6001600160a01b038681168252858116602083015284811660408301528316606082015260a060808201819052600090613c699083018461457f565b60005b838110156146b657818101518382015260200161469e565b8381111561332a5750506000910152565b600082516146d981846020870161469b565b9190910192915050565b602081526000825180602084015261470281604085016020870161469b565b601f01601f1916919091016040019291505056fea2646970667358221220c67e8fca36a392ffc78255f877c99882c98a625746868858035b2cf86a93d1ba64736f6c634300080c0033

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

000000000000000000000000c7ec1970b09224b317c52d92f37f5e1e4ff6b687000000000000000000000000967da4048cd07ab37855c090aaf366e4ce1b9f480000000000000000000000008dac419d5d81af8c8e795d0d73f64d5220e28cd800000000000000000000000049e35cd2bae043abd9074b6e5a649a5adeb05c3300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _routerOwner (address): 0xC7EC1970B09224B317c52d92f37F5e1E4fF6B687
Arg [1] : _oceanToken (address): 0x967da4048cD07aB37855c090aAF366e4ce1b9F48
Arg [2] : _bpoolTemplate (address): 0x8daC419D5D81Af8c8E795D0D73f64d5220e28cd8
Arg [3] : _opcCollector (address): 0x49E35cd2bAE043Abd9074B6e5a649a5AdEB05C33

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000c7ec1970b09224b317c52d92f37f5e1e4ff6b687
Arg [1] : 000000000000000000000000967da4048cd07ab37855c090aaf366e4ce1b9f48
Arg [2] : 0000000000000000000000008dac419d5d81af8c8e795d0d73f64d5220e28cd8
Arg [3] : 00000000000000000000000049e35cd2bae043abd9074b6e5a649a5adeb05c33
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.