ETH Price: $2,165.77 (+3.98%)

Transaction Decoder

Block:
10670200 at Aug-16-2020 09:04:12 AM +UTC
Transaction Fee:
0.03631030730327971 ETH $78.64
Gas Used:
266,230 Gas / 136.386986077 Gwei

Emitted Events:

41 AElfToken.Transfer( from=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e, to=0xb96253C913810238Ab092cca19876D42f4d29cb3, value=6434469350636463232452 )
42 SmartToken.Transfer( _from=0xb96253C913810238Ab092cca19876D42f4d29cb3, _to=LiquidityPoolV1Converter, _value=395096569028874441525 )
43 0xb96253c913810238ab092cca19876d42f4d29cb3.0x276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb( 0x276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0x00000000000000000000000000923b9a074762b93650716333b3e1473a15048e, 00000000000000000000000000000000000000000000015cd033471fc8c695c4, 0000000000000000000000000000000000000000000000156b10fc386da99335, 000000000000000000000000000000000000000000000000057d11ef24c12175 )
44 0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0000000000000000000000000000000000000000515a66cc42b3cc7041fb7040, 000000000000000000000000000000000000000535f64e5c0c095db31a161900 )
45 0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x0000000000000000000000000f2318565f1996cb1ed2f88e172135791bc1fcbf, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 000000000000000000000000000000000000000a6bec9cb81812bb66342c3200, 0000000000000000000000000000000000000001156fcbe991aa102325961660 )
46 0xb96253c913810238ab092cca19876d42f4d29cb3.0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24( 0x77f29993cf2c084e726f7e802da0719d6a0ade3e204badc7a3ffd57ecb768c24, 0x0000000000000000000000000f2318565f1996cb1ed2f88e172135791bc1fcbf, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 0000000000000000000000000000000000000000a2b4cd98856798e083f6e080, 0000000000000000000000000000000000000001156fcbe991aa102325961660 )
47 0xb96253c913810238ab092cca19876d42f4d29cb3.0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788( 0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788, 0x000000000000000000000000bf2179859fc6d5bee9bf9158632dc51678a4100e, 00000000000000000000000000000000000000000000245d3b26b111298d2adb, 00000000000000000000000000000000000000000000aed85778def551032488, 000000000000000000000000000000000000000000000000000000000007a120 )
48 0xb96253c913810238ab092cca19876d42f4d29cb3.0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788( 0x8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788, 0x0000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c, 00000000000000000000000000000000000000000000245d3b26b111298d2adb, 000000000000000000000000000000000000000000000aa9c213234d203821f2, 000000000000000000000000000000000000000000000000000000000007a120 )
49 BancorNetwork.Conversion( _smartToken=SmartToken, _fromToken=AElfToken, _toToken=SmartToken, _fromAmount=6434469350636463232452, _toAmount=395096569028874441525, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e )
50 LiquidityPoolV1Converter.Conversion( _fromToken=SmartToken, _toToken=0xEeeeeEee...eeeeeEEeE, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e, _amount=395096569028874441525, _return=1965633092060043762, _conversionFee=1967600692752796 )
51 LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=0xEeeeeEee...eeeeeEEeE, _rateN=9928145643145040540313000000, _rateD=1993780853157975740876574000000 )
52 LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=SmartToken, _rateN=3987561706315951481753148000000, _rateD=5885696366553071107328661500000 )
53 LiquidityPoolV1Converter.TokenRateUpdate( _token1=SmartToken, _token2=0xEeeeeEee...eeeeeEEeE, _rateN=19856291286290081080626000000, _rateD=5885696366553071107328661500000 )
54 LiquidityPoolV1Converter.PriceDataUpdate( _connectorToken=SmartToken, _tokenSupply=11771392733106142214657323, _connectorBalance=3987561706315951481753148, _connectorWeight=500000 )
55 LiquidityPoolV1Converter.PriceDataUpdate( _connectorToken=0xEeeeeEee...eeeeeEEeE, _tokenSupply=11771392733106142214657323, _connectorBalance=19856291286290081080626, _connectorWeight=500000 )
56 BancorNetwork.Conversion( _smartToken=SmartToken, _fromToken=SmartToken, _toToken=0xEeeeeEee...eeeeeEEeE, _fromAmount=395096569028874441525, _toAmount=1965633092060043762, _trader=[Sender] 0x00923b9a074762b93650716333b3e1473a15048e )

Account State Difference:

  Address   Before After State Difference Code
0x00923B9a...73A15048e
34.380051858425467365 Eth
Nonce: 26443
36.309374643182231417 Eth
Nonce: 26444
1.929322784756764052
0x1F573D6F...d79a7FF1C
(F2Pool Old)
2,601.159721020192827359 Eth2,601.196031327496107069 Eth0.03631030730327971
0xb96253C9...2f4d29cb3
(Bancor: Converter 20)
0xbf217985...678a4100e
0xE870D001...E22be4ABd
(Bancor: Converter 216)
19,858.256919382141124388 Eth19,856.291286290081080626 Eth1.965633092060043762

Execution Trace

