ETH Price: $1,846.80 (-2.49%)

Transaction Decoder

Block:
10285597 at Jun-17-2020 09:14:50 PM +UTC
Transaction Fee:
0.011425779 ETH $21.10
Gas Used:
423,177 Gas / 27 Gwei

Emitted Events:

20 BandToken.Transfer( from=[Sender] 0xa14710e6a47007116b889794b2b22482d1ec76ad, to=KyberNetwork, value=1078047427636864622705 )
21 BandToken.Approval( owner=[Sender] 0xa14710e6a47007116b889794b2b22482d1ec76ad, spender=[Receiver] KyberNetworkProxy, value=57896044618658097711785492504343953926634992332820282018650744576319700197263 )
22 BandToken.Transfer( from=KyberNetwork, to=KyberReserve, value=1078047427636864622705 )
23 BandToken.Approval( owner=KyberNetwork, spender=KyberReserve, value=57896044618658097711785492504343953926634992332820280817469388237321956952250 )
24 KyberNetwork.EtherReceival( sender=KyberReserve, amount=5638204898064034402 )
25 KyberReserve.TradeExecute( origin=KyberNetwork, src=BandToken, srcAmount=1078047427636864622705, destToken=0xEeeeeEee...eeeeeEEeE, destAmount=5638204898064034402, destAddress=KyberNetwork )
26 FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0xF1AA99C6...E35Cc504d, walletFee=1333662890827762704 )
27 FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=1333662890827762705 )
28 KyberNetwork.KyberTrade( trader=[Sender] 0xa14710e6a47007116b889794b2b22482d1ec76ad, src=BandToken, dest=0xEeeeeEee...eeeeeEEeE, srcAmount=1078047427636864622705, dstAmount=5638204898064034402, destAddress=[Sender] 0xa14710e6a47007116b889794b2b22482d1ec76ad, ethWeiValue=5638204898064034402, reserve1=KyberReserve, reserve2=0x00000000...000000000, hint=0x5045524D )
29 KyberNetworkProxy.ExecuteTrade( trader=[Sender] 0xa14710e6a47007116b889794b2b22482d1ec76ad, src=BandToken, dest=0xEeeeeEee...eeeeeEEeE, actualSrcAmount=1078047427636864622705, actualDestAmount=5638204898064034402 )

Account State Difference:

  Address   Before After State Difference Code
0x5c594600...37E8598ED
0x65bF64Ff...77ced86Cd
(Kyber: Contract)
0x8007aa43...E5fF626d1
(Kyber: Fee Burner)
0xA14710E6...2D1Ec76Ad
0.103678097 Eth
Nonce: 3
5.730457216064034402 Eth
Nonce: 4
5.626779119064034402
0xb06Cf173...53D8E4f90
(Kyber: Reserve BAND)
86.166688332253118955 Eth80.528483434189084553 Eth5.638204898064034402
0xBA11D00c...A186d7f55
(Ethermine)
1,526.851362598759384206 Eth1,526.862788377759384206 Eth0.011425779

Execution Trace

KyberNetworkProxy.tradeWithHint( src=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, srcAmount=1078047427636864622705, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0xA14710E6a47007116b889794b2B22482D1Ec76Ad, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=5073115162577374, walletId=0xF1AA99C69715F423086008eB9D06Dc1E35Cc504d, hint=0x5045524D ) => ( 5638204898064034402 )
  • BandToken.balanceOf( owner=0xA14710E6a47007116b889794b2B22482D1Ec76Ad ) => ( 1078047427636864622705 )
  • BandToken.transferFrom( from=0xA14710E6a47007116b889794b2B22482D1Ec76Ad, to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, value=1078047427636864622705 ) => ( True )
  • KyberNetwork.tradeWithHint( trader=0xA14710E6a47007116b889794b2B22482D1Ec76Ad, src=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, srcAmount=1078047427636864622705, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0xA14710E6a47007116b889794b2B22482D1Ec76Ad, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=5073115162577374, walletId=0xF1AA99C69715F423086008eB9D06Dc1E35Cc504d, hint=0x5045524D ) => ( 5638204898064034402 )
    • BandToken.balanceOf( owner=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd ) => ( 1078047427636864631353 )
    • KyberReserve.getConversionRate( src=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=1078047427636864622705, blockNumber=10285597 ) => ( 5230015631523067 )
      • LiquidityConversionRates.getRate( conversionToken=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, currentBlockNumber=10285597, buy=False, qtyInSrcWei=1078047427636864622705 ) => ( 5230015631523067 )
      • KyberUniswapReserve.getConversionRate( src=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=1078047427636864622705, blockNumber=10285597 ) => ( 4510799327771562 )
        • Vyper_contract.getTokenToEthInputPrice( tokens_sold=1078047427636864622705 ) => ( out=4862855611890231484 )
          • Vyper_contract.getTokenToEthInputPrice( tokens_sold=1078047427636864622705 ) => ( out=4862855611890231484 )
            • BandToken.balanceOf( owner=0x88616Cf228FFb3fB8ba336592A79A53De53D05eF ) => ( 5778184528778478810244 )
            • Vyper_contract.getEthToTokenInputPrice( eth_sold=4862855611890231484 ) => ( out=781345242876851318536 )
              • Vyper_contract.getEthToTokenInputPrice( eth_sold=4862855611890231484 ) => ( out=781345242876851318536 )
                • BandToken.balanceOf( owner=0x88616Cf228FFb3fB8ba336592A79A53De53D05eF ) => ( 5778184528778478810244 )
                • KyberReserve.trade( srcToken=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, srcAmount=1078047427636864622705, destToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=5230015631523067, validate=True ) => ( True )
                  • LiquidityConversionRates.recordImbalance( conversionToken=0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55, buyAmountInTwei=-1078047427636864622705, rateUpdateBlock=0, currentBlock=10285597 )
                  • BandToken.transferFrom( from=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, to=0xb06Cf173DA7E297aa6268139c7Cb67C53D8E4f90, value=1078047427636864622705 ) => ( True )
                  • ETH 5.638204898064034402 KyberNetwork.CALL( )
                  • ETH 5.638204898064034402 0xa14710e6a47007116b889794b2b22482d1ec76ad.CALL( )
                  • FeeBurner.handleFees( tradeWeiAmount=5638204898064034402, reserve=0xb06Cf173DA7E297aa6268139c7Cb67C53D8E4f90, wallet=0xF1AA99C69715F423086008eB9D06Dc1E35Cc504d ) => ( True )
                  • BandToken.balanceOf( owner=0xA14710E6a47007116b889794b2B22482D1Ec76Ad ) => ( 0 )
                    File 1 of 9: KyberNetworkProxy
                    pragma solidity 0.4.18;
                    
                    // File: contracts/ERC20Interface.sol
                    
                    // https://github.com/ethereum/EIPs/issues/20
                    interface ERC20 {
                        function totalSupply() public view returns (uint supply);
                        function balanceOf(address _owner) public view returns (uint balance);
                        function transfer(address _to, uint _value) public returns (bool success);
                        function transferFrom(address _from, address _to, uint _value) public returns (bool success);
                        function approve(address _spender, uint _value) public returns (bool success);
                        function allowance(address _owner, address _spender) public view returns (uint remaining);
                        function decimals() public view returns(uint digits);
                        event Approval(address indexed _owner, address indexed _spender, uint _value);
                    }
                    
                    // File: contracts/KyberNetworkInterface.sol
                    
                    /// @title Kyber Network interface
                    interface KyberNetworkInterface {
                        function maxGasPrice() public view returns(uint);
                        function getUserCapInWei(address user) public view returns(uint);
                        function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);
                        function enabled() public view returns(bool);
                        function info(bytes32 id) public view returns(uint);
                    
                        function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view
                            returns (uint expectedRate, uint slippageRate);
                    
                        function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,
                            uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);
                    }
                    
                    // File: contracts/KyberNetworkProxyInterface.sol
                    
                    /// @title Kyber Network interface
                    interface KyberNetworkProxyInterface {
                        function maxGasPrice() public view returns(uint);
                        function getUserCapInWei(address user) public view returns(uint);
                        function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);
                        function enabled() public view returns(bool);
                        function info(bytes32 id) public view returns(uint);
                    
                        function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view
                            returns (uint expectedRate, uint slippageRate);
                    
                        function tradeWithHint(ERC20 src, uint srcAmount, ERC20 dest, address destAddress, uint maxDestAmount,
                            uint minConversionRate, address walletId, bytes hint) public payable returns(uint);
                    }
                    
                    // File: contracts/SimpleNetworkInterface.sol
                    
                    /// @title simple interface for Kyber Network 
                    interface SimpleNetworkInterface {
                        function swapTokenToToken(ERC20 src, uint srcAmount, ERC20 dest, uint minConversionRate) public returns(uint);
                        function swapEtherToToken(ERC20 token, uint minConversionRate) public payable returns(uint);
                        function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) public returns(uint);
                    }
                    
                    // File: contracts/Utils.sol
                    
                    /// @title Kyber constants contract
                    contract Utils {
                    
                        ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
                        uint  constant internal PRECISION = (10**18);
                        uint  constant internal MAX_QTY   = (10**28); // 10B tokens
                        uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
                        uint  constant internal MAX_DECIMALS = 18;
                        uint  constant internal ETH_DECIMALS = 18;
                        mapping(address=>uint) internal decimals;
                    
                        function setDecimals(ERC20 token) internal {
                            if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
                            else decimals[token] = token.decimals();
                        }
                    
                        function getDecimals(ERC20 token) internal view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
                            uint tokenDecimals = decimals[token];
                            // technically, there might be token with decimals 0
                            // moreover, very possible that old tokens have decimals 0
                            // these tokens will just have higher gas fees.
                            if(tokenDecimals == 0) return token.decimals();
                    
                            return tokenDecimals;
                        }
                    
                        function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(srcQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                    
                            if (dstDecimals >= srcDecimals) {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
                            } else {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
                            }
                        }
                    
                        function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(dstQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                            
                            //source quantity is rounded up. to avoid dest quantity being too low.
                            uint numerator;
                            uint denominator;
                            if (srcDecimals >= dstDecimals) {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
                                denominator = rate;
                            } else {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty);
                                denominator = (rate * (10**(dstDecimals - srcDecimals)));
                            }
                            return (numerator + denominator - 1) / denominator; //avoid rounding down errors
                        }
                    }
                    
                    // File: contracts/Utils2.sol
                    
                    contract Utils2 is Utils {
                    
                        /// @dev get the balance of a user.
                        /// @param token The token type
                        /// @return The balance
                        function getBalance(ERC20 token, address user) public view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS)
                                return user.balance;
                            else
                                return token.balanceOf(user);
                        }
                    
                        function getDecimalsSafe(ERC20 token) internal returns(uint) {
                    
                            if (decimals[token] == 0) {
                                setDecimals(token);
                            }
                    
                            return decimals[token];
                        }
                    
                        function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {
                            return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
                        }
                    
                        function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {
                            return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
                        }
                    
                        function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)
                            internal pure returns(uint)
                        {
                            require(srcAmount <= MAX_QTY);
                            require(destAmount <= MAX_QTY);
                    
                            if (dstDecimals >= srcDecimals) {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));
                            } else {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);
                            }
                        }
                    }
                    
                    // File: contracts/PermissionGroups.sol
                    
                    contract PermissionGroups {
                    
                        address public admin;
                        address public pendingAdmin;
                        mapping(address=>bool) internal operators;
                        mapping(address=>bool) internal alerters;
                        address[] internal operatorsGroup;
                        address[] internal alertersGroup;
                        uint constant internal MAX_GROUP_SIZE = 50;
                    
                        function PermissionGroups() public {
                            admin = msg.sender;
                        }
                    
                        modifier onlyAdmin() {
                            require(msg.sender == admin);
                            _;
                        }
                    
                        modifier onlyOperator() {
                            require(operators[msg.sender]);
                            _;
                        }
                    
                        modifier onlyAlerter() {
                            require(alerters[msg.sender]);
                            _;
                        }
                    
                        function getOperators () external view returns(address[]) {
                            return operatorsGroup;
                        }
                    
                        function getAlerters () external view returns(address[]) {
                            return alertersGroup;
                        }
                    
                        event TransferAdminPending(address pendingAdmin);
                    
                        /**
                         * @dev Allows the current admin to set the pendingAdmin address.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdmin(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(pendingAdmin);
                            pendingAdmin = newAdmin;
                        }
                    
                        /**
                         * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdminQuickly(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(newAdmin);
                            AdminClaimed(newAdmin, admin);
                            admin = newAdmin;
                        }
                    
                        event AdminClaimed( address newAdmin, address previousAdmin);
                    
                        /**
                         * @dev Allows the pendingAdmin address to finalize the change admin process.
                         */
                        function claimAdmin() public {
                            require(pendingAdmin == msg.sender);
                            AdminClaimed(pendingAdmin, admin);
                            admin = pendingAdmin;
                            pendingAdmin = address(0);
                        }
                    
                        event AlerterAdded (address newAlerter, bool isAdd);
                    
                        function addAlerter(address newAlerter) public onlyAdmin {
                            require(!alerters[newAlerter]); // prevent duplicates.
                            require(alertersGroup.length < MAX_GROUP_SIZE);
                    
                            AlerterAdded(newAlerter, true);
                            alerters[newAlerter] = true;
                            alertersGroup.push(newAlerter);
                        }
                    
                        function removeAlerter (address alerter) public onlyAdmin {
                            require(alerters[alerter]);
                            alerters[alerter] = false;
                    
                            for (uint i = 0; i < alertersGroup.length; ++i) {
                                if (alertersGroup[i] == alerter) {
                                    alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                                    alertersGroup.length--;
                                    AlerterAdded(alerter, false);
                                    break;
                                }
                            }
                        }
                    
                        event OperatorAdded(address newOperator, bool isAdd);
                    
                        function addOperator(address newOperator) public onlyAdmin {
                            require(!operators[newOperator]); // prevent duplicates.
                            require(operatorsGroup.length < MAX_GROUP_SIZE);
                    
                            OperatorAdded(newOperator, true);
                            operators[newOperator] = true;
                            operatorsGroup.push(newOperator);
                        }
                    
                        function removeOperator (address operator) public onlyAdmin {
                            require(operators[operator]);
                            operators[operator] = false;
                    
                            for (uint i = 0; i < operatorsGroup.length; ++i) {
                                if (operatorsGroup[i] == operator) {
                                    operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                                    operatorsGroup.length -= 1;
                                    OperatorAdded(operator, false);
                                    break;
                                }
                            }
                        }
                    }
                    
                    // File: contracts/Withdrawable.sol
                    
                    /**
                     * @title Contracts that should be able to recover tokens or ethers
                     * @author Ilan Doron
                     * @dev This allows to recover any tokens or Ethers received in a contract.
                     * This will prevent any accidental loss of tokens.
                     */
                    contract Withdrawable is PermissionGroups {
                    
                        event TokenWithdraw(ERC20 token, uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw all ERC20 compatible tokens
                         * @param token ERC20 The address of the token contract
                         */
                        function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
                            require(token.transfer(sendTo, amount));
                            TokenWithdraw(token, amount, sendTo);
                        }
                    
                        event EtherWithdraw(uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw Ethers
                         */
                        function withdrawEther(uint amount, address sendTo) external onlyAdmin {
                            sendTo.transfer(amount);
                            EtherWithdraw(amount, sendTo);
                        }
                    }
                    
                    // File: contracts/KyberNetworkProxy.sol
                    
                    ////////////////////////////////////////////////////////////////////////////////////////////////////////
                    /// @title Kyber Network proxy for main contract
                    contract KyberNetworkProxy is KyberNetworkProxyInterface, SimpleNetworkInterface, Withdrawable, Utils2 {
                    
                        KyberNetworkInterface public kyberNetworkContract;
                    
                        function KyberNetworkProxy(address _admin) public {
                            require(_admin != address(0));
                            admin = _admin;
                        }
                    
                        /// @notice use token address ETH_TOKEN_ADDRESS for ether
                        /// @dev makes a trade between src and dest token and send dest token to destAddress
                        /// @param src Src token
                        /// @param srcAmount amount of src tokens
                        /// @param dest   Destination token
                        /// @param destAddress Address to send tokens to
                        /// @param maxDestAmount A limit on the amount of dest tokens
                        /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
                        /// @param walletId is the wallet ID to send part of the fees
                        /// @return amount of actual dest tokens
                        function trade(
                            ERC20 src,
                            uint srcAmount,
                            ERC20 dest,
                            address destAddress,
                            uint maxDestAmount,
                            uint minConversionRate,
                            address walletId
                        )
                            public
                            payable
                            returns(uint)
                        {
                            bytes memory hint;
                    
                            return tradeWithHint(
                                src,
                                srcAmount,
                                dest,
                                destAddress,
                                maxDestAmount,
                                minConversionRate,
                                walletId,
                                hint
                            );
                        }
                    
                        /// @dev makes a trade between src and dest token and send dest tokens to msg sender
                        /// @param src Src token
                        /// @param srcAmount amount of src tokens
                        /// @param dest Destination token
                        /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
                        /// @return amount of actual dest tokens
                        function swapTokenToToken(
                            ERC20 src,
                            uint srcAmount,
                            ERC20 dest,
                            uint minConversionRate
                        )
                            public
                            returns(uint)
                        {
                            bytes memory hint;
                    
                            return tradeWithHint(
                                src,
                                srcAmount,
                                dest,
                                msg.sender,
                                MAX_QTY,
                                minConversionRate,
                                0,
                                hint
                            );
                        }
                    
                        /// @dev makes a trade from Ether to token. Sends token to msg sender
                        /// @param token Destination token
                        /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
                        /// @return amount of actual dest tokens
                        function swapEtherToToken(ERC20 token, uint minConversionRate) public payable returns(uint) {
                            bytes memory hint;
                    
                            return tradeWithHint(
                                ETH_TOKEN_ADDRESS,
                                msg.value,
                                token,
                                msg.sender,
                                MAX_QTY,
                                minConversionRate,
                                0,
                                hint
                            );
                        }
                    
                        /// @dev makes a trade from token to Ether, sends Ether to msg sender
                        /// @param token Src token
                        /// @param srcAmount amount of src tokens
                        /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
                        /// @return amount of actual dest tokens
                        function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) public returns(uint) {
                            bytes memory hint;
                    
                            return tradeWithHint(
                                token,
                                srcAmount,
                                ETH_TOKEN_ADDRESS,
                                msg.sender,
                                MAX_QTY,
                                minConversionRate,
                                0,
                                hint
                            );
                        }
                    
                        struct UserBalance {
                            uint srcBalance;
                            uint destBalance;
                        }
                    
                        event ExecuteTrade(address indexed trader, ERC20 src, ERC20 dest, uint actualSrcAmount, uint actualDestAmount);
                    
                        /// @notice use token address ETH_TOKEN_ADDRESS for ether
                        /// @dev makes a trade between src and dest token and send dest token to destAddress
                        /// @param src Src token
                        /// @param srcAmount amount of src tokens
                        /// @param dest Destination token
                        /// @param destAddress Address to send tokens to
                        /// @param maxDestAmount A limit on the amount of dest tokens
                        /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled.
                        /// @param walletId is the wallet ID to send part of the fees
                        /// @param hint will give hints for the trade.
                        /// @return amount of actual dest tokens
                        function tradeWithHint(
                            ERC20 src,
                            uint srcAmount,
                            ERC20 dest,
                            address destAddress,
                            uint maxDestAmount,
                            uint minConversionRate,
                            address walletId,
                            bytes hint
                        )
                            public
                            payable
                            returns(uint)
                        {
                            require(src == ETH_TOKEN_ADDRESS || msg.value == 0);
                            
                            UserBalance memory userBalanceBefore;
                    
                            userBalanceBefore.srcBalance = getBalance(src, msg.sender);
                            userBalanceBefore.destBalance = getBalance(dest, destAddress);
                    
                            if (src == ETH_TOKEN_ADDRESS) {
                                userBalanceBefore.srcBalance += msg.value;
                            } else {
                                require(src.transferFrom(msg.sender, kyberNetworkContract, srcAmount));
                            }
                    
                            uint reportedDestAmount = kyberNetworkContract.tradeWithHint.value(msg.value)(
                                msg.sender,
                                src,
                                srcAmount,
                                dest,
                                destAddress,
                                maxDestAmount,
                                minConversionRate,
                                walletId,
                                hint
                            );
                    
                            TradeOutcome memory tradeOutcome = calculateTradeOutcome(
                                userBalanceBefore.srcBalance,
                                userBalanceBefore.destBalance,
                                src,
                                dest,
                                destAddress
                            );
                    
                            require(reportedDestAmount == tradeOutcome.userDeltaDestAmount);
                            require(tradeOutcome.userDeltaDestAmount <= maxDestAmount);
                            require(tradeOutcome.actualRate >= minConversionRate);
                    
                            ExecuteTrade(msg.sender, src, dest, tradeOutcome.userDeltaSrcAmount, tradeOutcome.userDeltaDestAmount);
                            return tradeOutcome.userDeltaDestAmount;
                        }
                    
                        event KyberNetworkSet(address newNetworkContract, address oldNetworkContract);
                    
                        function setKyberNetworkContract(KyberNetworkInterface _kyberNetworkContract) public onlyAdmin {
                    
                            require(_kyberNetworkContract != address(0));
                    
                            KyberNetworkSet(_kyberNetworkContract, kyberNetworkContract);
                    
                            kyberNetworkContract = _kyberNetworkContract;
                        }
                    
                        function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty)
                            public view
                            returns(uint expectedRate, uint slippageRate)
                        {
                            return kyberNetworkContract.getExpectedRate(src, dest, srcQty);
                        }
                    
                        function getUserCapInWei(address user) public view returns(uint) {
                            return kyberNetworkContract.getUserCapInWei(user);
                        }
                    
                        function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) {
                            return kyberNetworkContract.getUserCapInTokenWei(user, token);
                        }
                    
                        function maxGasPrice() public view returns(uint) {
                            return kyberNetworkContract.maxGasPrice();
                        }
                    
                        function enabled() public view returns(bool) {
                            return kyberNetworkContract.enabled();
                        }
                    
                        function info(bytes32 field) public view returns(uint) {
                            return kyberNetworkContract.info(field);
                        }
                    
                        struct TradeOutcome {
                            uint userDeltaSrcAmount;
                            uint userDeltaDestAmount;
                            uint actualRate;
                        }
                    
                        function calculateTradeOutcome (uint srcBalanceBefore, uint destBalanceBefore, ERC20 src, ERC20 dest,
                            address destAddress)
                            internal returns(TradeOutcome outcome)
                        {
                            uint userSrcBalanceAfter;
                            uint userDestBalanceAfter;
                    
                            userSrcBalanceAfter = getBalance(src, msg.sender);
                            userDestBalanceAfter = getBalance(dest, destAddress);
                    
                            //protect from underflow
                            require(userDestBalanceAfter > destBalanceBefore);
                            require(srcBalanceBefore > userSrcBalanceAfter);
                    
                            outcome.userDeltaDestAmount = userDestBalanceAfter - destBalanceBefore;
                            outcome.userDeltaSrcAmount = srcBalanceBefore - userSrcBalanceAfter;
                    
                            outcome.actualRate = calcRateFromQty(
                                    outcome.userDeltaSrcAmount,
                                    outcome.userDeltaDestAmount,
                                    getDecimalsSafe(src),
                                    getDecimalsSafe(dest)
                                );
                        }
                    }

                    File 2 of 9: KyberNetwork
                    {"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"ExpectedRateInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ExpectedRateInterface {\n    function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty, bool usePermissionless) public view\n        returns (uint expectedRate, uint slippageRate);\n}\n"},"FeeBurnerInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ninterface FeeBurnerInterface {\n    function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool);\n    function setReserveData(address reserve, uint feesInBps, address kncWallet) public;\n}\n"},"KyberNetwork.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./KyberReserveInterface.sol\";\nimport \"./KyberNetworkInterface.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./Utils3.sol\";\nimport \"./WhiteListInterface.sol\";\nimport \"./ExpectedRateInterface.sol\";\nimport \"./FeeBurnerInterface.sol\";\n\n\n/**\n * @title Helps contracts guard against reentrancy attacks.\n */\ncontract ReentrancyGuard {\n\n    /// @dev counter to allow mutex lock with only one SSTORE operation\n    uint256 private guardCounter = 1;\n\n    /**\n     * @dev Prevents a function from calling itself, directly or indirectly.\n     * Calling one `nonReentrant` function from\n     * another is not supported. Instead, you can implement a\n     * `private` function doing the actual work, and an `external`\n     * wrapper marked as `nonReentrant`.\n     */\n    modifier nonReentrant() {\n        guardCounter += 1;\n        uint256 localCounter = guardCounter;\n        _;\n        require(localCounter == guardCounter);\n    }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////\n/// @title Kyber Network main contract\ncontract KyberNetwork is Withdrawable, Utils3, KyberNetworkInterface, ReentrancyGuard {\n\n    bytes public constant PERM_HINT = \"PERM\";\n    uint  public constant PERM_HINT_GET_RATE = 1 \u003c\u003c 255; // for get rate. bit mask hint.\n\n    uint public negligibleRateDiff = 10; // basic rate steps will be in 0.01%\n    KyberReserveInterface[] public reserves;\n    mapping(address=\u003eReserveType) public reserveType;\n    WhiteListInterface public whiteListContract;\n    ExpectedRateInterface public expectedRateContract;\n    FeeBurnerInterface    public feeBurnerContract;\n    address               public kyberNetworkProxyContract;\n    uint                  public maxGasPriceValue = 50 * 1000 * 1000 * 1000; // 50 gwei\n    bool                  public isEnabled = false; // network is enabled\n    mapping(bytes32=\u003euint) public infoFields; // this is only a UI field for external app.\n\n    mapping(address=\u003eaddress[]) public reservesPerTokenSrc; //reserves supporting token to eth\n    mapping(address=\u003eaddress[]) public reservesPerTokenDest;//reserves support eth to token\n\n    enum ReserveType {NONE, PERMISSIONED, PERMISSIONLESS}\n    bytes internal constant EMPTY_HINT = \"\";\n\n    function KyberNetwork(address _admin) public {\n        require(_admin != address(0));\n        admin = _admin;\n    }\n\n    event EtherReceival(address indexed sender, uint amount);\n\n    /* solhint-disable no-complex-fallback */\n    function() public payable {\n        EtherReceival(msg.sender, msg.value);\n    }\n    /* solhint-enable no-complex-fallback */\n\n    struct TradeInput {\n        address trader;\n        ERC20 src;\n        uint srcAmount;\n        ERC20 dest;\n        address destAddress;\n        uint maxDestAmount;\n        uint minConversionRate;\n        address walletId;\n        bytes hint;\n    }\n\n    function tradeWithHint(\n        address trader,\n        ERC20 src,\n        uint srcAmount,\n        ERC20 dest,\n        address destAddress,\n        uint maxDestAmount,\n        uint minConversionRate,\n        address walletId,\n        bytes hint\n    )\n        public\n        nonReentrant\n        payable\n        returns(uint)\n    {\n        require(msg.sender == kyberNetworkProxyContract);\n        require((hint.length == 0) || (hint.length == 4));\n\n        TradeInput memory tradeInput;\n\n        tradeInput.trader = trader;\n        tradeInput.src = src;\n        tradeInput.srcAmount = srcAmount;\n        tradeInput.dest = dest;\n        tradeInput.destAddress = destAddress;\n        tradeInput.maxDestAmount = maxDestAmount;\n        tradeInput.minConversionRate = minConversionRate;\n        tradeInput.walletId = walletId;\n        tradeInput.hint = hint;\n\n        return trade(tradeInput);\n    }\n\n    event AddReserveToNetwork(KyberReserveInterface indexed reserve, bool add, bool isPermissionless);\n\n    /// @notice can be called only by operator\n    /// @dev add or deletes a reserve to/from the network.\n    /// @param reserve The reserve address.\n    /// @param isPermissionless is the new reserve from permissionless type.\n    function addReserve(KyberReserveInterface reserve, bool isPermissionless) public onlyOperator\n        returns(bool)\n    {\n        require(reserveType[reserve] == ReserveType.NONE);\n        reserves.push(reserve);\n\n        reserveType[reserve] = isPermissionless ? ReserveType.PERMISSIONLESS : ReserveType.PERMISSIONED;\n\n        AddReserveToNetwork(reserve, true, isPermissionless);\n\n        return true;\n    }\n\n    event RemoveReserveFromNetwork(KyberReserveInterface reserve);\n\n    /// @notice can be called only by operator\n    /// @dev removes a reserve from Kyber network.\n    /// @param reserve The reserve address.\n    /// @param index in reserve array.\n    function removeReserve(KyberReserveInterface reserve, uint index) public onlyOperator\n        returns(bool)\n    {\n\n        require(reserveType[reserve] != ReserveType.NONE);\n        require(reserves[index] == reserve);\n\n        reserveType[reserve] = ReserveType.NONE;\n        reserves[index] = reserves[reserves.length - 1];\n        reserves.length--;\n\n        RemoveReserveFromNetwork(reserve);\n\n        return true;\n    }\n\n    event ListReservePairs(address indexed reserve, ERC20 src, ERC20 dest, bool add);\n\n    /// @notice can be called only by operator\n    /// @dev allow or prevent a specific reserve to trade a pair of tokens\n    /// @param reserve The reserve address.\n    /// @param token token address\n    /// @param ethToToken will it support ether to token trade\n    /// @param tokenToEth will it support token to ether trade\n    /// @param add If true then list this pair, otherwise unlist it.\n    function listPairForReserve(address reserve, ERC20 token, bool ethToToken, bool tokenToEth, bool add)\n        public\n        onlyOperator\n        returns(bool)\n    {\n        require(reserveType[reserve] != ReserveType.NONE);\n\n        if (ethToToken) {\n            listPairs(reserve, token, false, add);\n\n            ListReservePairs(reserve, ETH_TOKEN_ADDRESS, token, add);\n        }\n\n        if (tokenToEth) {\n            listPairs(reserve, token, true, add);\n\n            if (add) {\n                require(token.approve(reserve, 2**255)); // approve infinity\n            } else {\n                require(token.approve(reserve, 0));\n            }\n\n            ListReservePairs(reserve, token, ETH_TOKEN_ADDRESS, add);\n        }\n\n        setDecimals(token);\n\n        return true;\n    }\n\n    event WhiteListContractSet(WhiteListInterface newContract, WhiteListInterface currentContract);\n\n    ///@param whiteList can be empty\n    function setWhiteList(WhiteListInterface whiteList) public onlyAdmin {\n        WhiteListContractSet(whiteList, whiteListContract);\n        whiteListContract = whiteList;\n    }\n\n    event ExpectedRateContractSet(ExpectedRateInterface newContract, ExpectedRateInterface currentContract);\n\n    function setExpectedRate(ExpectedRateInterface expectedRate) public onlyAdmin {\n        require(expectedRate != address(0));\n\n        ExpectedRateContractSet(expectedRate, expectedRateContract);\n        expectedRateContract = expectedRate;\n    }\n\n    event FeeBurnerContractSet(FeeBurnerInterface newContract, FeeBurnerInterface currentContract);\n\n    function setFeeBurner(FeeBurnerInterface feeBurner) public onlyAdmin {\n        require(feeBurner != address(0));\n\n        FeeBurnerContractSet(feeBurner, feeBurnerContract);\n        feeBurnerContract = feeBurner;\n    }\n\n    event KyberNetwrokParamsSet(uint maxGasPrice, uint negligibleRateDiff);\n\n    function setParams(\n        uint                  _maxGasPrice,\n        uint                  _negligibleRateDiff\n    )\n        public\n        onlyAdmin\n    {\n        require(_negligibleRateDiff \u003c= 100 * 100); // at most 100%\n\n        maxGasPriceValue = _maxGasPrice;\n        negligibleRateDiff = _negligibleRateDiff;\n        KyberNetwrokParamsSet(maxGasPriceValue, negligibleRateDiff);\n    }\n\n    event KyberNetworkSetEnable(bool isEnabled);\n\n    function setEnable(bool _enable) public onlyAdmin {\n        if (_enable) {\n            require(feeBurnerContract != address(0));\n            require(expectedRateContract != address(0));\n            require(kyberNetworkProxyContract != address(0));\n        }\n        isEnabled = _enable;\n\n        KyberNetworkSetEnable(isEnabled);\n    }\n\n    function setInfo(bytes32 field, uint value) public onlyOperator {\n        infoFields[field] = value;\n    }\n\n    event KyberProxySet(address proxy, address sender);\n\n    function setKyberProxy(address networkProxy) public onlyAdmin {\n        require(networkProxy != address(0));\n        kyberNetworkProxyContract = networkProxy;\n        KyberProxySet(kyberNetworkProxyContract, msg.sender);\n    }\n\n    /// @dev returns number of reserves\n    /// @return number of reserves\n    function getNumReserves() public view returns(uint) {\n        return reserves.length;\n    }\n\n    /// @notice should be called off chain\n    /// @dev get an array of all reserves\n    /// @return An array of all reserves\n    function getReserves() public view returns(KyberReserveInterface[]) {\n        return reserves;\n    }\n\n    function maxGasPrice() public view returns(uint) {\n        return maxGasPriceValue;\n    }\n\n    function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty)\n        public view\n        returns(uint expectedRate, uint slippageRate)\n    {\n        require(expectedRateContract != address(0));\n        if (src == dest) return (0,0);\n        bool includePermissionless = true;\n\n        if (srcQty \u0026 PERM_HINT_GET_RATE \u003e 0) {\n            includePermissionless = false;\n            srcQty = srcQty \u0026 ~PERM_HINT_GET_RATE;\n        }\n\n        return expectedRateContract.getExpectedRate(src, dest, srcQty, includePermissionless);\n    }\n\n    function getExpectedRateOnlyPermission(ERC20 src, ERC20 dest, uint srcQty)\n        public view\n        returns(uint expectedRate, uint slippageRate)\n    {\n        require(expectedRateContract != address(0));\n        if (src == dest) return (0,0);\n        return expectedRateContract.getExpectedRate(src, dest, srcQty, false);\n    }\n\n    function getUserCapInWei(address user) public view returns(uint) {\n        if (whiteListContract == address(0)) return (2 ** 255);\n        return whiteListContract.getUserCapInWei(user);\n    }\n\n    function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) {\n        //future feature\n        user;\n        token;\n        require(false);\n    }\n\n    struct BestRateResult {\n        uint rate;\n        address reserve1;\n        address reserve2;\n        uint weiAmount;\n        uint rateSrcToEth;\n        uint rateEthToDest;\n        uint destAmount;\n    }\n\n    /// @notice use token address ETH_TOKEN_ADDRESS for ether\n    /// @dev best conversion rate for a pair of tokens, if number of reserves have small differences. randomize\n    /// @param src Src token\n    /// @param dest Destination token\n    /// @return obsolete - used to return best reserve index. not relevant anymore for this API.\n    function findBestRate(ERC20 src, ERC20 dest, uint srcAmount) public view returns(uint obsolete, uint rate) {\n        BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, EMPTY_HINT);\n        return(0, result.rate);\n    }\n\n    function findBestRateOnlyPermission(ERC20 src, ERC20 dest, uint srcAmount)\n        public\n        view\n        returns(uint obsolete, uint rate)\n    {\n        BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, PERM_HINT);\n        return(0, result.rate);\n    }\n\n    function enabled() public view returns(bool) {\n        return isEnabled;\n    }\n\n    function info(bytes32 field) public view returns(uint) {\n        return infoFields[field];\n    }\n\n    /* solhint-disable code-complexity */\n    // Regarding complexity. Below code follows the required algorithm for choosing a reserve.\n    //  It has been tested, reviewed and found to be clear enough.\n    //@dev this function always src or dest are ether. can\u0027t do token to token\n    function searchBestRate(ERC20 src, ERC20 dest, uint srcAmount, bool usePermissionless)\n        public\n        view\n        returns(address, uint)\n    {\n        uint bestRate = 0;\n        uint bestReserve = 0;\n        uint numRelevantReserves = 0;\n\n        //return 1 for ether to ether\n        if (src == dest) return (reserves[bestReserve], PRECISION);\n\n        address[] memory reserveArr;\n\n        reserveArr = src == ETH_TOKEN_ADDRESS ? reservesPerTokenDest[dest] : reservesPerTokenSrc[src];\n\n        if (reserveArr.length == 0) return (reserves[bestReserve], bestRate);\n\n        uint[] memory rates = new uint[](reserveArr.length);\n        uint[] memory reserveCandidates = new uint[](reserveArr.length);\n\n        for (uint i = 0; i \u003c reserveArr.length; i++) {\n            //list all reserves that have this token.\n            if (!usePermissionless \u0026\u0026 reserveType[reserveArr[i]] == ReserveType.PERMISSIONLESS) {\n                continue;\n            }\n\n            rates[i] = (KyberReserveInterface(reserveArr[i])).getConversionRate(src, dest, srcAmount, block.number);\n\n            if (rates[i] \u003e bestRate) {\n                //best rate is highest rate\n                bestRate = rates[i];\n            }\n        }\n\n        if (bestRate \u003e 0) {\n            uint smallestRelevantRate = (bestRate * 10000) / (10000 + negligibleRateDiff);\n\n            for (i = 0; i \u003c reserveArr.length; i++) {\n                if (rates[i] \u003e= smallestRelevantRate) {\n                    reserveCandidates[numRelevantReserves++] = i;\n                }\n            }\n\n            if (numRelevantReserves \u003e 1) {\n                //when encountering small rate diff from bestRate. draw from relevant reserves\n                bestReserve = reserveCandidates[uint(block.blockhash(block.number-1)) % numRelevantReserves];\n            } else {\n                bestReserve = reserveCandidates[0];\n            }\n\n            bestRate = rates[bestReserve];\n        }\n\n        return (reserveArr[bestReserve], bestRate);\n    }\n    /* solhint-enable code-complexity */\n\n    function getReservesRates(ERC20 token, uint optionalAmount) public view\n        returns(address[] buyReserves, uint[] buyRates, address[] sellReserves, uint[] sellRates)\n    {\n        uint amount = optionalAmount \u003e 0 ? optionalAmount : 1000;\n        ERC20 ETH = ETH_TOKEN_ADDRESS;\n\n        buyReserves = reservesPerTokenDest[token];\n        buyRates = new uint[](buyReserves.length);\n\n        for (uint i = 0; i \u003c buyReserves.length; i++) {\n            buyRates[i] = (KyberReserveInterface(buyReserves[i])).getConversionRate(ETH, token, amount, block.number);\n        }\n\n        sellReserves = reservesPerTokenSrc[token];\n        sellRates = new uint[](sellReserves.length);\n\n        for (i = 0; i \u003c sellReserves.length; i++) {\n            sellRates[i] = (KyberReserveInterface(sellReserves[i])).getConversionRate(token, ETH, amount, block.number);\n        }\n    }\n\n    function findBestRateTokenToToken(ERC20 src, ERC20 dest, uint srcAmount, bytes hint) internal view\n        returns(BestRateResult result)\n    {\n        //by default we use permission less reserves\n        bool usePermissionless = true;\n\n        // if hint in first 4 bytes == \u0027PERM\u0027 only permissioned reserves will be used.\n        if ((hint.length \u003e= 4) \u0026\u0026 (keccak256(hint[0], hint[1], hint[2], hint[3]) == keccak256(PERM_HINT))) {\n            usePermissionless = false;\n        }\n\n        uint srcDecimals = getDecimals(src);\n        uint destDecimals = getDecimals(dest);\n\n        (result.reserve1, result.rateSrcToEth) =\n            searchBestRate(src, ETH_TOKEN_ADDRESS, srcAmount, usePermissionless);\n\n        result.weiAmount = calcDestAmountWithDecimals(srcDecimals, ETH_DECIMALS, srcAmount, result.rateSrcToEth);\n        //if weiAmount is zero, return zero rate to avoid revert in ETH -\u003e token call\n        if (result.weiAmount == 0) {\n            result.rate = 0;\n            return;\n        }\n        \n        (result.reserve2, result.rateEthToDest) =\n            searchBestRate(ETH_TOKEN_ADDRESS, dest, result.weiAmount, usePermissionless);\n\n        result.destAmount = calcDestAmountWithDecimals(ETH_DECIMALS, destDecimals, result.weiAmount, result.rateEthToDest);\n\n        result.rate = calcRateFromQty(srcAmount, result.destAmount, srcDecimals, destDecimals);\n    }\n\n    function listPairs(address reserve, ERC20 token, bool isTokenToEth, bool add) internal {\n        uint i;\n        address[] storage reserveArr = reservesPerTokenDest[token];\n\n        if (isTokenToEth) {\n            reserveArr = reservesPerTokenSrc[token];\n        }\n\n        for (i = 0; i \u003c reserveArr.length; i++) {\n            if (reserve == reserveArr[i]) {\n                if (add) {\n                    break; //already added\n                } else {\n                    //remove\n                    reserveArr[i] = reserveArr[reserveArr.length - 1];\n                    reserveArr.length--;\n                    break;\n                }\n            }\n        }\n\n        if (add \u0026\u0026 i == reserveArr.length) {\n            //if reserve wasn\u0027t found add it\n            reserveArr.push(reserve);\n        }\n    }\n\n    event KyberTrade(address indexed trader, ERC20 src, ERC20 dest, uint srcAmount, uint dstAmount,\n        address destAddress, uint ethWeiValue, address reserve1, address reserve2, bytes hint);\n\n    /* solhint-disable function-max-lines */\n    //  Most of the lines here are functions calls spread over multiple lines. We find this function readable enough\n    /// @notice use token address ETH_TOKEN_ADDRESS for ether\n    /// @dev trade api for kyber network.\n    /// @param tradeInput structure of trade inputs\n    function trade(TradeInput tradeInput) internal returns(uint) {\n        require(isEnabled);\n        require(tx.gasprice \u003c= maxGasPriceValue);\n        require(validateTradeInput(tradeInput.src, tradeInput.srcAmount, tradeInput.dest, tradeInput.destAddress));\n\n        BestRateResult memory rateResult =\n            findBestRateTokenToToken(tradeInput.src, tradeInput.dest, tradeInput.srcAmount, tradeInput.hint);\n\n        require(rateResult.rate \u003e 0);\n        require(rateResult.rate \u003c MAX_RATE);\n        require(rateResult.rate \u003e= tradeInput.minConversionRate);\n\n        uint actualDestAmount;\n        uint weiAmount;\n        uint actualSrcAmount;\n\n        (actualSrcAmount, weiAmount, actualDestAmount) = calcActualAmounts(tradeInput.src,\n            tradeInput.dest,\n            tradeInput.srcAmount,\n            tradeInput.maxDestAmount,\n            rateResult);\n\n        require(getUserCapInWei(tradeInput.trader) \u003e= weiAmount);\n        require(handleChange(tradeInput.src, tradeInput.srcAmount, actualSrcAmount, tradeInput.trader));\n\n        require(doReserveTrade(     //src to ETH\n                tradeInput.src,\n                actualSrcAmount,\n                ETH_TOKEN_ADDRESS,\n                this,\n                weiAmount,\n                KyberReserveInterface(rateResult.reserve1),\n                rateResult.rateSrcToEth,\n                true));\n\n        require(doReserveTrade(     //Eth to dest\n                ETH_TOKEN_ADDRESS,\n                weiAmount,\n                tradeInput.dest,\n                tradeInput.destAddress,\n                actualDestAmount,\n                KyberReserveInterface(rateResult.reserve2),\n                rateResult.rateEthToDest,\n                true));\n\n        if (tradeInput.src != ETH_TOKEN_ADDRESS) //\"fake\" trade. (ether to ether) - don\u0027t burn.\n            require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve1, tradeInput.walletId));\n        if (tradeInput.dest != ETH_TOKEN_ADDRESS) //\"fake\" trade. (ether to ether) - don\u0027t burn.\n            require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve2, tradeInput.walletId));\n\n        KyberTrade({\n            trader: tradeInput.trader,\n            src: tradeInput.src,\n            dest: tradeInput.dest,\n            srcAmount: actualSrcAmount,\n            dstAmount: actualDestAmount,\n            destAddress: tradeInput.destAddress,\n            ethWeiValue: weiAmount,\n            reserve1: (tradeInput.src == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve1,\n            reserve2:  (tradeInput.dest == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve2,\n            hint: tradeInput.hint\n        });\n\n        return actualDestAmount;\n    }\n    /* solhint-enable function-max-lines */\n\n    function calcActualAmounts (ERC20 src, ERC20 dest, uint srcAmount, uint maxDestAmount, BestRateResult rateResult)\n        internal view returns(uint actualSrcAmount, uint weiAmount, uint actualDestAmount)\n    {\n        if (rateResult.destAmount \u003e maxDestAmount) {\n            actualDestAmount = maxDestAmount;\n            weiAmount = calcSrcAmount(ETH_TOKEN_ADDRESS, dest, actualDestAmount, rateResult.rateEthToDest);\n            actualSrcAmount = calcSrcAmount(src, ETH_TOKEN_ADDRESS, weiAmount, rateResult.rateSrcToEth);\n            require(actualSrcAmount \u003c= srcAmount);\n        } else {\n            actualDestAmount = rateResult.destAmount;\n            actualSrcAmount = srcAmount;\n            weiAmount = rateResult.weiAmount;\n        }\n    }\n\n    /// @notice use token address ETH_TOKEN_ADDRESS for ether\n    /// @dev do one trade with a reserve\n    /// @param src Src token\n    /// @param amount amount of src tokens\n    /// @param dest   Destination token\n    /// @param destAddress Address to send tokens to\n    /// @param reserve Reserve to use\n    /// @param validate If true, additional validations are applicable\n    /// @return true if trade is successful\n    function doReserveTrade(\n        ERC20 src,\n        uint amount,\n        ERC20 dest,\n        address destAddress,\n        uint expectedDestAmount,\n        KyberReserveInterface reserve,\n        uint conversionRate,\n        bool validate\n    )\n        internal\n        returns(bool)\n    {\n        uint callValue = 0;\n\n        if (src == dest) {\n            //this is for a \"fake\" trade when both src and dest are ethers.\n            if (destAddress != (address(this)))\n                destAddress.transfer(amount);\n            return true;\n        }\n\n        if (src == ETH_TOKEN_ADDRESS) {\n            callValue = amount;\n        }\n\n        // reserve sends tokens/eth to network. network sends it to destination\n        require(reserve.trade.value(callValue)(src, amount, dest, this, conversionRate, validate));\n\n        if (destAddress != address(this)) {\n            //for token to token dest address is network. and Ether / token already here...\n            if (dest == ETH_TOKEN_ADDRESS) {\n                destAddress.transfer(expectedDestAmount);\n            } else {\n                require(dest.transfer(destAddress, expectedDestAmount));\n            }\n        }\n\n        return true;\n    }\n\n    /// when user sets max dest amount we could have too many source tokens == change. so we send it back to user.\n    function handleChange (ERC20 src, uint srcAmount, uint requiredSrcAmount, address trader) internal returns (bool) {\n\n        if (requiredSrcAmount \u003c srcAmount) {\n            //if there is \"change\" send back to trader\n            if (src == ETH_TOKEN_ADDRESS) {\n                trader.transfer(srcAmount - requiredSrcAmount);\n            } else {\n                require(src.transfer(trader, (srcAmount - requiredSrcAmount)));\n            }\n        }\n\n        return true;\n    }\n\n    /// @notice use token address ETH_TOKEN_ADDRESS for ether\n    /// @dev checks that user sent ether/tokens to contract before trade\n    /// @param src Src token\n    /// @param srcAmount amount of src tokens\n    /// @return true if tradeInput is valid\n    function validateTradeInput(ERC20 src, uint srcAmount, ERC20 dest, address destAddress)\n        internal\n        view\n        returns(bool)\n    {\n        require(srcAmount \u003c= MAX_QTY);\n        require(srcAmount != 0);\n        require(destAddress != address(0));\n        require(src != dest);\n\n        if (src == ETH_TOKEN_ADDRESS) {\n            require(msg.value == srcAmount);\n        } else {\n            require(msg.value == 0);\n            //funds should have been moved to this contract already.\n            require(src.balanceOf(this) \u003e= srcAmount);\n        }\n\n        return true;\n    }\n}\n"},"KyberNetworkInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber Network interface\ninterface KyberNetworkInterface {\n    function maxGasPrice() public view returns(uint);\n    function getUserCapInWei(address user) public view returns(uint);\n    function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);\n    function enabled() public view returns(bool);\n    function info(bytes32 id) public view returns(uint);\n\n    function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view\n        returns (uint expectedRate, uint slippageRate);\n\n    function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,\n        uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);\n}\n"},"KyberReserveInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n/// @title Kyber Reserve contract\ninterface KyberReserveInterface {\n\n    function trade(\n        ERC20 srcToken,\n        uint srcAmount,\n        ERC20 destToken,\n        address destAddress,\n        uint conversionRate,\n        bool validate\n    )\n        public\n        payable\n        returns(bool);\n\n    function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n    address public admin;\n    address public pendingAdmin;\n    mapping(address=\u003ebool) internal operators;\n    mapping(address=\u003ebool) internal alerters;\n    address[] internal operatorsGroup;\n    address[] internal alertersGroup;\n    uint constant internal MAX_GROUP_SIZE = 50;\n\n    function PermissionGroups() public {\n        admin = msg.sender;\n    }\n\n    modifier onlyAdmin() {\n        require(msg.sender == admin);\n        _;\n    }\n\n    modifier onlyOperator() {\n        require(operators[msg.sender]);\n        _;\n    }\n\n    modifier onlyAlerter() {\n        require(alerters[msg.sender]);\n        _;\n    }\n\n    function getOperators () external view returns(address[]) {\n        return operatorsGroup;\n    }\n\n    function getAlerters () external view returns(address[]) {\n        return alertersGroup;\n    }\n\n    event TransferAdminPending(address pendingAdmin);\n\n    /**\n     * @dev Allows the current admin to set the pendingAdmin address.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdmin(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(pendingAdmin);\n        pendingAdmin = newAdmin;\n    }\n\n    /**\n     * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdminQuickly(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(newAdmin);\n        AdminClaimed(newAdmin, admin);\n        admin = newAdmin;\n    }\n\n    event AdminClaimed( address newAdmin, address previousAdmin);\n\n    /**\n     * @dev Allows the pendingAdmin address to finalize the change admin process.\n     */\n    function claimAdmin() public {\n        require(pendingAdmin == msg.sender);\n        AdminClaimed(pendingAdmin, admin);\n        admin = pendingAdmin;\n        pendingAdmin = address(0);\n    }\n\n    event AlerterAdded (address newAlerter, bool isAdd);\n\n    function addAlerter(address newAlerter) public onlyAdmin {\n        require(!alerters[newAlerter]); // prevent duplicates.\n        require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n        AlerterAdded(newAlerter, true);\n        alerters[newAlerter] = true;\n        alertersGroup.push(newAlerter);\n    }\n\n    function removeAlerter (address alerter) public onlyAdmin {\n        require(alerters[alerter]);\n        alerters[alerter] = false;\n\n        for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n            if (alertersGroup[i] == alerter) {\n                alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n                alertersGroup.length--;\n                AlerterAdded(alerter, false);\n                break;\n            }\n        }\n    }\n\n    event OperatorAdded(address newOperator, bool isAdd);\n\n    function addOperator(address newOperator) public onlyAdmin {\n        require(!operators[newOperator]); // prevent duplicates.\n        require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n        OperatorAdded(newOperator, true);\n        operators[newOperator] = true;\n        operatorsGroup.push(newOperator);\n    }\n\n    function removeOperator (address operator) public onlyAdmin {\n        require(operators[operator]);\n        operators[operator] = false;\n\n        for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n            if (operatorsGroup[i] == operator) {\n                operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n                operatorsGroup.length -= 1;\n                OperatorAdded(operator, false);\n                break;\n            }\n        }\n    }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"Utils2.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils.sol\";\n\n\ncontract Utils2 is Utils {\n\n    /// @dev get the balance of a user.\n    /// @param token The token type\n    /// @return The balance\n    function getBalance(ERC20 token, address user) public view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS)\n            return user.balance;\n        else\n            return token.balanceOf(user);\n    }\n\n    function getDecimalsSafe(ERC20 token) internal returns(uint) {\n\n        if (decimals[token] == 0) {\n            setDecimals(token);\n        }\n\n        return decimals[token];\n    }\n\n    function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {\n        return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);\n    }\n\n    function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {\n        return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);\n    }\n\n    function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)\n        internal pure returns(uint)\n    {\n        require(srcAmount \u003c= MAX_QTY);\n        require(destAmount \u003c= MAX_QTY);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);\n        }\n    }\n}\n"},"Utils3.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils2.sol\";\n\n\ncontract Utils3 is Utils2 {\n\n    function calcDestAmountWithDecimals(uint srcDecimals, uint destDecimals, uint srcAmount, uint rate) internal pure returns(uint) {\n        return calcDstQty(srcAmount, srcDecimals, destDecimals, rate);\n    }\n\n}\n"},"WhiteListInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract WhiteListInterface {\n    function getUserCapInWei(address user) external view returns (uint userCapWei);\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}

                    File 3 of 9: BandToken
                    // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @title ERC20 interface
                     * @dev see https://github.com/ethereum/EIPs/issues/20
                     */
                    interface IERC20 {
                        function transfer(address to, uint256 value) external returns (bool);
                    
                        function approve(address spender, uint256 value) external returns (bool);
                    
                        function transferFrom(address from, address to, uint256 value) external returns (bool);
                    
                        function totalSupply() external view returns (uint256);
                    
                        function balanceOf(address who) external view returns (uint256);
                    
                        function allowance(address owner, address spender) external view returns (uint256);
                    
                        event Transfer(address indexed from, address indexed to, uint256 value);
                    
                        event Approval(address indexed owner, address indexed spender, uint256 value);
                    }
                    
                    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @title SafeMath
                     * @dev Unsigned math operations with safety checks that revert on error
                     */
                    library SafeMath {
                        /**
                        * @dev Multiplies two unsigned integers, reverts on overflow.
                        */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b);
                    
                            return c;
                        }
                    
                        /**
                        * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                        */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0);
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                        * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                        */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a);
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                        * @dev Adds two unsigned integers, reverts on overflow.
                        */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a);
                    
                            return c;
                        }
                    
                        /**
                        * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                        * reverts when dividing by zero.
                        */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b != 0);
                            return a % b;
                        }
                    }
                    
                    // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    
                    /**
                     * @title Standard ERC20 token
                     *
                     * @dev Implementation of the basic standard token.
                     * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
                     * Originally based on code by FirstBlood:
                     * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
                     *
                     * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
                     * all accounts just by listening to said events. Note that this isn't required by the specification, and other
                     * compliant implementations may not do it.
                     */
                    contract ERC20 is IERC20 {
                        using SafeMath for uint256;
                    
                        mapping (address => uint256) private _balances;
                    
                        mapping (address => mapping (address => uint256)) private _allowed;
                    
                        uint256 private _totalSupply;
                    
                        /**
                        * @dev Total number of tokens in existence
                        */
                        function totalSupply() public view returns (uint256) {
                            return _totalSupply;
                        }
                    
                        /**
                        * @dev Gets the balance of the specified address.
                        * @param owner The address to query the balance of.
                        * @return An uint256 representing the amount owned by the passed address.
                        */
                        function balanceOf(address owner) public view returns (uint256) {
                            return _balances[owner];
                        }
                    
                        /**
                         * @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];
                        }
                    
                        /**
                        * @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) public returns (bool) {
                            _transfer(msg.sender, 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) public returns (bool) {
                            require(spender != address(0));
                    
                            _allowed[msg.sender][spender] = value;
                            emit Approval(msg.sender, spender, value);
                            return true;
                        }
                    
                        /**
                         * @dev Transfer tokens from one address to another.
                         * Note that while this function emits an Approval event, this is not required as per the specification,
                         * and other compliant implementations may not emit the event.
                         * @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) public returns (bool) {
                            _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
                            _transfer(from, to, value);
                            emit Approval(from, msg.sender, _allowed[from][msg.sender]);
                            return true;
                        }
                    
                        /**
                         * @dev Increase the amount of tokens that an owner allowed to a 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
                         * Emits an Approval event.
                         * @param spender The address which will spend the funds.
                         * @param addedValue The amount of tokens to increase the allowance by.
                         */
                        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                            require(spender != address(0));
                    
                            _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue);
                            emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
                            return true;
                        }
                    
                        /**
                         * @dev Decrease the amount of tokens that an owner allowed to a spender.
                         * approve should be called when allowed_[_spender] == 0. To decrement
                         * allowed value is better to use this function to avoid 2 calls (and wait until
                         * the first transaction is mined)
                         * From MonolithDAO Token.sol
                         * Emits an Approval event.
                         * @param spender The address which will spend the funds.
                         * @param subtractedValue The amount of tokens to decrease the allowance by.
                         */
                        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                            require(spender != address(0));
                    
                            _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue);
                            emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
                            return true;
                        }
                    
                        /**
                        * @dev Transfer token for a specified addresses
                        * @param from The address to transfer from.
                        * @param to The address to transfer to.
                        * @param value The amount to be transferred.
                        */
                        function _transfer(address from, address to, uint256 value) internal {
                            require(to != address(0));
                    
                            _balances[from] = _balances[from].sub(value);
                            _balances[to] = _balances[to].add(value);
                            emit Transfer(from, to, value);
                        }
                    
                        /**
                         * @dev Internal function that mints an amount of the token and assigns it to
                         * an account. This encapsulates the modification of balances such that the
                         * proper events are emitted.
                         * @param account The account that will receive the created tokens.
                         * @param value The amount that will be created.
                         */
                        function _mint(address account, uint256 value) internal {
                            require(account != address(0));
                    
                            _totalSupply = _totalSupply.add(value);
                            _balances[account] = _balances[account].add(value);
                            emit Transfer(address(0), account, value);
                        }
                    
                        /**
                         * @dev Internal function that burns an amount of the token of a given
                         * account.
                         * @param account The account whose tokens will be burnt.
                         * @param value The amount that will be burnt.
                         */
                        function _burn(address account, uint256 value) internal {
                            require(account != address(0));
                    
                            _totalSupply = _totalSupply.sub(value);
                            _balances[account] = _balances[account].sub(value);
                            emit Transfer(account, address(0), value);
                        }
                    
                        /**
                         * @dev Internal function that burns an amount of the token of a given
                         * account, deducting from the sender's allowance for said account. Uses the
                         * internal burn function.
                         * Emits an Approval event (reflecting the reduced allowance).
                         * @param account The account whose tokens will be burnt.
                         * @param value The amount that will be burnt.
                         */
                        function _burnFrom(address account, uint256 value) internal {
                            _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value);
                            _burn(account, value);
                            emit Approval(account, msg.sender, _allowed[account][msg.sender]);
                        }
                    }
                    
                    // File: openzeppelin-solidity/contracts/access/Roles.sol
                    
                    pragma solidity ^0.5.0;
                    
                    /**
                     * @title Roles
                     * @dev Library for managing addresses assigned to a Role.
                     */
                    library Roles {
                        struct Role {
                            mapping (address => bool) bearer;
                        }
                    
                        /**
                         * @dev give an account access to this role
                         */
                        function add(Role storage role, address account) internal {
                            require(account != address(0));
                            require(!has(role, account));
                    
                            role.bearer[account] = true;
                        }
                    
                        /**
                         * @dev remove an account's access to this role
                         */
                        function remove(Role storage role, address account) internal {
                            require(account != address(0));
                            require(has(role, account));
                    
                            role.bearer[account] = false;
                        }
                    
                        /**
                         * @dev check if an account has this role
                         * @return bool
                         */
                        function has(Role storage role, address account) internal view returns (bool) {
                            require(account != address(0));
                            return role.bearer[account];
                        }
                    }
                    
                    // File: openzeppelin-solidity/contracts/access/roles/MinterRole.sol
                    
                    pragma solidity ^0.5.0;
                    
                    
                    contract MinterRole {
                        using Roles for Roles.Role;
                    
                        event MinterAdded(address indexed account);
                        event MinterRemoved(address indexed account);
                    
                        Roles.Role private _minters;
                    
                        constructor () internal {
                            _addMinter(msg.sender);
                        }
                    
                        modifier onlyMinter() {
                            require(isMinter(msg.sender));
                            _;
                        }
                    
                        function isMinter(address account) public view returns (bool) {
                            return _minters.has(account);
                        }
                    
                        function addMinter(address account) public onlyMinter {
                            _addMinter(account);
                        }
                    
                        function renounceMinter() public {
                            _removeMinter(msg.sender);
                        }
                    
                        function _addMinter(address account) internal {
                            _minters.add(account);
                            emit MinterAdded(account);
                        }
                    
                        function _removeMinter(address account) internal {
                            _minters.remove(account);
                            emit MinterRemoved(account);
                        }
                    }
                    
                    // File: contracts/token/ERC20Interface.sol
                    
                    pragma solidity 0.5.9;
                    
                    
                    interface ERC20Interface {
                      // Standard ERC-20 interface.
                      function transfer(address to, uint256 value) external returns (bool);
                      function approve(address spender, uint256 value) external returns (bool);
                      function transferFrom(address from, address to, uint256 value) external returns (bool);
                      function totalSupply() external view returns (uint256);
                      function balanceOf(address who) external view returns (uint256);
                      function allowance(address owner, address spender) external view returns (uint256);
                      // Extension of ERC-20 interface to support supply adjustment.
                      function mint(address to, uint256 value) external returns (bool);
                      function burn(address from, uint256 value) external returns (bool);
                    }
                    
                    // File: contracts/token/ERC20Base.sol
                    
                    pragma solidity 0.5.9;
                    
                    
                    
                    
                    
                    /// "ERC20Base" is the standard ERC-20 implementation that allows its minter to mint tokens. Both BandToken and
                    /// CommunityToken extend from ERC20Base. In addition to the standard functions, the class provides `transferAndCall`
                    /// function, which performs a transfer and invokes the given function using the provided data. If the destination
                    /// contract uses "ERC20Acceptor" interface, it can verify that the caller properly sends appropriate amount of tokens.
                    contract ERC20Base is ERC20Interface, ERC20, MinterRole {
                      string public name;
                      string public symbol;
                      uint8 public decimals = 18;
                    
                      constructor(string memory _name, string memory _symbol) public {
                        name = _name;
                        symbol = _symbol;
                      }
                    
                      function transferAndCall(address to, uint256 value, bytes4 sig, bytes memory data) public returns (bool) {
                        require(to != address(this));
                        _transfer(msg.sender, to, value);
                        (bool success,) = to.call(abi.encodePacked(sig, uint256(msg.sender), value, data));
                        require(success);
                        return true;
                      }
                    
                      function mint(address to, uint256 value) public onlyMinter returns (bool) {
                        _mint(to, value);
                        return true;
                      }
                    
                      function burn(address from, uint256 value) public onlyMinter returns (bool) {
                        _burn(from, value);
                        return true;
                      }
                    }
                    
                    // File: contracts/token/SnapshotToken.sol
                    
                    pragma solidity 0.5.9;
                    
                    
                    
                    
                    contract SnapshotToken is ERC20Base {
                      using SafeMath for uint256;
                    
                      /// IMPORTANT: votingPowers are kept as a linked list of ALL historical changes.
                      /// - This allows the contract to figure out voting power of the address at any nonce `n`, by
                      /// searching for the node that has the biggest nonce that is not greater than `n`.
                      /// - For efficiency, nonce and power are packed into one uint256 integer, with the top 64 bits
                      /// representing nonce, and the bottom 192 bits representing voting power.
                      mapping (address => mapping(uint256 => uint256)) _votingPower;
                      mapping (address => uint256) public votingPowerChangeCount;
                      uint256 public votingPowerChangeNonce = 0;
                    
                      /// Returns user voting power at the given index, that is, as of the user's index^th voting power change
                      function historicalVotingPowerAtIndex(address owner, uint256 index) public view returns (uint256) {
                        require(index <= votingPowerChangeCount[owner]);
                        return _votingPower[owner][index] & ((1 << 192) - 1);  // Lower 192 bits
                      }
                    
                      /// Returns user voting power at the given time. Under the hood, this performs binary search
                      /// to look for the largest index at which the nonce is not greater than 'nonce'.
                      /// The voting power at that index is the returning value.
                      function historicalVotingPowerAtNonce(address owner, uint256 nonce) public view returns (uint256) {
                        require(nonce <= votingPowerChangeNonce && nonce < (1 << 64));
                        uint256 start = 0;
                        uint256 end = votingPowerChangeCount[owner];
                        while (start < end) {
                          uint256 mid = start.add(end).add(1).div(2); /// Use (start+end+1)/2 to prevent infinite loop.
                          if ((_votingPower[owner][mid] >> 192) > nonce) {  /// Upper 64-bit nonce
                            /// If midTime > nonce, this mid can't possibly be the answer.
                            end = mid.sub(1);
                          } else {
                            /// Otherwise, search on the greater side, but still keep mid as a possible option.
                            start = mid;
                          }
                        }
                        return historicalVotingPowerAtIndex(owner, start);
                      }
                    
                      function _transfer(address from, address to, uint256 value) internal {
                        super._transfer(from, to, value);
                        votingPowerChangeNonce = votingPowerChangeNonce.add(1);
                        _changeVotingPower(from);
                        _changeVotingPower(to);
                      }
                    
                      function _mint(address account, uint256 amount) internal {
                        super._mint(account, amount);
                        votingPowerChangeNonce = votingPowerChangeNonce.add(1);
                        _changeVotingPower(account);
                      }
                    
                      function _burn(address account, uint256 amount) internal {
                        super._burn(account, amount);
                        votingPowerChangeNonce = votingPowerChangeNonce.add(1);
                        _changeVotingPower(account);
                      }
                    
                      function _changeVotingPower(address account) internal {
                        uint256 currentIndex = votingPowerChangeCount[account];
                        uint256 newPower = balanceOf(account);
                        require(newPower < (1 << 192));
                        require(votingPowerChangeNonce < (1 << 64));
                        currentIndex = currentIndex.add(1);
                        votingPowerChangeCount[account] = currentIndex;
                        _votingPower[account][currentIndex] = (votingPowerChangeNonce << 192) | newPower;
                      }
                    }
                    
                    // File: contracts/BandToken.sol
                    
                    pragma solidity 0.5.9;
                    
                    
                    
                    
                    /// "BandToken" is the native ERC-20 token of Band Protocol.
                    contract BandToken is ERC20Base("BandToken", "BAND"), SnapshotToken {}

                    File 4 of 9: KyberReserve
                    // File: contracts/ERC20Interface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    // https://github.com/ethereum/EIPs/issues/20
                    interface ERC20 {
                        function totalSupply() public view returns (uint supply);
                        function balanceOf(address _owner) public view returns (uint balance);
                        function transfer(address _to, uint _value) public returns (bool success);
                        function transferFrom(address _from, address _to, uint _value) public returns (bool success);
                        function approve(address _spender, uint _value) public returns (bool success);
                        function allowance(address _owner, address _spender) public view returns (uint remaining);
                        function decimals() public view returns(uint digits);
                        event Approval(address indexed _owner, address indexed _spender, uint _value);
                    }
                    
                    // File: contracts/Utils.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    /// @title Kyber constants contract
                    contract Utils {
                    
                        ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
                        uint  constant internal PRECISION = (10**18);
                        uint  constant internal MAX_QTY   = (10**28); // 10B tokens
                        uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
                        uint  constant internal MAX_DECIMALS = 18;
                        uint  constant internal ETH_DECIMALS = 18;
                        mapping(address=>uint) internal decimals;
                    
                        function setDecimals(ERC20 token) internal {
                            if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
                            else decimals[token] = token.decimals();
                        }
                    
                        function getDecimals(ERC20 token) internal view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
                            uint tokenDecimals = decimals[token];
                            // technically, there might be token with decimals 0
                            // moreover, very possible that old tokens have decimals 0
                            // these tokens will just have higher gas fees.
                            if(tokenDecimals == 0) return token.decimals();
                    
                            return tokenDecimals;
                        }
                    
                        function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(srcQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                    
                            if (dstDecimals >= srcDecimals) {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
                            } else {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
                            }
                        }
                    
                        function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(dstQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                            
                            //source quantity is rounded up. to avoid dest quantity being too low.
                            uint numerator;
                            uint denominator;
                            if (srcDecimals >= dstDecimals) {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
                                denominator = rate;
                            } else {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty);
                                denominator = (rate * (10**(dstDecimals - srcDecimals)));
                            }
                            return (numerator + denominator - 1) / denominator; //avoid rounding down errors
                        }
                    }
                    
                    // File: contracts/PermissionGroups.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    contract PermissionGroups {
                    
                        address public admin;
                        address public pendingAdmin;
                        mapping(address=>bool) internal operators;
                        mapping(address=>bool) internal alerters;
                        address[] internal operatorsGroup;
                        address[] internal alertersGroup;
                        uint constant internal MAX_GROUP_SIZE = 50;
                    
                        function PermissionGroups() public {
                            admin = msg.sender;
                        }
                    
                        modifier onlyAdmin() {
                            require(msg.sender == admin);
                            _;
                        }
                    
                        modifier onlyOperator() {
                            require(operators[msg.sender]);
                            _;
                        }
                    
                        modifier onlyAlerter() {
                            require(alerters[msg.sender]);
                            _;
                        }
                    
                        function getOperators () external view returns(address[]) {
                            return operatorsGroup;
                        }
                    
                        function getAlerters () external view returns(address[]) {
                            return alertersGroup;
                        }
                    
                        event TransferAdminPending(address pendingAdmin);
                    
                        /**
                         * @dev Allows the current admin to set the pendingAdmin address.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdmin(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(pendingAdmin);
                            pendingAdmin = newAdmin;
                        }
                    
                        /**
                         * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdminQuickly(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(newAdmin);
                            AdminClaimed(newAdmin, admin);
                            admin = newAdmin;
                        }
                    
                        event AdminClaimed( address newAdmin, address previousAdmin);
                    
                        /**
                         * @dev Allows the pendingAdmin address to finalize the change admin process.
                         */
                        function claimAdmin() public {
                            require(pendingAdmin == msg.sender);
                            AdminClaimed(pendingAdmin, admin);
                            admin = pendingAdmin;
                            pendingAdmin = address(0);
                        }
                    
                        event AlerterAdded (address newAlerter, bool isAdd);
                    
                        function addAlerter(address newAlerter) public onlyAdmin {
                            require(!alerters[newAlerter]); // prevent duplicates.
                            require(alertersGroup.length < MAX_GROUP_SIZE);
                    
                            AlerterAdded(newAlerter, true);
                            alerters[newAlerter] = true;
                            alertersGroup.push(newAlerter);
                        }
                    
                        function removeAlerter (address alerter) public onlyAdmin {
                            require(alerters[alerter]);
                            alerters[alerter] = false;
                    
                            for (uint i = 0; i < alertersGroup.length; ++i) {
                                if (alertersGroup[i] == alerter) {
                                    alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                                    alertersGroup.length--;
                                    AlerterAdded(alerter, false);
                                    break;
                                }
                            }
                        }
                    
                        event OperatorAdded(address newOperator, bool isAdd);
                    
                        function addOperator(address newOperator) public onlyAdmin {
                            require(!operators[newOperator]); // prevent duplicates.
                            require(operatorsGroup.length < MAX_GROUP_SIZE);
                    
                            OperatorAdded(newOperator, true);
                            operators[newOperator] = true;
                            operatorsGroup.push(newOperator);
                        }
                    
                        function removeOperator (address operator) public onlyAdmin {
                            require(operators[operator]);
                            operators[operator] = false;
                    
                            for (uint i = 0; i < operatorsGroup.length; ++i) {
                                if (operatorsGroup[i] == operator) {
                                    operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                                    operatorsGroup.length -= 1;
                                    OperatorAdded(operator, false);
                                    break;
                                }
                            }
                        }
                    }
                    
                    // File: contracts/Withdrawable.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    
                    /**
                     * @title Contracts that should be able to recover tokens or ethers
                     * @author Ilan Doron
                     * @dev This allows to recover any tokens or Ethers received in a contract.
                     * This will prevent any accidental loss of tokens.
                     */
                    contract Withdrawable is PermissionGroups {
                    
                        event TokenWithdraw(ERC20 token, uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw all ERC20 compatible tokens
                         * @param token ERC20 The address of the token contract
                         */
                        function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
                            require(token.transfer(sendTo, amount));
                            TokenWithdraw(token, amount, sendTo);
                        }
                    
                        event EtherWithdraw(uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw Ethers
                         */
                        function withdrawEther(uint amount, address sendTo) external onlyAdmin {
                            sendTo.transfer(amount);
                            EtherWithdraw(amount, sendTo);
                        }
                    }
                    
                    // File: contracts/ConversionRatesInterface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    interface ConversionRatesInterface {
                    
                        function recordImbalance(
                            ERC20 token,
                            int buyAmount,
                            uint rateUpdateBlock,
                            uint currentBlock
                        )
                            public;
                    
                        function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
                    }
                    
                    // File: contracts/SanityRatesInterface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    interface SanityRatesInterface {
                        function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
                    }
                    
                    // File: contracts/KyberReserveInterface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    /// @title Kyber Reserve contract
                    interface KyberReserveInterface {
                    
                        function trade(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            address destAddress,
                            uint conversionRate,
                            bool validate
                        )
                            public
                            payable
                            returns(bool);
                    
                        function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
                    }
                    
                    // File: contracts/reserves/KyberReserve.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    
                    
                    
                    
                    
                    /// @title Kyber Reserve contract
                    contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {
                    
                        address public kyberNetwork;
                        bool public tradeEnabled;
                        ConversionRatesInterface public conversionRatesContract;
                        SanityRatesInterface public sanityRatesContract;
                        mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
                        mapping(address=>address) public tokenWallet;
                    
                        function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
                            require(_admin != address(0));
                            require(_ratesContract != address(0));
                            require(_kyberNetwork != address(0));
                            kyberNetwork = _kyberNetwork;
                            conversionRatesContract = _ratesContract;
                            admin = _admin;
                            tradeEnabled = true;
                        }
                    
                        event DepositToken(ERC20 token, uint amount);
                    
                        function() public payable {
                            DepositToken(ETH_TOKEN_ADDRESS, msg.value);
                        }
                    
                        event TradeExecute(
                            address indexed origin,
                            address src,
                            uint srcAmount,
                            address destToken,
                            uint destAmount,
                            address destAddress
                        );
                    
                        function trade(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            address destAddress,
                            uint conversionRate,
                            bool validate
                        )
                            public
                            payable
                            returns(bool)
                        {
                            require(tradeEnabled);
                            require(msg.sender == kyberNetwork);
                    
                            require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
                    
                            return true;
                        }
                    
                        event TradeEnabled(bool enable);
                    
                        function enableTrade() public onlyAdmin returns(bool) {
                            tradeEnabled = true;
                            TradeEnabled(true);
                    
                            return true;
                        }
                    
                        function disableTrade() public onlyAlerter returns(bool) {
                            tradeEnabled = false;
                            TradeEnabled(false);
                    
                            return true;
                        }
                    
                        event WithdrawAddressApproved(ERC20 token, address addr, bool approve);
                    
                        function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
                            approvedWithdrawAddresses[keccak256(token, addr)] = approve;
                            WithdrawAddressApproved(token, addr, approve);
                    
                            setDecimals(token);
                            if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) {
                                tokenWallet[token] = this; // by default
                                require(token.approve(this, 2 ** 255));
                            }
                        }
                    
                        event NewTokenWallet(ERC20 token, address wallet);
                    
                        function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {
                            require(wallet != address(0x0));
                            tokenWallet[token] = wallet;
                            NewTokenWallet(token, wallet);
                        }
                    
                        event WithdrawFunds(ERC20 token, uint amount, address destination);
                    
                        function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
                            require(approvedWithdrawAddresses[keccak256(token, destination)]);
                    
                            if (token == ETH_TOKEN_ADDRESS) {
                                destination.transfer(amount);
                            } else {
                                require(token.transferFrom(tokenWallet[token], destination, amount));
                            }
                    
                            WithdrawFunds(token, amount, destination);
                    
                            return true;
                        }
                    
                        event SetContractAddresses(address network, address rate, address sanity);
                    
                        function setContracts(
                            address _kyberNetwork,
                            ConversionRatesInterface _conversionRates,
                            SanityRatesInterface _sanityRates
                        )
                            public
                            onlyAdmin
                        {
                            require(_kyberNetwork != address(0));
                            require(_conversionRates != address(0));
                    
                            kyberNetwork = _kyberNetwork;
                            conversionRatesContract = _conversionRates;
                            sanityRatesContract = _sanityRates;
                    
                            SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
                        }
                    
                        ////////////////////////////////////////////////////////////////////////////
                        /// status functions ///////////////////////////////////////////////////////
                        ////////////////////////////////////////////////////////////////////////////
                        function getBalance(ERC20 token) public view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS)
                                return this.balance;
                            else {
                                address wallet = tokenWallet[token];
                                uint balanceOfWallet = token.balanceOf(wallet);
                                uint allowanceOfWallet = token.allowance(wallet, this);
                    
                                return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;
                            }
                        }
                    
                        function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
                            uint dstDecimals = getDecimals(dest);
                            uint srcDecimals = getDecimals(src);
                    
                            return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
                        }
                    
                        function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
                            uint dstDecimals = getDecimals(dest);
                            uint srcDecimals = getDecimals(src);
                    
                            return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
                        }
                    
                        function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
                            ERC20 token;
                            bool  isBuy;
                    
                            if (!tradeEnabled) return 0;
                    
                            if (ETH_TOKEN_ADDRESS == src) {
                                isBuy = true;
                                token = dest;
                            } else if (ETH_TOKEN_ADDRESS == dest) {
                                isBuy = false;
                                token = src;
                            } else {
                                return 0; // pair is not listed
                            }
                    
                            uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);
                            uint destQty = getDestQty(src, dest, srcQty, rate);
                    
                            if (getBalance(dest) < destQty) return 0;
                    
                            if (sanityRatesContract != address(0)) {
                                uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
                                if (rate > sanityRate) return 0;
                            }
                    
                            return rate;
                        }
                    
                        /// @dev do a trade
                        /// @param srcToken Src token
                        /// @param srcAmount Amount of src token
                        /// @param destToken Destination token
                        /// @param destAddress Destination address to send tokens to
                        /// @param validate If true, additional validations are applicable
                        /// @return true iff trade is successful
                        function doTrade(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            address destAddress,
                            uint conversionRate,
                            bool validate
                        )
                            internal
                            returns(bool)
                        {
                            // can skip validation if done at kyber network level
                            if (validate) {
                                require(conversionRate > 0);
                                if (srcToken == ETH_TOKEN_ADDRESS)
                                    require(msg.value == srcAmount);
                                else
                                    require(msg.value == 0);
                            }
                    
                            uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
                            // sanity check
                            require(destAmount > 0);
                    
                            // add to imbalance
                            ERC20 token;
                            int tradeAmount;
                            if (srcToken == ETH_TOKEN_ADDRESS) {
                                tradeAmount = int(destAmount);
                                token = destToken;
                            } else {
                                tradeAmount = -1 * int(srcAmount);
                                token = srcToken;
                            }
                    
                            conversionRatesContract.recordImbalance(
                                token,
                                tradeAmount,
                                0,
                                block.number
                            );
                    
                            // collect src tokens
                            if (srcToken != ETH_TOKEN_ADDRESS) {
                                require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));
                            }
                    
                            // send dest tokens
                            if (destToken == ETH_TOKEN_ADDRESS) {
                                destAddress.transfer(destAmount);
                            } else {
                                require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));
                            }
                    
                            TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
                    
                            return true;
                        }
                    }

                    File 5 of 9: FeeBurner
                    {"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"FeeBurner.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./FeeBurnerInterface.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./Utils3.sol\";\nimport \"./KyberNetworkInterface.sol\";\n\n\ninterface BurnableToken {\n    function transferFrom(address _from, address _to, uint _value) public returns (bool);\n    function burnFrom(address _from, uint256 _value) public returns (bool);\n}\n\n\ncontract FeeBurner is Withdrawable, FeeBurnerInterface, Utils3 {\n\n    mapping(address=\u003euint) public reserveFeesInBps;\n    mapping(address=\u003eaddress) public reserveKNCWallet; //wallet holding knc per reserve. from here burn and send fees.\n    mapping(address=\u003euint) public walletFeesInBps; // wallet that is the source of tx is entitled so some fees.\n    mapping(address=\u003euint) public reserveFeeToBurn;\n    mapping(address=\u003euint) public feePayedPerReserve; // track burned fees and sent wallet fees per reserve.\n    mapping(address=\u003emapping(address=\u003euint)) public reserveFeeToWallet;\n    address public taxWallet;\n    uint public taxFeeBps = 0; // burned fees are taxed. % out of burned fees.\n\n    BurnableToken public knc;\n    KyberNetworkInterface public kyberNetwork;\n    uint public kncPerEthRatePrecision = 600 * PRECISION; //--\u003e 1 ether = 600 knc tokens\n\n    function FeeBurner(\n        address _admin,\n        BurnableToken _kncToken,\n        KyberNetworkInterface _kyberNetwork,\n        uint _initialKncToEthRatePrecision\n    )\n        public\n    {\n        require(_admin != address(0));\n        require(_kncToken != address(0));\n        require(_kyberNetwork != address(0));\n        require(_initialKncToEthRatePrecision != 0);\n\n        kyberNetwork = _kyberNetwork;\n        admin = _admin;\n        knc = _kncToken;\n        kncPerEthRatePrecision = _initialKncToEthRatePrecision;\n    }\n\n    event ReserveDataSet(address reserve, uint feeInBps, address kncWallet);\n\n    function setReserveData(address reserve, uint feesInBps, address kncWallet) public onlyOperator {\n        require(feesInBps \u003c 100); // make sure it is always \u003c 1%\n        require(kncWallet != address(0));\n        reserveFeesInBps[reserve] = feesInBps;\n        reserveKNCWallet[reserve] = kncWallet;\n        ReserveDataSet(reserve, feesInBps, kncWallet);\n    }\n\n    event WalletFeesSet(address wallet, uint feesInBps);\n\n    function setWalletFees(address wallet, uint feesInBps) public onlyAdmin {\n        require(feesInBps \u003c 10000); // under 100%\n        walletFeesInBps[wallet] = feesInBps;\n        WalletFeesSet(wallet, feesInBps);\n    }\n\n    event TaxFeesSet(uint feesInBps);\n\n    function setTaxInBps(uint _taxFeeBps) public onlyAdmin {\n        require(_taxFeeBps \u003c 10000); // under 100%\n        taxFeeBps = _taxFeeBps;\n        TaxFeesSet(_taxFeeBps);\n    }\n\n    event TaxWalletSet(address taxWallet);\n\n    function setTaxWallet(address _taxWallet) public onlyAdmin {\n        require(_taxWallet != address(0));\n        taxWallet = _taxWallet;\n        TaxWalletSet(_taxWallet);\n    }\n\n    event KNCRateSet(uint ethToKncRatePrecision, uint kyberEthKnc, uint kyberKncEth, address updater);\n\n    function setKNCRate() public {\n        //query kyber for knc rate sell and buy\n        uint kyberEthKncRate;\n        uint kyberKncEthRate;\n        (kyberEthKncRate, ) = kyberNetwork.getExpectedRate(ETH_TOKEN_ADDRESS, ERC20(knc), (10 ** 18));\n        (kyberKncEthRate, ) = kyberNetwork.getExpectedRate(ERC20(knc), ETH_TOKEN_ADDRESS, (10 ** 18));\n\n        //check \"reasonable\" spread == diff not too big. rate wasn\u0027t tampered.\n        require(kyberEthKncRate * kyberKncEthRate \u003c PRECISION ** 2 * 2);\n        require(kyberEthKncRate * kyberKncEthRate \u003e PRECISION ** 2 / 2);\n\n        require(kyberEthKncRate \u003c= MAX_RATE);\n        kncPerEthRatePrecision = kyberEthKncRate;\n        KNCRateSet(kncPerEthRatePrecision, kyberEthKncRate, kyberKncEthRate, msg.sender);\n    }\n\n    event AssignFeeToWallet(address reserve, address wallet, uint walletFee);\n    event AssignBurnFees(address reserve, uint burnFee);\n\n    function handleFees(uint tradeWeiAmount, address reserve, address wallet) public returns(bool) {\n        require(msg.sender == address(kyberNetwork));\n        require(tradeWeiAmount \u003c= MAX_QTY);\n\n        // MAX_DECIMALS = 18 = KNC_DECIMALS, use this value instead of calling getDecimals() to save gas\n        uint kncAmount = calcDestAmountWithDecimals(ETH_DECIMALS, MAX_DECIMALS, tradeWeiAmount, kncPerEthRatePrecision);\n        uint fee = kncAmount * reserveFeesInBps[reserve] / 10000;\n\n        uint walletFee = fee * walletFeesInBps[wallet] / 10000;\n        require(fee \u003e= walletFee);\n        uint feeToBurn = fee - walletFee;\n\n        if (walletFee \u003e 0) {\n            reserveFeeToWallet[reserve][wallet] += walletFee;\n            AssignFeeToWallet(reserve, wallet, walletFee);\n        }\n\n        if (feeToBurn \u003e 0) {\n            AssignBurnFees(reserve, feeToBurn);\n            reserveFeeToBurn[reserve] += feeToBurn;\n        }\n\n        return true;\n    }\n\n    event BurnAssignedFees(address indexed reserve, address sender, uint quantity);\n\n    event SendTaxFee(address indexed reserve, address sender, address taxWallet, uint quantity);\n\n    // this function is callable by anyone\n    function burnReserveFees(address reserve) public {\n        uint burnAmount = reserveFeeToBurn[reserve];\n        uint taxToSend = 0;\n        require(burnAmount \u003e 2);\n        reserveFeeToBurn[reserve] = 1; // leave 1 twei to avoid spikes in gas fee\n        if (taxWallet != address(0) \u0026\u0026 taxFeeBps != 0) {\n            taxToSend = (burnAmount - 1) * taxFeeBps / 10000;\n            require(burnAmount - 1 \u003e taxToSend);\n            burnAmount -= taxToSend;\n            if (taxToSend \u003e 0) {\n                require(knc.transferFrom(reserveKNCWallet[reserve], taxWallet, taxToSend));\n                SendTaxFee(reserve, msg.sender, taxWallet, taxToSend);\n            }\n        }\n        require(knc.burnFrom(reserveKNCWallet[reserve], burnAmount - 1));\n\n        //update reserve \"payments\" so far\n        feePayedPerReserve[reserve] += (taxToSend + burnAmount - 1);\n\n        BurnAssignedFees(reserve, msg.sender, (burnAmount - 1));\n    }\n\n    event SendWalletFees(address indexed wallet, address reserve, address sender);\n\n    // this function is callable by anyone\n    function sendFeeToWallet(address wallet, address reserve) public {\n        uint feeAmount = reserveFeeToWallet[reserve][wallet];\n        require(feeAmount \u003e 1);\n        reserveFeeToWallet[reserve][wallet] = 1; // leave 1 twei to avoid spikes in gas fee\n        require(knc.transferFrom(reserveKNCWallet[reserve], wallet, feeAmount - 1));\n\n        feePayedPerReserve[reserve] += (feeAmount - 1);\n        SendWalletFees(wallet, reserve, msg.sender);\n    }\n}\n"},"FeeBurnerInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\ninterface FeeBurnerInterface {\n    function handleFees (uint tradeWeiAmount, address reserve, address wallet) public returns(bool);\n    function setReserveData(address reserve, uint feesInBps, address kncWallet) public;\n}\n"},"KyberNetworkInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber Network interface\ninterface KyberNetworkInterface {\n    function maxGasPrice() public view returns(uint);\n    function getUserCapInWei(address user) public view returns(uint);\n    function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint);\n    function enabled() public view returns(bool);\n    function info(bytes32 id) public view returns(uint);\n\n    function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) public view\n        returns (uint expectedRate, uint slippageRate);\n\n    function tradeWithHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress,\n        uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) public payable returns(uint);\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n    address public admin;\n    address public pendingAdmin;\n    mapping(address=\u003ebool) internal operators;\n    mapping(address=\u003ebool) internal alerters;\n    address[] internal operatorsGroup;\n    address[] internal alertersGroup;\n    uint constant internal MAX_GROUP_SIZE = 50;\n\n    function PermissionGroups() public {\n        admin = msg.sender;\n    }\n\n    modifier onlyAdmin() {\n        require(msg.sender == admin);\n        _;\n    }\n\n    modifier onlyOperator() {\n        require(operators[msg.sender]);\n        _;\n    }\n\n    modifier onlyAlerter() {\n        require(alerters[msg.sender]);\n        _;\n    }\n\n    function getOperators () external view returns(address[]) {\n        return operatorsGroup;\n    }\n\n    function getAlerters () external view returns(address[]) {\n        return alertersGroup;\n    }\n\n    event TransferAdminPending(address pendingAdmin);\n\n    /**\n     * @dev Allows the current admin to set the pendingAdmin address.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdmin(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(pendingAdmin);\n        pendingAdmin = newAdmin;\n    }\n\n    /**\n     * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdminQuickly(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(newAdmin);\n        AdminClaimed(newAdmin, admin);\n        admin = newAdmin;\n    }\n\n    event AdminClaimed( address newAdmin, address previousAdmin);\n\n    /**\n     * @dev Allows the pendingAdmin address to finalize the change admin process.\n     */\n    function claimAdmin() public {\n        require(pendingAdmin == msg.sender);\n        AdminClaimed(pendingAdmin, admin);\n        admin = pendingAdmin;\n        pendingAdmin = address(0);\n    }\n\n    event AlerterAdded (address newAlerter, bool isAdd);\n\n    function addAlerter(address newAlerter) public onlyAdmin {\n        require(!alerters[newAlerter]); // prevent duplicates.\n        require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n        AlerterAdded(newAlerter, true);\n        alerters[newAlerter] = true;\n        alertersGroup.push(newAlerter);\n    }\n\n    function removeAlerter (address alerter) public onlyAdmin {\n        require(alerters[alerter]);\n        alerters[alerter] = false;\n\n        for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n            if (alertersGroup[i] == alerter) {\n                alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n                alertersGroup.length--;\n                AlerterAdded(alerter, false);\n                break;\n            }\n        }\n    }\n\n    event OperatorAdded(address newOperator, bool isAdd);\n\n    function addOperator(address newOperator) public onlyAdmin {\n        require(!operators[newOperator]); // prevent duplicates.\n        require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n        OperatorAdded(newOperator, true);\n        operators[newOperator] = true;\n        operatorsGroup.push(newOperator);\n    }\n\n    function removeOperator (address operator) public onlyAdmin {\n        require(operators[operator]);\n        operators[operator] = false;\n\n        for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n            if (operatorsGroup[i] == operator) {\n                operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n                operatorsGroup.length -= 1;\n                OperatorAdded(operator, false);\n                break;\n            }\n        }\n    }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"Utils2.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils.sol\";\n\n\ncontract Utils2 is Utils {\n\n    /// @dev get the balance of a user.\n    /// @param token The token type\n    /// @return The balance\n    function getBalance(ERC20 token, address user) public view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS)\n            return user.balance;\n        else\n            return token.balanceOf(user);\n    }\n\n    function getDecimalsSafe(ERC20 token) internal returns(uint) {\n\n        if (decimals[token] == 0) {\n            setDecimals(token);\n        }\n\n        return decimals[token];\n    }\n\n    function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {\n        return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);\n    }\n\n    function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {\n        return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);\n    }\n\n    function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)\n        internal pure returns(uint)\n    {\n        require(srcAmount \u003c= MAX_QTY);\n        require(destAmount \u003c= MAX_QTY);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);\n        }\n    }\n}\n"},"Utils3.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./Utils2.sol\";\n\n\ncontract Utils3 is Utils2 {\n\n    function calcDestAmountWithDecimals(uint srcDecimals, uint destDecimals, uint srcAmount, uint rate) internal pure returns(uint) {\n        return calcDstQty(srcAmount, srcDecimals, destDecimals, rate);\n    }\n\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}

                    File 6 of 9: LiquidityConversionRates
                    {"ConversionRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ConversionRatesInterface {\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public;\n\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);\n}\n"},"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"LiquidityConversionRates.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../../ConversionRatesInterface.sol\";\nimport \"../../Withdrawable.sol\";\nimport \"../../Utils.sol\";\nimport \"./LiquidityFormula.sol\";\n\n\ncontract LiquidityConversionRates is ConversionRatesInterface, LiquidityFormula, Withdrawable, Utils {\n\n    uint constant FORMULA_PRECISION_BITS = 40;\n\n    ERC20 public token;\n    address public reserveContract;\n\n    uint public numFpBits;\n    uint public formulaPrecision;\n\n    uint public rInFp;\n    uint public pMinInFp;\n\n    uint public maxEthCapBuyInFp;\n    uint public maxEthCapSellInFp;\n    uint public maxQtyInFp;\n\n    uint public feeInBps;\n    uint public collectedFeesInTwei = 0;\n\n    uint public maxBuyRateInPrecision;\n    uint public minBuyRateInPrecision;\n    uint public maxSellRateInPrecision;\n    uint public minSellRateInPrecision;\n\n    function LiquidityConversionRates(address _admin, ERC20 _token) public {\n        transferAdminQuickly(_admin);\n        token = _token;\n        setDecimals(token);\n        require(getDecimals(token) \u003c= MAX_DECIMALS);\n    }\n\n    event ReserveAddressSet(address reserve);\n\n    function setReserveAddress(address reserve) public onlyAdmin {\n        reserveContract = reserve;\n        ReserveAddressSet(reserve);\n    }\n\n    event LiquidityParamsSet(\n        uint rInFp,\n        uint pMinInFp,\n        uint numFpBits,\n        uint maxCapBuyInFp,\n        uint maxEthCapSellInFp,\n        uint feeInBps,\n        uint formulaPrecision,\n        uint maxQtyInFp,\n        uint maxBuyRateInPrecision,\n        uint minBuyRateInPrecision,\n        uint maxSellRateInPrecision,\n        uint minSellRateInPrecision\n    );\n\n    function setLiquidityParams(\n        uint _rInFp,\n        uint _pMinInFp,\n        uint _numFpBits,\n        uint _maxCapBuyInWei,\n        uint _maxCapSellInWei,\n        uint _feeInBps,\n        uint _maxTokenToEthRateInPrecision,\n        uint _minTokenToEthRateInPrecision\n    ) public onlyAdmin {\n        require(_numFpBits == FORMULA_PRECISION_BITS); // only used config, but keep in API\n        formulaPrecision = uint(1)\u003c\u003c_numFpBits; // require(formulaPrecision \u003c= MAX_QTY)\n        require(_feeInBps \u003c 10000);\n        require(_minTokenToEthRateInPrecision \u003c _maxTokenToEthRateInPrecision);\n        require(_minTokenToEthRateInPrecision \u003e 0);\n        require(_rInFp \u003e 0);\n        require(_pMinInFp \u003e 0);\n\n        rInFp = _rInFp;\n        pMinInFp = _pMinInFp;\n        maxQtyInFp = fromWeiToFp(MAX_QTY);\n        numFpBits = _numFpBits;\n        maxEthCapBuyInFp = fromWeiToFp(_maxCapBuyInWei);\n        maxEthCapSellInFp = fromWeiToFp(_maxCapSellInWei);\n        feeInBps = _feeInBps;\n        maxBuyRateInPrecision = PRECISION * PRECISION / _minTokenToEthRateInPrecision;\n        minBuyRateInPrecision = PRECISION * PRECISION / _maxTokenToEthRateInPrecision;\n        maxSellRateInPrecision = _maxTokenToEthRateInPrecision;\n        minSellRateInPrecision = _minTokenToEthRateInPrecision;\n\n        LiquidityParamsSet(\n            rInFp,\n            pMinInFp,\n            numFpBits,\n            maxEthCapBuyInFp,\n            maxEthCapSellInFp,\n            feeInBps,\n            formulaPrecision,\n            maxQtyInFp,\n            maxBuyRateInPrecision,\n            minBuyRateInPrecision,\n            maxSellRateInPrecision,\n            minSellRateInPrecision\n        );\n    }\n\n    function recordImbalance(\n        ERC20 conversionToken,\n        int buyAmountInTwei,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public\n    {\n        conversionToken;\n        rateUpdateBlock;\n        currentBlock;\n\n        require(msg.sender == reserveContract);\n        if (buyAmountInTwei \u003e 0) {\n            // Buy case\n            collectedFeesInTwei += calcCollectedFee(abs(buyAmountInTwei));\n        } else {\n            // Sell case\n            collectedFeesInTwei += abs(buyAmountInTwei) * feeInBps / 10000;\n        }\n    }\n\n    event CollectedFeesReset(uint resetFeesInTwei);\n\n    function resetCollectedFees() public onlyAdmin {\n        uint resetFeesInTwei = collectedFeesInTwei;\n        collectedFeesInTwei = 0;\n\n        CollectedFeesReset(resetFeesInTwei);\n    }\n\n    function getRate(\n            ERC20 conversionToken,\n            uint currentBlockNumber,\n            bool buy,\n            uint qtyInSrcWei\n    ) public view returns(uint) {\n\n        currentBlockNumber;\n\n        require(qtyInSrcWei \u003c= MAX_QTY);\n        uint eInFp = fromWeiToFp(reserveContract.balance);\n        uint rateInPrecision = getRateWithE(conversionToken, buy, qtyInSrcWei, eInFp);\n        require(rateInPrecision \u003c= MAX_RATE);\n        return rateInPrecision;\n    }\n\n    function getRateWithE(ERC20 conversionToken, bool buy, uint qtyInSrcWei, uint eInFp) public view returns(uint) {\n        uint deltaEInFp;\n        uint sellInputTokenQtyInFp;\n        uint deltaTInFp;\n        uint rateInPrecision;\n\n        require(qtyInSrcWei \u003c= MAX_QTY);\n        require(eInFp \u003c= maxQtyInFp);\n        if (conversionToken != token) return 0;\n\n        if (buy) {\n            // ETH goes in, token goes out\n            deltaEInFp = fromWeiToFp(qtyInSrcWei);\n            if (deltaEInFp \u003e maxEthCapBuyInFp) return 0;\n\n            if (deltaEInFp == 0) {\n                rateInPrecision = buyRateZeroQuantity(eInFp);\n            } else {\n                rateInPrecision = buyRate(eInFp, deltaEInFp);\n            }\n        } else {\n            sellInputTokenQtyInFp = fromTweiToFp(qtyInSrcWei);\n            deltaTInFp = valueAfterReducingFee(sellInputTokenQtyInFp);\n            if (deltaTInFp == 0) {\n                rateInPrecision = sellRateZeroQuantity(eInFp);\n                deltaEInFp = 0;\n            } else {\n                (rateInPrecision, deltaEInFp) = sellRate(eInFp, sellInputTokenQtyInFp, deltaTInFp);\n            }\n\n            if (deltaEInFp \u003e maxEthCapSellInFp) return 0;\n        }\n\n        rateInPrecision = rateAfterValidation(rateInPrecision, buy);\n        return rateInPrecision;\n    }\n\n    function rateAfterValidation(uint rateInPrecision, bool buy) public view returns(uint) {\n        uint minAllowRateInPrecision;\n        uint maxAllowedRateInPrecision;\n\n        if (buy) {\n            minAllowRateInPrecision = minBuyRateInPrecision;\n            maxAllowedRateInPrecision = maxBuyRateInPrecision;\n        } else {\n            minAllowRateInPrecision = minSellRateInPrecision;\n            maxAllowedRateInPrecision = maxSellRateInPrecision;\n        }\n\n        if ((rateInPrecision \u003e maxAllowedRateInPrecision) || (rateInPrecision \u003c minAllowRateInPrecision)) {\n            return 0;\n        } else if (rateInPrecision \u003e MAX_RATE) {\n            return 0;\n        } else {\n            return rateInPrecision;\n        }\n    }\n\n    function buyRate(uint eInFp, uint deltaEInFp) public view returns(uint) {\n        uint deltaTInFp = deltaTFunc(rInFp, pMinInFp, eInFp, deltaEInFp, formulaPrecision);\n        require(deltaTInFp \u003c= maxQtyInFp);\n        deltaTInFp = valueAfterReducingFee(deltaTInFp);\n        return deltaTInFp * PRECISION / deltaEInFp;\n    }\n\n    function buyRateZeroQuantity(uint eInFp) public view returns(uint) {\n        uint ratePreReductionInPrecision = formulaPrecision * PRECISION / pE(rInFp, pMinInFp, eInFp, formulaPrecision);\n        return valueAfterReducingFee(ratePreReductionInPrecision);\n    }\n\n    function sellRate(\n        uint eInFp,\n        uint sellInputTokenQtyInFp,\n        uint deltaTInFp\n    ) public view returns(uint rateInPrecision, uint deltaEInFp) {\n        deltaEInFp = deltaEFunc(rInFp, pMinInFp, eInFp, deltaTInFp, formulaPrecision, numFpBits);\n        require(deltaEInFp \u003c= maxQtyInFp);\n        rateInPrecision = deltaEInFp * PRECISION / sellInputTokenQtyInFp;\n    }\n\n    function sellRateZeroQuantity(uint eInFp) public view returns(uint) {\n        uint ratePreReductionInPrecision = pE(rInFp, pMinInFp, eInFp, formulaPrecision) * PRECISION / formulaPrecision;\n        return valueAfterReducingFee(ratePreReductionInPrecision);\n    }\n\n    function fromTweiToFp(uint qtyInTwei) public view returns(uint) {\n        require(qtyInTwei \u003c= MAX_QTY);\n        return qtyInTwei * formulaPrecision / (10 ** getDecimals(token));\n    }\n\n    function fromWeiToFp(uint qtyInwei) public view returns(uint) {\n        require(qtyInwei \u003c= MAX_QTY);\n        return qtyInwei * formulaPrecision / (10 ** ETH_DECIMALS);\n    }\n\n    function valueAfterReducingFee(uint val) public view returns(uint) {\n        require(val \u003c= BIG_NUMBER);\n        return ((10000 - feeInBps) * val) / 10000;\n    }\n\n    function calcCollectedFee(uint val) public view returns(uint) {\n        require(val \u003c= MAX_QTY);\n        return val * feeInBps / (10000 - feeInBps);\n    }\n\n    function abs(int val) public pure returns(uint) {\n        if (val \u003c 0) {\n            return uint(val * (-1));\n        } else {\n            return uint(val);\n        }\n    }\n}\n"},"LiquidityFormula.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract UtilMath {\n    uint public constant BIG_NUMBER = (uint(1)\u003c\u003cuint(200));\n\n    function checkMultOverflow(uint x, uint y) public pure returns(bool) {\n        if (y == 0) return false;\n        return (((x*y) / y) != x);\n    }\n\n    function compactFraction(uint p, uint q, uint precision) public pure returns (uint, uint) {\n        if (q \u003c precision * precision) return (p, q);\n        return compactFraction(p/precision, q/precision, precision);\n    }\n\n    /* solhint-disable code-complexity */\n    function exp(uint p, uint q, uint precision) public pure returns (uint) {\n        uint n = 0;\n        uint nFact = 1;\n        uint currentP = 1;\n        uint currentQ = 1;\n\n        uint sum = 0;\n        uint prevSum = 0;\n\n        while (true) {\n            if (checkMultOverflow(currentP, precision)) return sum;\n            if (checkMultOverflow(currentQ, nFact)) return sum;\n\n            sum += (currentP * precision) / (currentQ * nFact);\n\n            if (sum == prevSum) return sum;\n            prevSum = sum;\n\n            n++;\n\n            if (checkMultOverflow(currentP, p)) return sum;\n            if (checkMultOverflow(currentQ, q)) return sum;\n            if (checkMultOverflow(nFact, n)) return sum;\n\n            currentP *= p;\n            currentQ *= q;\n            nFact *= n;\n\n            (currentP, currentQ) = compactFraction(currentP, currentQ, precision);\n        }\n    }\n    /* solhint-enable code-complexity */\n\n    function countLeadingZeros(uint p, uint q) public pure returns (uint) {\n        uint denomator = (uint(1)\u003c\u003c255);\n        for (int i = 255; i \u003e= 0; i--) {\n            if ((q*denomator)/denomator != q) {\n                // overflow\n                denomator = denomator/2;\n                continue;\n            }\n            if (p/(q*denomator) \u003e 0) return uint(i);\n            denomator = denomator/2;\n        }\n\n        return uint(-1);\n    }\n\n    // log2 for a number that it in [1,2)\n    function log2ForSmallNumber(uint x, uint numPrecisionBits) public pure returns (uint) {\n        uint res = 0;\n        uint one = (uint(1)\u003c\u003cnumPrecisionBits);\n        uint two = 2 * one;\n        uint addition = one;\n\n        require((x \u003e= one) \u0026\u0026 (x \u003c= two));\n        require(numPrecisionBits \u003c 125);\n\n        for (uint i = numPrecisionBits; i \u003e 0; i--) {\n            x = (x*x) / one;\n            addition = addition/2;\n            if (x \u003e= two) {\n                x = x/2;\n                res += addition;\n            }\n        }\n\n        return res;\n    }\n\n    function logBase2 (uint p, uint q, uint numPrecisionBits) public pure returns (uint) {\n        uint n = 0;\n        uint precision = (uint(1)\u003c\u003cnumPrecisionBits);\n\n        if (p \u003e q) {\n            n = countLeadingZeros(p, q);\n        }\n\n        require(!checkMultOverflow(p, precision));\n        require(!checkMultOverflow(n, precision));\n        require(!checkMultOverflow(uint(1)\u003c\u003cn, q));\n\n        uint y = p * precision / (q * (uint(1)\u003c\u003cn));\n        uint log2Small = log2ForSmallNumber(y, numPrecisionBits);\n\n        require(n*precision \u003c= BIG_NUMBER);\n        require(log2Small \u003c= BIG_NUMBER);\n\n        return n * precision + log2Small;\n    }\n\n    function ln(uint p, uint q, uint numPrecisionBits) public pure returns (uint) {\n        uint ln2Numerator   = 6931471805599453094172;\n        uint ln2Denomerator = 10000000000000000000000;\n\n        uint log2x = logBase2(p, q, numPrecisionBits);\n\n        require(!checkMultOverflow(ln2Numerator, log2x));\n\n        return ln2Numerator * log2x / ln2Denomerator;\n    }\n}\n\n\ncontract LiquidityFormula is UtilMath {\n    function pE(uint r, uint pMIn, uint e, uint precision) public pure returns (uint) {\n        require(!checkMultOverflow(r, e));\n        uint expRE = exp(r*e, precision*precision, precision);\n        require(!checkMultOverflow(expRE, pMIn));\n        return pMIn*expRE / precision;\n    }\n\n    function deltaTFunc(uint r, uint pMIn, uint e, uint deltaE, uint precision) public pure returns (uint) {\n        uint pe = pE(r, pMIn, e, precision);\n        uint rpe = r * pe;\n\n        require(!checkMultOverflow(r, deltaE));\n        uint erdeltaE = exp(r*deltaE, precision*precision, precision);\n\n        require(erdeltaE \u003e= precision);\n        require(!checkMultOverflow(erdeltaE - precision, precision));\n        require(!checkMultOverflow((erdeltaE - precision)*precision, precision));\n        require(!checkMultOverflow((erdeltaE - precision)*precision*precision, precision));\n        require(!checkMultOverflow(rpe, erdeltaE));\n        require(!checkMultOverflow(r, pe));\n\n        return (erdeltaE - precision) * precision * precision * precision / (rpe*erdeltaE);\n    }\n\n    function deltaEFunc(uint r, uint pMIn, uint e, uint deltaT, uint precision, uint numPrecisionBits)\n        public pure\n        returns (uint)\n    {\n        uint pe = pE(r, pMIn, e, precision);\n        uint rpe = r * pe;\n\n        require(!checkMultOverflow(rpe, deltaT));\n        require(precision * precision + rpe * deltaT/precision \u003e precision * precision);\n        uint lnPart = ln(precision*precision + rpe*deltaT/precision, precision*precision, numPrecisionBits);\n\n        require(!checkMultOverflow(r, pe));\n        require(!checkMultOverflow(precision, precision));\n        require(!checkMultOverflow(rpe, deltaT));\n        require(!checkMultOverflow(lnPart, precision));\n\n        return lnPart * precision / r;\n    }\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n    address public admin;\n    address public pendingAdmin;\n    mapping(address=\u003ebool) internal operators;\n    mapping(address=\u003ebool) internal alerters;\n    address[] internal operatorsGroup;\n    address[] internal alertersGroup;\n    uint constant internal MAX_GROUP_SIZE = 50;\n\n    function PermissionGroups() public {\n        admin = msg.sender;\n    }\n\n    modifier onlyAdmin() {\n        require(msg.sender == admin);\n        _;\n    }\n\n    modifier onlyOperator() {\n        require(operators[msg.sender]);\n        _;\n    }\n\n    modifier onlyAlerter() {\n        require(alerters[msg.sender]);\n        _;\n    }\n\n    function getOperators () external view returns(address[]) {\n        return operatorsGroup;\n    }\n\n    function getAlerters () external view returns(address[]) {\n        return alertersGroup;\n    }\n\n    event TransferAdminPending(address pendingAdmin);\n\n    /**\n     * @dev Allows the current admin to set the pendingAdmin address.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdmin(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(pendingAdmin);\n        pendingAdmin = newAdmin;\n    }\n\n    /**\n     * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n     * @param newAdmin The address to transfer ownership to.\n     */\n    function transferAdminQuickly(address newAdmin) public onlyAdmin {\n        require(newAdmin != address(0));\n        TransferAdminPending(newAdmin);\n        AdminClaimed(newAdmin, admin);\n        admin = newAdmin;\n    }\n\n    event AdminClaimed( address newAdmin, address previousAdmin);\n\n    /**\n     * @dev Allows the pendingAdmin address to finalize the change admin process.\n     */\n    function claimAdmin() public {\n        require(pendingAdmin == msg.sender);\n        AdminClaimed(pendingAdmin, admin);\n        admin = pendingAdmin;\n        pendingAdmin = address(0);\n    }\n\n    event AlerterAdded (address newAlerter, bool isAdd);\n\n    function addAlerter(address newAlerter) public onlyAdmin {\n        require(!alerters[newAlerter]); // prevent duplicates.\n        require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n        AlerterAdded(newAlerter, true);\n        alerters[newAlerter] = true;\n        alertersGroup.push(newAlerter);\n    }\n\n    function removeAlerter (address alerter) public onlyAdmin {\n        require(alerters[alerter]);\n        alerters[alerter] = false;\n\n        for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n            if (alertersGroup[i] == alerter) {\n                alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n                alertersGroup.length--;\n                AlerterAdded(alerter, false);\n                break;\n            }\n        }\n    }\n\n    event OperatorAdded(address newOperator, bool isAdd);\n\n    function addOperator(address newOperator) public onlyAdmin {\n        require(!operators[newOperator]); // prevent duplicates.\n        require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n        OperatorAdded(newOperator, true);\n        operators[newOperator] = true;\n        operatorsGroup.push(newOperator);\n    }\n\n    function removeOperator (address operator) public onlyAdmin {\n        require(operators[operator]);\n        operators[operator] = false;\n\n        for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n            if (operatorsGroup[i] == operator) {\n                operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n                operatorsGroup.length -= 1;\n                OperatorAdded(operator, false);\n                break;\n            }\n        }\n    }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}

                    File 7 of 9: KyberUniswapReserve
                    // File: contracts/ERC20Interface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    // https://github.com/ethereum/EIPs/issues/20
                    interface ERC20 {
                        function totalSupply() public view returns (uint supply);
                        function balanceOf(address _owner) public view returns (uint balance);
                        function transfer(address _to, uint _value) public returns (bool success);
                        function transferFrom(address _from, address _to, uint _value) public returns (bool success);
                        function approve(address _spender, uint _value) public returns (bool success);
                        function allowance(address _owner, address _spender) public view returns (uint remaining);
                        function decimals() public view returns(uint digits);
                        event Approval(address indexed _owner, address indexed _spender, uint _value);
                    }
                    
                    // File: contracts/PermissionGroups.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    contract PermissionGroups {
                    
                        address public admin;
                        address public pendingAdmin;
                        mapping(address=>bool) internal operators;
                        mapping(address=>bool) internal alerters;
                        address[] internal operatorsGroup;
                        address[] internal alertersGroup;
                        uint constant internal MAX_GROUP_SIZE = 50;
                    
                        function PermissionGroups() public {
                            admin = msg.sender;
                        }
                    
                        modifier onlyAdmin() {
                            require(msg.sender == admin);
                            _;
                        }
                    
                        modifier onlyOperator() {
                            require(operators[msg.sender]);
                            _;
                        }
                    
                        modifier onlyAlerter() {
                            require(alerters[msg.sender]);
                            _;
                        }
                    
                        function getOperators () external view returns(address[]) {
                            return operatorsGroup;
                        }
                    
                        function getAlerters () external view returns(address[]) {
                            return alertersGroup;
                        }
                    
                        event TransferAdminPending(address pendingAdmin);
                    
                        /**
                         * @dev Allows the current admin to set the pendingAdmin address.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdmin(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(pendingAdmin);
                            pendingAdmin = newAdmin;
                        }
                    
                        /**
                         * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
                         * @param newAdmin The address to transfer ownership to.
                         */
                        function transferAdminQuickly(address newAdmin) public onlyAdmin {
                            require(newAdmin != address(0));
                            TransferAdminPending(newAdmin);
                            AdminClaimed(newAdmin, admin);
                            admin = newAdmin;
                        }
                    
                        event AdminClaimed( address newAdmin, address previousAdmin);
                    
                        /**
                         * @dev Allows the pendingAdmin address to finalize the change admin process.
                         */
                        function claimAdmin() public {
                            require(pendingAdmin == msg.sender);
                            AdminClaimed(pendingAdmin, admin);
                            admin = pendingAdmin;
                            pendingAdmin = address(0);
                        }
                    
                        event AlerterAdded (address newAlerter, bool isAdd);
                    
                        function addAlerter(address newAlerter) public onlyAdmin {
                            require(!alerters[newAlerter]); // prevent duplicates.
                            require(alertersGroup.length < MAX_GROUP_SIZE);
                    
                            AlerterAdded(newAlerter, true);
                            alerters[newAlerter] = true;
                            alertersGroup.push(newAlerter);
                        }
                    
                        function removeAlerter (address alerter) public onlyAdmin {
                            require(alerters[alerter]);
                            alerters[alerter] = false;
                    
                            for (uint i = 0; i < alertersGroup.length; ++i) {
                                if (alertersGroup[i] == alerter) {
                                    alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
                                    alertersGroup.length--;
                                    AlerterAdded(alerter, false);
                                    break;
                                }
                            }
                        }
                    
                        event OperatorAdded(address newOperator, bool isAdd);
                    
                        function addOperator(address newOperator) public onlyAdmin {
                            require(!operators[newOperator]); // prevent duplicates.
                            require(operatorsGroup.length < MAX_GROUP_SIZE);
                    
                            OperatorAdded(newOperator, true);
                            operators[newOperator] = true;
                            operatorsGroup.push(newOperator);
                        }
                    
                        function removeOperator (address operator) public onlyAdmin {
                            require(operators[operator]);
                            operators[operator] = false;
                    
                            for (uint i = 0; i < operatorsGroup.length; ++i) {
                                if (operatorsGroup[i] == operator) {
                                    operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
                                    operatorsGroup.length -= 1;
                                    OperatorAdded(operator, false);
                                    break;
                                }
                            }
                        }
                    }
                    
                    // File: contracts/Withdrawable.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    
                    /**
                     * @title Contracts that should be able to recover tokens or ethers
                     * @author Ilan Doron
                     * @dev This allows to recover any tokens or Ethers received in a contract.
                     * This will prevent any accidental loss of tokens.
                     */
                    contract Withdrawable is PermissionGroups {
                    
                        event TokenWithdraw(ERC20 token, uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw all ERC20 compatible tokens
                         * @param token ERC20 The address of the token contract
                         */
                        function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
                            require(token.transfer(sendTo, amount));
                            TokenWithdraw(token, amount, sendTo);
                        }
                    
                        event EtherWithdraw(uint amount, address sendTo);
                    
                        /**
                         * @dev Withdraw Ethers
                         */
                        function withdrawEther(uint amount, address sendTo) external onlyAdmin {
                            sendTo.transfer(amount);
                            EtherWithdraw(amount, sendTo);
                        }
                    }
                    
                    // File: contracts/KyberReserveInterface.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    /// @title Kyber Reserve contract
                    interface KyberReserveInterface {
                    
                        function trade(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            address destAddress,
                            uint conversionRate,
                            bool validate
                        )
                            public
                            payable
                            returns(bool);
                    
                        function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
                    }
                    
                    // File: contracts/Utils.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    /// @title Kyber constants contract
                    contract Utils {
                    
                        ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
                        uint  constant internal PRECISION = (10**18);
                        uint  constant internal MAX_QTY   = (10**28); // 10B tokens
                        uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH
                        uint  constant internal MAX_DECIMALS = 18;
                        uint  constant internal ETH_DECIMALS = 18;
                        mapping(address=>uint) internal decimals;
                    
                        function setDecimals(ERC20 token) internal {
                            if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
                            else decimals[token] = token.decimals();
                        }
                    
                        function getDecimals(ERC20 token) internal view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
                            uint tokenDecimals = decimals[token];
                            // technically, there might be token with decimals 0
                            // moreover, very possible that old tokens have decimals 0
                            // these tokens will just have higher gas fees.
                            if(tokenDecimals == 0) return token.decimals();
                    
                            return tokenDecimals;
                        }
                    
                        function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(srcQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                    
                            if (dstDecimals >= srcDecimals) {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
                            } else {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
                            }
                        }
                    
                        function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
                            require(dstQty <= MAX_QTY);
                            require(rate <= MAX_RATE);
                            
                            //source quantity is rounded up. to avoid dest quantity being too low.
                            uint numerator;
                            uint denominator;
                            if (srcDecimals >= dstDecimals) {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
                                denominator = rate;
                            } else {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                numerator = (PRECISION * dstQty);
                                denominator = (rate * (10**(dstDecimals - srcDecimals)));
                            }
                            return (numerator + denominator - 1) / denominator; //avoid rounding down errors
                        }
                    }
                    
                    // File: contracts/Utils2.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    contract Utils2 is Utils {
                    
                        /// @dev get the balance of a user.
                        /// @param token The token type
                        /// @return The balance
                        function getBalance(ERC20 token, address user) public view returns(uint) {
                            if (token == ETH_TOKEN_ADDRESS)
                                return user.balance;
                            else
                                return token.balanceOf(user);
                        }
                    
                        function getDecimalsSafe(ERC20 token) internal returns(uint) {
                    
                            if (decimals[token] == 0) {
                                setDecimals(token);
                            }
                    
                            return decimals[token];
                        }
                    
                        function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) {
                            return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
                        }
                    
                        function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) {
                            return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
                        }
                    
                        function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals)
                            internal pure returns(uint)
                        {
                            require(srcAmount <= MAX_QTY);
                            require(destAmount <= MAX_QTY);
                    
                            if (dstDecimals >= srcDecimals) {
                                require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
                                return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount));
                            } else {
                                require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
                                return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount);
                            }
                        }
                    }
                    
                    // File: contracts/reserves/bridgeReserves/uniswap/KyberUniswapReserve.sol
                    
                    pragma solidity 0.4.18;
                    
                    
                    
                    
                    
                    interface UniswapExchange {
                        function ethToTokenSwapInput(
                            uint256 min_tokens,
                            uint256 deadline
                        )
                            external
                            payable
                            returns (uint256  tokens_bought);
                    
                        function tokenToEthSwapInput(
                            uint256 tokens_sold,
                            uint256 min_eth,
                            uint256 deadline
                        )
                            external
                            returns (uint256  eth_bought);
                    
                        function getEthToTokenInputPrice(
                            uint256 eth_sold
                        )
                            external
                            view
                            returns (uint256 tokens_bought);
                    
                        function getTokenToEthInputPrice(
                            uint256 tokens_sold
                        )
                            external
                            view
                            returns (uint256 eth_bought);
                    }
                    
                    
                    interface UniswapFactory {
                        function getExchange(address token) external view returns (address exchange);
                    }
                    
                    
                    /*
                     * A reserve that connects to Uniswap.
                     *
                     * This reserve makes use of an internal inventory for locally filling orders
                     * using the reserve's inventory when certain conditions are met.
                     * Conditions are:
                     * - After trading the inventory will remain within defined limits.
                     * - Uniswap prices do not display internal arbitrage.
                     * - Uniswap ask and bid prices meet minimum spread requirements.
                     *
                     * An additional premium may be added to the converted price for optional
                     * promotions.
                     */
                    contract KyberUniswapReserve is KyberReserveInterface, Withdrawable, Utils2 {
                        // Parts per 10000
                        uint public constant DEFAULT_FEE_BPS = 25;
                    
                        UniswapFactory public uniswapFactory;
                        address public kyberNetwork;
                    
                        uint public feeBps = DEFAULT_FEE_BPS;
                    
                        // Uniswap exchange contract for every listed token
                        // token -> exchange
                        mapping (address => address) public tokenExchange;
                    
                        // Internal inventory balance limits
                        // token -> limit
                        mapping (address => uint) public internalInventoryMin;
                        mapping (address => uint) public internalInventoryMax;
                    
                        // Minimum spread in BPS required for using internal inventory
                        // token -> limit
                        mapping (address => uint) public internalActivationMinSpreadBps;
                    
                        // Premium BPS added to internal price (making it better).
                        // token -> limit
                        mapping (address => uint) public internalPricePremiumBps;
                    
                        bool public tradeEnabled = true;
                    
                        /**
                            Constructor
                        */
                        function KyberUniswapReserve(
                            UniswapFactory _uniswapFactory,
                            address _admin,
                            address _kyberNetwork
                        )
                            public
                        {
                            require(address(_uniswapFactory) != 0);
                            require(_admin != 0);
                            require(_kyberNetwork != 0);
                    
                            uniswapFactory = _uniswapFactory;
                            admin = _admin;
                            kyberNetwork = _kyberNetwork;
                        }
                    
                        function() public payable {
                            // anyone can deposit ether
                        }
                    
                        /**
                            Returns dest quantity / source quantity.
                            Last bit of the rate indicates whether to use internal inventory:
                              0 - use uniswap
                              1 - use internal inventory
                        */
                        function getConversionRate(
                            ERC20 src,
                            ERC20 dest,
                            uint srcQty,
                            uint blockNumber
                        )
                            public
                            view
                            returns(uint)
                        {
                            // This makes the UNUSED warning go away.
                            blockNumber;
                            if (!isValidTokens(src, dest)) return 0;
                            if (!tradeEnabled) return 0;
                            if (srcQty == 0) return 0;
                    
                            ERC20 token;
                            if (src == ETH_TOKEN_ADDRESS) {
                                token = dest;
                            } else if (dest == ETH_TOKEN_ADDRESS) {
                                token = src;
                            } else {
                                // Should never arrive here - isValidTokens requires one side to be ETH
                                revert();
                            }
                    
                            uint convertedQuantity;
                            uint rateSrcDest;
                            uint rateDestSrc;
                            (convertedQuantity, rateSrcDest) = calcUniswapConversion(src, dest, srcQty);
                    
                            if (convertedQuantity == 0) return 0;
                    
                            (, rateDestSrc) = calcUniswapConversion(dest, src, convertedQuantity);
                    
                            uint quantityWithPremium = addPremium(token, convertedQuantity);
                    
                            bool useInternalInventory = shouldUseInternalInventory(
                                src, /* srcToken */
                                srcQty, /* srcAmount */
                                dest, /* destToken */
                                quantityWithPremium, /* destAmount */
                                rateSrcDest, /* rateSrcDest */
                                rateDestSrc /* rateDestSrc */
                            );
                    
                            uint rate;
                            if (useInternalInventory) {
                                // If using internal inventory add premium to converted quantity
                                rate = calcRateFromQty(
                                    srcQty, /* srcAmount */
                                    quantityWithPremium, /* destAmount */
                                    getDecimals(src), /* srcDecimals */
                                    getDecimals(dest) /* dstDecimals */
                                );
                            } else {
                                // Use rate calculated from uniswap quantities after fees
                                rate = rateSrcDest;
                            }
                            return applyInternalInventoryHintToRate(rate, useInternalInventory);
                        }
                    
                        function applyInternalInventoryHintToRate(
                            uint rate,
                            bool useInternalInventory
                        )
                            internal
                            pure
                            returns(uint)
                        {
                            return rate % 2 == (useInternalInventory ? 1 : 0)
                                ? rate
                                : rate - 1;
                        }
                    
                    
                        event TradeExecute(
                            address indexed sender,
                            address src,
                            uint srcAmount,
                            address destToken,
                            uint destAmount,
                            address destAddress,
                            bool useInternalInventory
                        );
                    
                        /**
                          conversionRate: expected conversion rate should be >= this value.
                         */
                        function trade(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            address destAddress,
                            uint conversionRate,
                            bool validate
                        )
                            public
                            payable
                            returns(bool)
                        {
                            require(tradeEnabled);
                            require(msg.sender == kyberNetwork);
                            require(isValidTokens(srcToken, destToken));
                    
                            if (validate) {
                                require(conversionRate > 0);
                                if (srcToken == ETH_TOKEN_ADDRESS)
                                    require(msg.value == srcAmount);
                                else
                                    require(msg.value == 0);
                            }
                    
                            // Making sure srcAmount has been transfered to the reserve.
                            // If srcToken is ETH the value has already been transfered by calling
                            // the function.
                            if (srcToken != ETH_TOKEN_ADDRESS)
                                require(srcToken.transferFrom(msg.sender, address(this), srcAmount));
                    
                            uint expectedDestAmount = calcDestAmount(
                                srcToken, /* src */
                                destToken, /* dest */
                                srcAmount, /* srcAmount */
                                conversionRate /* rate */
                            );
                    
                            bool useInternalInventory = conversionRate % 2 == 1;
                    
                            uint destAmount;
                            UniswapExchange exchange;
                            if (srcToken == ETH_TOKEN_ADDRESS) {
                                if (!useInternalInventory) {
                                    // Deduct fees (in ETH) before converting
                                    uint quantity = deductFee(srcAmount);
                                    exchange = UniswapExchange(tokenExchange[address(destToken)]);
                                    destAmount = exchange.ethToTokenSwapInput.value(quantity)(
                                        1, /* min_tokens: uniswap requires it to be > 0 */
                                        2 ** 255 /* deadline */
                                    );
                                    require(destAmount >= expectedDestAmount);
                                }
                    
                                // Transfer user-expected dest amount
                                require(destToken.transfer(destAddress, expectedDestAmount));
                            } else {
                                if (!useInternalInventory) {
                                    exchange = UniswapExchange(tokenExchange[address(srcToken)]);
                                    destAmount = exchange.tokenToEthSwapInput(
                                        srcAmount,
                                        1, /* min_eth: uniswap requires it to be > 0 */
                                        2 ** 255 /* deadline */
                                    );
                                    // Deduct fees (in ETH) after converting
                                    destAmount = deductFee(destAmount);
                                    require(destAmount >= expectedDestAmount);
                                }
                    
                                // Transfer user-expected dest amount
                                destAddress.transfer(expectedDestAmount);
                            }
                    
                            TradeExecute(
                                msg.sender, /* sender */
                                srcToken, /* src */
                                srcAmount, /* srcAmount */
                                destToken, /* destToken */
                                expectedDestAmount, /* destAmount */
                                destAddress, /* destAddress */
                                useInternalInventory /* useInternalInventory */
                            );
                            return true;
                        }
                    
                        event FeeUpdated(
                            uint bps
                        );
                    
                        function setFee(
                            uint bps
                        )
                            public
                            onlyAdmin
                        {
                            require(bps <= 10000);
                    
                            feeBps = bps;
                    
                            FeeUpdated(bps);
                        }
                    
                        event InternalActivationConfigUpdated(
                            ERC20 token,
                            uint minSpreadBps,
                            uint premiumBps
                        );
                    
                        function setInternalActivationConfig(
                            ERC20 token,
                            uint minSpreadBps,
                            uint premiumBps
                        )
                            public
                            onlyAdmin
                        {
                            require(tokenExchange[address(token)] != address(0));
                            require(minSpreadBps <= 1000); // min spread <= 10%
                            require(premiumBps <= 500); // premium <= 5%
                    
                            internalActivationMinSpreadBps[address(token)] = minSpreadBps;
                            internalPricePremiumBps[address(token)] = premiumBps;
                    
                            InternalActivationConfigUpdated(token, minSpreadBps, premiumBps);
                        }
                    
                        event InternalInventoryLimitsUpdated(
                            ERC20 token,
                            uint minBalance,
                            uint maxBalance
                        );
                    
                        function setInternalInventoryLimits(
                            ERC20 token,
                            uint minBalance,
                            uint maxBalance
                        )
                            public
                            onlyOperator
                        {
                            require(tokenExchange[address(token)] != address(0));
                    
                            internalInventoryMin[address(token)] = minBalance;
                            internalInventoryMax[address(token)] = maxBalance;
                    
                            InternalInventoryLimitsUpdated(token, minBalance, maxBalance);
                        }
                    
                        event TokenListed(
                            ERC20 token,
                            UniswapExchange exchange
                        );
                    
                        function listToken(ERC20 token)
                            public
                            onlyAdmin
                        {
                            require(address(token) != 0);
                    
                            UniswapExchange uniswapExchange = UniswapExchange(
                                uniswapFactory.getExchange(token)
                            );
                            tokenExchange[address(token)] = address(uniswapExchange);
                            setDecimals(token);
                    
                            require(token.approve(uniswapExchange, 2 ** 255));
                    
                            // internal inventory disabled by default
                            internalInventoryMin[address(token)] = 2 ** 255;
                            internalInventoryMax[address(token)] = 0;
                            internalActivationMinSpreadBps[address(token)] = 0;
                            internalPricePremiumBps[address(token)] = 0;
                    
                            TokenListed(token, uniswapExchange);
                        }
                    
                        event TokenDelisted(ERC20 token);
                    
                        function delistToken(ERC20 token)
                            public
                            onlyAdmin
                        {
                            require(tokenExchange[address(token)] != address(0));
                    
                            delete tokenExchange[address(token)];
                            delete internalInventoryMin[address(token)];
                            delete internalInventoryMax[address(token)];
                            delete internalActivationMinSpreadBps[address(token)];
                            delete internalPricePremiumBps[address(token)];
                    
                            TokenDelisted(token);
                        }
                    
                        function isValidTokens(
                            ERC20 src,
                            ERC20 dest
                        )
                            public
                            view
                            returns(bool)
                        {
                            return (
                                (
                                    src == ETH_TOKEN_ADDRESS &&
                                    tokenExchange[address(dest)] != address(0)
                                ) ||
                                (
                                    tokenExchange[address(src)] != address(0) &&
                                    dest == ETH_TOKEN_ADDRESS
                                )
                            );
                        }
                    
                        event TradeEnabled(
                            bool enable
                        );
                    
                        function enableTrade()
                            public
                            onlyAdmin
                            returns(bool)
                        {
                            tradeEnabled = true;
                            TradeEnabled(true);
                            return true;
                        }
                    
                        function disableTrade()
                            public
                            onlyAlerter
                            returns(bool)
                        {
                            tradeEnabled = false;
                            TradeEnabled(false);
                            return true;
                        }
                    
                        event KyberNetworkSet(
                            address kyberNetwork
                        );
                    
                        function setKyberNetwork(
                            address _kyberNetwork
                        )
                            public
                            onlyAdmin
                        {
                            require(_kyberNetwork != 0);
                            kyberNetwork = _kyberNetwork;
                            KyberNetworkSet(kyberNetwork);
                        }
                    
                        /*
                         * Uses amounts and rates to check if the reserve's internal inventory can
                         * be used directly.
                         *
                         * rateEthToToken and rateTokenToEth are in kyber rate format meaning
                         * rate as numerator and 1e18 as denominator.
                         */
                        function shouldUseInternalInventory(
                            ERC20 srcToken,
                            uint srcAmount,
                            ERC20 destToken,
                            uint destAmount,
                            uint rateSrcDest,
                            uint rateDestSrc
                        )
                            public
                            view
                            returns(bool)
                        {
                            require(srcAmount < MAX_QTY);
                            require(destAmount < MAX_QTY);
                    
                            // Check for internal inventory balance limitations
                            ERC20 token;
                            if (srcToken == ETH_TOKEN_ADDRESS) {
                                token = destToken;
                                uint tokenBalance = token.balanceOf(this);
                                if (
                                    tokenBalance < destAmount ||
                                    tokenBalance - destAmount < internalInventoryMin[token]
                                ) {
                                    return false;
                                }
                            } else {
                                token = srcToken;
                                if (this.balance < destAmount) return false;
                                if (token.balanceOf(this) + srcAmount > internalInventoryMax[token]) {
                                    return false;
                                }
                            }
                    
                            uint normalizedDestSrc = 10 ** 36 / rateDestSrc;
                    
                            // Check for arbitrage
                            if (rateSrcDest > normalizedDestSrc) return false;
                    
                            uint activationSpread = internalActivationMinSpreadBps[token];
                            uint spread = uint(calculateSpreadBps(normalizedDestSrc, rateSrcDest));
                            return spread >= activationSpread;
                        }
                    
                        /*
                         * Spread calculation is (ask - bid) / ((ask + bid) / 2).
                         * We multiply by 10000 to get result in BPS.
                         *
                         * Note: if askRate > bidRate result will be negative indicating
                         * internal arbitrage.
                         */
                        function calculateSpreadBps(
                            uint _askRate,
                            uint _bidRate
                        )
                            public
                            pure
                            returns(int)
                        {
                            int askRate = int(_askRate);
                            int bidRate = int(_bidRate);
                            return 10000 * 2 * (askRate - bidRate) / (askRate + bidRate);
                        }
                    
                        function deductFee(
                            uint amount
                        )
                            public
                            view
                            returns(uint)
                        {
                            return amount * (10000 - feeBps) / 10000;
                        }
                    
                        function addPremium(
                            ERC20 token,
                            uint amount
                        )
                            public
                            view
                            returns(uint)
                        {
                            require(amount <= MAX_QTY);
                            return amount * (10000 + internalPricePremiumBps[token]) / 10000;
                        }
                    
                        function calcUniswapConversion(
                            ERC20 src,
                            ERC20 dest,
                            uint srcQty
                        )
                            internal
                            view
                            returns(uint destQty, uint rate)
                        {
                            UniswapExchange exchange;
                            if (src == ETH_TOKEN_ADDRESS) {
                                exchange = UniswapExchange(tokenExchange[address(dest)]);
                    
                                uint amountLessFee = deductFee(srcQty);
                                if (amountLessFee == 0) return (0, 0);
                    
                                destQty = exchange.getEthToTokenInputPrice(
                                    amountLessFee
                                );
                            } else {
                                exchange = UniswapExchange(tokenExchange[address(src)]);
                                destQty = deductFee(
                                    exchange.getTokenToEthInputPrice(srcQty)
                                );
                            }
                    
                            rate = calcRateFromQty(
                                srcQty, /* srcAmount */
                                destQty, /* destAmount */
                                getDecimals(src), /* srcDecimals */
                                getDecimals(dest) /* dstDecimals */
                            );
                        }
                    }

                    File 8 of 9: Vyper_contract
                    # @title Uniswap Exchange Interface V1
                    # @notice Source code found at https://github.com/uniswap
                    # @notice Use at your own risk
                    
                    contract Factory():
                        def getExchange(token_addr: address) -> address: constant
                    
                    contract Exchange():
                        def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
                        def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
                        def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
                    
                    TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
                    EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
                    AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
                    RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
                    Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
                    Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
                    
                    name: public(bytes32)                             # Uniswap V1
                    symbol: public(bytes32)                           # UNI-V1
                    decimals: public(uint256)                         # 18
                    totalSupply: public(uint256)                      # total number of UNI in existence
                    balances: uint256[address]                        # UNI balance of an address
                    allowances: (uint256[address])[address]           # UNI allowance of one address on another
                    token: address(ERC20)                             # address of the ERC20 token traded on this contract
                    factory: Factory                                  # interface for the factory that created this contract
                    
                    # @dev This function acts as a contract constructor which is not currently supported in contracts deployed
                    #      using create_with_code_of(). It is called once by the factory during contract creation.
                    @public
                    def setup(token_addr: address):
                        assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
                        self.factory = msg.sender
                        self.token = token_addr
                        self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
                        self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
                        self.decimals = 18
                    
                    # @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
                    # @dev min_liquidity does nothing when total UNI supply is 0.
                    # @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
                    # @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return The amount of UNI minted.
                    @public
                    @payable
                    def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
                        assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
                        total_liquidity: uint256 = self.totalSupply
                        if total_liquidity > 0:
                            assert min_liquidity > 0
                            eth_reserve: uint256(wei) = self.balance - msg.value
                            token_reserve: uint256 = self.token.balanceOf(self)
                            token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
                            liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
                            assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
                            self.balances[msg.sender] += liquidity_minted
                            self.totalSupply = total_liquidity + liquidity_minted
                            assert self.token.transferFrom(msg.sender, self, token_amount)
                            log.AddLiquidity(msg.sender, msg.value, token_amount)
                            log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
                            return liquidity_minted
                        else:
                            assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
                            assert self.factory.getExchange(self.token) == self
                            token_amount: uint256 = max_tokens
                            initial_liquidity: uint256 = as_unitless_number(self.balance)
                            self.totalSupply = initial_liquidity
                            self.balances[msg.sender] = initial_liquidity
                            assert self.token.transferFrom(msg.sender, self, token_amount)
                            log.AddLiquidity(msg.sender, msg.value, token_amount)
                            log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
                            return initial_liquidity
                    
                    # @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
                    # @param amount Amount of UNI burned.
                    # @param min_eth Minimum ETH withdrawn.
                    # @param min_tokens Minimum Tokens withdrawn.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return The amount of ETH and Tokens withdrawn.
                    @public
                    def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
                        assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
                        total_liquidity: uint256 = self.totalSupply
                        assert total_liquidity > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_amount: uint256(wei) = amount * self.balance / total_liquidity
                        token_amount: uint256 = amount * token_reserve / total_liquidity
                        assert eth_amount >= min_eth and token_amount >= min_tokens
                        self.balances[msg.sender] -= amount
                        self.totalSupply = total_liquidity - amount
                        send(msg.sender, eth_amount)
                        assert self.token.transfer(msg.sender, token_amount)
                        log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
                        log.Transfer(msg.sender, ZERO_ADDRESS, amount)
                        return eth_amount, token_amount
                    
                    # @dev Pricing function for converting between ETH and Tokens.
                    # @param input_amount Amount of ETH or Tokens being sold.
                    # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
                    # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
                    # @return Amount of ETH or Tokens bought.
                    @private
                    @constant
                    def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                        assert input_reserve > 0 and output_reserve > 0
                        input_amount_with_fee: uint256 = input_amount * 997
                        numerator: uint256 = input_amount_with_fee * output_reserve
                        denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
                        return numerator / denominator
                    
                    # @dev Pricing function for converting between ETH and Tokens.
                    # @param output_amount Amount of ETH or Tokens being bought.
                    # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
                    # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
                    # @return Amount of ETH or Tokens sold.
                    @private
                    @constant
                    def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                        assert input_reserve > 0 and output_reserve > 0
                        numerator: uint256 = input_reserve * output_amount * 1000
                        denominator: uint256 = (output_reserve - output_amount) * 997
                        return numerator / denominator + 1
                    
                    @private
                    def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                        assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
                        assert tokens_bought >= min_tokens
                        assert self.token.transfer(recipient, tokens_bought)
                        log.TokenPurchase(buyer, eth_sold, tokens_bought)
                        return tokens_bought
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies exact input (msg.value).
                    # @dev User cannot specify minimum output or deadline.
                    @public
                    @payable
                    def __default__():
                        self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies exact input (msg.value) and minimum output.
                    # @param min_tokens Minimum Tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of Tokens bought.
                    @public
                    @payable
                    def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
                        return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens and transfers Tokens to recipient.
                    # @dev User specifies exact input (msg.value) and minimum output
                    # @param min_tokens Minimum Tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output Tokens.
                    # @return Amount of Tokens bought.
                    @public
                    @payable
                    def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
                    
                    @private
                    def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                        assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
                        # Throws if eth_sold > max_eth
                        eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
                        if eth_refund > 0:
                            send(buyer, eth_refund)
                        assert self.token.transfer(recipient, tokens_bought)
                        log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
                        return as_wei_value(eth_sold, 'wei')
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies maximum input (msg.value) and exact output.
                    # @param tokens_bought Amount of tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of ETH sold.
                    @public
                    @payable
                    def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
                        return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens and transfers Tokens to recipient.
                    # @dev User specifies maximum input (msg.value) and exact output.
                    # @param tokens_bought Amount of tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output Tokens.
                    # @return Amount of ETH sold.
                    @public
                    @payable
                    def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                        assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                        assert wei_bought >= min_eth
                        send(recipient, wei_bought)
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        log.EthPurchase(buyer, tokens_sold, wei_bought)
                        return wei_bought
                    
                    
                    # @notice Convert Tokens to ETH.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_eth Minimum ETH purchased.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of ETH bought.
                    @public
                    def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
                        return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert Tokens to ETH and transfers ETH to recipient.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_eth Minimum ETH purchased.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @return Amount of ETH bought.
                    @public
                    def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                        assert deadline >= block.timestamp and eth_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                        # tokens sold is always > 0
                        assert max_tokens >= tokens_sold
                        send(recipient, eth_bought)
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        log.EthPurchase(buyer, tokens_sold, eth_bought)
                        return tokens_sold
                    
                    # @notice Convert Tokens to ETH.
                    # @dev User specifies maximum input and exact output.
                    # @param eth_bought Amount of ETH purchased.
                    # @param max_tokens Maximum Tokens sold.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of Tokens sold.
                    @public
                    def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
                        return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert Tokens to ETH and transfers ETH to recipient.
                    # @dev User specifies maximum input and exact output.
                    # @param eth_bought Amount of ETH purchased.
                    # @param max_tokens Maximum Tokens sold.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @return Amount of Tokens sold.
                    @public
                    def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                        assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
                        assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                        assert wei_bought >= min_eth_bought
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
                        log.EthPurchase(buyer, tokens_sold, wei_bought)
                        return tokens_bought
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr).
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (token_addr) bought.
                    @public
                    def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
                    #         Tokens (token_addr) to recipient.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (token_addr) bought.
                    @public
                    def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
                    
                    @private
                    def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                        assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
                        assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                        eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                        # tokens sold is always > 0
                        assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
                        log.EthPurchase(buyer, tokens_sold, eth_bought)
                        return tokens_sold
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr).
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
                    #         Tokens (token_addr) to recipient.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (exchange_addr.token) bought.
                    @public
                    def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
                    #         Tokens (exchange_addr.token) to recipient.
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (exchange_addr.token) bought.
                    @public
                    def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                        assert recipient != self
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
                    #         Tokens (exchange_addr.token) to recipient.
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                        assert recipient != self
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Public price function for ETH to Token trades with an exact input.
                    # @param eth_sold Amount of ETH sold.
                    # @return Amount of Tokens that can be bought with input ETH.
                    @public
                    @constant
                    def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
                        assert eth_sold > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
                    
                    # @notice Public price function for ETH to Token trades with an exact output.
                    # @param tokens_bought Amount of Tokens bought.
                    # @return Amount of ETH needed to buy output Tokens.
                    @public
                    @constant
                    def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
                        assert tokens_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
                        return as_wei_value(eth_sold, 'wei')
                    
                    # @notice Public price function for Token to ETH trades with an exact input.
                    # @param tokens_sold Amount of Tokens sold.
                    # @return Amount of ETH that can be bought with input Tokens.
                    @public
                    @constant
                    def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
                        assert tokens_sold > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        return as_wei_value(eth_bought, 'wei')
                    
                    # @notice Public price function for Token to ETH trades with an exact output.
                    # @param eth_bought Amount of output ETH.
                    # @return Amount of Tokens needed to buy output ETH.
                    @public
                    @constant
                    def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
                        assert eth_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                    
                    # @return Address of Token that is sold on this exchange.
                    @public
                    @constant
                    def tokenAddress() -> address:
                        return self.token
                    
                    # @return Address of factory that created this exchange.
                    @public
                    @constant
                    def factoryAddress() -> address(Factory):
                        return self.factory
                    
                    # ERC20 compatibility for exchange liquidity modified from
                    # https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
                    @public
                    @constant
                    def balanceOf(_owner : address) -> uint256:
                        return self.balances[_owner]
                    
                    @public
                    def transfer(_to : address, _value : uint256) -> bool:
                        self.balances[msg.sender] -= _value
                        self.balances[_to] += _value
                        log.Transfer(msg.sender, _to, _value)
                        return True
                    
                    @public
                    def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                        self.balances[_from] -= _value
                        self.balances[_to] += _value
                        self.allowances[_from][msg.sender] -= _value
                        log.Transfer(_from, _to, _value)
                        return True
                    
                    @public
                    def approve(_spender : address, _value : uint256) -> bool:
                        self.allowances[msg.sender][_spender] = _value
                        log.Approval(msg.sender, _spender, _value)
                        return True
                    
                    @public
                    @constant
                    def allowance(_owner : address, _spender : address) -> uint256:
                        return self.allowances[_owner][_spender]

                    File 9 of 9: Vyper_contract
                    # @title Uniswap Exchange Interface V1
                    # @notice Source code found at https://github.com/uniswap
                    # @notice Use at your own risk
                    
                    contract Factory():
                        def getExchange(token_addr: address) -> address: constant
                    
                    contract Exchange():
                        def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei): constant
                        def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256: modifying
                        def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei): modifying
                    
                    TokenPurchase: event({buyer: indexed(address), eth_sold: indexed(uint256(wei)), tokens_bought: indexed(uint256)})
                    EthPurchase: event({buyer: indexed(address), tokens_sold: indexed(uint256), eth_bought: indexed(uint256(wei))})
                    AddLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
                    RemoveLiquidity: event({provider: indexed(address), eth_amount: indexed(uint256(wei)), token_amount: indexed(uint256)})
                    Transfer: event({_from: indexed(address), _to: indexed(address), _value: uint256})
                    Approval: event({_owner: indexed(address), _spender: indexed(address), _value: uint256})
                    
                    name: public(bytes32)                             # Uniswap V1
                    symbol: public(bytes32)                           # UNI-V1
                    decimals: public(uint256)                         # 18
                    totalSupply: public(uint256)                      # total number of UNI in existence
                    balances: uint256[address]                        # UNI balance of an address
                    allowances: (uint256[address])[address]           # UNI allowance of one address on another
                    token: address(ERC20)                             # address of the ERC20 token traded on this contract
                    factory: Factory                                  # interface for the factory that created this contract
                    
                    # @dev This function acts as a contract constructor which is not currently supported in contracts deployed
                    #      using create_with_code_of(). It is called once by the factory during contract creation.
                    @public
                    def setup(token_addr: address):
                        assert (self.factory == ZERO_ADDRESS and self.token == ZERO_ADDRESS) and token_addr != ZERO_ADDRESS
                        self.factory = msg.sender
                        self.token = token_addr
                        self.name = 0x556e697377617020563100000000000000000000000000000000000000000000
                        self.symbol = 0x554e492d56310000000000000000000000000000000000000000000000000000
                        self.decimals = 18
                    
                    # @notice Deposit ETH and Tokens (self.token) at current ratio to mint UNI tokens.
                    # @dev min_liquidity does nothing when total UNI supply is 0.
                    # @param min_liquidity Minimum number of UNI sender will mint if total UNI supply is greater than 0.
                    # @param max_tokens Maximum number of tokens deposited. Deposits max amount if total UNI supply is 0.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return The amount of UNI minted.
                    @public
                    @payable
                    def addLiquidity(min_liquidity: uint256, max_tokens: uint256, deadline: timestamp) -> uint256:
                        assert deadline > block.timestamp and (max_tokens > 0 and msg.value > 0)
                        total_liquidity: uint256 = self.totalSupply
                        if total_liquidity > 0:
                            assert min_liquidity > 0
                            eth_reserve: uint256(wei) = self.balance - msg.value
                            token_reserve: uint256 = self.token.balanceOf(self)
                            token_amount: uint256 = msg.value * token_reserve / eth_reserve + 1
                            liquidity_minted: uint256 = msg.value * total_liquidity / eth_reserve
                            assert max_tokens >= token_amount and liquidity_minted >= min_liquidity
                            self.balances[msg.sender] += liquidity_minted
                            self.totalSupply = total_liquidity + liquidity_minted
                            assert self.token.transferFrom(msg.sender, self, token_amount)
                            log.AddLiquidity(msg.sender, msg.value, token_amount)
                            log.Transfer(ZERO_ADDRESS, msg.sender, liquidity_minted)
                            return liquidity_minted
                        else:
                            assert (self.factory != ZERO_ADDRESS and self.token != ZERO_ADDRESS) and msg.value >= 1000000000
                            assert self.factory.getExchange(self.token) == self
                            token_amount: uint256 = max_tokens
                            initial_liquidity: uint256 = as_unitless_number(self.balance)
                            self.totalSupply = initial_liquidity
                            self.balances[msg.sender] = initial_liquidity
                            assert self.token.transferFrom(msg.sender, self, token_amount)
                            log.AddLiquidity(msg.sender, msg.value, token_amount)
                            log.Transfer(ZERO_ADDRESS, msg.sender, initial_liquidity)
                            return initial_liquidity
                    
                    # @dev Burn UNI tokens to withdraw ETH and Tokens at current ratio.
                    # @param amount Amount of UNI burned.
                    # @param min_eth Minimum ETH withdrawn.
                    # @param min_tokens Minimum Tokens withdrawn.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return The amount of ETH and Tokens withdrawn.
                    @public
                    def removeLiquidity(amount: uint256, min_eth: uint256(wei), min_tokens: uint256, deadline: timestamp) -> (uint256(wei), uint256):
                        assert (amount > 0 and deadline > block.timestamp) and (min_eth > 0 and min_tokens > 0)
                        total_liquidity: uint256 = self.totalSupply
                        assert total_liquidity > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_amount: uint256(wei) = amount * self.balance / total_liquidity
                        token_amount: uint256 = amount * token_reserve / total_liquidity
                        assert eth_amount >= min_eth and token_amount >= min_tokens
                        self.balances[msg.sender] -= amount
                        self.totalSupply = total_liquidity - amount
                        send(msg.sender, eth_amount)
                        assert self.token.transfer(msg.sender, token_amount)
                        log.RemoveLiquidity(msg.sender, eth_amount, token_amount)
                        log.Transfer(msg.sender, ZERO_ADDRESS, amount)
                        return eth_amount, token_amount
                    
                    # @dev Pricing function for converting between ETH and Tokens.
                    # @param input_amount Amount of ETH or Tokens being sold.
                    # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
                    # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
                    # @return Amount of ETH or Tokens bought.
                    @private
                    @constant
                    def getInputPrice(input_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                        assert input_reserve > 0 and output_reserve > 0
                        input_amount_with_fee: uint256 = input_amount * 997
                        numerator: uint256 = input_amount_with_fee * output_reserve
                        denominator: uint256 = (input_reserve * 1000) + input_amount_with_fee
                        return numerator / denominator
                    
                    # @dev Pricing function for converting between ETH and Tokens.
                    # @param output_amount Amount of ETH or Tokens being bought.
                    # @param input_reserve Amount of ETH or Tokens (input type) in exchange reserves.
                    # @param output_reserve Amount of ETH or Tokens (output type) in exchange reserves.
                    # @return Amount of ETH or Tokens sold.
                    @private
                    @constant
                    def getOutputPrice(output_amount: uint256, input_reserve: uint256, output_reserve: uint256) -> uint256:
                        assert input_reserve > 0 and output_reserve > 0
                        numerator: uint256 = input_reserve * output_amount * 1000
                        denominator: uint256 = (output_reserve - output_amount) * 997
                        return numerator / denominator + 1
                    
                    @private
                    def ethToTokenInput(eth_sold: uint256(wei), min_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                        assert deadline >= block.timestamp and (eth_sold > 0 and min_tokens > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_bought: uint256 = self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance - eth_sold), token_reserve)
                        assert tokens_bought >= min_tokens
                        assert self.token.transfer(recipient, tokens_bought)
                        log.TokenPurchase(buyer, eth_sold, tokens_bought)
                        return tokens_bought
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies exact input (msg.value).
                    # @dev User cannot specify minimum output or deadline.
                    @public
                    @payable
                    def __default__():
                        self.ethToTokenInput(msg.value, 1, block.timestamp, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies exact input (msg.value) and minimum output.
                    # @param min_tokens Minimum Tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of Tokens bought.
                    @public
                    @payable
                    def ethToTokenSwapInput(min_tokens: uint256, deadline: timestamp) -> uint256:
                        return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens and transfers Tokens to recipient.
                    # @dev User specifies exact input (msg.value) and minimum output
                    # @param min_tokens Minimum Tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output Tokens.
                    # @return Amount of Tokens bought.
                    @public
                    @payable
                    def ethToTokenTransferInput(min_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.ethToTokenInput(msg.value, min_tokens, deadline, msg.sender, recipient)
                    
                    @private
                    def ethToTokenOutput(tokens_bought: uint256, max_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                        assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance - max_eth), token_reserve)
                        # Throws if eth_sold > max_eth
                        eth_refund: uint256(wei) = max_eth - as_wei_value(eth_sold, 'wei')
                        if eth_refund > 0:
                            send(buyer, eth_refund)
                        assert self.token.transfer(recipient, tokens_bought)
                        log.TokenPurchase(buyer, as_wei_value(eth_sold, 'wei'), tokens_bought)
                        return as_wei_value(eth_sold, 'wei')
                    
                    # @notice Convert ETH to Tokens.
                    # @dev User specifies maximum input (msg.value) and exact output.
                    # @param tokens_bought Amount of tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of ETH sold.
                    @public
                    @payable
                    def ethToTokenSwapOutput(tokens_bought: uint256, deadline: timestamp) -> uint256(wei):
                        return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert ETH to Tokens and transfers Tokens to recipient.
                    # @dev User specifies maximum input (msg.value) and exact output.
                    # @param tokens_bought Amount of tokens bought.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output Tokens.
                    # @return Amount of ETH sold.
                    @public
                    @payable
                    def ethToTokenTransferOutput(tokens_bought: uint256, deadline: timestamp, recipient: address) -> uint256(wei):
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.ethToTokenOutput(tokens_bought, msg.value, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToEthInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, buyer: address, recipient: address) -> uint256(wei):
                        assert deadline >= block.timestamp and (tokens_sold > 0 and min_eth > 0)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                        assert wei_bought >= min_eth
                        send(recipient, wei_bought)
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        log.EthPurchase(buyer, tokens_sold, wei_bought)
                        return wei_bought
                    
                    
                    # @notice Convert Tokens to ETH.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_eth Minimum ETH purchased.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of ETH bought.
                    @public
                    def tokenToEthSwapInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp) -> uint256(wei):
                        return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert Tokens to ETH and transfers ETH to recipient.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_eth Minimum ETH purchased.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @return Amount of ETH bought.
                    @public
                    def tokenToEthTransferInput(tokens_sold: uint256, min_eth: uint256(wei), deadline: timestamp, recipient: address) -> uint256(wei):
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.tokenToEthInput(tokens_sold, min_eth, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToEthOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, buyer: address, recipient: address) -> uint256:
                        assert deadline >= block.timestamp and eth_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                        # tokens sold is always > 0
                        assert max_tokens >= tokens_sold
                        send(recipient, eth_bought)
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        log.EthPurchase(buyer, tokens_sold, eth_bought)
                        return tokens_sold
                    
                    # @notice Convert Tokens to ETH.
                    # @dev User specifies maximum input and exact output.
                    # @param eth_bought Amount of ETH purchased.
                    # @param max_tokens Maximum Tokens sold.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @return Amount of Tokens sold.
                    @public
                    def tokenToEthSwapOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp) -> uint256:
                        return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, msg.sender)
                    
                    # @notice Convert Tokens to ETH and transfers ETH to recipient.
                    # @dev User specifies maximum input and exact output.
                    # @param eth_bought Amount of ETH purchased.
                    # @param max_tokens Maximum Tokens sold.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @return Amount of Tokens sold.
                    @public
                    def tokenToEthTransferOutput(eth_bought: uint256(wei), max_tokens: uint256, deadline: timestamp, recipient: address) -> uint256:
                        assert recipient != self and recipient != ZERO_ADDRESS
                        return self.tokenToEthOutput(eth_bought, max_tokens, deadline, msg.sender, recipient)
                    
                    @private
                    def tokenToTokenInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                        assert (deadline >= block.timestamp and tokens_sold > 0) and (min_tokens_bought > 0 and min_eth_bought > 0)
                        assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        wei_bought: uint256(wei) = as_wei_value(eth_bought, 'wei')
                        assert wei_bought >= min_eth_bought
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        tokens_bought: uint256 = Exchange(exchange_addr).ethToTokenTransferInput(min_tokens_bought, deadline, recipient, value=wei_bought)
                        log.EthPurchase(buyer, tokens_sold, wei_bought)
                        return tokens_bought
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr).
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (token_addr) bought.
                    @public
                    def tokenToTokenSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
                    #         Tokens (token_addr) to recipient.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (token_addr) bought.
                    @public
                    def tokenToTokenTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
                    
                    @private
                    def tokenToTokenOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, buyer: address, recipient: address, exchange_addr: address) -> uint256:
                        assert deadline >= block.timestamp and (tokens_bought > 0 and max_eth_sold > 0)
                        assert exchange_addr != self and exchange_addr != ZERO_ADDRESS
                        eth_bought: uint256(wei) = Exchange(exchange_addr).getEthToTokenOutputPrice(tokens_bought)
                        token_reserve: uint256 = self.token.balanceOf(self)
                        tokens_sold: uint256 = self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                        # tokens sold is always > 0
                        assert max_tokens_sold >= tokens_sold and max_eth_sold >= eth_bought
                        assert self.token.transferFrom(buyer, self, tokens_sold)
                        eth_sold: uint256(wei) = Exchange(exchange_addr).ethToTokenTransferOutput(tokens_bought, deadline, recipient, value=eth_bought)
                        log.EthPurchase(buyer, tokens_sold, eth_bought)
                        return tokens_sold
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr).
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToTokenSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (token_addr) and transfers
                    #         Tokens (token_addr) to recipient.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToTokenTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, token_addr: address) -> uint256:
                        exchange_addr: address = self.factory.getExchange(token_addr)
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (exchange_addr.token) bought.
                    @public
                    def tokenToExchangeSwapInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
                    #         Tokens (exchange_addr.token) to recipient.
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies exact input and minimum output.
                    # @param tokens_sold Amount of Tokens sold.
                    # @param min_tokens_bought Minimum Tokens (token_addr) purchased.
                    # @param min_eth_bought Minimum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (exchange_addr.token) bought.
                    @public
                    def tokenToExchangeTransferInput(tokens_sold: uint256, min_tokens_bought: uint256, min_eth_bought: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                        assert recipient != self
                        return self.tokenToTokenInput(tokens_sold, min_tokens_bought, min_eth_bought, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token).
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param exchange_addr The address of the exchange for the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToExchangeSwapOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, exchange_addr: address) -> uint256:
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, msg.sender, exchange_addr)
                    
                    # @notice Convert Tokens (self.token) to Tokens (exchange_addr.token) and transfers
                    #         Tokens (exchange_addr.token) to recipient.
                    # @dev Allows trades through contracts that were not deployed from the same factory.
                    # @dev User specifies maximum input and exact output.
                    # @param tokens_bought Amount of Tokens (token_addr) bought.
                    # @param max_tokens_sold Maximum Tokens (self.token) sold.
                    # @param max_eth_sold Maximum ETH purchased as intermediary.
                    # @param deadline Time after which this transaction can no longer be executed.
                    # @param recipient The address that receives output ETH.
                    # @param token_addr The address of the token being purchased.
                    # @return Amount of Tokens (self.token) sold.
                    @public
                    def tokenToExchangeTransferOutput(tokens_bought: uint256, max_tokens_sold: uint256, max_eth_sold: uint256(wei), deadline: timestamp, recipient: address, exchange_addr: address) -> uint256:
                        assert recipient != self
                        return self.tokenToTokenOutput(tokens_bought, max_tokens_sold, max_eth_sold, deadline, msg.sender, recipient, exchange_addr)
                    
                    # @notice Public price function for ETH to Token trades with an exact input.
                    # @param eth_sold Amount of ETH sold.
                    # @return Amount of Tokens that can be bought with input ETH.
                    @public
                    @constant
                    def getEthToTokenInputPrice(eth_sold: uint256(wei)) -> uint256:
                        assert eth_sold > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        return self.getInputPrice(as_unitless_number(eth_sold), as_unitless_number(self.balance), token_reserve)
                    
                    # @notice Public price function for ETH to Token trades with an exact output.
                    # @param tokens_bought Amount of Tokens bought.
                    # @return Amount of ETH needed to buy output Tokens.
                    @public
                    @constant
                    def getEthToTokenOutputPrice(tokens_bought: uint256) -> uint256(wei):
                        assert tokens_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_sold: uint256 = self.getOutputPrice(tokens_bought, as_unitless_number(self.balance), token_reserve)
                        return as_wei_value(eth_sold, 'wei')
                    
                    # @notice Public price function for Token to ETH trades with an exact input.
                    # @param tokens_sold Amount of Tokens sold.
                    # @return Amount of ETH that can be bought with input Tokens.
                    @public
                    @constant
                    def getTokenToEthInputPrice(tokens_sold: uint256) -> uint256(wei):
                        assert tokens_sold > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        eth_bought: uint256 = self.getInputPrice(tokens_sold, token_reserve, as_unitless_number(self.balance))
                        return as_wei_value(eth_bought, 'wei')
                    
                    # @notice Public price function for Token to ETH trades with an exact output.
                    # @param eth_bought Amount of output ETH.
                    # @return Amount of Tokens needed to buy output ETH.
                    @public
                    @constant
                    def getTokenToEthOutputPrice(eth_bought: uint256(wei)) -> uint256:
                        assert eth_bought > 0
                        token_reserve: uint256 = self.token.balanceOf(self)
                        return self.getOutputPrice(as_unitless_number(eth_bought), token_reserve, as_unitless_number(self.balance))
                    
                    # @return Address of Token that is sold on this exchange.
                    @public
                    @constant
                    def tokenAddress() -> address:
                        return self.token
                    
                    # @return Address of factory that created this exchange.
                    @public
                    @constant
                    def factoryAddress() -> address(Factory):
                        return self.factory
                    
                    # ERC20 compatibility for exchange liquidity modified from
                    # https://github.com/ethereum/vyper/blob/master/examples/tokens/ERC20.vy
                    @public
                    @constant
                    def balanceOf(_owner : address) -> uint256:
                        return self.balances[_owner]
                    
                    @public
                    def transfer(_to : address, _value : uint256) -> bool:
                        self.balances[msg.sender] -= _value
                        self.balances[_to] += _value
                        log.Transfer(msg.sender, _to, _value)
                        return True
                    
                    @public
                    def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                        self.balances[_from] -= _value
                        self.balances[_to] += _value
                        self.allowances[_from][msg.sender] -= _value
                        log.Transfer(_from, _to, _value)
                        return True
                    
                    @public
                    def approve(_spender : address, _value : uint256) -> bool:
                        self.allowances[msg.sender][_spender] = _value
                        log.Approval(msg.sender, _spender, _value)
                        return True
                    
                    @public
                    @constant
                    def allowance(_owner : address, _spender : address) -> uint256:
                        return self.allowances[_owner][_spender]