BancorNetwork.claimAndConvert2( _path=[0xbf2179859fc6D5BEE9Bf9158632Dc51678a4100e, 0x0F2318565f1996CB1eD2F88e172135791BC1FcBf, 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C, 0xb1CD6e4153B2a390Cf00A6556b0fC1458C4A5533, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE], _amount=6434469350636463232452, _minReturn=1955466604388854784, _affiliateAccount=0x0000000000000000000000000000000000000000, _affiliateFee=0 ) => ( 1965633092060043762 )
  • SmartToken.CALL( )
  • Bancor: Converter 20.STATICCALL( )
  • AElfToken.transferFrom( _from=0x00923B9a074762B93650716333B3E1473A15048e, _to=0xb96253C913810238Ab092cca19876D42f4d29cb3, _value=6434469350636463232452 ) => ( True )
  • ContractRegistry.addressOf( _contractName=424E54546F6B656E000000000000000000000000000000000000000000000000 ) => ( 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C )
  • SmartToken.CALL( )
  • Bancor: Converter 20.STATICCALL( )
  • SmartToken.CALL( )
  • LiquidityPoolV1Converter.STATICCALL( )
  • Bancor: Converter 20.e8dc12ff( )
    • ContractRegistry.addressOf( _contractName=42616E636F724E6574776F726B00000000000000000000000000000000000000 ) => ( 0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0 )
    • SmartToken.CALL( )
    • ContractRegistry.addressOf( _contractName=42616E636F72466F726D756C6100000000000000000000000000000000000000 ) => ( 0xA049894d5dcaD406b7C827D6dc6A0B58CA4AE73a )
    • BancorFormula.crossReserveTargetAmount( _sourceReserveBalance=819248098421426779623108, _sourceReserveWeight=500000, _targetReserveBalance=50750245706264316327207, _targetReserveWeight=500000, _amount=6434469350636463232452 ) => ( 395492061089964405930 )
    • AElfToken.balanceOf( _owner=0xb96253C913810238Ab092cca19876D42f4d29cb3 ) => ( balance=825682567772063242855560 )
    • AElfToken.balanceOf( _owner=0xb96253C913810238Ab092cca19876D42f4d29cb3 ) => ( balance=825682567772063242855560 )
    • SmartToken.transfer( _to=0xE870D00176b2C71AFD4c43ceA550228E22be4ABd, _value=395096569028874441525 ) => ( success=True )
    • SmartToken.CALL( )
    • LiquidityPoolV1Converter.convert( _sourceToken=0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C, _targetToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _amount=395096569028874441525, _trader=0x00923B9a074762B93650716333B3E1473A15048e, _beneficiary=0x00923B9a074762B93650716333B3E1473A15048e ) => ( 1965633092060043762 )
      • ContractRegistry.addressOf( _contractName=42616E636F724E6574776F726B00000000000000000000000000000000000000 ) => ( 0x2F9EC37d6CcFFf1caB21733BdaDEdE11c823cCB0 )
      • SmartToken.CALL( )
      • ContractRegistry.addressOf( _contractName=42616E636F72466F726D756C6100000000000000000000000000000000000000 ) => ( 0xA049894d5dcaD406b7C827D6dc6A0B58CA4AE73a )
      • BancorFormula.crossReserveTargetAmount( _sourceReserveBalance=3987166609746922607311623, _sourceReserveWeight=500000, _targetReserveBalance=19858256919382141124388, _targetReserveWeight=500000, _amount=395096569028874441525 ) => ( 1967600692752796558 )
      • SmartToken.balanceOf( 0xE870D00176b2C71AFD4c43ceA550228E22be4ABd ) => ( 3987561706315951481753148 )
      • SmartToken.balanceOf( 0xE870D00176b2C71AFD4c43ceA550228E22be4ABd ) => ( 3987561706315951481753148 )
      • ETH 1.965633092060043762 0x00923b9a074762b93650716333b3e1473a15048e.CALL( )
      • SmartToken.CALL( )
        claimAndConvert2[BancorNetwork (ln:1484)]
        File 1 of 8: BancorNetwork
        // File: contracts/token/interfaces/IERC20Token.sol
        
        pragma solidity 0.4.26;
        
        /*
            ERC20 Standard Token interface
        */
        contract IERC20Token {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public view returns (string) {this;}
            function symbol() public view returns (string) {this;}
            function decimals() public view returns (uint8) {this;}
            function totalSupply() public view returns (uint256) {this;}
            function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
            function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
        
            function transfer(address _to, uint256 _value) public returns (bool success);
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
            function approve(address _spender, uint256 _value) public returns (bool success);
        }
        
        // File: contracts/IBancorNetwork.sol
        
        pragma solidity 0.4.26;
        
        
        /*
            Bancor Network interface
        */
        contract IBancorNetwork {
            function convert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public payable returns (uint256);
        
            function claimAndConvert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public returns (uint256);
        
            function convertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public payable returns (uint256);
        
            function claimAndConvertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public returns (uint256);
        
            // deprecated, backward compatibility
            function convert(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn
            ) public payable returns (uint256);
        
            // deprecated, backward compatibility
            function claimAndConvert(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn
            ) public returns (uint256);
        
            // deprecated, backward compatibility
            function convertFor(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for
            ) public payable returns (uint256);
        
            // deprecated, backward compatibility
            function claimAndConvertFor(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for
            ) public returns (uint256);
        }
        
        // File: contracts/IConversionPathFinder.sol
        
        pragma solidity 0.4.26;
        
        
        /*
            Conversion Path Finder interface
        */
        contract IConversionPathFinder {
            function findPath(address _sourceToken, address _targetToken) public view returns (address[] memory);
        }
        
        // File: contracts/utility/interfaces/IOwned.sol
        
        pragma solidity 0.4.26;
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public view returns (address) {this;}
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        // File: contracts/utility/interfaces/ITokenHolder.sol
        
        pragma solidity 0.4.26;
        
        
        
        /*
            Token Holder interface
        */
        contract ITokenHolder is IOwned {
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
        }
        
        // File: contracts/converter/interfaces/IConverterAnchor.sol
        
        pragma solidity 0.4.26;
        
        
        
        /*
            Converter Anchor interface
        */
        contract IConverterAnchor is IOwned, ITokenHolder {
        }
        
        // File: contracts/utility/interfaces/IWhitelist.sol
        
        pragma solidity 0.4.26;
        
        /*
            Whitelist interface
        */
        contract IWhitelist {
            function isWhitelisted(address _address) public view returns (bool);
        }
        
        // File: contracts/converter/interfaces/IConverter.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        /*
            Converter interface
        */
        contract IConverter is IOwned {
            function converterType() public pure returns (uint16);
            function anchor() public view returns (IConverterAnchor) {this;}
            function isActive() public view returns (bool);
        
            function rateAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256);
            function convert(IERC20Token _sourceToken,
                             IERC20Token _targetToken,
                             uint256 _amount,
                             address _trader,
                             address _beneficiary) public payable returns (uint256);
        
            function conversionWhitelist() public view returns (IWhitelist) {this;}
            function conversionFee() public view returns (uint32) {this;}
            function maxConversionFee() public view returns (uint32) {this;}
            function reserveBalance(IERC20Token _reserveToken) public view returns (uint256);
            function() external payable;
        
            function transferAnchorOwnership(address _newOwner) public;
            function acceptAnchorOwnership() public;
            function setConversionFee(uint32 _conversionFee) public;
            function setConversionWhitelist(IWhitelist _whitelist) public;
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
            function withdrawETH(address _to) public;
            function addReserve(IERC20Token _token, uint32 _ratio) public;
        
            // deprecated, backward compatibility
            function token() public view returns (IConverterAnchor);
            function transferTokenOwnership(address _newOwner) public;
            function acceptTokenOwnership() public;
            function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool);
            function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256);
            function connectorTokens(uint256 _index) public view returns (IERC20Token);
            function connectorTokenCount() public view returns (uint16);
        }
        
        // File: contracts/converter/interfaces/IBancorFormula.sol
        
        pragma solidity 0.4.26;
        
        /*
            Bancor Formula interface
        */
        contract IBancorFormula {
            function purchaseRate(uint256 _supply,
                                  uint256 _reserveBalance,
                                  uint32 _reserveWeight,
                                  uint256 _amount)
                                  public view returns (uint256);
        
            function saleRate(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveWeight,
                              uint256 _amount)
                              public view returns (uint256);
        
            function crossReserveRate(uint256 _sourceReserveBalance,
                                      uint32 _sourceReserveWeight,
                                      uint256 _targetReserveBalance,
                                      uint32 _targetReserveWeight,
                                      uint256 _amount)
                                      public view returns (uint256);
        
            function fundCost(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveRatio,
                              uint256 _amount)
                              public view returns (uint256);
        
            function liquidateRate(uint256 _supply,
                                   uint256 _reserveBalance,
                                   uint32 _reserveRatio,
                                   uint256 _amount)
                                   public view returns (uint256);
        }
        
        // File: contracts/utility/Owned.sol
        
        pragma solidity 0.4.26;
        
        
        /**
          * @dev Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            /**
              * @dev triggered when the owner is updated
              *
              * @param _prevOwner previous owner
              * @param _newOwner  new owner
            */
            event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
        
            /**
              * @dev initializes a new Owned instance
            */
            constructor() public {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                _ownerOnly();
                _;
            }
        
            // error message binary size optimization
            function _ownerOnly() internal view {
                require(msg.sender == owner, "ERR_ACCESS_DENIED");
            }
        
            /**
              * @dev allows transferring the contract ownership
              * the new owner still needs to accept the transfer
              * can only be called by the contract owner
              *
              * @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner, "ERR_SAME_OWNER");
                newOwner = _newOwner;
            }
        
            /**
              * @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
                emit OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = address(0);
            }
        }
        
        // File: contracts/utility/Utils.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev Utilities & Common Modifiers
        */
        contract Utils {
            // verifies that a value is greater than zero
            modifier greaterThanZero(uint256 _value) {
                _greaterThanZero(_value);
                _;
            }
        
            // error message binary size optimization
            function _greaterThanZero(uint256 _value) internal pure {
                require(_value > 0, "ERR_ZERO_VALUE");
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                _validAddress(_address);
                _;
            }
        
            // error message binary size optimization
            function _validAddress(address _address) internal pure {
                require(_address != address(0), "ERR_INVALID_ADDRESS");
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                _notThis(_address);
                _;
            }
        
            // error message binary size optimization
            function _notThis(address _address) internal view {
                require(_address != address(this), "ERR_ADDRESS_IS_SELF");
            }
        }
        
        // File: contracts/utility/interfaces/IContractRegistry.sol
        
        pragma solidity 0.4.26;
        
        /*
            Contract Registry interface
        */
        contract IContractRegistry {
            function addressOf(bytes32 _contractName) public view returns (address);
        
            // deprecated, backward compatibility
            function getAddress(bytes32 _contractName) public view returns (address);
        }
        
        // File: contracts/utility/ContractRegistryClient.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /**
          * @dev Base contract for ContractRegistry clients
        */
        contract ContractRegistryClient is Owned, Utils {
            bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry";
            bytes32 internal constant BANCOR_NETWORK = "BancorNetwork";
            bytes32 internal constant BANCOR_FORMULA = "BancorFormula";
            bytes32 internal constant CONVERTER_FACTORY = "ConverterFactory";
            bytes32 internal constant CONVERSION_PATH_FINDER = "ConversionPathFinder";
            bytes32 internal constant CONVERTER_UPGRADER = "BancorConverterUpgrader";
            bytes32 internal constant CONVERTER_REGISTRY = "BancorConverterRegistry";
            bytes32 internal constant CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData";
            bytes32 internal constant BNT_TOKEN = "BNTToken";
            bytes32 internal constant BANCOR_X = "BancorX";
            bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader";
        
            IContractRegistry public registry;      // address of the current contract-registry
            IContractRegistry public prevRegistry;  // address of the previous contract-registry
            bool public onlyOwnerCanUpdateRegistry; // only an owner can update the contract-registry
        
            /**
              * @dev verifies that the caller is mapped to the given contract name
              *
              * @param _contractName    contract name
            */
            modifier only(bytes32 _contractName) {
                _only(_contractName);
                _;
            }
        
            // error message binary size optimization
            function _only(bytes32 _contractName) internal view {
                require(msg.sender == addressOf(_contractName), "ERR_ACCESS_DENIED");
            }
        
            /**
              * @dev initializes a new ContractRegistryClient instance
              *
              * @param  _registry   address of a contract-registry contract
            */
            constructor(IContractRegistry _registry) internal validAddress(_registry) {
                registry = IContractRegistry(_registry);
                prevRegistry = IContractRegistry(_registry);
            }
        
            /**
              * @dev updates to the new contract-registry
             */
            function updateRegistry() public {
                // verify that this function is permitted
                require(msg.sender == owner || !onlyOwnerCanUpdateRegistry, "ERR_ACCESS_DENIED");
        
                // get the new contract-registry
                IContractRegistry newRegistry = IContractRegistry(addressOf(CONTRACT_REGISTRY));
        
                // verify that the new contract-registry is different and not zero
                require(newRegistry != address(registry) && newRegistry != address(0), "ERR_INVALID_REGISTRY");
        
                // verify that the new contract-registry is pointing to a non-zero contract-registry
                require(newRegistry.addressOf(CONTRACT_REGISTRY) != address(0), "ERR_INVALID_REGISTRY");
        
                // save a backup of the current contract-registry before replacing it
                prevRegistry = registry;
        
                // replace the current contract-registry with the new contract-registry
                registry = newRegistry;
            }
        
            /**
              * @dev restores the previous contract-registry
            */
            function restoreRegistry() public ownerOnly {
                // restore the previous contract-registry
                registry = prevRegistry;
            }
        
            /**
              * @dev restricts the permission to update the contract-registry
              *
              * @param _onlyOwnerCanUpdateRegistry  indicates whether or not permission is restricted to owner only
            */
            function restrictRegistryUpdate(bool _onlyOwnerCanUpdateRegistry) public ownerOnly {
                // change the permission to update the contract-registry
                onlyOwnerCanUpdateRegistry = _onlyOwnerCanUpdateRegistry;
            }
        
            /**
              * @dev returns the address associated with the given contract name
              *
              * @param _contractName    contract name
              *
              * @return contract address
            */
            function addressOf(bytes32 _contractName) internal view returns (address) {
                return registry.addressOf(_contractName);
            }
        }
        
        // File: contracts/utility/ReentrancyGuard.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev ReentrancyGuard
          *
          * The contract provides protection against re-entrancy - calling a function (directly or
          * indirectly) from within itself.
        */
        contract ReentrancyGuard {
            // true while protected code is being executed, false otherwise
            bool private locked = false;
        
            /**
              * @dev ensures instantiation only by sub-contracts
            */
            constructor() internal {}
        
            // protects a function against reentrancy attacks
            modifier protected() {
                _protected();
                locked = true;
                _;
                locked = false;
            }
        
            // error message binary size optimization
            function _protected() internal view {
                require(!locked, "ERR_REENTRANCY");
            }
        }
        
        // File: contracts/utility/TokenHandler.sol
        
        pragma solidity 0.4.26;
        
        
        contract TokenHandler {
            bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
            bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
            bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));
        
            /**
              * @dev executes the ERC20 token's `approve` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _spender approved address
              * @param _value   allowance amount
            */
            function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
            }
        
            /**
              * @dev executes the ERC20 token's `transfer` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _to      target address
              * @param _value   transfer amount
            */
            function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
            }
        
            /**
              * @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _from    source address
              * @param _to      target address
              * @param _value   transfer amount
            */
            function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
            }
        
            /**
              * @dev executes a function on the ERC20 token and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _data    data to pass in to the token's contract for execution
            */
            function execute(IERC20Token _token, bytes memory _data) private {
                uint256[1] memory ret = [uint256(1)];
        
                assembly {
                    let success := call(
                        gas,            // gas remaining
                        _token,         // destination address
                        0,              // no ether
                        add(_data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
                        mload(_data),   // input length (loaded from the first 32 bytes in the `data` array)
                        ret,            // output buffer
                        32              // output length
                    )
                    if iszero(success) {
                        revert(0, 0)
                    }
                }
        
                require(ret[0] != 0, "ERR_TRANSFER_FAILED");
            }
        }
        
        // File: contracts/utility/TokenHolder.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        
        /**
          * @dev We consider every contract to be a 'token holder' since it's currently not possible
          * for a contract to deny receiving tokens.
          *
          * The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
          * the owner to send tokens that were sent to the contract by mistake back to their sender.
          *
          * Note that we use the non standard ERC-20 interface which has no return value for transfer
          * in order to support both non standard as well as standard token contracts.
          * see https://github.com/ethereum/solidity/issues/4116
        */
        contract TokenHolder is ITokenHolder, TokenHandler, Owned, Utils {
            /**
              * @dev withdraws tokens held by the contract and sends them to an account
              * can only be called by the owner
              *
              * @param _token   ERC20 token contract address
              * @param _to      account to receive the new amount
              * @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_token)
                validAddress(_to)
                notThis(_to)
            {
                safeTransfer(_token, _to, _amount);
            }
        }
        
        // File: contracts/utility/SafeMath.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev Library for basic math operations with overflow/underflow protection
        */
        library SafeMath {
            /**
              * @dev returns the sum of _x and _y, reverts if the calculation overflows
              *
              * @param _x   value 1
              * @param _y   value 2
              *
              * @return sum
            */
            function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x + _y;
                require(z >= _x, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev returns the difference of _x minus _y, reverts if the calculation underflows
              *
              * @param _x   minuend
              * @param _y   subtrahend
              *
              * @return difference
            */
            function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_x >= _y, "ERR_UNDERFLOW");
                return _x - _y;
            }
        
            /**
              * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
              *
              * @param _x   factor 1
              * @param _y   factor 2
              *
              * @return product
            */
            function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
                // gas optimization
                if (_x == 0)
                    return 0;
        
                uint256 z = _x * _y;
                require(z / _x == _y, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
              *
              * @param _x   dividend
              * @param _y   divisor
              *
              * @return quotient
            */
            function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_y > 0, "ERR_DIVIDE_BY_ZERO");
                uint256 c = _x / _y;
                return c;
            }
        }
        
        // File: contracts/token/interfaces/IEtherToken.sol
        
        pragma solidity 0.4.26;
        
        
        /*
            Ether Token interface
        */
        contract IEtherToken is IERC20Token {
            function deposit() public payable;
            function withdraw(uint256 _amount) public;
            function depositTo(address _to) public payable;
            function withdrawTo(address _to, uint256 _amount) public;
        }
        
        // File: contracts/token/interfaces/ISmartToken.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /*
            Smart Token interface
        */
        contract ISmartToken is IConverterAnchor, IERC20Token {
            function disableTransfers(bool _disable) public;
            function issue(address _to, uint256 _amount) public;
            function destroy(address _from, uint256 _amount) public;
        }
        
        // File: contracts/bancorx/interfaces/IBancorX.sol
        
        pragma solidity 0.4.26;
        
        
        contract IBancorX {
            function token() public view returns (IERC20Token) {this;}
            function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public;
            function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256);
        }
        
        // File: contracts/BancorNetwork.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        
        
        
        
        
        
        
        
        // interface of older converters for backward compatibility
        contract ILegacyConverter {
            function change(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, uint256 _minReturn) public returns (uint256);
        }
        
        /**
          * @dev The BancorNetwork contract is the main entry point for Bancor token conversions.
          * It also allows for the conversion of any token in the Bancor Network to any other token in a single
          * transaction by providing a conversion path.
          *
          * A note on Conversion Path: Conversion path is a data structure that is used when converting a token
          * to another token in the Bancor Network, when the conversion cannot necessarily be done by a single
          * converter and might require multiple 'hops'.
          * The path defines which converters should be used and what kind of conversion should be done in each step.
          *
          * The path format doesn't include complex structure; instead, it is represented by a single array
          * in which each 'hop' is represented by a 2-tuple - converter anchor & target token.
          * In addition, the first element is always the source token.
          * The converter anchor is only used as a pointer to a converter (since converter addresses are more
          * likely to change as opposed to anchor addresses).
          *
          * Format:
          * [source token, converter anchor, target token, converter anchor, target token...]
        */
        contract BancorNetwork is IBancorNetwork, TokenHolder, ContractRegistryClient, ReentrancyGuard {
            using SafeMath for uint256;
        
            uint256 private constant CONVERSION_FEE_RESOLUTION = 1000000;
            uint256 private constant AFFILIATE_FEE_RESOLUTION = 1000000;
            address private constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
        
            struct ConversionStep {
                IConverter converter;
                IConverterAnchor anchor;
                IERC20Token sourceToken;
                IERC20Token targetToken;
                address beneficiary;
                bool isV28OrHigherConverter;
                bool processAffiliateFee;
            }
        
            uint256 public maxAffiliateFee = 30000;     // maximum affiliate-fee
        
            mapping (address => bool) public etherTokens;       // list of all supported ether tokens
        
            /**
              * @dev triggered when a conversion between two tokens occurs
              *
              * @param _smartToken  anchor governed by the converter
              * @param _fromToken   source ERC20 token
              * @param _toToken     target ERC20 token
              * @param _fromAmount  amount converted, in the source token
              * @param _toAmount    amount returned, minus conversion fee
              * @param _trader      wallet that initiated the trade
            */
            event Conversion(
                address indexed _smartToken,
                address indexed _fromToken,
                address indexed _toToken,
                uint256 _fromAmount,
                uint256 _toAmount,
                address _trader
            );
        
            /**
              * @dev initializes a new BancorNetwork instance
              *
              * @param _registry    address of a contract registry contract
            */
            constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public {
                etherTokens[ETH_RESERVE_ADDRESS] = true;
            }
        
            /**
              * @dev allows the owner to update the maximum affiliate-fee
              *
              * @param _maxAffiliateFee   maximum affiliate-fee
            */
            function setMaxAffiliateFee(uint256 _maxAffiliateFee)
                public
                ownerOnly
            {
                require(_maxAffiliateFee <= AFFILIATE_FEE_RESOLUTION, "ERR_INVALID_AFFILIATE_FEE");
                maxAffiliateFee = _maxAffiliateFee;
            }
        
            /**
              * @dev allows the owner to register/unregister ether tokens
              *
              * @param _token       ether token contract address
              * @param _register    true to register, false to unregister
            */
            function registerEtherToken(IEtherToken _token, bool _register)
                public
                ownerOnly
                validAddress(_token)
                notThis(_token)
            {
                etherTokens[_token] = _register;
            }
        
            /**
              * @dev returns the conversion path between two tokens in the network
              * note that this method is quite expensive in terms of gas and should generally be called off-chain
              *
              * @param _sourceToken source token address
              * @param _targetToken target token address
              *
              * @return conversion path between the two tokens
            */
            function conversionPath(IERC20Token _sourceToken, IERC20Token _targetToken) public view returns (address[]) {
                IConversionPathFinder pathFinder = IConversionPathFinder(addressOf(CONVERSION_PATH_FINDER));
                return pathFinder.findPath(_sourceToken, _targetToken);
            }
        
            /**
              * @dev returns the expected rate of converting a given amount on a given path
              * note that there is no support for circular paths
              *
              * @param _path        conversion path (see conversion path format above)
              * @param _amount      amount of _path[0] tokens received from the sender
              *
              * @return expected rate
            */
            function rateByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256) {
                uint256 amount;
                uint256 fee;
                uint256 supply;
                uint256 balance;
                uint32 weight;
                IConverter converter;
                IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
        
                amount = _amount;
        
                // verify that the number of elements is larger than 2 and odd
                require(_path.length > 2 && _path.length % 2 == 1, "ERR_INVALID_PATH");
        
                // iterate over the conversion path
                for (uint256 i = 2; i < _path.length; i += 2) {
                    IERC20Token sourceToken = _path[i - 2];
                    IERC20Token anchor = _path[i - 1];
                    IERC20Token targetToken = _path[i];
        
                    converter = IConverter(IConverterAnchor(anchor).owner());
        
                    // backward compatibility
                    sourceToken = getConverterTokenAddress(converter, sourceToken);
                    targetToken = getConverterTokenAddress(converter, targetToken);
        
                    if (targetToken == anchor) { // buy the smart token
                        // check if the current smart token has changed
                        if (i < 3 || anchor != _path[i - 3])
                            supply = ISmartToken(anchor).totalSupply();
        
                        // get the amount & the conversion fee
                        balance = converter.getConnectorBalance(sourceToken);
                        (, weight, , , ) = converter.connectors(sourceToken);
                        amount = formula.purchaseRate(supply, balance, weight, amount);
                        fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION);
                        amount -= fee;
        
                        // update the smart token supply for the next iteration
                        supply = supply.add(amount);
                    }
                    else if (sourceToken == anchor) { // sell the smart token
                        // check if the current smart token has changed
                        if (i < 3 || anchor != _path[i - 3])
                            supply = ISmartToken(anchor).totalSupply();
        
                        // get the amount & the conversion fee
                        balance = converter.getConnectorBalance(targetToken);
                        (, weight, , , ) = converter.connectors(targetToken);
                        amount = formula.saleRate(supply, balance, weight, amount);
                        fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION);
                        amount -= fee;
        
                        // update the smart token supply for the next iteration
                        supply = supply.sub(amount);
                    }
                    else { // cross reserve conversion
                        (amount, fee) = getReturn(converter, sourceToken, targetToken, amount);
                    }
                }
        
                return amount;
            }
        
            /**
              * @dev converts the token to any other token in the bancor network by following
              * a predefined conversion path and transfers the result tokens to a target account
              * affiliate account/fee can also be passed in to receive a conversion fee (on top of the liquidity provider fees)
              * note that the network should already have been given allowance of the source token (if not ETH)
              *
              * @param _path                conversion path, see conversion path format above
              * @param _amount              amount to convert from, in the source token
              * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
              * @param _beneficiary         account that will receive the conversion result or 0x0 to send the result to the sender account
              * @param _affiliateAccount    wallet address to receive the affiliate fee or 0x0 to disable affiliate fee
              * @param _affiliateFee        affiliate fee in PPM or 0 to disable affiliate fee
              *
              * @return amount of tokens received from the conversion
            */
            function convertByPath(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary, address _affiliateAccount, uint256 _affiliateFee)
                public
                payable
                protected
                greaterThanZero(_minReturn)
                returns (uint256)
            {
                // verify that the path contrains at least a single 'hop' and that the number of elements is odd
                require(_path.length > 2 && _path.length % 2 == 1, "ERR_INVALID_PATH");
        
                // validate msg.value and prepare the source token for the conversion
                handleSourceToken(_path[0], IConverterAnchor(_path[1]), _amount);
        
                // check if affiliate fee is enabled
                bool affiliateFeeEnabled = false;
                if (address(_affiliateAccount) == 0) {
                    require(_affiliateFee == 0, "ERR_INVALID_AFFILIATE_FEE");
                }
                else {
                    require(0 < _affiliateFee && _affiliateFee <= maxAffiliateFee, "ERR_INVALID_AFFILIATE_FEE");
                    affiliateFeeEnabled = true;
                }
        
                // check if beneficiary is set
                address beneficiary = msg.sender;
                if (_beneficiary != address(0))
                    beneficiary = _beneficiary;
        
                // convert and get the resulting amount
                ConversionStep[] memory data = createConversionData(_path, beneficiary, affiliateFeeEnabled);
                uint256 amount = doConversion(data, _amount, _minReturn, _affiliateAccount, _affiliateFee);
        
                // handle the conversion target tokens
                handleTargetToken(data, amount, beneficiary);
        
                return amount;
            }
        
            /**
              * @dev converts any other token to BNT in the bancor network by following
              a predefined conversion path and transfers the result to an account on a different blockchain
              * note that the network should already have been given allowance of the source token (if not ETH)
              *
              * @param _path                conversion path, see conversion path format above
              * @param _amount              amount to convert from, in the source token
              * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
              * @param _targetBlockchain    blockchain BNT will be issued on
              * @param _targetAccount       address/account on the target blockchain to send the BNT to
              * @param _conversionId        pre-determined unique (if non zero) id which refers to this transaction
              *
              * @return the amount of BNT received from this conversion
            */
            function xConvert(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                bytes32 _targetBlockchain,
                bytes32 _targetAccount,
                uint256 _conversionId
            )
                public
                payable
                returns (uint256)
            {
                return xConvert2(_path, _amount, _minReturn, _targetBlockchain, _targetAccount, _conversionId, address(0), 0);
            }
        
            /**
              * @dev converts any other token to BNT in the bancor network by following
              a predefined conversion path and transfers the result to an account on a different blockchain
              * note that the network should already have been given allowance of the source token (if not ETH)
              *
              * @param _path                conversion path, see conversion path format above
              * @param _amount              amount to convert from, in the source token
              * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
              * @param _targetBlockchain    blockchain BNT will be issued on
              * @param _targetAccount       address/account on the target blockchain to send the BNT to
              * @param _conversionId        pre-determined unique (if non zero) id which refers to this transaction
              * @param _affiliateAccount    affiliate account
              * @param _affiliateFee        affiliate fee in PPM
              *
              * @return the amount of BNT received from this conversion
            */
            function xConvert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                bytes32 _targetBlockchain,
                bytes32 _targetAccount,
                uint256 _conversionId,
                address _affiliateAccount,
                uint256 _affiliateFee
            )
                public
                payable
                greaterThanZero(_minReturn)
                returns (uint256)
            {
                IERC20Token targetToken = _path[_path.length - 1];
                IBancorX bancorX = IBancorX(addressOf(BANCOR_X));
        
                // verify that the destination token is BNT
                require(targetToken == addressOf(BNT_TOKEN), "ERR_INVALID_TARGET_TOKEN");
        
                // convert and get the resulting amount
                uint256 amount = convertByPath(_path, _amount, _minReturn, this, _affiliateAccount, _affiliateFee);
        
                // grant BancorX allowance
                ensureAllowance(targetToken, bancorX, amount);
        
                // transfer the resulting amount to BancorX
                bancorX.xTransfer(_targetBlockchain, _targetAccount, amount, _conversionId);
        
                return amount;
            }
        
            /**
              * @dev allows a user to convert a token that was sent from another blockchain into any other
              * token on the BancorNetwork
              * ideally this transaction is created before the previous conversion is even complete, so
              * so the input amount isn't known at that point - the amount is actually take from the
              * BancorX contract directly by specifying the conversion id
              *
              * @param _path            conversion path
              * @param _bancorX         address of the BancorX contract for the source token
              * @param _conversionId    pre-determined unique (if non zero) id which refers to this conversion
              * @param _minReturn       if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
              * @param _beneficiary     wallet to receive the conversion result
              *
              * @return amount of tokens received from the conversion
            */
            function completeXConversion(IERC20Token[] _path, IBancorX _bancorX, uint256 _conversionId, uint256 _minReturn, address _beneficiary)
                public returns (uint256)
            {
                // verify that the source token is the BancorX token
                require(_path[0] == _bancorX.token(), "ERR_INVALID_SOURCE_TOKEN");
        
                // get conversion amount from BancorX contract
                uint256 amount = _bancorX.getXTransferAmount(_conversionId, msg.sender);
        
                // perform the conversion
                return convertByPath(_path, amount, _minReturn, _beneficiary, address(0), 0);
            }
        
            /**
              * @dev executes the actual conversion by following the conversion path
              *
              * @param _data                conversion data, see ConversionStep struct above
              * @param _amount              amount to convert from, in the source token
              * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be greater than zero
              * @param _affiliateAccount    affiliate account
              * @param _affiliateFee        affiliate fee in PPM
              *
              * @return amount of tokens received from the conversion
            */
            function doConversion(
                ConversionStep[] _data,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) private returns (uint256) {
                uint256 toAmount;
                uint256 fromAmount = _amount;
        
                // iterate over the conversion data
                for (uint256 i = 0; i < _data.length; i++) {
                    ConversionStep memory stepData = _data[i];
        
                    // newer converter
                    if (stepData.isV28OrHigherConverter) {
                        // transfer the tokens to the converter only if the network contract currently holds the tokens
                        // not needed with ETH or if it's the first conversion step
                        if (i != 0 && _data[i - 1].beneficiary == address(this) && !etherTokens[stepData.sourceToken])
                            safeTransfer(stepData.sourceToken, stepData.converter, fromAmount);
                    }
                    // older converter
                    // if the source token is the smart token, no need to do any transfers as the converter controls it
                    else if (stepData.sourceToken != ISmartToken(stepData.anchor)) {
                        // grant allowance for it to transfer the tokens from the network contract
                        ensureAllowance(stepData.sourceToken, stepData.converter, fromAmount);
                    }
        
                    // do the conversion
                    if (!stepData.isV28OrHigherConverter)
                        toAmount = ILegacyConverter(stepData.converter).change(stepData.sourceToken, stepData.targetToken, fromAmount, 1);
                    else if (etherTokens[stepData.sourceToken])
                        toAmount = stepData.converter.convert.value(msg.value)(stepData.sourceToken, stepData.targetToken, fromAmount, msg.sender, stepData.beneficiary);
                    else
                        toAmount = stepData.converter.convert(stepData.sourceToken, stepData.targetToken, fromAmount, msg.sender, stepData.beneficiary);
        
                    // pay affiliate-fee if needed
                    if (stepData.processAffiliateFee) {
                        uint256 affiliateAmount = toAmount.mul(_affiliateFee).div(AFFILIATE_FEE_RESOLUTION);
                        require(stepData.targetToken.transfer(_affiliateAccount, affiliateAmount), "ERR_FEE_TRANSFER_FAILED");
                        toAmount -= affiliateAmount;
                    }
        
                    emit Conversion(stepData.anchor, stepData.sourceToken, stepData.targetToken, fromAmount, toAmount, msg.sender);
                    fromAmount = toAmount;
                }
        
                // ensure the trade meets the minimum requested amount
                require(toAmount >= _minReturn, "ERR_RETURN_TOO_LOW");
        
                return toAmount;
            }
        
            /**
              * @dev validates msg.value and prepares the conversion source token for the conversion
              *
              * @param _sourceToken source token of the first conversion step
              * @param _anchor      converter anchor of the first conversion step
              * @param _amount      amount to convert from, in the source token
            */
            function handleSourceToken(IERC20Token _sourceToken, IConverterAnchor _anchor, uint256 _amount) private {
                IConverter firstConverter = IConverter(_anchor.owner());
                bool isNewerConverter = isV28OrHigherConverter(firstConverter);
        
                // ETH
                if (msg.value > 0) {
                    // validate msg.value
                    require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH");
        
                    // EtherToken converter - deposit the ETH into the EtherToken
                    // note that it can still be a non ETH converter if the path is wrong
                    // but such conversion will simply revert
                    if (!isNewerConverter)
                        IEtherToken(getConverterEtherTokenAddress(firstConverter)).deposit.value(msg.value)();
                }
                // EtherToken
                else if (etherTokens[_sourceToken]) {
                    // claim the tokens - if the source token is ETH reserve, this call will fail
                    // since in that case the transaction must be sent with msg.value
                    safeTransferFrom(_sourceToken, msg.sender, this, _amount);
        
                    // ETH converter - withdraw the ETH
                    if (isNewerConverter)
                        IEtherToken(_sourceToken).withdraw(_amount);
                }
                // other ERC20 token
                else {
                    // newer converter - transfer the tokens from the sender directly to the converter
                    // otherwise claim the tokens
                    if (isNewerConverter)
                        safeTransferFrom(_sourceToken, msg.sender, firstConverter, _amount);
                    else
                        safeTransferFrom(_sourceToken, msg.sender, this, _amount);
                }
            }
        
            /**
              * @dev handles the conversion target token if the network still holds it at the end of the conversion
              *
              * @param _data        conversion data, see ConversionStep struct above
              * @param _amount      conversion return amount, in the target token
              * @param _beneficiary wallet to receive the conversion result
            */
            function handleTargetToken(ConversionStep[] _data, uint256 _amount, address _beneficiary) private {
                ConversionStep memory stepData = _data[_data.length - 1];
        
                // network contract doesn't hold the tokens, do nothing
                if (stepData.beneficiary != address(this))
                    return;
        
                IERC20Token targetToken = stepData.targetToken;
        
                // ETH / EtherToken
                if (etherTokens[targetToken]) {
                    // newer converter should send ETH directly to the beneficiary
                    assert(!stepData.isV28OrHigherConverter);
        
                    // EtherToken converter - withdraw the ETH and transfer to the beneficiary
                    IEtherToken(targetToken).withdrawTo(_beneficiary, _amount);
                }
                // other ERC20 token
                else {
                    safeTransfer(targetToken, _beneficiary, _amount);
                }
            }
        
            /**
              * @dev creates a memory cache of all conversion steps data to minimize logic and external calls during conversions
              *
              * @param _conversionPath      conversion path, see conversion path format above
              * @param _beneficiary         wallet to receive the conversion result
              * @param _affiliateFeeEnabled true if affiliate fee was requested by the sender, false if not
              *
              * @return cached conversion data to be ingested later on by the conversion flow
            */
            function createConversionData(IERC20Token[] _conversionPath, address _beneficiary, bool _affiliateFeeEnabled) private view returns (ConversionStep[]) {
                ConversionStep[] memory data = new ConversionStep[](_conversionPath.length / 2);
        
                bool affiliateFeeProcessed = false;
                address bntToken = addressOf(BNT_TOKEN);
                // iterate the conversion path and create the conversion data for each step
                uint256 i;
                for (i = 0; i < _conversionPath.length - 1; i += 2) {
                    IConverterAnchor anchor = IConverterAnchor(_conversionPath[i + 1]);
                    IConverter converter = IConverter(anchor.owner());
                    IERC20Token targetToken = _conversionPath[i + 2];
        
                    // check if the affiliate fee should be processed in this step
                    bool processAffiliateFee = _affiliateFeeEnabled && !affiliateFeeProcessed && targetToken == bntToken;
                    if (processAffiliateFee)
                        affiliateFeeProcessed = true;
        
                    data[i / 2] = ConversionStep({
                        // set the converter anchor
                        anchor: anchor,
        
                        // set the converter
                        converter: converter,
        
                        // set the source/target tokens
                        sourceToken: _conversionPath[i],
                        targetToken: targetToken,
        
                        // requires knowledge about the next step, so initialize in the next phase
                        beneficiary: address(0),
        
                        // set flags
                        isV28OrHigherConverter: isV28OrHigherConverter(converter),
                        processAffiliateFee: processAffiliateFee
                    });
                }
        
                // ETH support
                // source is ETH
                ConversionStep memory stepData = data[0];
                if (etherTokens[stepData.sourceToken]) {
                    // newer converter - replace the source token address with ETH reserve address
                    if (stepData.isV28OrHigherConverter)
                        stepData.sourceToken = IERC20Token(ETH_RESERVE_ADDRESS);
                    // older converter - replace the source token with the EtherToken address used by the converter
                    else
                        stepData.sourceToken = IERC20Token(getConverterEtherTokenAddress(stepData.converter));
                }
        
                // target is ETH
                stepData = data[data.length - 1];
                if (etherTokens[stepData.targetToken]) {
                    // newer converter - replace the target token address with ETH reserve address
                    if (stepData.isV28OrHigherConverter)
                        stepData.targetToken = IERC20Token(ETH_RESERVE_ADDRESS);
                    // older converter - replace the target token with the EtherToken address used by the converter
                    else
                        stepData.targetToken = IERC20Token(getConverterEtherTokenAddress(stepData.converter));
                }
        
                // set the beneficiary for each step
                for (i = 0; i < data.length; i++) {
                    stepData = data[i];
        
                    // first check if the converter in this step is newer as older converters don't even support the beneficiary argument
                    if (stepData.isV28OrHigherConverter) {
                        // if affiliate fee is processed in this step, beneficiary is the network contract
                        if (stepData.processAffiliateFee)
                            stepData.beneficiary = this;
                        // if it's the last step, beneficiary is the final beneficiary
                        else if (i == data.length - 1)
                            stepData.beneficiary = _beneficiary;
                        // if the converter in the next step is newer, beneficiary is the next converter
                        else if (data[i + 1].isV28OrHigherConverter)
                            stepData.beneficiary = data[i + 1].converter;
                        // the converter in the next step is older, beneficiary is the network contract
                        else
                            stepData.beneficiary = this;
                    }
                    else {
                        // converter in this step is older, beneficiary is the network contract
                        stepData.beneficiary = this;
                    }
                }
        
                return data;
            }
        
            /**
              * @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't.
              * Note that we use the non standard erc-20 interface in which `approve` has no return value so that
              * this function will work for both standard and non standard tokens
              *
              * @param _token   token to check the allowance in
              * @param _spender approved address
              * @param _value   allowance amount
            */
            function ensureAllowance(IERC20Token _token, address _spender, uint256 _value) private {
                uint256 allowance = _token.allowance(this, _spender);
                if (allowance < _value) {
                    if (allowance > 0)
                        safeApprove(_token, _spender, 0);
                    safeApprove(_token, _spender, _value);
                }
            }
        
            // legacy - returns the address of an EtherToken used by the converter
            function getConverterEtherTokenAddress(IConverter _converter) private view returns (address) {
                uint256 reserveCount = _converter.connectorTokenCount();
                for (uint256 i = 0; i < reserveCount; i++) {
                    address reserveTokenAddress = _converter.connectorTokens(i);
                    if (etherTokens[reserveTokenAddress])
                        return reserveTokenAddress;
                }
        
                return ETH_RESERVE_ADDRESS;
            }
        
            // legacy - if the token is an ether token, returns the ETH reserve address
            // used by the converter, otherwise returns the input token address
            function getConverterTokenAddress(IConverter _converter, IERC20Token _token) private view returns (IERC20Token) {
                if (!etherTokens[_token])
                    return _token;
        
                if (isV28OrHigherConverter(_converter))
                    return IERC20Token(ETH_RESERVE_ADDRESS);
        
                return IERC20Token(getConverterEtherTokenAddress(_converter));
            }
        
            bytes4 private constant GET_RETURN_FUNC_SELECTOR = bytes4(keccak256("getReturn(address,address,uint256)"));
        
            // using assembly code since older converter versions have different return values
            function getReturn(address _dest, address _sourceToken, address _targetToken, uint256 _amount) internal view returns (uint256, uint256) {
                uint256[2] memory ret;
                bytes memory data = abi.encodeWithSelector(GET_RETURN_FUNC_SELECTOR, _sourceToken, _targetToken, _amount);
        
                assembly {
                    let success := staticcall(
                        gas,           // gas remaining
                        _dest,         // destination address
                        add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
                        mload(data),   // input length (loaded from the first 32 bytes in the `data` array)
                        ret,           // output buffer
                        64             // output length
                    )
                    if iszero(success) {
                        revert(0, 0)
                    }
                }
        
                return (ret[0], ret[1]);
            }
        
            bytes4 private constant IS_V28_OR_HIGHER_FUNC_SELECTOR = bytes4(keccak256("isV28OrHigher()"));
        
            // using assembly code to identify converter version
            // can't rely on the version number since the function had a different signature in older converters
            function isV28OrHigherConverter(IConverter _converter) internal view returns (bool) {
                bool success;
                uint256[1] memory ret;
                bytes memory data = abi.encodeWithSelector(IS_V28_OR_HIGHER_FUNC_SELECTOR);
        
                assembly {
                    success := staticcall(
                        5000,          // isV28OrHigher consumes 190 gas, but just for extra safety
                        _converter,    // destination address
                        add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
                        mload(data),   // input length (loaded from the first 32 bytes in the `data` array)
                        ret,           // output buffer
                        32             // output length
                    )
                }
        
                return success && ret[0] != 0;
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256, uint256) {
                return (rateByPath(_path, _amount), 0);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function convert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public payable returns (uint256) {
                return convertByPath(_path, _amount, _minReturn, address(0), address(0), 0);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function convert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            )
                public
                payable
                returns (uint256)
            {
                return convertByPath(_path, _amount, _minReturn, address(0), _affiliateAccount, _affiliateFee);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function convertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary) public payable returns (uint256) {
                return convertByPath(_path, _amount, _minReturn, _beneficiary, address(0), 0);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function convertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _beneficiary,
                address _affiliateAccount,
                uint256 _affiliateFee
            )
                public
                payable
                greaterThanZero(_minReturn)
                returns (uint256)
            {
                return convertByPath(_path, _amount, _minReturn, _beneficiary, _affiliateAccount, _affiliateFee);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function claimAndConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public returns (uint256) {
                return convertByPath(_path, _amount, _minReturn, address(0), address(0), 0);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function claimAndConvert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            )
                public
                returns (uint256)
            {
                return convertByPath(_path, _amount, _minReturn, address(0), _affiliateAccount, _affiliateFee);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function claimAndConvertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _beneficiary) public returns (uint256) {
                return convertByPath(_path, _amount, _minReturn, _beneficiary, address(0), 0);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function claimAndConvertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _beneficiary,
                address _affiliateAccount,
                uint256 _affiliateFee
            )
                public
                returns (uint256)
            {
                return convertByPath(_path, _amount, _minReturn, _beneficiary, _affiliateAccount, _affiliateFee);
            }
        }

        File 2 of 8: AElfToken
        pragma solidity ^0.4.18;
        
        
        /**
         * @title SafeMath
         * @dev Math operations with safety checks that throw on error
         */
        library SafeMath {
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) {
              return 0;
            }
            uint256 c = a * b;
            assert(c / a == b);
            return c;
          }
        
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // assert(b > 0); // Solidity automatically throws when dividing by 0
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            return c;
          }
        
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            assert(b <= a);
            return a - b;
          }
        
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            assert(c >= a);
            return c;
          }
        }
        
        
        /**
         * @title Ownable
         * @dev The Ownable contract has an owner address, and provides basic authorization control
         * functions, this simplifies the implementation of "user permissions".
         */
        contract Ownable {
          address public owner;
        
        
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
        
          /**
           * @dev The Ownable constructor sets the original `owner` of the contract to the sender
           * account.
           */
          function Ownable() public {
            owner = msg.sender;
          }
        
        
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
            require(msg.sender == owner);
            _;
          }
        
        
          /**
           * @dev Allows the current owner to transfer control of the contract to a newOwner.
           * @param newOwner The address to transfer ownership to.
           */
          function transferOwnership(address newOwner) public onlyOwner {
            require(newOwner != address(0));
            OwnershipTransferred(owner, newOwner);
            owner = newOwner;
          }
        
        }
        
        /**
         * @title ERC20 interface
         * @dev see https://github.com/ethereum/EIPs/issues/20
         */
        contract ERC20 {
          uint256 public totalSupply;
          function balanceOf(address who) public view returns (uint256);
          function transfer(address to, uint256 value) public returns (bool);
          function allowance(address owner, address spender) public view returns (uint256);
          function transferFrom(address from, address to, uint256 value) public returns (bool);
          function approve(address spender, uint256 value) public returns (bool);
          event Transfer(address indexed from, address indexed to, uint256 value);
          event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        
        contract AElfToken is ERC20, Ownable {
          using SafeMath for uint256;
        
          
          // the controller of minting and destroying tokens
          address public aelfDevMultisig = 0x6d3E0B5abFc141cAa674a3c11e1580e6fff2a0B9;
          // the controller of approving of minting and withdraw tokens
          address public aelfCommunityMultisig = 0x4885B422656D4B316C9C7Abc0c0Ab31A2677d9f0;
        
          struct TokensWithLock {
            uint256 value;
            uint256 blockNumber;
          }
          // Balances for each account
          mapping(address => uint256) balances;
          // Tokens with time lock
          // Only when the tokens' blockNumber is less than current block number,
          // can the tokens be minted to the owner
          mapping(address => TokensWithLock) lockTokens;
          
          // Owner of account approves the transfer of an amount to another account
          mapping(address => mapping (address => uint256)) allowed;
          // Token Cap
          uint256 public totalSupplyCap = 1e27;
          // Token Info
          string public name = "ELF Token";
          string public symbol = "ELF";
          uint8 public decimals = 18;
        
          bool public mintingFinished = false;
          // the block number when deploy
          uint256 public deployBlockNumber = getCurrentBlockNumber();
          // the min threshold of lock time
          uint256 public constant TIMETHRESHOLD = 7200;
          // the time when mintTokensWithinTime can be called
          uint256 public constant MINTTIME = 216000;
          // the lock time of minted tokens
          uint256 public durationOfLock = 7200;
          // True if transfers are allowed
          bool public transferable = false;
          // True if the transferable can be change
          bool public canSetTransferable = true;
        
        
          modifier canMint() {
            require(!mintingFinished);
            _;
          }
        
          modifier only(address _address) {
            require(msg.sender == _address);
            _;
          }
        
          modifier nonZeroAddress(address _address) {
            require(_address != address(0));
            _;
          }
        
          modifier canTransfer() {
            require(transferable == true);
            _;
          }
        
          event SetDurationOfLock(address indexed _caller);
          event ApproveMintTokens(address indexed _owner, uint256 _amount);
          event WithdrawMintTokens(address indexed _owner, uint256 _amount);
          event MintTokens(address indexed _owner, uint256 _amount);
          event BurnTokens(address indexed _owner, uint256 _amount);
          event MintFinished(address indexed _caller);
          event SetTransferable(address indexed _address, bool _transferable);
          event SetAElfDevMultisig(address indexed _old, address indexed _new);
          event SetAElfCommunityMultisig(address indexed _old, address indexed _new);
          event DisableSetTransferable(address indexed _address, bool _canSetTransferable);
        
          /**
           * @dev transfer token for a specified address
           * @param _to The address to transfer to.
           * @param _value The amount to be transferred.
           */
          function transfer(address _to, uint256 _value) canTransfer public returns (bool) {
            require(_to != address(0));
            require(_value <= balances[msg.sender]);
        
            // SafeMath.sub will throw if there is not enough balance.
            balances[msg.sender] = balances[msg.sender].sub(_value);
            balances[_to] = balances[_to].add(_value);
            Transfer(msg.sender, _to, _value);
            return true;
          }
        
          /**
           * @dev Gets the balance of the specified address.
           * @param _owner The address to query the the balance of.
           * @return An uint256 representing the amount owned by the passed address.
           */
          function balanceOf(address _owner) public view returns (uint256 balance) {
            return balances[_owner];
          }
        
          /**
           * @dev Transfer tokens from one address to another
           * @param _from address The address which you want to send tokens from
           * @param _to address The address which you want to transfer to
           * @param _value uint256 the amount of tokens to be transferred
           */
          function transferFrom(address _from, address _to, uint256 _value) canTransfer public returns (bool) {
            require(_to != address(0));
            require(_value <= balances[_from]);
            require(_value <= allowed[_from][msg.sender]);
        
            balances[_from] = balances[_from].sub(_value);
            balances[_to] = balances[_to].add(_value);
            allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
            Transfer(_from, _to, _value);
            return true;
          }
        
          /**
           * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
           *
           * 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
           * @param _spender The address which will spend the funds.
           * @param _value The amount of tokens to be spent.
           */
          function approve(address _spender, uint256 _value) canTransfer public returns (bool) {
            allowed[msg.sender][_spender] = _value;
            Approval(msg.sender, _spender, _value);
            return true;
          }
        
          /**
           * @dev Function to check the amount of tokens that an owner allowed to a spender.
           * @param _owner address The address which owns the funds.
           * @param _spender address The address which will spend the funds.
           * @return A uint256 specifying the amount of tokens still available for the spender.
           */
          function allowance(address _owner, address _spender) public view returns (uint256) {
            return allowed[_owner][_spender];
          }
        
          /**
           * approve should be called when allowed[_spender] == 0. To increment
           * allowed value is better to use this function to avoid 2 calls (and wait until
           * the first transaction is mined)
           * From MonolithDAO Token.sol
           */
          function increaseApproval(address _spender, uint256 _addedValue) canTransfer public returns (bool) {
            allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
            Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
            return true;
          }
        
          function decreaseApproval(address _spender, uint256 _subtractedValue) canTransfer public returns (bool) {
            uint256 oldValue = allowed[msg.sender][_spender];
            if (_subtractedValue > oldValue) {
              allowed[msg.sender][_spender] = 0;
            } else {
              allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
            }
            Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
            return true;
          }
          /**
           * @dev Enables token holders to transfer their tokens freely if true
           * @param _transferable True if transfers are allowed
           */
          function setTransferable(bool _transferable) only(aelfDevMultisig) public {
            require(canSetTransferable == true);
            transferable = _transferable;
            SetTransferable(msg.sender, _transferable);
          }
        
          /**
           * @dev disable the canSetTransferable
           */
          function disableSetTransferable() only(aelfDevMultisig) public {
            transferable = true;
            canSetTransferable = false;
            DisableSetTransferable(msg.sender, false);
          }
        
          /**
           * @dev Set the aelfMultisig
           * @param _aelfDevMultisig The new aelfMultisig
           */
          function setAElfDevMultisig(address _aelfDevMultisig) only(aelfDevMultisig) nonZeroAddress(_aelfDevMultisig) public {
            aelfDevMultisig = _aelfDevMultisig;
            SetAElfDevMultisig(msg.sender, _aelfDevMultisig);
          }
          /**
           * @dev Set the aelfCommunityMultisig
           * @param _aelfCommunityMultisig The new aelfCommunityMultisig
           */
          function setAElfCommunityMultisig(address _aelfCommunityMultisig) only(aelfCommunityMultisig) nonZeroAddress(_aelfCommunityMultisig) public {
            aelfCommunityMultisig = _aelfCommunityMultisig;
            SetAElfCommunityMultisig(msg.sender, _aelfCommunityMultisig);
          }
          /**
           * @dev Set the duration of lock of tokens approved of minting
           * @param _durationOfLock the new duration of lock
           */
          function setDurationOfLock(uint256 _durationOfLock) canMint only(aelfCommunityMultisig) public {
            require(_durationOfLock >= TIMETHRESHOLD);
            durationOfLock = _durationOfLock;
            SetDurationOfLock(msg.sender);
          }
          /**
           * @dev Get the quantity of locked tokens
           * @param _owner The address of locked tokens
           * @return the quantity and the lock time of locked tokens
           */
           function getLockTokens(address _owner) nonZeroAddress(_owner) view public returns (uint256 value, uint256 blockNumber) {
             return (lockTokens[_owner].value, lockTokens[_owner].blockNumber);
           }
        
          /**
           * @dev Approve of minting `_amount` tokens that are assigned to `_owner`
           * @param _owner The address that will be assigned the new tokens
           * @param _amount The quantity of tokens approved of mintting
           * @return True if the tokens are approved of mintting correctly
           */
          function approveMintTokens(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfCommunityMultisig) public returns (bool) {
            require(_amount > 0);
            uint256 previousLockTokens = lockTokens[_owner].value;
            require(previousLockTokens + _amount >= previousLockTokens);
            uint256 curTotalSupply = totalSupply;
            require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
            require(curTotalSupply + _amount <= totalSupplyCap);  // Check for overflow of total supply cap
            uint256 previousBalanceTo = balanceOf(_owner);
            require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
            lockTokens[_owner].value = previousLockTokens.add(_amount);
            uint256 curBlockNumber = getCurrentBlockNumber();
            lockTokens[_owner].blockNumber = curBlockNumber.add(durationOfLock);
            ApproveMintTokens(_owner, _amount);
            return true;
          }
          /**
           * @dev Withdraw approval of minting `_amount` tokens that are assigned to `_owner`
           * @param _owner The address that will be withdrawn the tokens
           * @param _amount The quantity of tokens withdrawn approval of mintting
           * @return True if the tokens are withdrawn correctly
           */
          function withdrawMintTokens(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfCommunityMultisig) public returns (bool) {
            require(_amount > 0);
            uint256 previousLockTokens = lockTokens[_owner].value;
            require(previousLockTokens - _amount >= 0);
            lockTokens[_owner].value = previousLockTokens.sub(_amount);
            if (previousLockTokens - _amount == 0) {
              lockTokens[_owner].blockNumber = 0;
            }
            WithdrawMintTokens(_owner, _amount);
            return true;
          }
          /**
           * @dev Mints `_amount` tokens that are assigned to `_owner`
           * @param _owner The address that will be assigned the new tokens
           * @return True if the tokens are minted correctly
           */
          function mintTokens(address _owner) canMint only(aelfDevMultisig) nonZeroAddress(_owner) public returns (bool) {
            require(lockTokens[_owner].blockNumber <= getCurrentBlockNumber());
            uint256 _amount = lockTokens[_owner].value;
            uint256 curTotalSupply = totalSupply;
            require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
            require(curTotalSupply + _amount <= totalSupplyCap);  // Check for overflow of total supply cap
            uint256 previousBalanceTo = balanceOf(_owner);
            require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
            
            totalSupply = curTotalSupply.add(_amount);
            balances[_owner] = previousBalanceTo.add(_amount);
            lockTokens[_owner].value = 0;
            lockTokens[_owner].blockNumber = 0;
            MintTokens(_owner, _amount);
            Transfer(0, _owner, _amount);
            return true;
          }
          /**
           * @dev Mints `_amount` tokens that are assigned to `_owner` within one day after deployment
           * the tokens minted will be added to balance immediately
           * @param _owner The address that will be assigned the new tokens
           * @param _amount The quantity of tokens withdrawn minted
           * @return True if the tokens are minted correctly
           */
          function mintTokensWithinTime(address _owner, uint256 _amount) nonZeroAddress(_owner) canMint only(aelfDevMultisig) public returns (bool) {
            require(_amount > 0);
            require(getCurrentBlockNumber() < (deployBlockNumber + MINTTIME));
            uint256 curTotalSupply = totalSupply;
            require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
            require(curTotalSupply + _amount <= totalSupplyCap);  // Check for overflow of total supply cap
            uint256 previousBalanceTo = balanceOf(_owner);
            require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
            
            totalSupply = curTotalSupply.add(_amount);
            balances[_owner] = previousBalanceTo.add(_amount);
            MintTokens(_owner, _amount);
            Transfer(0, _owner, _amount);
            return true;
          }
          /**
           * @dev Transfer tokens to multiple addresses
           * @param _addresses The addresses that will receieve tokens
           * @param _amounts The quantity of tokens that will be transferred
           * @return True if the tokens are transferred correctly
           */
          function transferForMultiAddresses(address[] _addresses, uint256[] _amounts) canTransfer public returns (bool) {
            for (uint256 i = 0; i < _addresses.length; i++) {
              require(_addresses[i] != address(0));
              require(_amounts[i] <= balances[msg.sender]);
              require(_amounts[i] > 0);
        
              // SafeMath.sub will throw if there is not enough balance.
              balances[msg.sender] = balances[msg.sender].sub(_amounts[i]);
              balances[_addresses[i]] = balances[_addresses[i]].add(_amounts[i]);
              Transfer(msg.sender, _addresses[i], _amounts[i]);
            }
            return true;
          }
        
          /**
           * @dev Burns `_amount` tokens from `_owner`
           * @param _amount The quantity of tokens being burned
           * @return True if the tokens are burned correctly
           */
          function burnTokens(uint256 _amount) public returns (bool) {
            require(_amount > 0);
            uint256 curTotalSupply = totalSupply;
            require(curTotalSupply >= _amount);
            uint256 previousBalanceTo = balanceOf(msg.sender);
            require(previousBalanceTo >= _amount);
            totalSupply = curTotalSupply.sub(_amount);
            balances[msg.sender] = previousBalanceTo.sub(_amount);
            BurnTokens(msg.sender, _amount);
            Transfer(msg.sender, 0, _amount);
            return true;
          }
          /**
           * @dev Function to stop minting new tokens.
           * @return True if the operation was successful.
           */
          function finishMinting() only(aelfDevMultisig) canMint public returns (bool) {
            mintingFinished = true;
            MintFinished(msg.sender);
            return true;
          }
        
          function getCurrentBlockNumber() private view returns (uint256) {
            return block.number;
          }
        }

        File 3 of 8: LiquidityPoolV1Converter
        // File: contracts/utility/interfaces/IOwned.sol
        
        pragma solidity 0.4.26;
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public view returns (address) {this;}
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        // File: contracts/token/interfaces/IERC20Token.sol
        
        pragma solidity 0.4.26;
        
        /*
            ERC20 Standard Token interface
        */
        contract IERC20Token {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public view returns (string) {this;}
            function symbol() public view returns (string) {this;}
            function decimals() public view returns (uint8) {this;}
            function totalSupply() public view returns (uint256) {this;}
            function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
            function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
        
            function transfer(address _to, uint256 _value) public returns (bool success);
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
            function approve(address _spender, uint256 _value) public returns (bool success);
        }
        
        // File: contracts/utility/interfaces/ITokenHolder.sol
        
        pragma solidity 0.4.26;
        
        
        
        /*
            Token Holder interface
        */
        contract ITokenHolder is IOwned {
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
        }
        
        // File: contracts/converter/interfaces/IConverterAnchor.sol
        
        pragma solidity 0.4.26;
        
        
        
        /*
            Converter Anchor interface
        */
        contract IConverterAnchor is IOwned, ITokenHolder {
        }
        
        // File: contracts/utility/interfaces/IWhitelist.sol
        
        pragma solidity 0.4.26;
        
        /*
            Whitelist interface
        */
        contract IWhitelist {
            function isWhitelisted(address _address) public view returns (bool);
        }
        
        // File: contracts/converter/interfaces/IConverter.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        /*
            Converter interface
        */
        contract IConverter is IOwned {
            function converterType() public pure returns (uint16);
            function anchor() public view returns (IConverterAnchor) {this;}
            function isActive() public view returns (bool);
        
            function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256);
            function convert(IERC20Token _sourceToken,
                             IERC20Token _targetToken,
                             uint256 _amount,
                             address _trader,
                             address _beneficiary) public payable returns (uint256);
        
            function conversionWhitelist() public view returns (IWhitelist) {this;}
            function conversionFee() public view returns (uint32) {this;}
            function maxConversionFee() public view returns (uint32) {this;}
            function reserveBalance(IERC20Token _reserveToken) public view returns (uint256);
            function() external payable;
        
            function transferAnchorOwnership(address _newOwner) public;
            function acceptAnchorOwnership() public;
            function setConversionFee(uint32 _conversionFee) public;
            function setConversionWhitelist(IWhitelist _whitelist) public;
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
            function withdrawETH(address _to) public;
            function addReserve(IERC20Token _token, uint32 _ratio) public;
        
            // deprecated, backward compatibility
            function token() public view returns (IConverterAnchor);
            function transferTokenOwnership(address _newOwner) public;
            function acceptTokenOwnership() public;
            function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool);
            function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256);
            function connectorTokens(uint256 _index) public view returns (IERC20Token);
            function connectorTokenCount() public view returns (uint16);
        }
        
        // File: contracts/converter/interfaces/IConverterUpgrader.sol
        
        pragma solidity 0.4.26;
        
        /*
            Converter Upgrader interface
        */
        contract IConverterUpgrader {
            function upgrade(bytes32 _version) public;
            function upgrade(uint16 _version) public;
        }
        
        // File: contracts/converter/interfaces/IBancorFormula.sol
        
        pragma solidity 0.4.26;
        
        /*
            Bancor Formula interface
        */
        contract IBancorFormula {
            function purchaseTargetAmount(uint256 _supply,
                                          uint256 _reserveBalance,
                                          uint32 _reserveWeight,
                                          uint256 _amount)
                                          public view returns (uint256);
        
            function saleTargetAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveWeight,
                                      uint256 _amount)
                                      public view returns (uint256);
        
            function crossReserveTargetAmount(uint256 _sourceReserveBalance,
                                              uint32 _sourceReserveWeight,
                                              uint256 _targetReserveBalance,
                                              uint32 _targetReserveWeight,
                                              uint256 _amount)
                                              public view returns (uint256);
        
            function fundCost(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveRatio,
                              uint256 _amount)
                              public view returns (uint256);
        
            function fundSupplyAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveRatio,
                                      uint256 _amount)
                                      public view returns (uint256);
        
            function liquidateReserveAmount(uint256 _supply,
                                            uint256 _reserveBalance,
                                            uint32 _reserveRatio,
                                            uint256 _amount)
                                            public view returns (uint256);
        }
        
        // File: contracts/IBancorNetwork.sol
        
        pragma solidity 0.4.26;
        
        
        /*
            Bancor Network interface
        */
        contract IBancorNetwork {
            function convert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public payable returns (uint256);
        
            function claimAndConvert2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public returns (uint256);
        
            function convertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public payable returns (uint256);
        
            function claimAndConvertFor2(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for,
                address _affiliateAccount,
                uint256 _affiliateFee
            ) public returns (uint256);
        
            // deprecated, backward compatibility
            function convert(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn
            ) public payable returns (uint256);
        
            // deprecated, backward compatibility
            function claimAndConvert(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn
            ) public returns (uint256);
        
            // deprecated, backward compatibility
            function convertFor(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for
            ) public payable returns (uint256);
        
            // deprecated, backward compatibility
            function claimAndConvertFor(
                IERC20Token[] _path,
                uint256 _amount,
                uint256 _minReturn,
                address _for
            ) public returns (uint256);
        }
        
        // File: contracts/utility/Owned.sol
        
        pragma solidity 0.4.26;
        
        
        /**
          * @dev Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            /**
              * @dev triggered when the owner is updated
              *
              * @param _prevOwner previous owner
              * @param _newOwner  new owner
            */
            event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
        
            /**
              * @dev initializes a new Owned instance
            */
            constructor() public {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                _ownerOnly();
                _;
            }
        
            // error message binary size optimization
            function _ownerOnly() internal view {
                require(msg.sender == owner, "ERR_ACCESS_DENIED");
            }
        
            /**
              * @dev allows transferring the contract ownership
              * the new owner still needs to accept the transfer
              * can only be called by the contract owner
              *
              * @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner, "ERR_SAME_OWNER");
                newOwner = _newOwner;
            }
        
            /**
              * @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
                emit OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = address(0);
            }
        }
        
        // File: contracts/utility/Utils.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev Utilities & Common Modifiers
        */
        contract Utils {
            // verifies that a value is greater than zero
            modifier greaterThanZero(uint256 _value) {
                _greaterThanZero(_value);
                _;
            }
        
            // error message binary size optimization
            function _greaterThanZero(uint256 _value) internal pure {
                require(_value > 0, "ERR_ZERO_VALUE");
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                _validAddress(_address);
                _;
            }
        
            // error message binary size optimization
            function _validAddress(address _address) internal pure {
                require(_address != address(0), "ERR_INVALID_ADDRESS");
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                _notThis(_address);
                _;
            }
        
            // error message binary size optimization
            function _notThis(address _address) internal view {
                require(_address != address(this), "ERR_ADDRESS_IS_SELF");
            }
        }
        
        // File: contracts/utility/interfaces/IContractRegistry.sol
        
        pragma solidity 0.4.26;
        
        /*
            Contract Registry interface
        */
        contract IContractRegistry {
            function addressOf(bytes32 _contractName) public view returns (address);
        
            // deprecated, backward compatibility
            function getAddress(bytes32 _contractName) public view returns (address);
        }
        
        // File: contracts/utility/ContractRegistryClient.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /**
          * @dev Base contract for ContractRegistry clients
        */
        contract ContractRegistryClient is Owned, Utils {
            bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry";
            bytes32 internal constant BANCOR_NETWORK = "BancorNetwork";
            bytes32 internal constant BANCOR_FORMULA = "BancorFormula";
            bytes32 internal constant CONVERTER_FACTORY = "ConverterFactory";
            bytes32 internal constant CONVERSION_PATH_FINDER = "ConversionPathFinder";
            bytes32 internal constant CONVERTER_UPGRADER = "BancorConverterUpgrader";
            bytes32 internal constant CONVERTER_REGISTRY = "BancorConverterRegistry";
            bytes32 internal constant CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData";
            bytes32 internal constant BNT_TOKEN = "BNTToken";
            bytes32 internal constant BANCOR_X = "BancorX";
            bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader";
        
            IContractRegistry public registry;      // address of the current contract-registry
            IContractRegistry public prevRegistry;  // address of the previous contract-registry
            bool public onlyOwnerCanUpdateRegistry; // only an owner can update the contract-registry
        
            /**
              * @dev verifies that the caller is mapped to the given contract name
              *
              * @param _contractName    contract name
            */
            modifier only(bytes32 _contractName) {
                _only(_contractName);
                _;
            }
        
            // error message binary size optimization
            function _only(bytes32 _contractName) internal view {
                require(msg.sender == addressOf(_contractName), "ERR_ACCESS_DENIED");
            }
        
            /**
              * @dev initializes a new ContractRegistryClient instance
              *
              * @param  _registry   address of a contract-registry contract
            */
            constructor(IContractRegistry _registry) internal validAddress(_registry) {
                registry = IContractRegistry(_registry);
                prevRegistry = IContractRegistry(_registry);
            }
        
            /**
              * @dev updates to the new contract-registry
             */
            function updateRegistry() public {
                // verify that this function is permitted
                require(msg.sender == owner || !onlyOwnerCanUpdateRegistry, "ERR_ACCESS_DENIED");
        
                // get the new contract-registry
                IContractRegistry newRegistry = IContractRegistry(addressOf(CONTRACT_REGISTRY));
        
                // verify that the new contract-registry is different and not zero
                require(newRegistry != address(registry) && newRegistry != address(0), "ERR_INVALID_REGISTRY");
        
                // verify that the new contract-registry is pointing to a non-zero contract-registry
                require(newRegistry.addressOf(CONTRACT_REGISTRY) != address(0), "ERR_INVALID_REGISTRY");
        
                // save a backup of the current contract-registry before replacing it
                prevRegistry = registry;
        
                // replace the current contract-registry with the new contract-registry
                registry = newRegistry;
            }
        
            /**
              * @dev restores the previous contract-registry
            */
            function restoreRegistry() public ownerOnly {
                // restore the previous contract-registry
                registry = prevRegistry;
            }
        
            /**
              * @dev restricts the permission to update the contract-registry
              *
              * @param _onlyOwnerCanUpdateRegistry  indicates whether or not permission is restricted to owner only
            */
            function restrictRegistryUpdate(bool _onlyOwnerCanUpdateRegistry) public ownerOnly {
                // change the permission to update the contract-registry
                onlyOwnerCanUpdateRegistry = _onlyOwnerCanUpdateRegistry;
            }
        
            /**
              * @dev returns the address associated with the given contract name
              *
              * @param _contractName    contract name
              *
              * @return contract address
            */
            function addressOf(bytes32 _contractName) internal view returns (address) {
                return registry.addressOf(_contractName);
            }
        }
        
        // File: contracts/utility/ReentrancyGuard.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev ReentrancyGuard
          *
          * The contract provides protection against re-entrancy - calling a function (directly or
          * indirectly) from within itself.
        */
        contract ReentrancyGuard {
            // true while protected code is being executed, false otherwise
            bool private locked = false;
        
            /**
              * @dev ensures instantiation only by sub-contracts
            */
            constructor() internal {}
        
            // protects a function against reentrancy attacks
            modifier protected() {
                _protected();
                locked = true;
                _;
                locked = false;
            }
        
            // error message binary size optimization
            function _protected() internal view {
                require(!locked, "ERR_REENTRANCY");
            }
        }
        
        // File: contracts/utility/SafeMath.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev Library for basic math operations with overflow/underflow protection
        */
        library SafeMath {
            /**
              * @dev returns the sum of _x and _y, reverts if the calculation overflows
              *
              * @param _x   value 1
              * @param _y   value 2
              *
              * @return sum
            */
            function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x + _y;
                require(z >= _x, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev returns the difference of _x minus _y, reverts if the calculation underflows
              *
              * @param _x   minuend
              * @param _y   subtrahend
              *
              * @return difference
            */
            function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_x >= _y, "ERR_UNDERFLOW");
                return _x - _y;
            }
        
            /**
              * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
              *
              * @param _x   factor 1
              * @param _y   factor 2
              *
              * @return product
            */
            function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
                // gas optimization
                if (_x == 0)
                    return 0;
        
                uint256 z = _x * _y;
                require(z / _x == _y, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
              *
              * @param _x   dividend
              * @param _y   divisor
              *
              * @return quotient
            */
            function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_y > 0, "ERR_DIVIDE_BY_ZERO");
                uint256 c = _x / _y;
                return c;
            }
        }
        
        // File: contracts/utility/TokenHandler.sol
        
        pragma solidity 0.4.26;
        
        
        contract TokenHandler {
            bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
            bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
            bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));
        
            /**
              * @dev executes the ERC20 token's `approve` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _spender approved address
              * @param _value   allowance amount
            */
            function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
            }
        
            /**
              * @dev executes the ERC20 token's `transfer` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _to      target address
              * @param _value   transfer amount
            */
            function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
            }
        
            /**
              * @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _from    source address
              * @param _to      target address
              * @param _value   transfer amount
            */
            function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal {
               execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
            }
        
            /**
              * @dev executes a function on the ERC20 token and reverts upon failure
              * the main purpose of this function is to prevent a non standard ERC20 token
              * from failing silently
              *
              * @param _token   ERC20 token address
              * @param _data    data to pass in to the token's contract for execution
            */
            function execute(IERC20Token _token, bytes memory _data) private {
                uint256[1] memory ret = [uint256(1)];
        
                assembly {
                    let success := call(
                        gas,            // gas remaining
                        _token,         // destination address
                        0,              // no ether
                        add(_data, 32), // input buffer (starts after the first 32 bytes in the `data` array)
                        mload(_data),   // input length (loaded from the first 32 bytes in the `data` array)
                        ret,            // output buffer
                        32              // output length
                    )
                    if iszero(success) {
                        revert(0, 0)
                    }
                }
        
                require(ret[0] != 0, "ERR_TRANSFER_FAILED");
            }
        }
        
        // File: contracts/utility/TokenHolder.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        
        /**
          * @dev We consider every contract to be a 'token holder' since it's currently not possible
          * for a contract to deny receiving tokens.
          *
          * The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
          * the owner to send tokens that were sent to the contract by mistake back to their sender.
          *
          * Note that we use the non standard ERC-20 interface which has no return value for transfer
          * in order to support both non standard as well as standard token contracts.
          * see https://github.com/ethereum/solidity/issues/4116
        */
        contract TokenHolder is ITokenHolder, TokenHandler, Owned, Utils {
            /**
              * @dev withdraws tokens held by the contract and sends them to an account
              * can only be called by the owner
              *
              * @param _token   ERC20 token contract address
              * @param _to      account to receive the new amount
              * @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_token)
                validAddress(_to)
                notThis(_to)
            {
                safeTransfer(_token, _to, _amount);
            }
        }
        
        // File: contracts/token/interfaces/IEtherToken.sol
        
        pragma solidity 0.4.26;
        
        
        /*
            Ether Token interface
        */
        contract IEtherToken is IERC20Token {
            function deposit() public payable;
            function withdraw(uint256 _amount) public;
            function depositTo(address _to) public payable;
            function withdrawTo(address _to, uint256 _amount) public;
        }
        
        // File: contracts/bancorx/interfaces/IBancorX.sol
        
        pragma solidity 0.4.26;
        
        
        contract IBancorX {
            function token() public view returns (IERC20Token) {this;}
            function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public;
            function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256);
        }
        
        // File: contracts/converter/ConverterBase.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        
        
        
        
        
        
        
        
        
        /**
          * @dev ConverterBase
          *
          * The converter contains the main logic for conversions between different ERC20 tokens.
          *
          * It is also the upgradable part of the mechanism (note that upgrades are opt-in).
          *
          * The anchor must be set on construction and cannot be changed afterwards.
          * Wrappers are provided for some of the anchor's functions, for easier access.
          *
          * Once the converter accepts ownership of the anchor, it becomes the anchor's sole controller
          * and can execute any of its functions.
          *
          * To upgrade the converter, anchor ownership must be transferred to a new converter, along with
          * any relevant data.
          *
          * Note that the converter can transfer anchor ownership to a new converter that
          * doesn't allow upgrades anymore, for finalizing the relationship between the converter
          * and the anchor.
          *
          * Converter types (defined as uint16 type) -
          * 0 = liquid token converter
          * 1 = liquidity pool v1 converter
          * 2 = liquidity pool v2 converter
          *
          * Note that converters don't currently support tokens with transfer fees.
        */
        contract ConverterBase is IConverter, TokenHandler, TokenHolder, ContractRegistryClient, ReentrancyGuard {
            using SafeMath for uint256;
        
            uint32 internal constant WEIGHT_RESOLUTION = 1000000;
            uint64 internal constant CONVERSION_FEE_RESOLUTION = 1000000;
            address internal constant ETH_RESERVE_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
        
            struct Reserve {
                uint256 balance;    // reserve balance
                uint32 weight;      // reserve weight, represented in ppm, 1-1000000
                bool deprecated1;   // deprecated
                bool deprecated2;   // deprecated
                bool isSet;         // true if the reserve is valid, false otherwise
            }
        
            /**
              * @dev version number
            */
            uint16 public constant version = 30;
        
            IConverterAnchor public anchor;                 // converter anchor contract
            IWhitelist public conversionWhitelist;          // whitelist contract with list of addresses that are allowed to use the converter
            IERC20Token[] public reserveTokens;             // ERC20 standard token addresses (prior version 17, use 'connectorTokens' instead)
            mapping (address => Reserve) public reserves;   // reserve token addresses -> reserve data (prior version 17, use 'connectors' instead)
            uint32 public reserveRatio = 0;                 // ratio between the reserves and the market cap, equal to the total reserve weights
            uint32 public maxConversionFee = 0;             // maximum conversion fee for the lifetime of the contract,
                                                            // represented in ppm, 0...1000000 (0 = no fee, 100 = 0.01%, 1000000 = 100%)
            uint32 public conversionFee = 0;                // current conversion fee, represented in ppm, 0...maxConversionFee
            bool public constant conversionsEnabled = true; // deprecated, backward compatibility
        
            /**
              * @dev triggered when the converter is activated
              *
              * @param _anchor      converter anchor
              * @param _activated   true if the converter was activated, false if it was deactivated
            */
            event Activation(IConverterAnchor _anchor, bool _activated);
        
            /**
              * @dev triggered when a conversion between two tokens occurs
              *
              * @param _fromToken       source ERC20 token
              * @param _toToken         target ERC20 token
              * @param _trader          wallet that initiated the trade
              * @param _amount          amount converted, in the source token
              * @param _return          amount returned, minus conversion fee
              * @param _conversionFee   conversion fee
            */
            event Conversion(
                address indexed _fromToken,
                address indexed _toToken,
                address indexed _trader,
                uint256 _amount,
                uint256 _return,
                int256 _conversionFee
            );
        
            /**
              * @dev triggered when the rate between two tokens in the converter changes
              * note that the event might be dispatched for rate updates between any two tokens in the converter
              * note that prior to version 28, you should use the 'PriceDataUpdate' event instead
              *
              * @param  _token1 address of the first token
              * @param  _token2 address of the second token
              * @param  _rateN  rate of 1 unit of `_token1` in `_token2` (numerator)
              * @param  _rateD  rate of 1 unit of `_token1` in `_token2` (denominator)
            */
            event TokenRateUpdate(
                address indexed _token1,
                address indexed _token2,
                uint256 _rateN,
                uint256 _rateD
            );
        
            /**
              * @dev triggered when the conversion fee is updated
              *
              * @param  _prevFee    previous fee percentage, represented in ppm
              * @param  _newFee     new fee percentage, represented in ppm
            */
            event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee);
        
            /**
              * @dev used by sub-contracts to initialize a new converter
              *
              * @param  _anchor             anchor governed by the converter
              * @param  _registry           address of a contract registry contract
              * @param  _maxConversionFee   maximum conversion fee, represented in ppm
            */
            constructor(
                IConverterAnchor _anchor,
                IContractRegistry _registry,
                uint32 _maxConversionFee
            )
                validAddress(_anchor)
                ContractRegistryClient(_registry)
                internal
                validConversionFee(_maxConversionFee)
            {
                anchor = _anchor;
                maxConversionFee = _maxConversionFee;
            }
        
            // ensures that the converter is active
            modifier active() {
                _active();
                _;
            }
        
            // error message binary size optimization
            function _active() internal view {
                require(isActive(), "ERR_INACTIVE");
            }
        
            // ensures that the converter is not active
            modifier inactive() {
                _inactive();
                _;
            }
        
            // error message binary size optimization
            function _inactive() internal view {
                require(!isActive(), "ERR_ACTIVE");
            }
        
            // validates a reserve token address - verifies that the address belongs to one of the reserve tokens
            modifier validReserve(IERC20Token _address) {
                _validReserve(_address);
                _;
            }
        
            // error message binary size optimization
            function _validReserve(IERC20Token _address) internal view {
                require(reserves[_address].isSet, "ERR_INVALID_RESERVE");
            }
        
            // validates conversion fee
            modifier validConversionFee(uint32 _conversionFee) {
                _validConversionFee(_conversionFee);
                _;
            }
        
            // error message binary size optimization
            function _validConversionFee(uint32 _conversionFee) internal pure {
                require(_conversionFee <= CONVERSION_FEE_RESOLUTION, "ERR_INVALID_CONVERSION_FEE");
            }
        
            // validates reserve weight
            modifier validReserveWeight(uint32 _weight) {
                _validReserveWeight(_weight);
                _;
            }
        
            // error message binary size optimization
            function _validReserveWeight(uint32 _weight) internal pure {
                require(_weight > 0 && _weight <= WEIGHT_RESOLUTION, "ERR_INVALID_RESERVE_WEIGHT");
            }
        
            /**
              * @dev deposits ether
              * can only be called if the converter has an ETH reserve
            */
            function() external payable {
                require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_INVALID_RESERVE"); // require(hasETHReserve(), "ERR_INVALID_RESERVE");
                // a workaround for a problem when running solidity-coverage
                // see https://github.com/sc-forks/solidity-coverage/issues/487
            }
        
            /**
              * @dev withdraws ether
              * can only be called by the owner if the converter is inactive or by upgrader contract
              * can only be called after the upgrader contract has accepted the ownership of this contract
              * can only be called if the converter has an ETH reserve
              *
              * @param _to  address to send the ETH to
            */
            function withdrawETH(address _to)
                public
                protected
                ownerOnly
                validReserve(IERC20Token(ETH_RESERVE_ADDRESS))
            {
                address converterUpgrader = addressOf(CONVERTER_UPGRADER);
        
                // verify that the converter is inactive or that the owner is the upgrader contract
                require(!isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED");
                _to.transfer(address(this).balance);
        
                // sync the ETH reserve balance
                syncReserveBalance(IERC20Token(ETH_RESERVE_ADDRESS));
            }
        
            /**
              * @dev checks whether or not the converter version is 28 or higher
              *
              * @return true, since the converter version is 28 or higher
            */
            function isV28OrHigher() public pure returns (bool) {
                return true;
            }
        
            /**
              * @dev allows the owner to update & enable the conversion whitelist contract address
              * when set, only addresses that are whitelisted are actually allowed to use the converter
              * note that the whitelist check is actually done by the BancorNetwork contract
              *
              * @param _whitelist    address of a whitelist contract
            */
            function setConversionWhitelist(IWhitelist _whitelist)
                public
                ownerOnly
                notThis(_whitelist)
            {
                conversionWhitelist = _whitelist;
            }
        
            /**
              * @dev returns true if the converter is active, false otherwise
            */
            function isActive() public view returns (bool) {
                return anchor.owner() == address(this);
            }
        
            /**
              * @dev transfers the anchor ownership
              * the new owner needs to accept the transfer
              * can only be called by the converter upgrder while the upgrader is the owner
              * note that prior to version 28, you should use 'transferAnchorOwnership' instead
              *
              * @param _newOwner    new token owner
            */
            function transferAnchorOwnership(address _newOwner)
                public
                ownerOnly
                only(CONVERTER_UPGRADER)
            {
                anchor.transferOwnership(_newOwner);
            }
        
            /**
              * @dev accepts ownership of the anchor after an ownership transfer
              * most converters are also activated as soon as they accept the anchor ownership
              * can only be called by the contract owner
              * note that prior to version 28, you should use 'acceptTokenOwnership' instead
            */
            function acceptAnchorOwnership() public ownerOnly {
                // verify the the converter has at least one reserve
                require(reserveTokenCount() > 0, "ERR_INVALID_RESERVE_COUNT");
                anchor.acceptOwnership();
                syncReserveBalances();
            }
        
            /**
              * @dev withdraws tokens held by the anchor and sends them to an account
              * can only be called by the owner
              *
              * @param _token   ERC20 token contract address
              * @param _to      account to receive the new amount
              * @param _amount  amount to withdraw
            */
            function withdrawFromAnchor(IERC20Token _token, address _to, uint256 _amount) public ownerOnly {
                anchor.withdrawTokens(_token, _to, _amount);
            }
        
            /**
              * @dev updates the current conversion fee
              * can only be called by the contract owner
              *
              * @param _conversionFee new conversion fee, represented in ppm
            */
            function setConversionFee(uint32 _conversionFee) public ownerOnly {
                require(_conversionFee <= maxConversionFee, "ERR_INVALID_CONVERSION_FEE");
                emit ConversionFeeUpdate(conversionFee, _conversionFee);
                conversionFee = _conversionFee;
            }
        
            /**
              * @dev withdraws tokens held by the converter and sends them to an account
              * can only be called by the owner
              * note that reserve tokens can only be withdrawn by the owner while the converter is inactive
              * unless the owner is the converter upgrader contract
              *
              * @param _token   ERC20 token contract address
              * @param _to      account to receive the new amount
              * @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public protected ownerOnly {
                address converterUpgrader = addressOf(CONVERTER_UPGRADER);
        
                // if the token is not a reserve token, allow withdrawal
                // otherwise verify that the converter is inactive or that the owner is the upgrader contract
                require(!reserves[_token].isSet || !isActive() || owner == converterUpgrader, "ERR_ACCESS_DENIED");
                super.withdrawTokens(_token, _to, _amount);
        
                // if the token is a reserve token, sync the reserve balance
                if (reserves[_token].isSet)
                    syncReserveBalance(_token);
            }
        
            /**
              * @dev upgrades the converter to the latest version
              * can only be called by the owner
              * note that the owner needs to call acceptOwnership on the new converter after the upgrade
            */
            function upgrade() public ownerOnly {
                IConverterUpgrader converterUpgrader = IConverterUpgrader(addressOf(CONVERTER_UPGRADER));
        
                transferOwnership(converterUpgrader);
                converterUpgrader.upgrade(version);
                acceptOwnership();
            }
        
            /**
              * @dev returns the number of reserve tokens defined
              * note that prior to version 17, you should use 'connectorTokenCount' instead
              *
              * @return number of reserve tokens
            */
            function reserveTokenCount() public view returns (uint16) {
                return uint16(reserveTokens.length);
            }
        
            /**
              * @dev defines a new reserve token for the converter
              * can only be called by the owner while the converter is inactive
              *
              * @param _token   address of the reserve token
              * @param _weight  reserve weight, represented in ppm, 1-1000000
            */
            function addReserve(IERC20Token _token, uint32 _weight)
                public
                ownerOnly
                inactive
                validAddress(_token)
                notThis(_token)
                validReserveWeight(_weight)
            {
                // validate input
                require(_token != address(anchor) && !reserves[_token].isSet, "ERR_INVALID_RESERVE");
                require(_weight <= WEIGHT_RESOLUTION - reserveRatio, "ERR_INVALID_RESERVE_WEIGHT");
                require(reserveTokenCount() < uint16(-1), "ERR_INVALID_RESERVE_COUNT");
        
                Reserve storage newReserve = reserves[_token];
                newReserve.balance = 0;
                newReserve.weight = _weight;
                newReserve.isSet = true;
                reserveTokens.push(_token);
                reserveRatio += _weight;
            }
        
            /**
              * @dev returns the reserve's weight
              * added in version 28
              *
              * @param _reserveToken    reserve token contract address
              *
              * @return reserve weight
            */
            function reserveWeight(IERC20Token _reserveToken)
                public
                view
                validReserve(_reserveToken)
                returns (uint32)
            {
                return reserves[_reserveToken].weight;
            }
        
            /**
              * @dev returns the reserve's balance
              * note that prior to version 17, you should use 'getConnectorBalance' instead
              *
              * @param _reserveToken    reserve token contract address
              *
              * @return reserve balance
            */
            function reserveBalance(IERC20Token _reserveToken)
                public
                view
                validReserve(_reserveToken)
                returns (uint256)
            {
                return reserves[_reserveToken].balance;
            }
        
            /**
              * @dev checks whether or not the converter has an ETH reserve
              *
              * @return true if the converter has an ETH reserve, false otherwise
            */
            function hasETHReserve() public view returns (bool) {
                return reserves[ETH_RESERVE_ADDRESS].isSet;
            }
        
            /**
              * @dev converts a specific amount of source tokens to target tokens
              * can only be called by the bancor network contract
              *
              * @param _sourceToken source ERC20 token
              * @param _targetToken target ERC20 token
              * @param _amount      amount of tokens to convert (in units of the source token)
              * @param _trader      address of the caller who executed the conversion
              * @param _beneficiary wallet to receive the conversion result
              *
              * @return amount of tokens received (in units of the target token)
            */
            function convert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary)
                public
                payable
                protected
                only(BANCOR_NETWORK)
                returns (uint256)
            {
                // validate input
                require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET");
        
                // if a whitelist is set, verify that both and trader and the beneficiary are whitelisted
                require(conversionWhitelist == address(0) ||
                        (conversionWhitelist.isWhitelisted(_trader) && conversionWhitelist.isWhitelisted(_beneficiary)),
                        "ERR_NOT_WHITELISTED");
        
                return doConvert(_sourceToken, _targetToken, _amount, _trader, _beneficiary);
            }
        
            /**
              * @dev converts a specific amount of source tokens to target tokens
              * called by ConverterBase and allows the inherited contracts to implement custom conversion logic
              *
              * @param _sourceToken source ERC20 token
              * @param _targetToken target ERC20 token
              * @param _amount      amount of tokens to convert (in units of the source token)
              * @param _trader      address of the caller who executed the conversion
              * @param _beneficiary wallet to receive the conversion result
              *
              * @return amount of tokens received (in units of the target token)
            */
            function doConvert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary) internal returns (uint256);
        
            /**
              * @dev returns the conversion fee for a given return amount
              *
              * @param _amount  return amount
              *
              * @return conversion fee
            */
            function calculateFee(uint256 _amount) internal view returns (uint256) {
                return _amount.mul(conversionFee).div(CONVERSION_FEE_RESOLUTION);
            }
        
            /**
              * @dev syncs the stored reserve balance for a given reserve with the real reserve balance
              *
              * @param _reserveToken    address of the reserve token
            */
            function syncReserveBalance(IERC20Token _reserveToken) internal validReserve(_reserveToken) {
                if (_reserveToken == ETH_RESERVE_ADDRESS)
                    reserves[_reserveToken].balance = address(this).balance;
                else
                    reserves[_reserveToken].balance = _reserveToken.balanceOf(this);
            }
        
            /**
              * @dev syncs all stored reserve balances
            */
            function syncReserveBalances() internal {
                uint256 reserveCount = reserveTokens.length;
                for (uint256 i = 0; i < reserveCount; i++)
                    syncReserveBalance(reserveTokens[i]);
            }
        
            /**
              * @dev helper, dispatches the Conversion event
              *
              * @param _sourceToken     source ERC20 token
              * @param _targetToken     target ERC20 token
              * @param _trader          address of the caller who executed the conversion
              * @param _amount          amount purchased/sold (in the source token)
              * @param _returnAmount    amount returned (in the target token)
            */
            function dispatchConversionEvent(
                IERC20Token _sourceToken,
                IERC20Token _targetToken,
                address _trader,
                uint256 _amount,
                uint256 _returnAmount,
                uint256 _feeAmount)
                internal
            {
                // fee amount is converted to 255 bits -
                // negative amount means the fee is taken from the source token, positive amount means its taken from the target token
                // currently the fee is always taken from the target token
                // since we convert it to a signed number, we first ensure that it's capped at 255 bits to prevent overflow
                assert(_feeAmount < 2 ** 255);
                emit Conversion(_sourceToken, _targetToken, _trader, _amount, _returnAmount, int256(_feeAmount));
            }
        
            /**
              * @dev deprecated since version 28, backward compatibility - use only for earlier versions
            */
            function token() public view returns (IConverterAnchor) {
                return anchor;
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function transferTokenOwnership(address _newOwner) public ownerOnly {
                transferAnchorOwnership(_newOwner);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function acceptTokenOwnership() public ownerOnly {
                acceptAnchorOwnership();
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool) {
                Reserve memory reserve = reserves[_address];
                return(reserve.balance, reserve.weight, false, false, reserve.isSet);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function connectorTokens(uint256 _index) public view returns (IERC20Token) {
                return ConverterBase.reserveTokens[_index];
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function connectorTokenCount() public view returns (uint16) {
                return reserveTokenCount();
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256) {
                return reserveBalance(_connectorToken);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function getReturn(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount) public view returns (uint256, uint256) {
                return targetAmountAndFee(_sourceToken, _targetToken, _amount);
            }
        }
        
        // File: contracts/converter/LiquidityPoolConverter.sol
        
        pragma solidity 0.4.26;
        
        
        /**
          * @dev Liquidity Pool Converter
          *
          * The liquidity pool converter is the base contract for specific types of converters that
          * manage liquidity pools.
          *
          * Liquidity pools have 2 reserves or more and they allow converting between them.
          *
          * Note that TokenRateUpdate events are dispatched for pool tokens as well.
          * The pool token is the first token in the event in that case.
        */
        contract LiquidityPoolConverter is ConverterBase {
            /**
              * @dev triggered after liquidity is added
              *
              * @param  _provider       liquidity provider
              * @param  _reserveToken   reserve token address
              * @param  _amount         reserve token amount
              * @param  _newBalance     reserve token new balance
              * @param  _newSupply      pool token new supply
            */
            event LiquidityAdded(
                address indexed _provider,
                address indexed _reserveToken,
                uint256 _amount,
                uint256 _newBalance,
                uint256 _newSupply
            );
        
            /**
              * @dev triggered after liquidity is removed
              *
              * @param  _provider       liquidity provider
              * @param  _reserveToken   reserve token address
              * @param  _amount         reserve token amount
              * @param  _newBalance     reserve token new balance
              * @param  _newSupply      pool token new supply
            */
            event LiquidityRemoved(
                address indexed _provider,
                address indexed _reserveToken,
                uint256 _amount,
                uint256 _newBalance,
                uint256 _newSupply
            );
        
            /**
              * @dev initializes a new LiquidityPoolConverter instance
              *
              * @param  _anchor             anchor governed by the converter
              * @param  _registry           address of a contract registry contract
              * @param  _maxConversionFee   maximum conversion fee, represented in ppm
            */
            constructor(
                IConverterAnchor _anchor,
                IContractRegistry _registry,
                uint32 _maxConversionFee
            )
                ConverterBase(_anchor, _registry, _maxConversionFee)
                internal
            {
            }
        
            /**
              * @dev accepts ownership of the anchor after an ownership transfer
              * also activates the converter
              * can only be called by the contract owner
              * note that prior to version 28, you should use 'acceptTokenOwnership' instead
            */
            function acceptTokenOwnership() public {
                // verify that the converter has at least 2 reserves
                require(reserveTokenCount() > 1, "ERR_INVALID_RESERVE_COUNT");
                super.acceptTokenOwnership();
            }
        }
        
        // File: contracts/converter/interfaces/ITypedConverterFactory.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /*
            Typed Converter Factory interface
        */
        contract ITypedConverterFactory {
            function converterType() public pure returns (uint16);
            function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) public returns (IConverter);
        }
        
        // File: contracts/token/interfaces/ISmartToken.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /*
            Smart Token interface
        */
        contract ISmartToken is IConverterAnchor, IERC20Token {
            function disableTransfers(bool _disable) public;
            function issue(address _to, uint256 _amount) public;
            function destroy(address _from, uint256 _amount) public;
        }
        
        // File: contracts/converter/LiquidityPoolV1Converter.sol
        
        pragma solidity 0.4.26;
        
        
        
        
        /*
            LiquidityPoolV1Converter Factory
        */
        contract LiquidityPoolV1ConverterFactory is ITypedConverterFactory {
            /**
              * @dev returns the converter type the factory is associated with
              *
              * @return converter type
            */
            function converterType() public pure returns (uint16) {
                return 1;
            }
        
            /**
              * @dev creates a new converter with the given arguments and transfers
              * the ownership to the caller
              *
              * @param _anchor            anchor governed by the converter
              * @param _registry          address of a contract registry contract
              * @param _maxConversionFee  maximum conversion fee, represented in ppm
              *
              * @return a new converter
            */
            function createConverter(IConverterAnchor _anchor, IContractRegistry _registry, uint32 _maxConversionFee) public returns (IConverter) {
                ConverterBase converter = new LiquidityPoolV1Converter(ISmartToken(_anchor), _registry, _maxConversionFee);
                converter.transferOwnership(msg.sender);
                return converter;
            }
        }
        
        /**
          * @dev Liquidity Pool v1 Converter
          *
          * The liquidity pool v1 converter is a specialized version of a converter that manages
          * a classic bancor liquidity pool.
          *
          * Even though classic pools can have many reserves, the most common configuration of
          * the pool has 2 reserves with 50%/50% weights.
        */
        contract LiquidityPoolV1Converter is LiquidityPoolConverter {
            IEtherToken internal etherToken = IEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315);
        
            /**
              * @dev triggered after a conversion with new price data
              * deprecated, use `TokenRateUpdate` from version 28 and up
              *
              * @param  _connectorToken     reserve token
              * @param  _tokenSupply        smart token supply
              * @param  _connectorBalance   reserve balance
              * @param  _connectorWeight    reserve weight
            */
            event PriceDataUpdate(
                address indexed _connectorToken,
                uint256 _tokenSupply,
                uint256 _connectorBalance,
                uint32 _connectorWeight
            );
        
            /**
              * @dev initializes a new LiquidityPoolV1Converter instance
              *
              * @param  _token              pool token governed by the converter
              * @param  _registry           address of a contract registry contract
              * @param  _maxConversionFee   maximum conversion fee, represented in ppm
            */
            constructor(
                ISmartToken _token,
                IContractRegistry _registry,
                uint32 _maxConversionFee
            )
                LiquidityPoolConverter(_token, _registry, _maxConversionFee)
                public
            {
            }
        
            /**
              * @dev returns the converter type
              *
              * @return see the converter types in the the main contract doc
            */
            function converterType() public pure returns (uint16) {
                return 1;
            }
        
            /**
              * @dev accepts ownership of the anchor after an ownership transfer
              * also activates the converter
              * can only be called by the contract owner
              * note that prior to version 28, you should use 'acceptTokenOwnership' instead
            */
            function acceptAnchorOwnership() public ownerOnly {
                super.acceptAnchorOwnership();
        
                emit Activation(anchor, true);
            }
        
            /**
              * @dev returns the expected target amount of converting one reserve to another along with the fee
              *
              * @param _sourceToken contract address of the source reserve token
              * @param _targetToken contract address of the target reserve token
              * @param _amount      amount of tokens received from the user
              *
              * @return expected target amount
              * @return expected fee
            */
            function targetAmountAndFee(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount)
                public
                view
                active
                validReserve(_sourceToken)
                validReserve(_targetToken)
                returns (uint256, uint256)
            {
                // validate input
                require(_sourceToken != _targetToken, "ERR_SAME_SOURCE_TARGET");
        
                uint256 amount = IBancorFormula(addressOf(BANCOR_FORMULA)).crossReserveTargetAmount(
                    reserveBalance(_sourceToken),
                    reserves[_sourceToken].weight,
                    reserveBalance(_targetToken),
                    reserves[_targetToken].weight,
                    _amount
                );
        
                // return the amount minus the conversion fee and the conversion fee
                uint256 fee = calculateFee(amount);
                return (amount - fee, fee);
            }
        
            /**
              * @dev converts a specific amount of source tokens to target tokens
              * can only be called by the bancor network contract
              *
              * @param _sourceToken source ERC20 token
              * @param _targetToken target ERC20 token
              * @param _amount      amount of tokens to convert (in units of the source token)
              * @param _trader      address of the caller who executed the conversion
              * @param _beneficiary wallet to receive the conversion result
              *
              * @return amount of tokens received (in units of the target token)
            */
            function doConvert(IERC20Token _sourceToken, IERC20Token _targetToken, uint256 _amount, address _trader, address _beneficiary)
                internal
                returns (uint256)
            {
                // get expected target amount and fee
                (uint256 amount, uint256 fee) = targetAmountAndFee(_sourceToken, _targetToken, _amount);
        
                // ensure that the trade gives something in return
                require(amount != 0, "ERR_ZERO_TARGET_AMOUNT");
        
                // ensure that the trade won't deplete the reserve balance
                uint256 targetReserveBalance = reserveBalance(_targetToken);
                assert(amount < targetReserveBalance);
        
                // ensure that the input amount was already deposited
                if (_sourceToken == ETH_RESERVE_ADDRESS)
                    require(msg.value == _amount, "ERR_ETH_AMOUNT_MISMATCH");
                else
                    require(msg.value == 0 && _sourceToken.balanceOf(this).sub(reserveBalance(_sourceToken)) >= _amount, "ERR_INVALID_AMOUNT");
        
                // sync the reserve balances
                syncReserveBalance(_sourceToken);
                reserves[_targetToken].balance = reserves[_targetToken].balance.sub(amount);
        
                // transfer funds to the beneficiary in the to reserve token
                if (_targetToken == ETH_RESERVE_ADDRESS)
                    _beneficiary.transfer(amount);
                else
                    safeTransfer(_targetToken, _beneficiary, amount);
        
                // dispatch the conversion event
                dispatchConversionEvent(_sourceToken, _targetToken, _trader, _amount, amount, fee);
        
                // dispatch rate updates
                dispatchRateEvents(_sourceToken, _targetToken);
        
                return amount;
            }
        
            /**
              * @dev increases the pool's liquidity and mints new shares in the pool to the caller
              * note that prior to version 28, you should use 'fund' instead
              *
              * @param _reserveTokens   address of each reserve token
              * @param _reserveAmounts  amount of each reserve token
              * @param _minReturn       token minimum return-amount
            */
            function addLiquidity(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _minReturn)
                public
                payable
                protected
                active
            {
                // verify the user input
                verifyLiquidityInput(_reserveTokens, _reserveAmounts, _minReturn);
        
                // if one of the reserves is ETH, then verify that the input amount of ETH is equal to the input value of ETH
                for (uint256 i = 0; i < _reserveTokens.length; i++)
                    if (_reserveTokens[i] == ETH_RESERVE_ADDRESS)
                        require(_reserveAmounts[i] == msg.value, "ERR_ETH_AMOUNT_MISMATCH");
        
                // if the input value of ETH is larger than zero, then verify that one of the reserves is ETH
                if (msg.value > 0)
                    require(reserves[ETH_RESERVE_ADDRESS].isSet, "ERR_NO_ETH_RESERVE");
        
                // get the total supply
                uint256 totalSupply = ISmartToken(anchor).totalSupply();
        
                // transfer from the user an equally-worth amount of each one of the reserve tokens
                uint256 amount = addLiquidityToPool(_reserveTokens, _reserveAmounts, totalSupply);
        
                // verify that the equivalent amount of tokens is equal to or larger than the user's expectation
                require(amount >= _minReturn, "ERR_RETURN_TOO_LOW");
        
                // issue the tokens to the user
                ISmartToken(anchor).issue(msg.sender, amount);
            }
        
            /**
              * @dev decreases the pool's liquidity and burns the caller's shares in the pool
              * note that prior to version 28, you should use 'liquidate' instead
              *
              * @param _amount                  token amount
              * @param _reserveTokens           address of each reserve token
              * @param _reserveMinReturnAmounts minimum return-amount of each reserve token
            */
            function removeLiquidity(uint256 _amount, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts)
                public
                protected
                active
            {
                // verify the user input
                verifyLiquidityInput(_reserveTokens, _reserveMinReturnAmounts, _amount);
        
                // get the total supply BEFORE destroying the user tokens
                uint256 totalSupply = ISmartToken(anchor).totalSupply();
        
                // destroy the user tokens
                ISmartToken(anchor).destroy(msg.sender, _amount);
        
                // transfer to the user an equivalent amount of each one of the reserve tokens
                removeLiquidityFromPool(_reserveTokens, _reserveMinReturnAmounts, totalSupply, _amount);
            }
        
            /**
              * @dev increases the pool's liquidity and mints new shares in the pool to the caller
              * for example, if the caller increases the supply by 10%,
              * then it will cost an amount equal to 10% of each reserve token balance
              * note that starting from version 28, you should use 'addLiquidity' instead
              *
              * @param _amount  amount to increase the supply by (in the pool token)
            */
            function fund(uint256 _amount) public payable protected {
                syncReserveBalances();
                reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value);
        
                uint256 supply = ISmartToken(anchor).totalSupply();
                IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
        
                // iterate through the reserve tokens and transfer a percentage equal to the weight between
                // _amount and the total supply in each reserve from the caller to the converter
                uint256 reserveCount = reserveTokens.length;
                for (uint256 i = 0; i < reserveCount; i++) {
                    IERC20Token reserveToken = reserveTokens[i];
                    uint256 rsvBalance = reserves[reserveToken].balance;
                    uint256 reserveAmount = formula.fundCost(supply, rsvBalance, reserveRatio, _amount);
        
                    // transfer funds from the caller in the reserve token
                    if (reserveToken == ETH_RESERVE_ADDRESS) {
                        if (msg.value > reserveAmount) {
                            msg.sender.transfer(msg.value - reserveAmount);
                        }
                        else if (msg.value < reserveAmount) {
                            require(msg.value == 0, "ERR_INVALID_ETH_VALUE");
                            safeTransferFrom(etherToken, msg.sender, this, reserveAmount);
                            etherToken.withdraw(reserveAmount);
                        }
                    }
                    else {
                        safeTransferFrom(reserveToken, msg.sender, this, reserveAmount);
                    }
        
                    // sync the reserve balance
                    uint256 newReserveBalance = rsvBalance.add(reserveAmount);
                    reserves[reserveToken].balance = newReserveBalance;
        
                    uint256 newPoolTokenSupply = supply.add(_amount);
        
                    // dispatch liquidity update for the pool token/reserve
                    emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
        
                    // dispatch the `TokenRateUpdate` event for the pool token
                    uint32 reserveWeight = reserves[reserveToken].weight;
                    dispatchPoolTokenRateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserveWeight);
                }
        
                // issue new funds to the caller in the pool token
                ISmartToken(anchor).issue(msg.sender, _amount);
            }
        
            /**
              * @dev decreases the pool's liquidity and burns the caller's shares in the pool
              * for example, if the holder sells 10% of the supply,
              * then they will receive 10% of each reserve token balance in return
              * note that starting from version 28, you should use 'removeLiquidity' instead
              *
              * @param _amount  amount to liquidate (in the pool token)
            */
            function liquidate(uint256 _amount) public protected {
                require(_amount > 0, "ERR_ZERO_AMOUNT");
        
                uint256 totalSupply = ISmartToken(anchor).totalSupply();
                ISmartToken(anchor).destroy(msg.sender, _amount);
        
                uint256[] memory reserveMinReturnAmounts = new uint256[](reserveTokens.length);
                for (uint256 i = 0; i < reserveMinReturnAmounts.length; i++)
                    reserveMinReturnAmounts[i] = 1;
        
                removeLiquidityFromPool(reserveTokens, reserveMinReturnAmounts, totalSupply, _amount);
            }
        
            /**
              * @dev verifies that a given array of tokens is identical to the converter's array of reserve tokens
              * we take this input in order to allow specifying the corresponding reserve amounts in any order
              *
              * @param _reserveTokens   array of reserve tokens
              * @param _reserveAmounts  array of reserve amounts
              * @param _amount          token amount
            */
            function verifyLiquidityInput(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _amount) private view {
                uint256 i;
                uint256 j;
        
                uint256 length = reserveTokens.length;
                require(length == _reserveTokens.length, "ERR_INVALID_RESERVE");
                require(length == _reserveAmounts.length, "ERR_INVALID_AMOUNT");
        
                for (i = 0; i < length; i++) {
                    // verify that every input reserve token is included in the reserve tokens
                    require(reserves[_reserveTokens[i]].isSet, "ERR_INVALID_RESERVE");
                    for (j = 0; j < length; j++) {
                        if (reserveTokens[i] == _reserveTokens[j])
                            break;
                    }
                    // verify that every reserve token is included in the input reserve tokens
                    require(j < length, "ERR_INVALID_RESERVE");
                    // verify that every input reserve token amount is larger than zero
                    require(_reserveAmounts[i] > 0, "ERR_INVALID_AMOUNT");
                }
        
                // verify that the input token amount is larger than zero
                require(_amount > 0, "ERR_ZERO_AMOUNT");
            }
        
            /**
              * @dev adds liquidity (reserve) to the pool
              *
              * @param _reserveTokens   address of each reserve token
              * @param _reserveAmounts  amount of each reserve token
              * @param _totalSupply     token total supply
            */
            function addLiquidityToPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply)
                private
                returns (uint256)
            {
                if (_totalSupply == 0)
                    return addLiquidityToEmptyPool(_reserveTokens, _reserveAmounts);
                return addLiquidityToNonEmptyPool(_reserveTokens, _reserveAmounts, _totalSupply);
            }
        
            /**
              * @dev adds liquidity (reserve) to the pool when it's empty
              *
              * @param _reserveTokens   address of each reserve token
              * @param _reserveAmounts  amount of each reserve token
            */
            function addLiquidityToEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts)
                private
                returns (uint256)
            {
                // calculate the geometric-mean of the reserve amounts approved by the user
                uint256 amount = geometricMean(_reserveAmounts);
        
                // transfer each one of the reserve amounts from the user to the pool
                for (uint256 i = 0; i < _reserveTokens.length; i++) {
                    if (_reserveTokens[i] != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction
                        safeTransferFrom(_reserveTokens[i], msg.sender, this, _reserveAmounts[i]);
        
                    reserves[_reserveTokens[i]].balance = _reserveAmounts[i];
        
                    emit LiquidityAdded(msg.sender, _reserveTokens[i], _reserveAmounts[i], _reserveAmounts[i], amount);
        
                    // dispatch the `TokenRateUpdate` event for the pool token
                    uint32 reserveWeight = reserves[_reserveTokens[i]].weight;
                    dispatchPoolTokenRateEvent(amount, _reserveTokens[i], _reserveAmounts[i], reserveWeight);
                }
        
                return amount;
            }
        
            /**
              * @dev adds liquidity (reserve) to the pool when it's not empty
              *
              * @param _reserveTokens   address of each reserve token
              * @param _reserveAmounts  amount of each reserve token
              * @param _totalSupply     token total supply
            */
            function addLiquidityToNonEmptyPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts, uint256 _totalSupply)
                private
                returns (uint256)
            {
                syncReserveBalances();
                reserves[ETH_RESERVE_ADDRESS].balance = reserves[ETH_RESERVE_ADDRESS].balance.sub(msg.value);
        
                IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
                uint256 amount = getMinShare(formula, _totalSupply, _reserveTokens, _reserveAmounts);
                uint256 newPoolTokenSupply = _totalSupply.add(amount);
        
                for (uint256 i = 0; i < _reserveTokens.length; i++) {
                    IERC20Token reserveToken = _reserveTokens[i];
                    uint256 rsvBalance = reserves[reserveToken].balance;
                    uint256 reserveAmount = formula.fundCost(_totalSupply, rsvBalance, reserveRatio, amount);
                    require(reserveAmount > 0, "ERR_ZERO_TARGET_AMOUNT");
                    assert(reserveAmount <= _reserveAmounts[i]);
        
                    // transfer each one of the reserve amounts from the user to the pool
                    if (reserveToken != ETH_RESERVE_ADDRESS) // ETH has already been transferred as part of the transaction
                        safeTransferFrom(reserveToken, msg.sender, this, reserveAmount);
                    else if (_reserveAmounts[i] > reserveAmount) // transfer the extra amount of ETH back to the user
                        msg.sender.transfer(_reserveAmounts[i] - reserveAmount);
        
                    uint256 newReserveBalance = rsvBalance.add(reserveAmount);
                    reserves[reserveToken].balance = newReserveBalance;
        
                    emit LiquidityAdded(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
        
                    // dispatch the `TokenRateUpdate` event for the pool token
                    uint32 reserveWeight = reserves[_reserveTokens[i]].weight;
                    dispatchPoolTokenRateEvent(newPoolTokenSupply, _reserveTokens[i], newReserveBalance, reserveWeight);
                }
        
                return amount;
            }
        
            /**
              * @dev removes liquidity (reserve) from the pool
              *
              * @param _reserveTokens           address of each reserve token
              * @param _reserveMinReturnAmounts minimum return-amount of each reserve token
              * @param _totalSupply             token total supply
              * @param _amount                  token amount
            */
            function removeLiquidityFromPool(IERC20Token[] memory _reserveTokens, uint256[] memory _reserveMinReturnAmounts, uint256 _totalSupply, uint256 _amount)
                private
            {
                syncReserveBalances();
        
                IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA));
                uint256 newPoolTokenSupply = _totalSupply.sub(_amount);
        
                for (uint256 i = 0; i < _reserveTokens.length; i++) {
                    IERC20Token reserveToken = _reserveTokens[i];
                    uint256 rsvBalance = reserves[reserveToken].balance;
                    uint256 reserveAmount = formula.liquidateReserveAmount(_totalSupply, rsvBalance, reserveRatio, _amount);
                    require(reserveAmount >= _reserveMinReturnAmounts[i], "ERR_ZERO_TARGET_AMOUNT");
        
                    uint256 newReserveBalance = rsvBalance.sub(reserveAmount);
                    reserves[reserveToken].balance = newReserveBalance;
        
                    // transfer each one of the reserve amounts from the pool to the user
                    if (reserveToken == ETH_RESERVE_ADDRESS)
                        msg.sender.transfer(reserveAmount);
                    else
                        safeTransfer(reserveToken, msg.sender, reserveAmount);
        
                    emit LiquidityRemoved(msg.sender, reserveToken, reserveAmount, newReserveBalance, newPoolTokenSupply);
        
                    // dispatch the `TokenRateUpdate` event for the pool token
                    uint32 reserveWeight = reserves[reserveToken].weight;
                    dispatchPoolTokenRateEvent(newPoolTokenSupply, reserveToken, newReserveBalance, reserveWeight);
                }
            }
        
            function getMinShare(IBancorFormula formula, uint256 _totalSupply, IERC20Token[] memory _reserveTokens, uint256[] memory _reserveAmounts) private view returns (uint256) {
                uint256 minIndex = 0;
                for (uint256 i = 1; i < _reserveTokens.length; i++) {
                    if (_reserveAmounts[i].mul(reserves[_reserveTokens[minIndex]].balance) < _reserveAmounts[minIndex].mul(reserves[_reserveTokens[i]].balance))
                        minIndex = i;
                }
                return formula.fundSupplyAmount(_totalSupply, reserves[_reserveTokens[minIndex]].balance, reserveRatio, _reserveAmounts[minIndex]);
            }
        
            /**
              * @dev calculates the number of decimal digits in a given value
              *
              * @param _x   value (assumed positive)
              * @return the number of decimal digits in the given value
            */
            function decimalLength(uint256 _x) public pure returns (uint256) {
                uint256 y = 0;
                for (uint256 x = _x; x > 0; x /= 10)
                    y++;
                return y;
            }
        
            /**
              * @dev calculates the nearest integer to a given quotient
              *
              * @param _n   quotient numerator
              * @param _d   quotient denominator
              * @return the nearest integer to the given quotient
            */
            function roundDiv(uint256 _n, uint256 _d) public pure returns (uint256) {
                return (_n + _d / 2) / _d;
            }
        
            /**
              * @dev calculates the average number of decimal digits in a given list of values
              *
              * @param _values  list of values (each of which assumed positive)
              * @return the average number of decimal digits in the given list of values
            */
            function geometricMean(uint256[] memory _values) public pure returns (uint256) {
                uint256 numOfDigits = 0;
                uint256 length = _values.length;
                for (uint256 i = 0; i < length; i++)
                    numOfDigits += decimalLength(_values[i]);
                return uint256(10) ** (roundDiv(numOfDigits, length) - 1);
            }
        
             /**
              * @dev dispatches rate events for both reserves / pool tokens
              * only used to circumvent the `stack too deep` compiler error
              *
              * @param _sourceToken address of the source reserve token
              * @param _targetToken address of the target reserve token
            */
            function dispatchRateEvents(IERC20Token _sourceToken, IERC20Token _targetToken) private {
                uint256 poolTokenSupply = ISmartToken(anchor).totalSupply();
                uint256 sourceReserveBalance = reserveBalance(_sourceToken);
                uint256 targetReserveBalance = reserveBalance(_targetToken);
                uint32 sourceReserveWeight = reserves[_sourceToken].weight;
                uint32 targetReserveWeight = reserves[_targetToken].weight;
        
                // dispatch token rate update event
                uint256 rateN = targetReserveBalance.mul(sourceReserveWeight);
                uint256 rateD = sourceReserveBalance.mul(targetReserveWeight);
                emit TokenRateUpdate(_sourceToken, _targetToken, rateN, rateD);
        
                // dispatch the `TokenRateUpdate` event for the pool token
                dispatchPoolTokenRateEvent(poolTokenSupply, _sourceToken, sourceReserveBalance, sourceReserveWeight);
                dispatchPoolTokenRateEvent(poolTokenSupply, _targetToken, targetReserveBalance, targetReserveWeight);
        
                // dispatch price data update events (deprecated events)
                emit PriceDataUpdate(_sourceToken, poolTokenSupply, sourceReserveBalance, sourceReserveWeight);
                emit PriceDataUpdate(_targetToken, poolTokenSupply, targetReserveBalance, targetReserveWeight);
            }
        
            /**
              * @dev dispatches the `TokenRateUpdate` for the pool token
              * only used to circumvent the `stack too deep` compiler error
              *
              * @param _poolTokenSupply total pool token supply
              * @param _reserveToken    address of the reserve token
              * @param _reserveBalance  reserve balance
              * @param _reserveWeight   reserve weight
            */
            function dispatchPoolTokenRateEvent(uint256 _poolTokenSupply, IERC20Token _reserveToken, uint256 _reserveBalance, uint32 _reserveWeight) private {
                emit TokenRateUpdate(anchor, _reserveToken, _reserveBalance.mul(WEIGHT_RESOLUTION), _poolTokenSupply.mul(_reserveWeight));
            }
        }

        File 4 of 8: SmartToken
        pragma solidity ^0.4.11;
        
        /*
            Overflow protected math functions
        */
        contract SafeMath {
            /**
                constructor
            */
            function SafeMath() {
            }
        
            /**
                @dev returns the sum of _x and _y, asserts if the calculation overflows
        
                @param _x   value 1
                @param _y   value 2
        
                @return sum
            */
            function safeAdd(uint256 _x, uint256 _y) internal returns (uint256) {
                uint256 z = _x + _y;
                assert(z >= _x);
                return z;
            }
        
            /**
                @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
        
                @param _x   minuend
                @param _y   subtrahend
        
                @return difference
            */
            function safeSub(uint256 _x, uint256 _y) internal returns (uint256) {
                assert(_x >= _y);
                return _x - _y;
            }
        
            /**
                @dev returns the product of multiplying _x by _y, asserts if the calculation overflows
        
                @param _x   factor 1
                @param _y   factor 2
        
                @return product
            */
            function safeMul(uint256 _x, uint256 _y) internal returns (uint256) {
                uint256 z = _x * _y;
                assert(_x == 0 || z / _x == _y);
                return z;
            }
        } 
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public constant returns (address owner) { owner; }
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        /*
            Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            event OwnerUpdate(address _prevOwner, address _newOwner);
        
            /**
                @dev constructor
            */
            function Owned() {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                assert(msg.sender == owner);
                _;
            }
        
            /**
                @dev allows transferring the contract ownership
                the new owner still need to accept the transfer
                can only be called by the contract owner
        
                @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner);
                newOwner = _newOwner;
            }
        
            /**
                @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner);
                OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = 0x0;
            }
        }
        
        /*
            Token Holder interface
        */
        contract ITokenHolder is IOwned {
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
        }
        
        /*
            We consider every contract to be a 'token holder' since it's currently not possible
            for a contract to deny receiving tokens.
        
            The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
            the owner to send tokens that were sent to the contract by mistake back to their sender.
        */
        contract TokenHolder is ITokenHolder, Owned {
            /**
                @dev constructor
            */
            function TokenHolder() {
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                require(_address != 0x0);
                _;
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                require(_address != address(this));
                _;
            }
        
            /**
                @dev withdraws tokens held by the contract and sends them to an account
                can only be called by the owner
        
                @param _token   ERC20 token contract address
                @param _to      account to receive the new amount
                @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_token)
                validAddress(_to)
                notThis(_to)
            {
                assert(_token.transfer(_to, _amount));
            }
        }
        
        /*
            ERC20 Standard Token interface
        */
        contract IERC20Token {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public constant returns (string name) { name; }
            function symbol() public constant returns (string symbol) { symbol; }
            function decimals() public constant returns (uint8 decimals) { decimals; }
            function totalSupply() public constant returns (uint256 totalSupply) { totalSupply; }
            function balanceOf(address _owner) public constant returns (uint256 balance) { _owner; balance; }
            function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { _owner; _spender; remaining; }
        
            function transfer(address _to, uint256 _value) public returns (bool success);
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
            function approve(address _spender, uint256 _value) public returns (bool success);
        }
        
        /**
            ERC20 Standard Token implementation
        */
        contract ERC20Token is IERC20Token, SafeMath {
            string public standard = 'Token 0.1';
            string public name = '';
            string public symbol = '';
            uint8 public decimals = 0;
            uint256 public totalSupply = 0;
            mapping (address => uint256) public balanceOf;
            mapping (address => mapping (address => uint256)) public allowance;
        
            event Transfer(address indexed _from, address indexed _to, uint256 _value);
            event Approval(address indexed _owner, address indexed _spender, uint256 _value);
        
            /**
                @dev constructor
        
                @param _name        token name
                @param _symbol      token symbol
                @param _decimals    decimal points, for display purposes
            */
            function ERC20Token(string _name, string _symbol, uint8 _decimals) {
                require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
        
                name = _name;
                symbol = _symbol;
                decimals = _decimals;
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                require(_address != 0x0);
                _;
            }
        
            /**
                @dev send coins
                throws on any error rather then return a false flag to minimize user errors
        
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value)
                public
                validAddress(_to)
                returns (bool success)
            {
                balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value);
                balanceOf[_to] = safeAdd(balanceOf[_to], _value);
                Transfer(msg.sender, _to, _value);
                return true;
            }
        
            /**
                @dev an account/contract attempts to get the coins
                throws on any error rather then return a false flag to minimize user errors
        
                @param _from    source address
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value)
                public
                validAddress(_from)
                validAddress(_to)
                returns (bool success)
            {
                allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value);
                balanceOf[_from] = safeSub(balanceOf[_from], _value);
                balanceOf[_to] = safeAdd(balanceOf[_to], _value);
                Transfer(_from, _to, _value);
                return true;
            }
        
            /**
                @dev allow another account/contract to spend some tokens on your behalf
                throws on any error rather then return a false flag to minimize user errors
        
                also, to minimize the risk of the approve/transferFrom attack vector
                (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
                in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
        
                @param _spender approved address
                @param _value   allowance amount
        
                @return true if the approval was successful, false if it wasn't
            */
            function approve(address _spender, uint256 _value)
                public
                validAddress(_spender)
                returns (bool success)
            {
                // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
                require(_value == 0 || allowance[msg.sender][_spender] == 0);
        
                allowance[msg.sender][_spender] = _value;
                Approval(msg.sender, _spender, _value);
                return true;
            }
        }
        
        /*
            Smart Token interface
        */
        contract ISmartToken is ITokenHolder, IERC20Token {
            function disableTransfers(bool _disable) public;
            function issue(address _to, uint256 _amount) public;
            function destroy(address _from, uint256 _amount) public;
        }
        
        /*
            Smart Token v0.2
        
            'Owned' is specified here for readability reasons
        */
        contract SmartToken is ISmartToken, ERC20Token, Owned, TokenHolder {
            string public version = '0.2';
        
            bool public transfersEnabled = true;    // true if transfer/transferFrom are enabled, false if not
        
            // triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory
            event NewSmartToken(address _token);
            // triggered when the total supply is increased
            event Issuance(uint256 _amount);
            // triggered when the total supply is decreased
            event Destruction(uint256 _amount);
        
            /**
                @dev constructor
        
                @param _name       token name
                @param _symbol     token short symbol, 1-6 characters
                @param _decimals   for display purposes only
            */
            function SmartToken(string _name, string _symbol, uint8 _decimals)
                ERC20Token(_name, _symbol, _decimals)
            {
                require(bytes(_symbol).length <= 6); // validate input
                NewSmartToken(address(this));
            }
        
            // allows execution only when transfers aren't disabled
            modifier transfersAllowed {
                assert(transfersEnabled);
                _;
            }
        
            /**
                @dev disables/enables transfers
                can only be called by the contract owner
        
                @param _disable    true to disable transfers, false to enable them
            */
            function disableTransfers(bool _disable) public ownerOnly {
                transfersEnabled = !_disable;
            }
        
            /**
                @dev increases the token supply and sends the new tokens to an account
                can only be called by the contract owner
        
                @param _to         account to receive the new amount
                @param _amount     amount to increase the supply by
            */
            function issue(address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_to)
                notThis(_to)
            {
                totalSupply = safeAdd(totalSupply, _amount);
                balanceOf[_to] = safeAdd(balanceOf[_to], _amount);
        
                Issuance(_amount);
                Transfer(this, _to, _amount);
            }
        
            /**
                @dev removes tokens from an account and decreases the token supply
                can only be called by the contract owner
        
                @param _from       account to remove the amount from
                @param _amount     amount to decrease the supply by
            */
            function destroy(address _from, uint256 _amount)
                public
                ownerOnly
            {
                balanceOf[_from] = safeSub(balanceOf[_from], _amount);
                totalSupply = safeSub(totalSupply, _amount);
        
                Transfer(_from, this, _amount);
                Destruction(_amount);
            }
        
            // ERC20 standard method overrides with some extra functionality
        
            /**
                @dev send coins
                throws on any error rather then return a false flag to minimize user errors
                note that when transferring to the smart token's address, the coins are actually destroyed
        
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transfer(_to, _value));
        
                // transferring to the contract address destroys tokens
                if (_to == address(this)) {
                    balanceOf[_to] -= _value;
                    totalSupply -= _value;
                    Destruction(_value);
                }
        
                return true;
            }
        
            /**
                @dev an account/contract attempts to get the coins
                throws on any error rather then return a false flag to minimize user errors
                note that when transferring to the smart token's address, the coins are actually destroyed
        
                @param _from    source address
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transferFrom(_from, _to, _value));
        
                // transferring to the contract address destroys tokens
                if (_to == address(this)) {
                    balanceOf[_to] -= _value;
                    totalSupply -= _value;
                    Destruction(_value);
                }
        
                return true;
            }
        }

        File 5 of 8: SmartToken
        pragma solidity ^0.4.11;
        
        /*
            Utilities & Common Modifiers
        */
        contract Utils {
            /**
                constructor
            */
            function Utils() {
            }
        
            // verifies that an amount is greater than zero
            modifier greaterThanZero(uint256 _amount) {
                require(_amount > 0);
                _;
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                require(_address != 0x0);
                _;
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                require(_address != address(this));
                _;
            }
        
            // Overflow protected math functions
        
            /**
                @dev returns the sum of _x and _y, asserts if the calculation overflows
        
                @param _x   value 1
                @param _y   value 2
        
                @return sum
            */
            function safeAdd(uint256 _x, uint256 _y) internal constant returns (uint256) {
                uint256 z = _x + _y;
                assert(z >= _x);
                return z;
            }
        
            /**
                @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
        
                @param _x   minuend
                @param _y   subtrahend
        
                @return difference
            */
            function safeSub(uint256 _x, uint256 _y) internal constant returns (uint256) {
                assert(_x >= _y);
                return _x - _y;
            }
        
            /**
                @dev returns the product of multiplying _x by _y, asserts if the calculation overflows
        
                @param _x   factor 1
                @param _y   factor 2
        
                @return product
            */
            function safeMul(uint256 _x, uint256 _y) internal constant returns (uint256) {
                uint256 z = _x * _y;
                assert(_x == 0 || z / _x == _y);
                return z;
            }
        }
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public constant returns (address) {}
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        /*
            Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            event OwnerUpdate(address _prevOwner, address _newOwner);
        
            /**
                @dev constructor
            */
            function Owned() {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                assert(msg.sender == owner);
                _;
            }
        
            /**
                @dev allows transferring the contract ownership
                the new owner still needs to accept the transfer
                can only be called by the contract owner
        
                @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner);
                newOwner = _newOwner;
            }
        
            /**
                @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner);
                OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = 0x0;
            }
        }
        
        /*
            ERC20 Standard Token interface
        */
        contract IERC20Token {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public constant returns (string) {}
            function symbol() public constant returns (string) {}
            function decimals() public constant returns (uint8) {}
            function totalSupply() public constant returns (uint256) {}
            function balanceOf(address _owner) public constant returns (uint256) { _owner; }
            function allowance(address _owner, address _spender) public constant returns (uint256) { _owner; _spender; }
        
            function transfer(address _to, uint256 _value) public returns (bool success);
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
            function approve(address _spender, uint256 _value) public returns (bool success);
        }
        
        /**
            ERC20 Standard Token implementation
        */
        contract ERC20Token is IERC20Token, Utils {
            string public standard = 'Token 0.1';
            string public name = '';
            string public symbol = '';
            uint8 public decimals = 0;
            uint256 public totalSupply = 0;
            mapping (address => uint256) public balanceOf;
            mapping (address => mapping (address => uint256)) public allowance;
        
            event Transfer(address indexed _from, address indexed _to, uint256 _value);
            event Approval(address indexed _owner, address indexed _spender, uint256 _value);
        
            /**
                @dev constructor
        
                @param _name        token name
                @param _symbol      token symbol
                @param _decimals    decimal points, for display purposes
            */
            function ERC20Token(string _name, string _symbol, uint8 _decimals) {
                require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
        
                name = _name;
                symbol = _symbol;
                decimals = _decimals;
            }
        
            /**
                @dev send coins
                throws on any error rather then return a false flag to minimize user errors
        
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value)
                public
                validAddress(_to)
                returns (bool success)
            {
                balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], _value);
                balanceOf[_to] = safeAdd(balanceOf[_to], _value);
                Transfer(msg.sender, _to, _value);
                return true;
            }
        
            /**
                @dev an account/contract attempts to get the coins
                throws on any error rather then return a false flag to minimize user errors
        
                @param _from    source address
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value)
                public
                validAddress(_from)
                validAddress(_to)
                returns (bool success)
            {
                allowance[_from][msg.sender] = safeSub(allowance[_from][msg.sender], _value);
                balanceOf[_from] = safeSub(balanceOf[_from], _value);
                balanceOf[_to] = safeAdd(balanceOf[_to], _value);
                Transfer(_from, _to, _value);
                return true;
            }
        
            /**
                @dev allow another account/contract to spend some tokens on your behalf
                throws on any error rather then return a false flag to minimize user errors
        
                also, to minimize the risk of the approve/transferFrom attack vector
                (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
                in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
        
                @param _spender approved address
                @param _value   allowance amount
        
                @return true if the approval was successful, false if it wasn't
            */
            function approve(address _spender, uint256 _value)
                public
                validAddress(_spender)
                returns (bool success)
            {
                // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
                require(_value == 0 || allowance[msg.sender][_spender] == 0);
        
                allowance[msg.sender][_spender] = _value;
                Approval(msg.sender, _spender, _value);
                return true;
            }
        }
        
        /*
            Token Holder interface
        */
        contract ITokenHolder is IOwned {
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
        }
        
        /*
            We consider every contract to be a 'token holder' since it's currently not possible
            for a contract to deny receiving tokens.
        
            The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
            the owner to send tokens that were sent to the contract by mistake back to their sender.
        */
        contract TokenHolder is ITokenHolder, Owned, Utils {
            /**
                @dev constructor
            */
            function TokenHolder() {
            }
        
            /**
                @dev withdraws tokens held by the contract and sends them to an account
                can only be called by the owner
        
                @param _token   ERC20 token contract address
                @param _to      account to receive the new amount
                @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_token)
                validAddress(_to)
                notThis(_to)
            {
                assert(_token.transfer(_to, _amount));
            }
        }
        
        /*
            Smart Token interface
        */
        contract ISmartToken is IOwned, IERC20Token {
            function disableTransfers(bool _disable) public;
            function issue(address _to, uint256 _amount) public;
            function destroy(address _from, uint256 _amount) public;
        }
        
        /*
            Smart Token v0.3
        
            'Owned' is specified here for readability reasons
        */
        contract SmartToken is ISmartToken, Owned, ERC20Token, TokenHolder {
            string public version = '0.3';
        
            bool public transfersEnabled = true;    // true if transfer/transferFrom are enabled, false if not
        
            // triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory
            event NewSmartToken(address _token);
            // triggered when the total supply is increased
            event Issuance(uint256 _amount);
            // triggered when the total supply is decreased
            event Destruction(uint256 _amount);
        
            /**
                @dev constructor
        
                @param _name       token name
                @param _symbol     token short symbol, minimum 1 character
                @param _decimals   for display purposes only
            */
            function SmartToken(string _name, string _symbol, uint8 _decimals)
                ERC20Token(_name, _symbol, _decimals)
            {
                NewSmartToken(address(this));
            }
        
            // allows execution only when transfers aren't disabled
            modifier transfersAllowed {
                assert(transfersEnabled);
                _;
            }
        
            /**
                @dev disables/enables transfers
                can only be called by the contract owner
        
                @param _disable    true to disable transfers, false to enable them
            */
            function disableTransfers(bool _disable) public ownerOnly {
                transfersEnabled = !_disable;
            }
        
            /**
                @dev increases the token supply and sends the new tokens to an account
                can only be called by the contract owner
        
                @param _to         account to receive the new amount
                @param _amount     amount to increase the supply by
            */
            function issue(address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_to)
                notThis(_to)
            {
                totalSupply = safeAdd(totalSupply, _amount);
                balanceOf[_to] = safeAdd(balanceOf[_to], _amount);
        
                Issuance(_amount);
                Transfer(this, _to, _amount);
            }
        
            /**
                @dev removes tokens from an account and decreases the token supply
                can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account
        
                @param _from       account to remove the amount from
                @param _amount     amount to decrease the supply by
            */
            function destroy(address _from, uint256 _amount) public {
                require(msg.sender == _from || msg.sender == owner); // validate input
        
                balanceOf[_from] = safeSub(balanceOf[_from], _amount);
                totalSupply = safeSub(totalSupply, _amount);
        
                Transfer(_from, this, _amount);
                Destruction(_amount);
            }
        
            // ERC20 standard method overrides with some extra functionality
        
            /**
                @dev send coins
                throws on any error rather then return a false flag to minimize user errors
                in addition to the standard checks, the function throws if transfers are disabled
        
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transfer(_to, _value));
                return true;
            }
        
            /**
                @dev an account/contract attempts to get the coins
                throws on any error rather then return a false flag to minimize user errors
                in addition to the standard checks, the function throws if transfers are disabled
        
                @param _from    source address
                @param _to      target address
                @param _value   transfer amount
        
                @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transferFrom(_from, _to, _value));
                return true;
            }
        }

        File 6 of 8: ContractRegistry
        pragma solidity ^0.4.24;
        
        // File: contracts/utility/interfaces/IOwned.sol
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public view returns (address) {}
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        // File: contracts/utility/Owned.sol
        
        /*
            Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
        
            /**
                @dev constructor
            */
            constructor() public {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                require(msg.sender == owner);
                _;
            }
        
            /**
                @dev allows transferring the contract ownership
                the new owner still needs to accept the transfer
                can only be called by the contract owner
        
                @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner);
                newOwner = _newOwner;
            }
        
            /**
                @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner);
                emit OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = address(0);
            }
        }
        
        // File: contracts/utility/Utils.sol
        
        /*
            Utilities & Common Modifiers
        */
        contract Utils {
            /**
                constructor
            */
            constructor() public {
            }
        
            // verifies that an amount is greater than zero
            modifier greaterThanZero(uint256 _amount) {
                require(_amount > 0);
                _;
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                require(_address != address(0));
                _;
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                require(_address != address(this));
                _;
            }
        
            // Overflow protected math functions
        
            /**
                @dev returns the sum of _x and _y, asserts if the calculation overflows
        
                @param _x   value 1
                @param _y   value 2
        
                @return sum
            */
            function safeAdd(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x + _y;
                assert(z >= _x);
                return z;
            }
        
            /**
                @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
        
                @param _x   minuend
                @param _y   subtrahend
        
                @return difference
            */
            function safeSub(uint256 _x, uint256 _y) internal pure returns (uint256) {
                assert(_x >= _y);
                return _x - _y;
            }
        
            /**
                @dev returns the product of multiplying _x by _y, asserts if the calculation overflows
        
                @param _x   factor 1
                @param _y   factor 2
        
                @return product
            */
            function safeMul(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x * _y;
                assert(_x == 0 || z / _x == _y);
                return z;
            }
        }
        
        // File: contracts/utility/interfaces/IContractRegistry.sol
        
        /*
            Contract Registry interface
        */
        contract IContractRegistry {
            function addressOf(bytes32 _contractName) public view returns (address);
        
            // deprecated, backward compatibility
            function getAddress(bytes32 _contractName) public view returns (address);
        }
        
        // File: contracts/ContractIds.sol
        
        /**
            Id definitions for bancor contracts
        
            Can be used in conjunction with the contract registry to get contract addresses
        */
        contract ContractIds {
            // generic
            bytes32 public constant CONTRACT_FEATURES = "ContractFeatures";
            bytes32 public constant CONTRACT_REGISTRY = "ContractRegistry";
        
            // bancor logic
            bytes32 public constant BANCOR_NETWORK = "BancorNetwork";
            bytes32 public constant BANCOR_FORMULA = "BancorFormula";
            bytes32 public constant BANCOR_GAS_PRICE_LIMIT = "BancorGasPriceLimit";
            bytes32 public constant BANCOR_CONVERTER_UPGRADER = "BancorConverterUpgrader";
            bytes32 public constant BANCOR_CONVERTER_FACTORY = "BancorConverterFactory";
        
            // Ids of BNT converter and BNT token
            bytes32 public constant BNT_TOKEN = "BNTToken";
            bytes32 public constant BNT_CONVERTER = "BNTConverter";
        
            // Id of BancorX contract
            bytes32 public constant BANCOR_X = "BancorX";
        }
        
        // File: contracts/utility/ContractRegistry.sol
        
        /**
            Contract Registry
        
            The contract registry keeps contract addresses by name.
            The owner can update contract addresses so that a contract name always points to the latest version
            of the given contract.
            Other contracts can query the registry to get updated addresses instead of depending on specific
            addresses.
        
            Note that contract names are limited to 32 bytes UTF8 encoded ASCII strings to optimize gas costs
        */
        contract ContractRegistry is IContractRegistry, Owned, Utils, ContractIds {
            struct RegistryItem {
                address contractAddress;    // contract address
                uint256 nameIndex;          // index of the item in the list of contract names
                bool isSet;                 // used to tell if the mapping element is defined
            }
        
            mapping (bytes32 => RegistryItem) private items;    // name -> RegistryItem mapping
            string[] public contractNames;                      // list of all registered contract names
        
            // triggered when an address pointed to by a contract name is modified
            event AddressUpdate(bytes32 indexed _contractName, address _contractAddress);
        
            /**
                @dev constructor
            */
            constructor() public {
                registerAddress(ContractIds.CONTRACT_REGISTRY, address(this));
            }
        
            /**
                @dev returns the number of items in the registry
        
                @return number of items
            */
            function itemCount() public view returns (uint256) {
                return contractNames.length;
            }
        
            /**
                @dev returns the address associated with the given contract name
        
                @param _contractName    contract name
        
                @return contract address
            */
            function addressOf(bytes32 _contractName) public view returns (address) {
                return items[_contractName].contractAddress;
            }
        
            /**
                @dev registers a new address for the contract name in the registry
        
                @param _contractName     contract name
                @param _contractAddress  contract address
            */
            function registerAddress(bytes32 _contractName, address _contractAddress)
                public
                ownerOnly
                validAddress(_contractAddress)
            {
                require(_contractName.length > 0); // validate input
        
                // update the address in the registry
                items[_contractName].contractAddress = _contractAddress;
        
                if (!items[_contractName].isSet) {
                    // mark the item as set
                    items[_contractName].isSet = true;
                    // add the contract name to the name list
                    uint256 i = contractNames.push(bytes32ToString(_contractName));
                    // update the item's index in the list
                    items[_contractName].nameIndex = i - 1;
                }
        
                // dispatch the address update event
                emit AddressUpdate(_contractName, _contractAddress);
            }
        
            /**
                @dev removes an existing contract address from the registry
        
                @param _contractName contract name
            */
            function unregisterAddress(bytes32 _contractName) public ownerOnly {
                require(_contractName.length > 0); // validate input
        
                // remove the address from the registry
                items[_contractName].contractAddress = address(0);
        
                // if there are multiple items in the registry, move the last element to the deleted element's position
                // and modify last element's registryItem.nameIndex in the items collection to point to the right position in contractNames
                if (contractNames.length > 1) {
                    string memory lastContractNameString = contractNames[contractNames.length - 1];
                    uint256 unregisterIndex = items[_contractName].nameIndex;
        
                    contractNames[unregisterIndex] = lastContractNameString;
                    bytes32 lastContractName = stringToBytes32(lastContractNameString);
                    RegistryItem storage registryItem = items[lastContractName];
                    registryItem.nameIndex = unregisterIndex;
                }
        
                // remove the last element from the name list
                contractNames.length--;
                // zero the deleted element's index
                items[_contractName].nameIndex = 0;
        
                // dispatch the address update event
                emit AddressUpdate(_contractName, address(0));
            }
        
            /**
                @dev utility, converts bytes32 to a string
                note that the bytes32 argument is assumed to be UTF8 encoded ASCII string
        
                @return string representation of the given bytes32 argument
            */
            function bytes32ToString(bytes32 _bytes) private pure returns (string) {
                bytes memory byteArray = new bytes(32);
                for (uint256 i; i < 32; i++) {
                    byteArray[i] = _bytes[i];
                }
        
                return string(byteArray);
            }
        
            // @dev utility, converts string to bytes32
            function stringToBytes32(string memory _string) private pure returns (bytes32) {
                bytes32 result;
                assembly {
                    result := mload(add(_string,32))
                }
                return result;
            }
        
            // deprecated, backward compatibility
            function getAddress(bytes32 _contractName) public view returns (address) {
                return addressOf(_contractName);
            }
        }

        File 7 of 8: SmartToken
        pragma solidity 0.4.26;
        
        // File: contracts/token/interfaces/IERC20Token.sol
        
        /*
            ERC20 Standard Token interface
        */
        contract IERC20Token {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public view returns (string) {this;}
            function symbol() public view returns (string) {this;}
            function decimals() public view returns (uint8) {this;}
            function totalSupply() public view returns (uint256) {this;}
            function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
            function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
        
            function transfer(address _to, uint256 _value) public returns (bool success);
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
            function approve(address _spender, uint256 _value) public returns (bool success);
        }
        
        // File: contracts/utility/Utils.sol
        
        /**
          * @dev Utilities & Common Modifiers
        */
        contract Utils {
            /**
              * constructor
            */
            constructor() public {
            }
        
            // verifies that an amount is greater than zero
            modifier greaterThanZero(uint256 _amount) {
                require(_amount > 0);
                _;
            }
        
            // validates an address - currently only checks that it isn't null
            modifier validAddress(address _address) {
                require(_address != address(0));
                _;
            }
        
            // verifies that the address is different than this contract address
            modifier notThis(address _address) {
                require(_address != address(this));
                _;
            }
        
        }
        
        // File: contracts/utility/SafeMath.sol
        
        /**
          * @dev Library for basic math operations with overflow/underflow protection
        */
        library SafeMath {
            /**
              * @dev returns the sum of _x and _y, reverts if the calculation overflows
              * 
              * @param _x   value 1
              * @param _y   value 2
              * 
              * @return sum
            */
            function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x + _y;
                require(z >= _x);
                return z;
            }
        
            /**
              * @dev returns the difference of _x minus _y, reverts if the calculation underflows
              * 
              * @param _x   minuend
              * @param _y   subtrahend
              * 
              * @return difference
            */
            function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_x >= _y);
                return _x - _y;
            }
        
            /**
              * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
              * 
              * @param _x   factor 1
              * @param _y   factor 2
              * 
              * @return product
            */
            function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
                // gas optimization
                if (_x == 0)
                    return 0;
        
                uint256 z = _x * _y;
                require(z / _x == _y);
                return z;
            }
        
              /**
                * ev Integer division of two numbers truncating the quotient, reverts on division by zero.
                * 
                * aram _x   dividend
                * aram _y   divisor
                * 
                * eturn quotient
            */
            function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_y > 0);
                uint256 c = _x / _y;
        
                return c;
            }
        }
        
        // File: contracts/token/ERC20Token.sol
        
        /**
          * @dev ERC20 Standard Token implementation
        */
        contract ERC20Token is IERC20Token, Utils {
            using SafeMath for uint256;
        
        
            string public name;
            string public symbol;
            uint8 public decimals;
            uint256 public totalSupply;
            mapping (address => uint256) public balanceOf;
            mapping (address => mapping (address => uint256)) public allowance;
        
            /**
              * @dev triggered when tokens are transferred between wallets
              * 
              * @param _from    source address
              * @param _to      target address
              * @param _value   transfer amount
            */
            event Transfer(address indexed _from, address indexed _to, uint256 _value);
        
            /**
              * @dev triggered when a wallet allows another wallet to transfer tokens from on its behalf
              * 
              * @param _owner   wallet that approves the allowance
              * @param _spender wallet that receives the allowance
              * @param _value   allowance amount
            */
            event Approval(address indexed _owner, address indexed _spender, uint256 _value);
        
            /**
              * @dev initializes a new ERC20Token instance
              * 
              * @param _name        token name
              * @param _symbol      token symbol
              * @param _decimals    decimal points, for display purposes
              * @param _totalSupply total supply of token units
            */
            constructor(string _name, string _symbol, uint8 _decimals, uint256 _totalSupply) public {
                require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input
        
                name = _name;
                symbol = _symbol;
                decimals = _decimals;
                totalSupply = _totalSupply;
                balanceOf[msg.sender] = _totalSupply;
            }
        
            /**
              * @dev send coins
              * throws on any error rather then return a false flag to minimize user errors
              * 
              * @param _to      target address
              * @param _value   transfer amount
              * 
              * @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value)
                public
                validAddress(_to)
                returns (bool success)
            {
                balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
                balanceOf[_to] = balanceOf[_to].add(_value);
                emit Transfer(msg.sender, _to, _value);
                return true;
            }
        
            /**
              * @dev an account/contract attempts to get the coins
              * throws on any error rather then return a false flag to minimize user errors
              * 
              * @param _from    source address
              * @param _to      target address
              * @param _value   transfer amount
              * 
              * @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value)
                public
                validAddress(_from)
                validAddress(_to)
                returns (bool success)
            {
                allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
                balanceOf[_from] = balanceOf[_from].sub(_value);
                balanceOf[_to] = balanceOf[_to].add(_value);
                emit Transfer(_from, _to, _value);
                return true;
            }
        
            /**
              * @dev allow another account/contract to spend some tokens on your behalf
              * throws on any error rather then return a false flag to minimize user errors
              * 
              * also, to minimize the risk of the approve/transferFrom attack vector
              * (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
              * in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value
              * 
              * @param _spender approved address
              * @param _value   allowance amount
              * 
              * @return true if the approval was successful, false if it wasn't
            */
            function approve(address _spender, uint256 _value)
                public
                validAddress(_spender)
                returns (bool success)
            {
                // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
                require(_value == 0 || allowance[msg.sender][_spender] == 0);
        
                allowance[msg.sender][_spender] = _value;
                emit Approval(msg.sender, _spender, _value);
                return true;
            }
        }
        
        // File: contracts/utility/interfaces/IOwned.sol
        
        /*
            Owned contract interface
        */
        contract IOwned {
            // this function isn't abstract since the compiler emits automatically generated getter functions as external
            function owner() public view returns (address) {this;}
        
            function transferOwnership(address _newOwner) public;
            function acceptOwnership() public;
        }
        
        // File: contracts/token/interfaces/ISmartToken.sol
        
        /*
            Smart Token interface
        */
        contract ISmartToken is IOwned, IERC20Token {
            function disableTransfers(bool _disable) public;
            function issue(address _to, uint256 _amount) public;
            function destroy(address _from, uint256 _amount) public;
        }
        
        // File: contracts/utility/Owned.sol
        
        /**
          * @dev Provides support and utilities for contract ownership
        */
        contract Owned is IOwned {
            address public owner;
            address public newOwner;
        
            /**
              * @dev triggered when the owner is updated
              * 
              * @param _prevOwner previous owner
              * @param _newOwner  new owner
            */
            event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
        
            /**
              * @dev initializes a new Owned instance
            */
            constructor() public {
                owner = msg.sender;
            }
        
            // allows execution by the owner only
            modifier ownerOnly {
                require(msg.sender == owner);
                _;
            }
        
            /**
              * @dev allows transferring the contract ownership
              * the new owner still needs to accept the transfer
              * can only be called by the contract owner
              * 
              * @param _newOwner    new contract owner
            */
            function transferOwnership(address _newOwner) public ownerOnly {
                require(_newOwner != owner);
                newOwner = _newOwner;
            }
        
            /**
              * @dev used by a new owner to accept an ownership transfer
            */
            function acceptOwnership() public {
                require(msg.sender == newOwner);
                emit OwnerUpdate(owner, newOwner);
                owner = newOwner;
                newOwner = address(0);
            }
        }
        
        // File: contracts/utility/interfaces/ITokenHolder.sol
        
        /*
            Token Holder interface
        */
        contract ITokenHolder is IOwned {
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
        }
        
        // File: contracts/token/interfaces/INonStandardERC20.sol
        
        /*
            ERC20 Standard Token interface which doesn't return true/false for transfer, transferFrom and approve
        */
        contract INonStandardERC20 {
            // these functions aren't abstract since the compiler emits automatically generated getter functions as external
            function name() public view returns (string) {this;}
            function symbol() public view returns (string) {this;}
            function decimals() public view returns (uint8) {this;}
            function totalSupply() public view returns (uint256) {this;}
            function balanceOf(address _owner) public view returns (uint256) {_owner; this;}
            function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;}
        
            function transfer(address _to, uint256 _value) public;
            function transferFrom(address _from, address _to, uint256 _value) public;
            function approve(address _spender, uint256 _value) public;
        }
        
        // File: contracts/utility/TokenHolder.sol
        
        /**
          * @dev We consider every contract to be a 'token holder' since it's currently not possible
          * for a contract to deny receiving tokens.
          * 
          * The TokenHolder's contract sole purpose is to provide a safety mechanism that allows
          * the owner to send tokens that were sent to the contract by mistake back to their sender.
          * 
          * Note that we use the non standard ERC-20 interface which has no return value for transfer
          * in order to support both non standard as well as standard token contracts.
          * see https://github.com/ethereum/solidity/issues/4116
        */
        contract TokenHolder is ITokenHolder, Owned, Utils {
            /**
              * @dev initializes a new TokenHolder instance
            */
            constructor() public {
            }
        
            /**
              * @dev withdraws tokens held by the contract and sends them to an account
              * can only be called by the owner
              * 
              * @param _token   ERC20 token contract address
              * @param _to      account to receive the new amount
              * @param _amount  amount to withdraw
            */
            function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_token)
                validAddress(_to)
                notThis(_to)
            {
                INonStandardERC20(_token).transfer(_to, _amount);
            }
        }
        
        // File: contracts/token/SmartToken.sol
        
        /**
          * @dev Smart Token
          * 
          * 'Owned' is specified here for readability reasons
        */
        contract SmartToken is ISmartToken, Owned, ERC20Token, TokenHolder {
            using SafeMath for uint256;
        
        
            string public version = '0.3';
        
            bool public transfersEnabled = true;    // true if transfer/transferFrom are enabled, false if not
        
            /**
              * @dev triggered when a smart token is deployed
              * the _token address is defined for forward compatibility, in case the event is trigger by a factory
              * 
              * @param _token  new smart token address
            */
            event NewSmartToken(address _token);
        
            /**
              * @dev triggered when the total supply is increased
              * 
              * @param _amount  amount that gets added to the supply
            */
            event Issuance(uint256 _amount);
        
            /**
              * @dev triggered when the total supply is decreased
              * 
              * @param _amount  amount that gets removed from the supply
            */
            event Destruction(uint256 _amount);
        
            /**
              * @dev initializes a new SmartToken instance
              * 
              * @param _name       token name
              * @param _symbol     token short symbol, minimum 1 character
              * @param _decimals   for display purposes only
            */
            constructor(string _name, string _symbol, uint8 _decimals)
                public
                ERC20Token(_name, _symbol, _decimals, 0)
            {
                emit NewSmartToken(address(this));
            }
        
            // allows execution only when transfers aren't disabled
            modifier transfersAllowed {
                assert(transfersEnabled);
                _;
            }
        
            /**
              * @dev disables/enables transfers
              * can only be called by the contract owner
              * 
              * @param _disable    true to disable transfers, false to enable them
            */
            function disableTransfers(bool _disable) public ownerOnly {
                transfersEnabled = !_disable;
            }
        
            /**
              * @dev increases the token supply and sends the new tokens to an account
              * can only be called by the contract owner
              * 
              * @param _to         account to receive the new amount
              * @param _amount     amount to increase the supply by
            */
            function issue(address _to, uint256 _amount)
                public
                ownerOnly
                validAddress(_to)
                notThis(_to)
            {
                totalSupply = totalSupply.add(_amount);
                balanceOf[_to] = balanceOf[_to].add(_amount);
        
                emit Issuance(_amount);
                emit Transfer(this, _to, _amount);
            }
        
            /**
              * @dev removes tokens from an account and decreases the token supply
              * can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account
              * 
              * @param _from       account to remove the amount from
              * @param _amount     amount to decrease the supply by
            */
            function destroy(address _from, uint256 _amount) public {
                require(msg.sender == _from || msg.sender == owner); // validate input
        
                balanceOf[_from] = balanceOf[_from].sub(_amount);
                totalSupply = totalSupply.sub(_amount);
        
                emit Transfer(_from, this, _amount);
                emit Destruction(_amount);
            }
        
            // ERC20 standard method overrides with some extra functionality
        
            /**
              * @dev send coins
              * throws on any error rather then return a false flag to minimize user errors
              * in addition to the standard checks, the function throws if transfers are disabled
              * 
              * @param _to      target address
              * @param _value   transfer amount
              * 
              * @return true if the transfer was successful, false if it wasn't
            */
            function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transfer(_to, _value));
                return true;
            }
        
            /**
              * @dev an account/contract attempts to get the coins
              * throws on any error rather then return a false flag to minimize user errors
              * in addition to the standard checks, the function throws if transfers are disabled
              * 
              * @param _from    source address
              * @param _to      target address
              * @param _value   transfer amount
              * 
              * @return true if the transfer was successful, false if it wasn't
            */
            function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) {
                assert(super.transferFrom(_from, _to, _value));
                return true;
            }
        }
        

        File 8 of 8: BancorFormula
        // File: solidity/contracts/converter/interfaces/IBancorFormula.sol
        
        pragma solidity 0.4.26;
        
        /*
            Bancor Formula interface
        */
        contract IBancorFormula {
            function purchaseTargetAmount(uint256 _supply,
                                          uint256 _reserveBalance,
                                          uint32 _reserveWeight,
                                          uint256 _amount)
                                          public view returns (uint256);
        
            function saleTargetAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveWeight,
                                      uint256 _amount)
                                      public view returns (uint256);
        
            function crossReserveTargetAmount(uint256 _sourceReserveBalance,
                                              uint32 _sourceReserveWeight,
                                              uint256 _targetReserveBalance,
                                              uint32 _targetReserveWeight,
                                              uint256 _amount)
                                              public view returns (uint256);
        
            function fundCost(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveRatio,
                              uint256 _amount)
                              public view returns (uint256);
        
            function fundSupplyAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveRatio,
                                      uint256 _amount)
                                      public view returns (uint256);
        
            function liquidateReserveAmount(uint256 _supply,
                                            uint256 _reserveBalance,
                                            uint32 _reserveRatio,
                                            uint256 _amount)
                                            public view returns (uint256);
        
            function balancedWeights(uint256 _primaryReserveStakedBalance,
                                     uint256 _primaryReserveBalance,
                                     uint256 _secondaryReserveBalance,
                                     uint256 _reserveRateNumerator,
                                     uint256 _reserveRateDenominator)
                                     public view returns (uint32, uint32);
        }
        
        // File: solidity/contracts/utility/SafeMath.sol
        
        pragma solidity 0.4.26;
        
        /**
          * @dev Library for basic math operations with overflow/underflow protection
        */
        library SafeMath {
            /**
              * @dev returns the sum of _x and _y, reverts if the calculation overflows
              *
              * @param _x   value 1
              * @param _y   value 2
              *
              * @return sum
            */
            function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
                uint256 z = _x + _y;
                require(z >= _x, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev returns the difference of _x minus _y, reverts if the calculation underflows
              *
              * @param _x   minuend
              * @param _y   subtrahend
              *
              * @return difference
            */
            function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_x >= _y, "ERR_UNDERFLOW");
                return _x - _y;
            }
        
            /**
              * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
              *
              * @param _x   factor 1
              * @param _y   factor 2
              *
              * @return product
            */
            function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
                // gas optimization
                if (_x == 0)
                    return 0;
        
                uint256 z = _x * _y;
                require(z / _x == _y, "ERR_OVERFLOW");
                return z;
            }
        
            /**
              * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
              *
              * @param _x   dividend
              * @param _y   divisor
              *
              * @return quotient
            */
            function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
                require(_y > 0, "ERR_DIVIDE_BY_ZERO");
                uint256 c = _x / _y;
                return c;
            }
        }
        
        // File: solidity/contracts/converter/BancorFormula.sol
        
        pragma solidity 0.4.26;
        
        
        
        contract BancorFormula is IBancorFormula {
            using SafeMath for uint256;
        
            uint16 public constant version = 8;
        
            uint256 private constant ONE = 1;
            uint32 private constant MAX_WEIGHT = 1000000;
            uint8 private constant MIN_PRECISION = 32;
            uint8 private constant MAX_PRECISION = 127;
        
            /**
              * Auto-generated via 'PrintIntScalingFactors.py'
            */
            uint256 private constant FIXED_1 = 0x080000000000000000000000000000000;
            uint256 private constant FIXED_2 = 0x100000000000000000000000000000000;
            uint256 private constant MAX_NUM = 0x200000000000000000000000000000000;
        
            /**
              * Auto-generated via 'PrintLn2ScalingFactors.py'
            */
            uint256 private constant LN2_NUMERATOR   = 0x3f80fe03f80fe03f80fe03f80fe03f8;
            uint256 private constant LN2_DENOMINATOR = 0x5b9de1d10bf4103d647b0955897ba80;
        
            /**
              * Auto-generated via 'PrintFunctionOptimalLog.py' and 'PrintFunctionOptimalExp.py'
            */
            uint256 private constant OPT_LOG_MAX_VAL = 0x15bf0a8b1457695355fb8ac404e7a79e3;
            uint256 private constant OPT_EXP_MAX_VAL = 0x800000000000000000000000000000000;
        
            /**
              * Auto-generated via 'PrintLambertFactors.py'
            */
            uint256 private constant LAMBERT_CONV_RADIUS = 0x002f16ac6c59de6f8d5d6f63c1482a7c86;
            uint256 private constant LAMBERT_POS2_SAMPLE = 0x0003060c183060c183060c183060c18306;
            uint256 private constant LAMBERT_POS2_MAXVAL = 0x01af16ac6c59de6f8d5d6f63c1482a7c80;
            uint256 private constant LAMBERT_POS3_MAXVAL = 0x6b22d43e72c326539cceeef8bb48f255ff;
        
            /**
              * Auto-generated via 'PrintWeightFactors.py'
            */
            uint256 private constant MAX_UNF_WEIGHT = 0x10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea9;
        
            /**
              * Auto-generated via 'PrintMaxExpArray.py'
            */
            uint256[128] private maxExpArray;
            function initMaxExpArray() private {
            //  maxExpArray[  0] = 0x6bffffffffffffffffffffffffffffffff;
            //  maxExpArray[  1] = 0x67ffffffffffffffffffffffffffffffff;
            //  maxExpArray[  2] = 0x637fffffffffffffffffffffffffffffff;
            //  maxExpArray[  3] = 0x5f6fffffffffffffffffffffffffffffff;
            //  maxExpArray[  4] = 0x5b77ffffffffffffffffffffffffffffff;
            //  maxExpArray[  5] = 0x57b3ffffffffffffffffffffffffffffff;
            //  maxExpArray[  6] = 0x5419ffffffffffffffffffffffffffffff;
            //  maxExpArray[  7] = 0x50a2ffffffffffffffffffffffffffffff;
            //  maxExpArray[  8] = 0x4d517fffffffffffffffffffffffffffff;
            //  maxExpArray[  9] = 0x4a233fffffffffffffffffffffffffffff;
            //  maxExpArray[ 10] = 0x47165fffffffffffffffffffffffffffff;
            //  maxExpArray[ 11] = 0x4429afffffffffffffffffffffffffffff;
            //  maxExpArray[ 12] = 0x415bc7ffffffffffffffffffffffffffff;
            //  maxExpArray[ 13] = 0x3eab73ffffffffffffffffffffffffffff;
            //  maxExpArray[ 14] = 0x3c1771ffffffffffffffffffffffffffff;
            //  maxExpArray[ 15] = 0x399e96ffffffffffffffffffffffffffff;
            //  maxExpArray[ 16] = 0x373fc47fffffffffffffffffffffffffff;
            //  maxExpArray[ 17] = 0x34f9e8ffffffffffffffffffffffffffff;
            //  maxExpArray[ 18] = 0x32cbfd5fffffffffffffffffffffffffff;
            //  maxExpArray[ 19] = 0x30b5057fffffffffffffffffffffffffff;
            //  maxExpArray[ 20] = 0x2eb40f9fffffffffffffffffffffffffff;
            //  maxExpArray[ 21] = 0x2cc8340fffffffffffffffffffffffffff;
            //  maxExpArray[ 22] = 0x2af09481ffffffffffffffffffffffffff;
            //  maxExpArray[ 23] = 0x292c5bddffffffffffffffffffffffffff;
            //  maxExpArray[ 24] = 0x277abdcdffffffffffffffffffffffffff;
            //  maxExpArray[ 25] = 0x25daf6657fffffffffffffffffffffffff;
            //  maxExpArray[ 26] = 0x244c49c65fffffffffffffffffffffffff;
            //  maxExpArray[ 27] = 0x22ce03cd5fffffffffffffffffffffffff;
            //  maxExpArray[ 28] = 0x215f77c047ffffffffffffffffffffffff;
            //  maxExpArray[ 29] = 0x1fffffffffffffffffffffffffffffffff;
            //  maxExpArray[ 30] = 0x1eaefdbdabffffffffffffffffffffffff;
            //  maxExpArray[ 31] = 0x1d6bd8b2ebffffffffffffffffffffffff;
                maxExpArray[ 32] = 0x1c35fedd14ffffffffffffffffffffffff;
                maxExpArray[ 33] = 0x1b0ce43b323fffffffffffffffffffffff;
                maxExpArray[ 34] = 0x19f0028ec1ffffffffffffffffffffffff;
                maxExpArray[ 35] = 0x18ded91f0e7fffffffffffffffffffffff;
                maxExpArray[ 36] = 0x17d8ec7f0417ffffffffffffffffffffff;
                maxExpArray[ 37] = 0x16ddc6556cdbffffffffffffffffffffff;
                maxExpArray[ 38] = 0x15ecf52776a1ffffffffffffffffffffff;
                maxExpArray[ 39] = 0x15060c256cb2ffffffffffffffffffffff;
                maxExpArray[ 40] = 0x1428a2f98d72ffffffffffffffffffffff;
                maxExpArray[ 41] = 0x13545598e5c23fffffffffffffffffffff;
                maxExpArray[ 42] = 0x1288c4161ce1dfffffffffffffffffffff;
                maxExpArray[ 43] = 0x11c592761c666fffffffffffffffffffff;
                maxExpArray[ 44] = 0x110a688680a757ffffffffffffffffffff;
                maxExpArray[ 45] = 0x1056f1b5bedf77ffffffffffffffffffff;
                maxExpArray[ 46] = 0x0faadceceeff8bffffffffffffffffffff;
                maxExpArray[ 47] = 0x0f05dc6b27edadffffffffffffffffffff;
                maxExpArray[ 48] = 0x0e67a5a25da4107fffffffffffffffffff;
                maxExpArray[ 49] = 0x0dcff115b14eedffffffffffffffffffff;
                maxExpArray[ 50] = 0x0d3e7a392431239fffffffffffffffffff;
                maxExpArray[ 51] = 0x0cb2ff529eb71e4fffffffffffffffffff;
                maxExpArray[ 52] = 0x0c2d415c3db974afffffffffffffffffff;
                maxExpArray[ 53] = 0x0bad03e7d883f69bffffffffffffffffff;
                maxExpArray[ 54] = 0x0b320d03b2c343d5ffffffffffffffffff;
                maxExpArray[ 55] = 0x0abc25204e02828dffffffffffffffffff;
                maxExpArray[ 56] = 0x0a4b16f74ee4bb207fffffffffffffffff;
                maxExpArray[ 57] = 0x09deaf736ac1f569ffffffffffffffffff;
                maxExpArray[ 58] = 0x0976bd9952c7aa957fffffffffffffffff;
                maxExpArray[ 59] = 0x09131271922eaa606fffffffffffffffff;
                maxExpArray[ 60] = 0x08b380f3558668c46fffffffffffffffff;
                maxExpArray[ 61] = 0x0857ddf0117efa215bffffffffffffffff;
                maxExpArray[ 62] = 0x07ffffffffffffffffffffffffffffffff;
                maxExpArray[ 63] = 0x07abbf6f6abb9d087fffffffffffffffff;
                maxExpArray[ 64] = 0x075af62cbac95f7dfa7fffffffffffffff;
                maxExpArray[ 65] = 0x070d7fb7452e187ac13fffffffffffffff;
                maxExpArray[ 66] = 0x06c3390ecc8af379295fffffffffffffff;
                maxExpArray[ 67] = 0x067c00a3b07ffc01fd6fffffffffffffff;
                maxExpArray[ 68] = 0x0637b647c39cbb9d3d27ffffffffffffff;
                maxExpArray[ 69] = 0x05f63b1fc104dbd39587ffffffffffffff;
                maxExpArray[ 70] = 0x05b771955b36e12f7235ffffffffffffff;
                maxExpArray[ 71] = 0x057b3d49dda84556d6f6ffffffffffffff;
                maxExpArray[ 72] = 0x054183095b2c8ececf30ffffffffffffff;
                maxExpArray[ 73] = 0x050a28be635ca2b888f77fffffffffffff;
                maxExpArray[ 74] = 0x04d5156639708c9db33c3fffffffffffff;
                maxExpArray[ 75] = 0x04a23105873875bd52dfdfffffffffffff;
                maxExpArray[ 76] = 0x0471649d87199aa990756fffffffffffff;
                maxExpArray[ 77] = 0x04429a21a029d4c1457cfbffffffffffff;
                maxExpArray[ 78] = 0x0415bc6d6fb7dd71af2cb3ffffffffffff;
                maxExpArray[ 79] = 0x03eab73b3bbfe282243ce1ffffffffffff;
                maxExpArray[ 80] = 0x03c1771ac9fb6b4c18e229ffffffffffff;
                maxExpArray[ 81] = 0x0399e96897690418f785257fffffffffff;
                maxExpArray[ 82] = 0x0373fc456c53bb779bf0ea9fffffffffff;
                maxExpArray[ 83] = 0x034f9e8e490c48e67e6ab8bfffffffffff;
                maxExpArray[ 84] = 0x032cbfd4a7adc790560b3337ffffffffff;
                maxExpArray[ 85] = 0x030b50570f6e5d2acca94613ffffffffff;
                maxExpArray[ 86] = 0x02eb40f9f620fda6b56c2861ffffffffff;
                maxExpArray[ 87] = 0x02cc8340ecb0d0f520a6af58ffffffffff;
                maxExpArray[ 88] = 0x02af09481380a0a35cf1ba02ffffffffff;
                maxExpArray[ 89] = 0x0292c5bdd3b92ec810287b1b3fffffffff;
                maxExpArray[ 90] = 0x0277abdcdab07d5a77ac6d6b9fffffffff;
                maxExpArray[ 91] = 0x025daf6654b1eaa55fd64df5efffffffff;
                maxExpArray[ 92] = 0x0244c49c648baa98192dce88b7ffffffff;
                maxExpArray[ 93] = 0x022ce03cd5619a311b2471268bffffffff;
                maxExpArray[ 94] = 0x0215f77c045fbe885654a44a0fffffffff;
                maxExpArray[ 95] = 0x01ffffffffffffffffffffffffffffffff;
                maxExpArray[ 96] = 0x01eaefdbdaaee7421fc4d3ede5ffffffff;
                maxExpArray[ 97] = 0x01d6bd8b2eb257df7e8ca57b09bfffffff;
                maxExpArray[ 98] = 0x01c35fedd14b861eb0443f7f133fffffff;
                maxExpArray[ 99] = 0x01b0ce43b322bcde4a56e8ada5afffffff;
                maxExpArray[100] = 0x019f0028ec1fff007f5a195a39dfffffff;
                maxExpArray[101] = 0x018ded91f0e72ee74f49b15ba527ffffff;
                maxExpArray[102] = 0x017d8ec7f04136f4e5615fd41a63ffffff;
                maxExpArray[103] = 0x016ddc6556cdb84bdc8d12d22e6fffffff;
                maxExpArray[104] = 0x015ecf52776a1155b5bd8395814f7fffff;
                maxExpArray[105] = 0x015060c256cb23b3b3cc3754cf40ffffff;
                maxExpArray[106] = 0x01428a2f98d728ae223ddab715be3fffff;
                maxExpArray[107] = 0x013545598e5c23276ccf0ede68034fffff;
                maxExpArray[108] = 0x01288c4161ce1d6f54b7f61081194fffff;
                maxExpArray[109] = 0x011c592761c666aa641d5a01a40f17ffff;
                maxExpArray[110] = 0x0110a688680a7530515f3e6e6cfdcdffff;
                maxExpArray[111] = 0x01056f1b5bedf75c6bcb2ce8aed428ffff;
                maxExpArray[112] = 0x00faadceceeff8a0890f3875f008277fff;
                maxExpArray[113] = 0x00f05dc6b27edad306388a600f6ba0bfff;
                maxExpArray[114] = 0x00e67a5a25da41063de1495d5b18cdbfff;
                maxExpArray[115] = 0x00dcff115b14eedde6fc3aa5353f2e4fff;
                maxExpArray[116] = 0x00d3e7a3924312399f9aae2e0f868f8fff;
                maxExpArray[117] = 0x00cb2ff529eb71e41582cccd5a1ee26fff;
                maxExpArray[118] = 0x00c2d415c3db974ab32a51840c0b67edff;
                maxExpArray[119] = 0x00bad03e7d883f69ad5b0a186184e06bff;
                maxExpArray[120] = 0x00b320d03b2c343d4829abd6075f0cc5ff;
                maxExpArray[121] = 0x00abc25204e02828d73c6e80bcdb1a95bf;
                maxExpArray[122] = 0x00a4b16f74ee4bb2040a1ec6c15fbbf2df;
                maxExpArray[123] = 0x009deaf736ac1f569deb1b5ae3f36c130f;
                maxExpArray[124] = 0x00976bd9952c7aa957f5937d790ef65037;
                maxExpArray[125] = 0x009131271922eaa6064b73a22d0bd4f2bf;
                maxExpArray[126] = 0x008b380f3558668c46c91c49a2f8e967b9;
                maxExpArray[127] = 0x00857ddf0117efa215952912839f6473e6;
            }
        
            /**
              * Auto-generated via 'PrintLambertArray.py'
            */
            uint256[128] private lambertArray;
            function initLambertArray() private {
                lambertArray[  0] = 0x60e393c68d20b1bd09deaabc0373b9c5;
                lambertArray[  1] = 0x5f8f46e4854120989ed94719fb4c2011;
                lambertArray[  2] = 0x5e479ebb9129fb1b7e72a648f992b606;
                lambertArray[  3] = 0x5d0bd23fe42dfedde2e9586be12b85fe;
                lambertArray[  4] = 0x5bdb29ddee979308ddfca81aeeb8095a;
                lambertArray[  5] = 0x5ab4fd8a260d2c7e2c0d2afcf0009dad;
                lambertArray[  6] = 0x5998b31359a55d48724c65cf09001221;
                lambertArray[  7] = 0x5885bcad2b322dfc43e8860f9c018cf5;
                lambertArray[  8] = 0x577b97aa1fe222bb452fdf111b1f0be2;
                lambertArray[  9] = 0x5679cb5e3575632e5baa27e2b949f704;
                lambertArray[ 10] = 0x557fe8241b3a31c83c732f1cdff4a1c5;
                lambertArray[ 11] = 0x548d868026504875d6e59bbe95fc2a6b;
                lambertArray[ 12] = 0x53a2465ce347cf34d05a867c17dd3088;
                lambertArray[ 13] = 0x52bdce5dcd4faed59c7f5511cf8f8acc;
                lambertArray[ 14] = 0x51dfcb453c07f8da817606e7885f7c3e;
                lambertArray[ 15] = 0x5107ef6b0a5a2be8f8ff15590daa3cce;
                lambertArray[ 16] = 0x5035f241d6eae0cd7bacba119993de7b;
                lambertArray[ 17] = 0x4f698fe90d5b53d532171e1210164c66;
                lambertArray[ 18] = 0x4ea288ca297a0e6a09a0eee240e16c85;
                lambertArray[ 19] = 0x4de0a13fdcf5d4213fc398ba6e3becde;
                lambertArray[ 20] = 0x4d23a145eef91fec06b06140804c4808;
                lambertArray[ 21] = 0x4c6b5430d4c1ee5526473db4ae0f11de;
                lambertArray[ 22] = 0x4bb7886c240562eba11f4963a53b4240;
                lambertArray[ 23] = 0x4b080f3f1cb491d2d521e0ea4583521e;
                lambertArray[ 24] = 0x4a5cbc96a05589cb4d86be1db3168364;
                lambertArray[ 25] = 0x49b566d40243517658d78c33162d6ece;
                lambertArray[ 26] = 0x4911e6a02e5507a30f947383fd9a3276;
                lambertArray[ 27] = 0x487216c2b31be4adc41db8a8d5cc0c88;
                lambertArray[ 28] = 0x47d5d3fc4a7a1b188cd3d788b5c5e9fc;
                lambertArray[ 29] = 0x473cfce4871a2c40bc4f9e1c32b955d0;
                lambertArray[ 30] = 0x46a771ca578ab878485810e285e31c67;
                lambertArray[ 31] = 0x4615149718aed4c258c373dc676aa72d;
                lambertArray[ 32] = 0x4585c8b3f8fe489c6e1833ca47871384;
                lambertArray[ 33] = 0x44f972f174e41e5efb7e9d63c29ce735;
                lambertArray[ 34] = 0x446ff970ba86d8b00beb05ecebf3c4dc;
                lambertArray[ 35] = 0x43e9438ec88971812d6f198b5ccaad96;
                lambertArray[ 36] = 0x436539d11ff7bea657aeddb394e809ef;
                lambertArray[ 37] = 0x42e3c5d3e5a913401d86f66db5d81c2c;
                lambertArray[ 38] = 0x4264d2395303070ea726cbe98df62174;
                lambertArray[ 39] = 0x41e84a9a593bb7194c3a6349ecae4eea;
                lambertArray[ 40] = 0x416e1b785d13eba07a08f3f18876a5ab;
                lambertArray[ 41] = 0x40f6322ff389d423ba9dd7e7e7b7e809;
                lambertArray[ 42] = 0x40807cec8a466880ecf4184545d240a4;
                lambertArray[ 43] = 0x400cea9ce88a8d3ae668e8ea0d9bf07f;
                lambertArray[ 44] = 0x3f9b6ae8772d4c55091e0ed7dfea0ac1;
                lambertArray[ 45] = 0x3f2bee253fd84594f54bcaafac383a13;
                lambertArray[ 46] = 0x3ebe654e95208bb9210c575c081c5958;
                lambertArray[ 47] = 0x3e52c1fc5665635b78ce1f05ad53c086;
                lambertArray[ 48] = 0x3de8f65ac388101ddf718a6f5c1eff65;
                lambertArray[ 49] = 0x3d80f522d59bd0b328ca012df4cd2d49;
                lambertArray[ 50] = 0x3d1ab193129ea72b23648a161163a85a;
                lambertArray[ 51] = 0x3cb61f68d32576c135b95cfb53f76d75;
                lambertArray[ 52] = 0x3c5332d9f1aae851a3619e77e4cc8473;
                lambertArray[ 53] = 0x3bf1e08edbe2aa109e1525f65759ef73;
                lambertArray[ 54] = 0x3b921d9cff13fa2c197746a3dfc4918f;
                lambertArray[ 55] = 0x3b33df818910bfc1a5aefb8f63ae2ac4;
                lambertArray[ 56] = 0x3ad71c1c77e34fa32a9f184967eccbf6;
                lambertArray[ 57] = 0x3a7bc9abf2c5bb53e2f7384a8a16521a;
                lambertArray[ 58] = 0x3a21dec7e76369783a68a0c6385a1c57;
                lambertArray[ 59] = 0x39c9525de6c9cdf7c1c157ca4a7a6ee3;
                lambertArray[ 60] = 0x39721bad3dc85d1240ff0190e0adaac3;
                lambertArray[ 61] = 0x391c324344d3248f0469eb28dd3d77e0;
                lambertArray[ 62] = 0x38c78df7e3c796279fb4ff84394ab3da;
                lambertArray[ 63] = 0x387426ea4638ae9aae08049d3554c20a;
                lambertArray[ 64] = 0x3821f57dbd2763256c1a99bbd2051378;
                lambertArray[ 65] = 0x37d0f256cb46a8c92ff62fbbef289698;
                lambertArray[ 66] = 0x37811658591ffc7abdd1feaf3cef9b73;
                lambertArray[ 67] = 0x37325aa10e9e82f7df0f380f7997154b;
                lambertArray[ 68] = 0x36e4b888cfb408d873b9a80d439311c6;
                lambertArray[ 69] = 0x3698299e59f4bb9de645fc9b08c64cca;
                lambertArray[ 70] = 0x364ca7a5012cb603023b57dd3ebfd50d;
                lambertArray[ 71] = 0x36022c928915b778ab1b06aaee7e61d4;
                lambertArray[ 72] = 0x35b8b28d1a73dc27500ffe35559cc028;
                lambertArray[ 73] = 0x357033e951fe250ec5eb4e60955132d7;
                lambertArray[ 74] = 0x3528ab2867934e3a21b5412e4c4f8881;
                lambertArray[ 75] = 0x34e212f66c55057f9676c80094a61d59;
                lambertArray[ 76] = 0x349c66289e5b3c4b540c24f42fa4b9bb;
                lambertArray[ 77] = 0x34579fbbd0c733a9c8d6af6b0f7d00f7;
                lambertArray[ 78] = 0x3413bad2e712288b924b5882b5b369bf;
                lambertArray[ 79] = 0x33d0b2b56286510ef730e213f71f12e9;
                lambertArray[ 80] = 0x338e82ce00e2496262c64457535ba1a1;
                lambertArray[ 81] = 0x334d26a96b373bb7c2f8ea1827f27a92;
                lambertArray[ 82] = 0x330c99f4f4211469e00b3e18c31475ea;
                lambertArray[ 83] = 0x32ccd87d6486094999c7d5e6f33237d8;
                lambertArray[ 84] = 0x328dde2dd617b6665a2e8556f250c1af;
                lambertArray[ 85] = 0x324fa70e9adc270f8262755af5a99af9;
                lambertArray[ 86] = 0x32122f443110611ca51040f41fa6e1e3;
                lambertArray[ 87] = 0x31d5730e42c0831482f0f1485c4263d8;
                lambertArray[ 88] = 0x31996ec6b07b4a83421b5ebc4ab4e1f1;
                lambertArray[ 89] = 0x315e1ee0a68ff46bb43ec2b85032e876;
                lambertArray[ 90] = 0x31237fe7bc4deacf6775b9efa1a145f8;
                lambertArray[ 91] = 0x30e98e7f1cc5a356e44627a6972ea2ff;
                lambertArray[ 92] = 0x30b04760b8917ec74205a3002650ec05;
                lambertArray[ 93] = 0x3077a75c803468e9132ce0cf3224241d;
                lambertArray[ 94] = 0x303fab57a6a275c36f19cda9bace667a;
                lambertArray[ 95] = 0x3008504beb8dcbd2cf3bc1f6d5a064f0;
                lambertArray[ 96] = 0x2fd19346ed17dac61219ce0c2c5ac4b0;
                lambertArray[ 97] = 0x2f9b7169808c324b5852fd3d54ba9714;
                lambertArray[ 98] = 0x2f65e7e711cf4b064eea9c08cbdad574;
                lambertArray[ 99] = 0x2f30f405093042ddff8a251b6bf6d103;
                lambertArray[100] = 0x2efc931a3750f2e8bfe323edfe037574;
                lambertArray[101] = 0x2ec8c28e46dbe56d98685278339400cb;
                lambertArray[102] = 0x2e957fd933c3926d8a599b602379b851;
                lambertArray[103] = 0x2e62c882c7c9ed4473412702f08ba0e5;
                lambertArray[104] = 0x2e309a221c12ba361e3ed695167feee2;
                lambertArray[105] = 0x2dfef25d1f865ae18dd07cfea4bcea10;
                lambertArray[106] = 0x2dcdcee821cdc80decc02c44344aeb31;
                lambertArray[107] = 0x2d9d2d8562b34944d0b201bb87260c83;
                lambertArray[108] = 0x2d6d0c04a5b62a2c42636308669b729a;
                lambertArray[109] = 0x2d3d6842c9a235517fc5a0332691528f;
                lambertArray[110] = 0x2d0e402963fe1ea2834abc408c437c10;
                lambertArray[111] = 0x2cdf91ae602647908aff975e4d6a2a8c;
                lambertArray[112] = 0x2cb15ad3a1eb65f6d74a75da09a1b6c5;
                lambertArray[113] = 0x2c8399a6ab8e9774d6fcff373d210727;
                lambertArray[114] = 0x2c564c4046f64edba6883ca06bbc4535;
                lambertArray[115] = 0x2c2970c431f952641e05cb493e23eed3;
                lambertArray[116] = 0x2bfd0560cd9eb14563bc7c0732856c18;
                lambertArray[117] = 0x2bd1084ed0332f7ff4150f9d0ef41a2c;
                lambertArray[118] = 0x2ba577d0fa1628b76d040b12a82492fb;
                lambertArray[119] = 0x2b7a5233cd21581e855e89dc2f1e8a92;
                lambertArray[120] = 0x2b4f95cd46904d05d72bdcde337d9cc7;
                lambertArray[121] = 0x2b2540fc9b4d9abba3faca6691914675;
                lambertArray[122] = 0x2afb5229f68d0830d8be8adb0a0db70f;
                lambertArray[123] = 0x2ad1c7c63a9b294c5bc73a3ba3ab7a2b;
                lambertArray[124] = 0x2aa8a04ac3cbe1ee1c9c86361465dbb8;
                lambertArray[125] = 0x2a7fda392d725a44a2c8aeb9ab35430d;
                lambertArray[126] = 0x2a57741b18cde618717792b4faa216db;
                lambertArray[127] = 0x2a2f6c81f5d84dd950a35626d6d5503a;
            }
        
            /**
              * @dev should be executed after construction (too large for the constructor)
            */
            function init() public {
                initMaxExpArray();
                initLambertArray();
            }
        
            /**
              * @dev given a token supply, reserve balance, weight and a deposit amount (in the reserve token),
              * calculates the target amount for a given conversion (in the main token)
              *
              * Formula:
              * return = _supply * ((1 + _amount / _reserveBalance) ^ (_reserveWeight / 1000000) - 1)
              *
              * @param _supply          smart token supply
              * @param _reserveBalance  reserve balance
              * @param _reserveWeight   reserve weight, represented in ppm (1-1000000)
              * @param _amount          amount of reserve tokens to get the target amount for
              *
              * @return smart token amount
            */
            function purchaseTargetAmount(uint256 _supply,
                                          uint256 _reserveBalance,
                                          uint32 _reserveWeight,
                                          uint256 _amount)
                                          public view returns (uint256)
            {
                // validate input
                require(_supply > 0, "ERR_INVALID_SUPPLY");
                require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
        
                // special case for 0 deposit amount
                if (_amount == 0)
                    return 0;
        
                // special case if the weight = 100%
                if (_reserveWeight == MAX_WEIGHT)
                    return _supply.mul(_amount) / _reserveBalance;
        
                uint256 result;
                uint8 precision;
                uint256 baseN = _amount.add(_reserveBalance);
                (result, precision) = power(baseN, _reserveBalance, _reserveWeight, MAX_WEIGHT);
                uint256 temp = _supply.mul(result) >> precision;
                return temp - _supply;
            }
        
            /**
              * @dev given a token supply, reserve balance, weight and a sell amount (in the main token),
              * calculates the target amount for a given conversion (in the reserve token)
              *
              * Formula:
              * return = _reserveBalance * (1 - (1 - _amount / _supply) ^ (1000000 / _reserveWeight))
              *
              * @param _supply          smart token supply
              * @param _reserveBalance  reserve balance
              * @param _reserveWeight   reserve weight, represented in ppm (1-1000000)
              * @param _amount          amount of smart tokens to get the target amount for
              *
              * @return reserve token amount
            */
            function saleTargetAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveWeight,
                                      uint256 _amount)
                                      public view returns (uint256)
            {
                // validate input
                require(_supply > 0, "ERR_INVALID_SUPPLY");
                require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
                require(_amount <= _supply, "ERR_INVALID_AMOUNT");
        
                // special case for 0 sell amount
                if (_amount == 0)
                    return 0;
        
                // special case for selling the entire supply
                if (_amount == _supply)
                    return _reserveBalance;
        
                // special case if the weight = 100%
                if (_reserveWeight == MAX_WEIGHT)
                    return _reserveBalance.mul(_amount) / _supply;
        
                uint256 result;
                uint8 precision;
                uint256 baseD = _supply - _amount;
                (result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveWeight);
                uint256 temp1 = _reserveBalance.mul(result);
                uint256 temp2 = _reserveBalance << precision;
                return (temp1 - temp2) / result;
            }
        
            /**
              * @dev given two reserve balances/weights and a sell amount (in the first reserve token),
              * calculates the target amount for a conversion from the source reserve token to the target reserve token
              *
              * Formula:
              * return = _targetReserveBalance * (1 - (_sourceReserveBalance / (_sourceReserveBalance + _amount)) ^ (_sourceReserveWeight / _targetReserveWeight))
              *
              * @param _sourceReserveBalance    source reserve balance
              * @param _sourceReserveWeight     source reserve weight, represented in ppm (1-1000000)
              * @param _targetReserveBalance    target reserve balance
              * @param _targetReserveWeight     target reserve weight, represented in ppm (1-1000000)
              * @param _amount                  source reserve amount
              *
              * @return target reserve amount
            */
            function crossReserveTargetAmount(uint256 _sourceReserveBalance,
                                              uint32 _sourceReserveWeight,
                                              uint256 _targetReserveBalance,
                                              uint32 _targetReserveWeight,
                                              uint256 _amount)
                                              public view returns (uint256)
            {
                // validate input
                require(_sourceReserveBalance > 0 && _targetReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_sourceReserveWeight > 0 && _sourceReserveWeight <= MAX_WEIGHT &&
                        _targetReserveWeight > 0 && _targetReserveWeight <= MAX_WEIGHT, "ERR_INVALID_RESERVE_WEIGHT");
        
                // special case for equal weights
                if (_sourceReserveWeight == _targetReserveWeight)
                    return _targetReserveBalance.mul(_amount) / _sourceReserveBalance.add(_amount);
        
                uint256 result;
                uint8 precision;
                uint256 baseN = _sourceReserveBalance.add(_amount);
                (result, precision) = power(baseN, _sourceReserveBalance, _sourceReserveWeight, _targetReserveWeight);
                uint256 temp1 = _targetReserveBalance.mul(result);
                uint256 temp2 = _targetReserveBalance << precision;
                return (temp1 - temp2) / result;
            }
        
            /**
              * @dev given a smart token supply, reserve balance, reserve ratio and an amount of requested smart tokens,
              * calculates the amount of reserve tokens required for purchasing the given amount of smart tokens
              *
              * Formula:
              * return = _reserveBalance * (((_supply + _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio) - 1)
              *
              * @param _supply          smart token supply
              * @param _reserveBalance  reserve balance
              * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
              * @param _amount          requested amount of smart tokens
              *
              * @return reserve token amount
            */
            function fundCost(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveRatio,
                              uint256 _amount)
                              public view returns (uint256)
            {
                // validate input
                require(_supply > 0, "ERR_INVALID_SUPPLY");
                require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
        
                // special case for 0 amount
                if (_amount == 0)
                    return 0;
        
                // special case if the reserve ratio = 100%
                if (_reserveRatio == MAX_WEIGHT)
                    return (_amount.mul(_reserveBalance) - 1) / _supply + 1;
        
                uint256 result;
                uint8 precision;
                uint256 baseN = _supply.add(_amount);
                (result, precision) = power(baseN, _supply, MAX_WEIGHT, _reserveRatio);
                uint256 temp = ((_reserveBalance.mul(result) - 1) >> precision) + 1;
                return temp - _reserveBalance;
            }
        
            /**
              * @dev given a smart token supply, reserve balance, reserve ratio and an amount of reserve tokens to fund with,
              * calculates the amount of smart tokens received for purchasing with the given amount of reserve tokens
              *
              * Formula:
              * return = _supply * ((_amount / _reserveBalance + 1) ^ (_reserveRatio / MAX_WEIGHT) - 1)
              *
              * @param _supply          smart token supply
              * @param _reserveBalance  reserve balance
              * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
              * @param _amount          amount of reserve tokens to fund with
              *
              * @return smart token amount
            */
            function fundSupplyAmount(uint256 _supply,
                                      uint256 _reserveBalance,
                                      uint32 _reserveRatio,
                                      uint256 _amount)
                                      public view returns (uint256)
            {
                // validate input
                require(_supply > 0, "ERR_INVALID_SUPPLY");
                require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
        
                // special case for 0 amount
                if (_amount == 0)
                    return 0;
        
                // special case if the reserve ratio = 100%
                if (_reserveRatio == MAX_WEIGHT)
                    return _amount.mul(_supply) / _reserveBalance;
        
                uint256 result;
                uint8 precision;
                uint256 baseN = _reserveBalance.add(_amount);
                (result, precision) = power(baseN, _reserveBalance, _reserveRatio, MAX_WEIGHT);
                uint256 temp = _supply.mul(result) >> precision;
                return temp - _supply;
            }
        
            /**
              * @dev given a smart token supply, reserve balance, reserve ratio and an amount of smart tokens to liquidate,
              * calculates the amount of reserve tokens received for selling the given amount of smart tokens
              *
              * Formula:
              * return = _reserveBalance * (1 - ((_supply - _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio))
              *
              * @param _supply          smart token supply
              * @param _reserveBalance  reserve balance
              * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
              * @param _amount          amount of smart tokens to liquidate
              *
              * @return reserve token amount
            */
            function liquidateReserveAmount(uint256 _supply,
                                            uint256 _reserveBalance,
                                            uint32 _reserveRatio,
                                            uint256 _amount)
                                            public view returns (uint256)
            {
                // validate input
                require(_supply > 0, "ERR_INVALID_SUPPLY");
                require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2, "ERR_INVALID_RESERVE_RATIO");
                require(_amount <= _supply, "ERR_INVALID_AMOUNT");
        
                // special case for 0 amount
                if (_amount == 0)
                    return 0;
        
                // special case for liquidating the entire supply
                if (_amount == _supply)
                    return _reserveBalance;
        
                // special case if the reserve ratio = 100%
                if (_reserveRatio == MAX_WEIGHT)
                    return _amount.mul(_reserveBalance) / _supply;
        
                uint256 result;
                uint8 precision;
                uint256 baseD = _supply - _amount;
                (result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveRatio);
                uint256 temp1 = _reserveBalance.mul(result);
                uint256 temp2 = _reserveBalance << precision;
                return (temp1 - temp2) / result;
            }
        
            /**
              * @dev The arbitrage incentive is to convert to the point where the on-chain price is equal to the off-chain price.
              * We want this operation to also impact the primary reserve balance becoming equal to the primary reserve staked balance.
              * In other words, we want the arbitrager to convert the difference between the reserve balance and the reserve staked balance.
              *
              * Formula input:
              * - let t denote the primary reserve token staked balance
              * - let s denote the primary reserve token balance
              * - let r denote the secondary reserve token balance
              * - let q denote the numerator of the rate between the tokens
              * - let p denote the denominator of the rate between the tokens
              * Where p primary tokens are equal to q secondary tokens
              *
              * Formula output:
              * - compute x = W(t / r * q / p * log(s / t)) / log(s / t)
              * - return x / (1 + x) as the weight of the primary reserve token
              * - return 1 / (1 + x) as the weight of the secondary reserve token
              * Where W is the Lambert W Function
              *
              * If the rate-provider provides the rates for a common unit, for example:
              * - P = 2 ==> 2 primary reserve tokens = 1 ether
              * - Q = 3 ==> 3 secondary reserve tokens = 1 ether
              * Then you can simply use p = P and q = Q
              *
              * If the rate-provider provides the rates for a single unit, for example:
              * - P = 2 ==> 1 primary reserve token = 2 ethers
              * - Q = 3 ==> 1 secondary reserve token = 3 ethers
              * Then you can simply use p = Q and q = P
              *
              * @param _primaryReserveStakedBalance the primary reserve token staked balance
              * @param _primaryReserveBalance       the primary reserve token balance
              * @param _secondaryReserveBalance     the secondary reserve token balance
              * @param _reserveRateNumerator        the numerator of the rate between the tokens
              * @param _reserveRateDenominator      the denominator of the rate between the tokens
              *
              * Note that `numerator / denominator` should represent the amount of secondary tokens equal to one primary token
              *
              * @return the weight of the primary reserve token and the weight of the secondary reserve token, both in ppm (0-1000000)
            */
            function balancedWeights(uint256 _primaryReserveStakedBalance,
                                     uint256 _primaryReserveBalance,
                                     uint256 _secondaryReserveBalance,
                                     uint256 _reserveRateNumerator,
                                     uint256 _reserveRateDenominator)
                                     public view returns (uint32, uint32)
            {
                if (_primaryReserveStakedBalance == _primaryReserveBalance)
                    require(_primaryReserveStakedBalance > 0 || _secondaryReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                else
                    require(_primaryReserveStakedBalance > 0 && _primaryReserveBalance > 0 && _secondaryReserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
                require(_reserveRateNumerator > 0 && _reserveRateDenominator > 0, "ERR_INVALID_RESERVE_RATE");
        
                uint256 tq = _primaryReserveStakedBalance.mul(_reserveRateNumerator);
                uint256 rp = _secondaryReserveBalance.mul(_reserveRateDenominator);
        
                if (_primaryReserveStakedBalance < _primaryReserveBalance)
                    return balancedWeightsByStake(_primaryReserveBalance, _primaryReserveStakedBalance, tq, rp, true);
        
                if (_primaryReserveStakedBalance > _primaryReserveBalance)
                    return balancedWeightsByStake(_primaryReserveStakedBalance, _primaryReserveBalance, tq, rp, false);
        
                return normalizedWeights(tq, rp);
            }
        
            /**
              * @dev General Description:
              *     Determine a value of precision.
              *     Calculate an integer approximation of (_baseN / _baseD) ^ (_expN / _expD) * 2 ^ precision.
              *     Return the result along with the precision used.
              *
              * Detailed Description:
              *     Instead of calculating "base ^ exp", we calculate "e ^ (log(base) * exp)".
              *     The value of "log(base)" is represented with an integer slightly smaller than "log(base) * 2 ^ precision".
              *     The larger "precision" is, the more accurately this value represents the real value.
              *     However, the larger "precision" is, the more bits are required in order to store this value.
              *     And the exponentiation function, which takes "x" and calculates "e ^ x", is limited to a maximum exponent (maximum value of "x").
              *     This maximum exponent depends on the "precision" used, and it is given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
              *     Hence we need to determine the highest precision which can be used for the given input, before calling the exponentiation function.
              *     This allows us to compute "base ^ exp" with maximum accuracy and without exceeding 256 bits in any of the intermediate computations.
              *     This functions assumes that "_expN < 2 ^ 256 / log(MAX_NUM - 1)", otherwise the multiplication should be replaced with a "safeMul".
              *     Since we rely on unsigned-integer arithmetic and "base < 1" ==> "log(base) < 0", this function does not support "_baseN < _baseD".
            */
            function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) internal view returns (uint256, uint8) {
                require(_baseN < MAX_NUM);
        
                uint256 baseLog;
                uint256 base = _baseN * FIXED_1 / _baseD;
                if (base < OPT_LOG_MAX_VAL) {
                    baseLog = optimalLog(base);
                }
                else {
                    baseLog = generalLog(base);
                }
        
                uint256 baseLogTimesExp = baseLog * _expN / _expD;
                if (baseLogTimesExp < OPT_EXP_MAX_VAL) {
                    return (optimalExp(baseLogTimesExp), MAX_PRECISION);
                }
                else {
                    uint8 precision = findPositionInMaxExpArray(baseLogTimesExp);
                    return (generalExp(baseLogTimesExp >> (MAX_PRECISION - precision), precision), precision);
                }
            }
        
            /**
              * @dev computes log(x / FIXED_1) * FIXED_1.
              * This functions assumes that "x >= FIXED_1", because the output would be negative otherwise.
            */
            function generalLog(uint256 x) internal pure returns (uint256) {
                uint256 res = 0;
        
                // If x >= 2, then we compute the integer part of log2(x), which is larger than 0.
                if (x >= FIXED_2) {
                    uint8 count = floorLog2(x / FIXED_1);
                    x >>= count; // now x < 2
                    res = count * FIXED_1;
                }
        
                // If x > 1, then we compute the fraction part of log2(x), which is larger than 0.
                if (x > FIXED_1) {
                    for (uint8 i = MAX_PRECISION; i > 0; --i) {
                        x = (x * x) / FIXED_1; // now 1 < x < 4
                        if (x >= FIXED_2) {
                            x >>= 1; // now 1 < x < 2
                            res += ONE << (i - 1);
                        }
                    }
                }
        
                return res * LN2_NUMERATOR / LN2_DENOMINATOR;
            }
        
            /**
              * @dev computes the largest integer smaller than or equal to the binary logarithm of the input.
            */
            function floorLog2(uint256 _n) internal pure returns (uint8) {
                uint8 res = 0;
        
                if (_n < 256) {
                    // At most 8 iterations
                    while (_n > 1) {
                        _n >>= 1;
                        res += 1;
                    }
                }
                else {
                    // Exactly 8 iterations
                    for (uint8 s = 128; s > 0; s >>= 1) {
                        if (_n >= (ONE << s)) {
                            _n >>= s;
                            res |= s;
                        }
                    }
                }
        
                return res;
            }
        
            /**
              * @dev the global "maxExpArray" is sorted in descending order, and therefore the following statements are equivalent:
              * - This function finds the position of [the smallest value in "maxExpArray" larger than or equal to "x"]
              * - This function finds the highest position of [a value in "maxExpArray" larger than or equal to "x"]
            */
            function findPositionInMaxExpArray(uint256 _x) internal view returns (uint8) {
                uint8 lo = MIN_PRECISION;
                uint8 hi = MAX_PRECISION;
        
                while (lo + 1 < hi) {
                    uint8 mid = (lo + hi) / 2;
                    if (maxExpArray[mid] >= _x)
                        lo = mid;
                    else
                        hi = mid;
                }
        
                if (maxExpArray[hi] >= _x)
                    return hi;
                if (maxExpArray[lo] >= _x)
                    return lo;
        
                require(false);
            }
        
            /**
              * @dev this function can be auto-generated by the script 'PrintFunctionGeneralExp.py'.
              * it approximates "e ^ x" via maclaurin summation: "(x^0)/0! + (x^1)/1! + ... + (x^n)/n!".
              * it returns "e ^ (x / 2 ^ precision) * 2 ^ precision", that is, the result is upshifted for accuracy.
              * the global "maxExpArray" maps each "precision" to "((maximumExponent + 1) << (MAX_PRECISION - precision)) - 1".
              * the maximum permitted value for "x" is therefore given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
            */
            function generalExp(uint256 _x, uint8 _precision) internal pure returns (uint256) {
                uint256 xi = _x;
                uint256 res = 0;
        
                xi = (xi * _x) >> _precision; res += xi * 0x3442c4e6074a82f1797f72ac0000000; // add x^02 * (33! / 02!)
                xi = (xi * _x) >> _precision; res += xi * 0x116b96f757c380fb287fd0e40000000; // add x^03 * (33! / 03!)
                xi = (xi * _x) >> _precision; res += xi * 0x045ae5bdd5f0e03eca1ff4390000000; // add x^04 * (33! / 04!)
                xi = (xi * _x) >> _precision; res += xi * 0x00defabf91302cd95b9ffda50000000; // add x^05 * (33! / 05!)
                xi = (xi * _x) >> _precision; res += xi * 0x002529ca9832b22439efff9b8000000; // add x^06 * (33! / 06!)
                xi = (xi * _x) >> _precision; res += xi * 0x00054f1cf12bd04e516b6da88000000; // add x^07 * (33! / 07!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000a9e39e257a09ca2d6db51000000; // add x^08 * (33! / 08!)
                xi = (xi * _x) >> _precision; res += xi * 0x000012e066e7b839fa050c309000000; // add x^09 * (33! / 09!)
                xi = (xi * _x) >> _precision; res += xi * 0x000001e33d7d926c329a1ad1a800000; // add x^10 * (33! / 10!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000002bee513bdb4a6b19b5f800000; // add x^11 * (33! / 11!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000003a9316fa79b88eccf2a00000; // add x^12 * (33! / 12!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000048177ebe1fa812375200000; // add x^13 * (33! / 13!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000005263fe90242dcbacf00000; // add x^14 * (33! / 14!)
                xi = (xi * _x) >> _precision; res += xi * 0x000000000057e22099c030d94100000; // add x^15 * (33! / 15!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000057e22099c030d9410000; // add x^16 * (33! / 16!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000052b6b54569976310000; // add x^17 * (33! / 17!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000004985f67696bf748000; // add x^18 * (33! / 18!)
                xi = (xi * _x) >> _precision; res += xi * 0x000000000000003dea12ea99e498000; // add x^19 * (33! / 19!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000000031880f2214b6e000; // add x^20 * (33! / 20!)
                xi = (xi * _x) >> _precision; res += xi * 0x000000000000000025bcff56eb36000; // add x^21 * (33! / 21!)
                xi = (xi * _x) >> _precision; res += xi * 0x000000000000000001b722e10ab1000; // add x^22 * (33! / 22!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000001317c70077000; // add x^23 * (33! / 23!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000cba84aafa00; // add x^24 * (33! / 24!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000082573a0a00; // add x^25 * (33! / 25!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000005035ad900; // add x^26 * (33! / 26!)
                xi = (xi * _x) >> _precision; res += xi * 0x000000000000000000000002f881b00; // add x^27 * (33! / 27!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000001b29340; // add x^28 * (33! / 28!)
                xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000000000efc40; // add x^29 * (33! / 29!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000007fe0; // add x^30 * (33! / 30!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000420; // add x^31 * (33! / 31!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000021; // add x^32 * (33! / 32!)
                xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000001; // add x^33 * (33! / 33!)
        
                return res / 0x688589cc0e9505e2f2fee5580000000 + _x + (ONE << _precision); // divide by 33! and then add x^1 / 1! + x^0 / 0!
            }
        
            /**
              * @dev computes log(x / FIXED_1) * FIXED_1
              * Input range: FIXED_1 <= x <= OPT_LOG_MAX_VAL - 1
              * Auto-generated via 'PrintFunctionOptimalLog.py'
              * Detailed description:
              * - Rewrite the input as a product of natural exponents and a single residual r, such that 1 < r < 2
              * - The natural logarithm of each (pre-calculated) exponent is the degree of the exponent
              * - The natural logarithm of r is calculated via Taylor series for log(1 + x), where x = r - 1
              * - The natural logarithm of the input is calculated by summing up the intermediate results above
              * - For example: log(250) = log(e^4 * e^1 * e^0.5 * 1.021692859) = 4 + 1 + 0.5 + log(1 + 0.021692859)
            */
            function optimalLog(uint256 x) internal pure returns (uint256) {
                uint256 res = 0;
        
                uint256 y;
                uint256 z;
                uint256 w;
        
                if (x >= 0xd3094c70f034de4b96ff7d5b6f99fcd8) {res += 0x40000000000000000000000000000000; x = x * FIXED_1 / 0xd3094c70f034de4b96ff7d5b6f99fcd8;} // add 1 / 2^1
                if (x >= 0xa45af1e1f40c333b3de1db4dd55f29a7) {res += 0x20000000000000000000000000000000; x = x * FIXED_1 / 0xa45af1e1f40c333b3de1db4dd55f29a7;} // add 1 / 2^2
                if (x >= 0x910b022db7ae67ce76b441c27035c6a1) {res += 0x10000000000000000000000000000000; x = x * FIXED_1 / 0x910b022db7ae67ce76b441c27035c6a1;} // add 1 / 2^3
                if (x >= 0x88415abbe9a76bead8d00cf112e4d4a8) {res += 0x08000000000000000000000000000000; x = x * FIXED_1 / 0x88415abbe9a76bead8d00cf112e4d4a8;} // add 1 / 2^4
                if (x >= 0x84102b00893f64c705e841d5d4064bd3) {res += 0x04000000000000000000000000000000; x = x * FIXED_1 / 0x84102b00893f64c705e841d5d4064bd3;} // add 1 / 2^5
                if (x >= 0x8204055aaef1c8bd5c3259f4822735a2) {res += 0x02000000000000000000000000000000; x = x * FIXED_1 / 0x8204055aaef1c8bd5c3259f4822735a2;} // add 1 / 2^6
                if (x >= 0x810100ab00222d861931c15e39b44e99) {res += 0x01000000000000000000000000000000; x = x * FIXED_1 / 0x810100ab00222d861931c15e39b44e99;} // add 1 / 2^7
                if (x >= 0x808040155aabbbe9451521693554f733) {res += 0x00800000000000000000000000000000; x = x * FIXED_1 / 0x808040155aabbbe9451521693554f733;} // add 1 / 2^8
        
                z = y = x - FIXED_1;
                w = y * y / FIXED_1;
                res += z * (0x100000000000000000000000000000000 - y) / 0x100000000000000000000000000000000; z = z * w / FIXED_1; // add y^01 / 01 - y^02 / 02
                res += z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y) / 0x200000000000000000000000000000000; z = z * w / FIXED_1; // add y^03 / 03 - y^04 / 04
                res += z * (0x099999999999999999999999999999999 - y) / 0x300000000000000000000000000000000; z = z * w / FIXED_1; // add y^05 / 05 - y^06 / 06
                res += z * (0x092492492492492492492492492492492 - y) / 0x400000000000000000000000000000000; z = z * w / FIXED_1; // add y^07 / 07 - y^08 / 08
                res += z * (0x08e38e38e38e38e38e38e38e38e38e38e - y) / 0x500000000000000000000000000000000; z = z * w / FIXED_1; // add y^09 / 09 - y^10 / 10
                res += z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y) / 0x600000000000000000000000000000000; z = z * w / FIXED_1; // add y^11 / 11 - y^12 / 12
                res += z * (0x089d89d89d89d89d89d89d89d89d89d89 - y) / 0x700000000000000000000000000000000; z = z * w / FIXED_1; // add y^13 / 13 - y^14 / 14
                res += z * (0x088888888888888888888888888888888 - y) / 0x800000000000000000000000000000000;                      // add y^15 / 15 - y^16 / 16
        
                return res;
            }
        
            /**
              * @dev computes e ^ (x / FIXED_1) * FIXED_1
              * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1
              * auto-generated via 'PrintFunctionOptimalExp.py'
              * Detailed description:
              * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible
              * - The exponentiation of each binary exponent is given (pre-calculated)
              * - The exponentiation of r is calculated via Taylor series for e^x, where x = r
              * - The exponentiation of the input is calculated by multiplying the intermediate results above
              * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859
            */
            function optimalExp(uint256 x) internal pure returns (uint256) {
                uint256 res = 0;
        
                uint256 y;
                uint256 z;
        
                z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3)
                z = z * y / FIXED_1; res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
                z = z * y / FIXED_1; res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
                z = z * y / FIXED_1; res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
                z = z * y / FIXED_1; res += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
                z = z * y / FIXED_1; res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
                z = z * y / FIXED_1; res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
                z = z * y / FIXED_1; res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
                z = z * y / FIXED_1; res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
                z = z * y / FIXED_1; res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
                z = z * y / FIXED_1; res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
                z = z * y / FIXED_1; res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
                z = z * y / FIXED_1; res += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
                z = z * y / FIXED_1; res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
                z = z * y / FIXED_1; res += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
                z = z * y / FIXED_1; res += z * 0x000000000001c638; // add y^16 * (20! / 16!)
                z = z * y / FIXED_1; res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
                z = z * y / FIXED_1; res += z * 0x000000000000017c; // add y^18 * (20! / 18!)
                z = z * y / FIXED_1; res += z * 0x0000000000000014; // add y^19 * (20! / 19!)
                z = z * y / FIXED_1; res += z * 0x0000000000000001; // add y^20 * (20! / 20!)
                res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
        
                if ((x & 0x010000000000000000000000000000000) != 0) res = res * 0x1c3d6a24ed82218787d624d3e5eba95f9 / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3)
                if ((x & 0x020000000000000000000000000000000) != 0) res = res * 0x18ebef9eac820ae8682b9793ac6d1e778 / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2)
                if ((x & 0x040000000000000000000000000000000) != 0) res = res * 0x1368b2fc6f9609fe7aceb46aa619baed5 / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1)
                if ((x & 0x080000000000000000000000000000000) != 0) res = res * 0x0bc5ab1b16779be3575bd8f0520a9f21e / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0)
                if ((x & 0x100000000000000000000000000000000) != 0) res = res * 0x0454aaa8efe072e7f6ddbab84b40a55c5 / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1)
                if ((x & 0x200000000000000000000000000000000) != 0) res = res * 0x00960aadc109e7a3bf4578099615711d7 / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2)
                if ((x & 0x400000000000000000000000000000000) != 0) res = res * 0x0002bf84208204f5977f9a8cf01fdc307 / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3)
        
                return res;
            }
        
            /**
              * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
            */
            function lowerStake(uint256 _x) internal view returns (uint256) {
                if (_x <= LAMBERT_CONV_RADIUS)
                    return lambertPos1(_x);
                if (_x <= LAMBERT_POS2_MAXVAL)
                    return lambertPos2(_x);
                if (_x <= LAMBERT_POS3_MAXVAL)
                    return lambertPos3(_x);
                require(false);
            }
        
            /**
              * @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
            */
            function higherStake(uint256 _x) internal pure returns (uint256) {
                if (_x <= LAMBERT_CONV_RADIUS)
                    return lambertNeg1(_x);
                return FIXED_1 * FIXED_1 / _x;
            }
        
            /**
              * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
              * input range: 1 <= x <= 1 / e * FIXED_1
              * auto-generated via 'PrintFunctionLambertPos1.py'
            */
            function lambertPos1(uint256 _x) internal pure returns (uint256) {
                uint256 xi = _x;
                uint256 res = (FIXED_1 - _x) * 0xde1bc4d19efcac82445da75b00000000; // x^(1-1) * (34! * 1^(1-1) / 1!) - x^(2-1) * (34! * 2^(2-1) / 2!)
        
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // sub x^(04-1) * (34! * 04^(04-1) / 04!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // sub x^(06-1) * (34! * 06^(06-1) / 06!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x000000002d207601f46a99b4112418400000000000; // sub x^(08-1) * (34! * 08^(08-1) / 08!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // sub x^(10-1) * (34! * 10^(10-1) / 10!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // sub x^(12-1) * (34! * 12^(12-1) / 12!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // sub x^(14-1) * (34! * 14^(14-1) / 14!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x000000bac08546b867cdaa20000000000000000000; // sub x^(16-1) * (34! * 16^(16-1) / 16!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x000004851d99f82060df265f3309b26f8200000000; // sub x^(18-1) * (34! * 18^(18-1) / 18!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // sub x^(20-1) * (34! * 20^(20-1) / 20!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // sub x^(22-1) * (34! * 22^(22-1) / 22!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // sub x^(24-1) * (34! * 24^(24-1) / 24!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // sub x^(26-1) * (34! * 26^(26-1) / 26!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // sub x^(28-1) * (34! * 28^(28-1) / 28!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x053a044ebd984351493e1786af38d39a0800000000; // sub x^(30-1) * (34! * 30^(30-1) / 30!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0x231000000000000000000000000000000000000000; // sub x^(32-1) * (34! * 32^(32-1) / 32!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
                xi = (xi * _x) / FIXED_1; res -= xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // sub x^(34-1) * (34! * 34^(34-1) / 34!)
        
                return res / 0xde1bc4d19efcac82445da75b00000000; // divide by 34!
            }
        
            /**
              * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
              * input range: LAMBERT_CONV_RADIUS + 1 <= x <= LAMBERT_POS2_MAXVAL
            */
            function lambertPos2(uint256 _x) internal view returns (uint256) {
                uint256 x = _x - LAMBERT_CONV_RADIUS - 1;
                uint256 i = x / LAMBERT_POS2_SAMPLE;
                uint256 a = LAMBERT_POS2_SAMPLE * i;
                uint256 b = LAMBERT_POS2_SAMPLE * (i + 1);
                uint256 c = lambertArray[i];
                uint256 d = lambertArray[i + 1];
                return (c * (b - x) + d * (x - a)) / LAMBERT_POS2_SAMPLE;
            }
        
            /**
              * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
              * input range: LAMBERT_POS2_MAXVAL + 1 <= x <= LAMBERT_POS3_MAXVAL
            */
            function lambertPos3(uint256 _x) internal pure returns (uint256) {
                uint256 l1 = _x < OPT_LOG_MAX_VAL ? optimalLog(_x) : generalLog(_x);
                uint256 l2 = l1 < OPT_LOG_MAX_VAL ? optimalLog(l1) : generalLog(l1);
                return (l1 - l2 + l2 * FIXED_1 / l1) * FIXED_1 / _x;
            }
        
            /**
              * @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
              * input range: 1 <= x <= 1 / e * FIXED_1
              * auto-generated via 'PrintFunctionLambertNeg1.py'
            */
            function lambertNeg1(uint256 _x) internal pure returns (uint256) {
                uint256 xi = _x;
                uint256 res = 0;
        
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // add x^(04-1) * (34! * 04^(04-1) / 04!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // add x^(06-1) * (34! * 06^(06-1) / 06!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000002d207601f46a99b4112418400000000000; // add x^(08-1) * (34! * 08^(08-1) / 08!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // add x^(10-1) * (34! * 10^(10-1) / 10!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // add x^(12-1) * (34! * 12^(12-1) / 12!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // add x^(14-1) * (34! * 14^(14-1) / 14!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000000bac08546b867cdaa20000000000000000000; // add x^(16-1) * (34! * 16^(16-1) / 16!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000004851d99f82060df265f3309b26f8200000000; // add x^(18-1) * (34! * 18^(18-1) / 18!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // add x^(20-1) * (34! * 20^(20-1) / 20!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // add x^(22-1) * (34! * 22^(22-1) / 22!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // add x^(24-1) * (34! * 24^(24-1) / 24!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // add x^(26-1) * (34! * 26^(26-1) / 26!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // add x^(28-1) * (34! * 28^(28-1) / 28!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x053a044ebd984351493e1786af38d39a0800000000; // add x^(30-1) * (34! * 30^(30-1) / 30!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x231000000000000000000000000000000000000000; // add x^(32-1) * (34! * 32^(32-1) / 32!)
                xi = (xi * _x) / FIXED_1; res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
                xi = (xi * _x) / FIXED_1; res += xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // add x^(34-1) * (34! * 34^(34-1) / 34!)
        
                return res / 0xde1bc4d19efcac82445da75b00000000 + _x + FIXED_1; // divide by 34! and then add x^(2-1) * (34! * 2^(2-1) / 2!) + x^(1-1) * (34! * 1^(1-1) / 1!)
            }
        
            /**
              * @dev computes the weights based on "W(log(hi / lo) * tq / rp) * tq / rp", where "W" is a variation of the Lambert W function.
            */
            function balancedWeightsByStake(uint256 _hi, uint256 _lo, uint256 _tq, uint256 _rp, bool _lowerStake) internal view returns (uint32, uint32) {
                (_tq, _rp) = safeFactors(_tq, _rp);
                uint256 f = _hi.mul(FIXED_1) / _lo;
                uint256 g = f < OPT_LOG_MAX_VAL ? optimalLog(f) : generalLog(f);
                uint256 x = g.mul(_tq) / _rp;
                uint256 y = _lowerStake ? lowerStake(x) : higherStake(x);
                return normalizedWeights(y.mul(_tq), _rp.mul(FIXED_1));
            }
        
            /**
              * @dev reduces "a" and "b" while maintaining their ratio.
            */
            function safeFactors(uint256 _a, uint256 _b) internal pure returns (uint256, uint256) {
                if (_a <= FIXED_2 && _b <= FIXED_2)
                    return (_a, _b);
                if (_a < FIXED_2)
                    return (_a * FIXED_2 / _b, FIXED_2);
                if (_b < FIXED_2)
                    return (FIXED_2, _b * FIXED_2 / _a);
                uint256 c = _a > _b ? _a : _b;
                uint256 n = floorLog2(c / FIXED_1);
                return (_a >> n, _b >> n);
            }
        
            /**
              * @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)".
            */
            function normalizedWeights(uint256 _a, uint256 _b) internal pure returns (uint32, uint32) {
                if (_a <= _b)
                    return accurateWeights(_a, _b);
                (uint32 y, uint32 x) = accurateWeights(_b, _a);
                return (x, y);
            }
        
            /**
              * @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)", assuming that "a <= b".
            */
            function accurateWeights(uint256 _a, uint256 _b) internal pure returns (uint32, uint32) {
                if (_a > MAX_UNF_WEIGHT) {
                    uint256 c = _a / (MAX_UNF_WEIGHT + 1) + 1;
                    _a /= c;
                    _b /= c;
                }
                uint256 x = roundDiv(_a * MAX_WEIGHT, _a.add(_b));
                uint256 y = MAX_WEIGHT - x;
                return (uint32(x), uint32(y));
            }
        
            /**
              * @dev computes the nearest integer to a given quotient without overflowing or underflowing.
            */
            function roundDiv(uint256 _n, uint256 _d) internal pure returns (uint256) {
                return _n / _d + _n % _d / (_d - _d / 2);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculatePurchaseReturn(uint256 _supply,
                                             uint256 _reserveBalance,
                                             uint32 _reserveWeight,
                                             uint256 _amount)
                                             public view returns (uint256)
            {
                return purchaseTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculateSaleReturn(uint256 _supply,
                                         uint256 _reserveBalance,
                                         uint32 _reserveWeight,
                                         uint256 _amount)
                                         public view returns (uint256)
            {
                return saleTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculateCrossReserveReturn(uint256 _sourceReserveBalance,
                                                 uint32 _sourceReserveWeight,
                                                 uint256 _targetReserveBalance,
                                                 uint32 _targetReserveWeight,
                                                 uint256 _amount)
                                                 public view returns (uint256)
            {
                return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculateCrossConnectorReturn(uint256 _sourceReserveBalance,
                                                   uint32 _sourceReserveWeight,
                                                   uint256 _targetReserveBalance,
                                                   uint32 _targetReserveWeight,
                                                   uint256 _amount)
                                                   public view returns (uint256)
            {
                return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculateFundCost(uint256 _supply,
                                       uint256 _reserveBalance,
                                       uint32 _reserveRatio,
                                       uint256 _amount)
                                       public view returns (uint256)
            {
                return fundCost(_supply, _reserveBalance, _reserveRatio, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function calculateLiquidateReturn(uint256 _supply,
                                              uint256 _reserveBalance,
                                              uint32 _reserveRatio,
                                              uint256 _amount)
                                              public view returns (uint256)
            {
                return liquidateReserveAmount(_supply, _reserveBalance, _reserveRatio, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function purchaseRate(uint256 _supply,
                                  uint256 _reserveBalance,
                                  uint32 _reserveWeight,
                                  uint256 _amount)
                                  public view returns (uint256)
            {
                return purchaseTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function saleRate(uint256 _supply,
                              uint256 _reserveBalance,
                              uint32 _reserveWeight,
                              uint256 _amount)
                              public view returns (uint256)
            {
                return saleTargetAmount(_supply, _reserveBalance, _reserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function crossReserveRate(uint256 _sourceReserveBalance,
                                      uint32 _sourceReserveWeight,
                                      uint256 _targetReserveBalance,
                                      uint32 _targetReserveWeight,
                                      uint256 _amount)
                                      public view returns (uint256)
            {
                return crossReserveTargetAmount(_sourceReserveBalance, _sourceReserveWeight, _targetReserveBalance, _targetReserveWeight, _amount);
            }
        
            /**
              * @dev deprecated, backward compatibility
            */
            function liquidateRate(uint256 _supply,
                                   uint256 _reserveBalance,
                                   uint32 _reserveRatio,
                                   uint256 _amount)
                                   public view returns (uint256)
            {
                return liquidateReserveAmount(_supply, _reserveBalance, _reserveRatio, _amount);
            }
        }