ETH Price: $1,958.16 (-2.08%)

Transaction Decoder

Block:
10258645 at Jun-13-2020 05:19:44 PM +UTC
Transaction Fee:
0.00817966 ETH $16.02
Gas Used:
408,983 Gas / 20 Gwei

Emitted Events:

11 EthLendToken.Transfer( _from=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, _to=KyberNetwork, _value=3604148702765674280811 )
12 EthLendToken.Transfer( _from=KyberNetwork, _to=KyberReserve, _value=3604148702765674280811 )
13 KyberNetwork.EtherReceival( sender=KyberReserve, amount=1412894614423318504 )
14 KyberReserve.TradeExecute( origin=KyberNetwork, src=EthLendToken, srcAmount=3604148702765674280811, destToken=0xEeeeeEee...eeeeeEEeE, destAmount=1412894614423318504, destAddress=KyberNetwork )
15 RepublicToken.Transfer( from=KyberReserve, to=KyberNetwork, value=3177187964488720040436 )
16 KyberReserve.TradeExecute( origin=KyberNetwork, src=0xEeeeeEee...eeeeeEEeE, srcAmount=1412894614423318504, destToken=RepublicToken, destAmount=3177187964488720040436, destAddress=KyberNetwork )
17 RepublicToken.Transfer( from=KyberNetwork, to=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, value=3177187964488720040436 )
18 FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0x440bBd6a...16874faa9, walletFee=202189408791872350 )
19 FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=471775287181035486 )
20 FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0x440bBd6a...16874faa9, walletFee=202189408791872350 )
21 FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=471775287181035486 )
22 KyberNetwork.KyberTrade( trader=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, src=EthLendToken, dest=RepublicToken, srcAmount=3604148702765674280811, dstAmount=3177187964488720040436, destAddress=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, ethWeiValue=1412894614423318504, reserve1=KyberReserve, reserve2=KyberReserve, hint=0x5045524D )
23 KyberNetworkProxy.ExecuteTrade( trader=[Sender] 0xd1a83a93f553d87e2927f18497ec6d5ac0465e65, src=EthLendToken, dest=RepublicToken, actualSrcAmount=3604148702765674280811, actualDestAmount=3177187964488720040436 )

Account State Difference:

  Address   Before After State Difference Code
0x2f4B3134...55a906963
0x408e4187...656052a38
0x45eb33D0...aE596EA57
(Kyber: Reserve REN)
210.626324740573305813 Eth212.039219354996624317 Eth1.412894614423318504
(Spark Pool)
58.630260701603742049 Eth58.638440361603742049 Eth0.00817966
0x63825c17...bD36A0D8F
(Kyber: Reserve)
3,640.114768167976945754 Eth3,638.70187355355362725 Eth1.412894614423318504
0x65bF64Ff...77ced86Cd
(Kyber: Contract)
0x798AbDA6...d3d11191B
(Kyber: Conversion Rates)
0x8007aa43...E5fF626d1
(Kyber: Fee Burner)
0x80fB784B...9931aab03
0xd1A83A93...aC0465e65
45.706453376556338371 Eth
Nonce: 447
45.698273716556338371 Eth
Nonce: 448
0.00817966

Execution Trace

KyberNetworkProxy.tradeWithHint( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, dest=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=855090225103408578, walletId=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9, hint=0x5045524D ) => ( 3177187964488720040436 )
  • EthLendToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( 3604148702765674280811 )
  • RepublicToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( balance=0 )
  • EthLendToken.transferFrom( _from=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, _to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _value=3604148702765674280811 ) => ( True )
  • KyberNetwork.tradeWithHint( trader=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, dest=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=855090225103408578, walletId=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9, hint=0x5045524D ) => ( 3177187964488720040436 )
    • EthLendToken.balanceOf( _owner=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd ) => ( 3604148702765674863704 )
    • KyberReserve.getConversionRate( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=3604148702765674280811, blockNumber=10258645 ) => ( 392018956748128 )
      • ConversionRates.getRate( token=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, currentBlockNumber=10258645, buy=False, qty=3604148702765674280811 ) => ( 392018956748128 )
      • SanityRates.getSanityRate( src=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ) => ( 439826750000000 )
      • KyberReserve.getConversionRate( src=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, dest=0x408e41876cCCDC0F92210600ef50372656052a38, srcQty=1412894614423318504, blockNumber=10258645 ) => ( 2248708383523358927314 )
        • LiquidityConversionRates.getRate( conversionToken=0x408e41876cCCDC0F92210600ef50372656052a38, currentBlockNumber=10258645, buy=True, qtyInSrcWei=1412894614423318504 ) => ( 2248708383523358927314 )
        • RepublicToken.balanceOf( _owner=0x45eb33D008801d547990cAF3b63B4F8aE596EA57 ) => ( balance=516997733618317278783239 )
        • RepublicToken.allowance( _owner=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, _spender=0x45eb33D008801d547990cAF3b63B4F8aE596EA57 ) => ( 57896044618658097711785492504343953926634992332820221954033810829822162136671 )
        • KyberReserve.trade( srcToken=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcAmount=3604148702765674280811, destToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=392018956748128, validate=True ) => ( True )
          • ConversionRates.recordImbalance( token=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, buyAmount=-3604148702765674280811, rateUpdateBlock=0, currentBlock=10258645 )
          • EthLendToken.transferFrom( _from=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _to=0x63825c174ab367968EC60f061753D3bbD36A0D8F, _value=3604148702765674280811 ) => ( True )
          • ETH 1.412894614423318504 KyberNetwork.CALL( )
          • ETH 1.412894614423318504 KyberReserve.trade( srcToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcAmount=1412894614423318504, destToken=0x408e41876cCCDC0F92210600ef50372656052a38, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=2248708383523358927314, validate=True ) => ( True )
            • LiquidityConversionRates.recordImbalance( conversionToken=0x408e41876cCCDC0F92210600ef50372656052a38, buyAmountInTwei=3177187964488720040436, rateUpdateBlock=0, currentBlock=10258645 )
            • RepublicToken.transferFrom( _from=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, _to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _value=3177187964488720040436 ) => ( True )
            • RepublicToken.transfer( _to=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65, _value=3177187964488720040436 ) => ( True )
            • FeeBurner.handleFees( tradeWeiAmount=1412894614423318504, reserve=0x63825c174ab367968EC60f061753D3bbD36A0D8F, wallet=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9 ) => ( True )
            • FeeBurner.handleFees( tradeWeiAmount=1412894614423318504, reserve=0x45eb33D008801d547990cAF3b63B4F8aE596EA57, wallet=0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9 ) => ( True )
            • EthLendToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( 0 )
            • RepublicToken.balanceOf( _owner=0xd1A83A93F553d87E2927f18497eC6D5aC0465e65 ) => ( balance=3177187964488720040436 )
              File 1 of 10: 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 10: 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 10: EthLendToken
              pragma solidity ^0.4.16;
              
              contract SafeMath {
                   function safeMul(uint a, uint b) internal returns (uint) {
                        uint c = a * b;
                        assert(a == 0 || c / a == b);
                        return c;
                   }
              
                   function safeSub(uint a, uint b) internal returns (uint) {
                        assert(b <= a);
                        return a - b;
                   }
              
                   function safeAdd(uint a, uint b) internal returns (uint) {
                        uint c = a + b;
                        assert(c>=a && c>=b);
                        return c;
                   }
              }
              
              // Standard token interface (ERC 20)
              // https://github.com/ethereum/EIPs/issues/20
              contract Token is SafeMath {
                   // Functions:
                   /// @return total amount of tokens
                   function totalSupply() constant returns (uint256 supply);
              
                   /// @param _owner The address from which the balance will be retrieved
                   /// @return The balance
                   function balanceOf(address _owner) constant returns (uint256 balance);
              
                   /// @notice send `_value` token to `_to` from `msg.sender`
                   /// @param _to The address of the recipient
                   /// @param _value The amount of token to be transferred
                   function transfer(address _to, uint256 _value) returns(bool);
              
                   /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
                   /// @param _from The address of the sender
                   /// @param _to The address of the recipient
                   /// @param _value The amount of token to be transferred
                   /// @return Whether the transfer was successful or not
                   function transferFrom(address _from, address _to, uint256 _value) returns(bool);
              
                   /// @notice `msg.sender` approves `_addr` to spend `_value` tokens
                   /// @param _spender The address of the account able to transfer the tokens
                   /// @param _value The amount of wei to be approved for transfer
                   /// @return Whether the approval was successful or not
                   function approve(address _spender, uint256 _value) returns (bool success);
              
                   /// @param _owner The address of the account owning tokens
                   /// @param _spender The address of the account able to transfer the tokens
                   /// @return Amount of remaining tokens allowed to spent
                   function allowance(address _owner, address _spender) constant returns (uint256 remaining);
              
                   // Events:
                   event Transfer(address indexed _from, address indexed _to, uint256 _value);
                   event Approval(address indexed _owner, address indexed _spender, uint256 _value);
              }
              
              contract StdToken is Token {
                   // Fields:
                   mapping(address => uint256) balances;
                   mapping (address => mapping (address => uint256)) allowed;
                   uint public supply = 0;
              
                   // Functions:
                   function transfer(address _to, uint256 _value) returns(bool) {
                        require(balances[msg.sender] >= _value);
                        require(balances[_to] + _value > balances[_to]);
              
                        balances[msg.sender] = safeSub(balances[msg.sender],_value);
                        balances[_to] = safeAdd(balances[_to],_value);
              
                        Transfer(msg.sender, _to, _value);
                        return true;
                   }
              
                   function transferFrom(address _from, address _to, uint256 _value) returns(bool){
                        require(balances[_from] >= _value);
                        require(allowed[_from][msg.sender] >= _value);
                        require(balances[_to] + _value > balances[_to]);
              
                        balances[_to] = safeAdd(balances[_to],_value);
                        balances[_from] = safeSub(balances[_from],_value);
                        allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender],_value);
              
                        Transfer(_from, _to, _value);
                        return true;
                   }
              
                   function totalSupply() constant returns (uint256) {
                        return supply;
                   }
              
                   function balanceOf(address _owner) constant returns (uint256) {
                        return balances[_owner];
                   }
              
                   function approve(address _spender, uint256 _value) returns (bool) {
                        // To change the approve amount you first have to reduce the addresses`
                        //  allowance to zero by calling `approve(_spender, 0)` if it is not
                        //  already 0 to mitigate the race condition described here:
                        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                        require((_value == 0) || (allowed[msg.sender][_spender] == 0));
              
                        allowed[msg.sender][_spender] = _value;
                        Approval(msg.sender, _spender, _value);
              
                        return true;
                   }
              
                   function allowance(address _owner, address _spender) constant returns (uint256) {
                        return allowed[_owner][_spender];
                   }
              }
              
              contract EthLendToken is StdToken
              {
              /// Fields:
                  string public constant name = "EthLend Token";
                  string public constant symbol = "LEND";
                  uint public constant decimals = 18;
              
                  // this includes DEVELOPERS_BONUS
                  uint public constant TOTAL_SUPPLY = 1300000000 * (1 ether / 1 wei);
                  uint public constant DEVELOPERS_BONUS = 300000000 * (1 ether / 1 wei);
              
                  uint public constant PRESALE_PRICE = 30000;  // per 1 Ether
                  uint public constant PRESALE_MAX_ETH = 2000;
                  // 60 mln tokens sold during presale
                  uint public constant PRESALE_TOKEN_SUPPLY_LIMIT = PRESALE_PRICE * PRESALE_MAX_ETH * (1 ether / 1 wei);
              
                  uint public constant ICO_PRICE1 = 27500;     // per 1 Ether
                  uint public constant ICO_PRICE2 = 26250;     // per 1 Ether
                  uint public constant ICO_PRICE3 = 25000;     // per 1 Ether
              
                  // 1bln - this includes presale tokens
                  uint public constant TOTAL_SOLD_TOKEN_SUPPLY_LIMIT = 1000000000* (1 ether / 1 wei);
              
                  enum State{
                     Init,
                     Paused,
              
                     PresaleRunning,
                     PresaleFinished,
              
                     ICORunning,
                     ICOFinished
                  }
              
                  State public currentState = State.Init;
                  bool public enableTransfers = false;
              
                  address public teamTokenBonus = 0;
              
                  // Gathered funds can be withdrawn only to escrow's address.
                  address public escrow = 0;
              
                  // Token manager has exclusive priveleges to call administrative
                  // functions on this contract.
                  address public tokenManager = 0;
              
                  uint public presaleSoldTokens = 0;
                  uint public icoSoldTokens = 0;
                  uint public totalSoldTokens = 0;
              
              /// Modifiers:
                  modifier onlyTokenManager()
                  {
                      require(msg.sender==tokenManager); 
                      _; 
                  }
              
                  modifier onlyInState(State state)
                  {
                      require(state==currentState); 
                      _; 
                  }
              
              /// Events:
                  event LogBuy(address indexed owner, uint value);
                  event LogBurn(address indexed owner, uint value);
              
              /// Functions:
                  /// @dev Constructor
                  /// @param _tokenManager Token manager address.
                  function EthLendToken(address _tokenManager, address _escrow, address _teamTokenBonus) 
                  {
                      tokenManager = _tokenManager;
                      teamTokenBonus = _teamTokenBonus;
                      escrow = _escrow;
              
                      // send team bonus immediately
                      uint teamBonus = DEVELOPERS_BONUS;
                      balances[_teamTokenBonus] += teamBonus;
                      supply+= teamBonus;
              
                      assert(PRESALE_TOKEN_SUPPLY_LIMIT==60000000 * (1 ether / 1 wei));
                      assert(TOTAL_SOLD_TOKEN_SUPPLY_LIMIT==1000000000 * (1 ether / 1 wei));
                  }
              
                  function buyTokens() public payable
                  {
                      require(currentState==State.PresaleRunning || currentState==State.ICORunning);
              
                      if(currentState==State.PresaleRunning){
                          return buyTokensPresale();
                      }else{
                          return buyTokensICO();
                      }
                  }
              
                  function buyTokensPresale() public payable onlyInState(State.PresaleRunning)
                  {
                      // min - 1 ETH
                      require(msg.value >= (1 ether / 1 wei));
                      uint newTokens = msg.value * PRESALE_PRICE;
              
                      require(presaleSoldTokens + newTokens <= PRESALE_TOKEN_SUPPLY_LIMIT);
              
                      balances[msg.sender] += newTokens;
                      supply+= newTokens;
                      presaleSoldTokens+= newTokens;
                      totalSoldTokens+= newTokens;
              
                      LogBuy(msg.sender, newTokens);
                  }
              
                  function buyTokensICO() public payable onlyInState(State.ICORunning)
                  {
                      // min - 0.01 ETH
                      require(msg.value >= ((1 ether / 1 wei) / 100));
                      uint newTokens = msg.value * getPrice();
              
                      require(totalSoldTokens + newTokens <= TOTAL_SOLD_TOKEN_SUPPLY_LIMIT);
              
                      balances[msg.sender] += newTokens;
                      supply+= newTokens;
                      icoSoldTokens+= newTokens;
                      totalSoldTokens+= newTokens;
              
                      LogBuy(msg.sender, newTokens);
                  }
              
                  function getPrice()constant returns(uint)
                  {
                      if(currentState==State.ICORunning){
                           if(icoSoldTokens<(200000000 * (1 ether / 1 wei))){
                                return ICO_PRICE1;
                           }
                           
                           if(icoSoldTokens<(300000000 * (1 ether / 1 wei))){
                                return ICO_PRICE2;
                           }
              
                           return ICO_PRICE3;
                      }else{
                           return PRESALE_PRICE;
                      }
                  }
              
                  function setState(State _nextState) public onlyTokenManager
                  {
                      //setState() method call shouldn't be entertained after ICOFinished
                      require(currentState != State.ICOFinished);
                      
                      currentState = _nextState;
                      // enable/disable transfers
                      //enable transfers only after ICOFinished, disable otherwise
                      enableTransfers = (currentState==State.ICOFinished);
                  }
              
                  function withdrawEther() public onlyTokenManager
                  {
                      if(this.balance > 0) 
                      {
                          require(escrow.send(this.balance));
                      }
                  }
              
              /// Overrides:
                  function transfer(address _to, uint256 _value) returns(bool){
                      require(enableTransfers);
                      return super.transfer(_to,_value);
                  }
              
                  function transferFrom(address _from, address _to, uint256 _value) returns(bool){
                      require(enableTransfers);
                      return super.transferFrom(_from,_to,_value);
                  }
              
                  function approve(address _spender, uint256 _value) returns (bool) {
                      require(enableTransfers);
                      return super.approve(_spender,_value);
                  }
              
              /// Setters/getters
                  function setTokenManager(address _mgr) public onlyTokenManager
                  {
                      tokenManager = _mgr;
                  }
              
                  // Default fallback function
                  function() payable 
                  {
                      buyTokens();
                  }
              }

              File 4 of 10: KyberReserve
              pragma solidity 0.4.18;
              
              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
                  }
              }
              
              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;
                          }
                      }
                  }
              }
              
              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);
              }
              
              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);
              }
              
              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);
              }
              
              interface SanityRatesInterface {
                  function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
              }
              
              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);
                  }
              }
              
              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
              
                  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);
                  }
              
                  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.transfer(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
                          return token.balanceOf(this);
                  }
              
                  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  buy;
              
                      if (!tradeEnabled) return 0;
              
                      if (ETH_TOKEN_ADDRESS == src) {
                          buy = true;
                          token = dest;
                      } else if (ETH_TOKEN_ADDRESS == dest) {
                          buy = false;
                          token = src;
                      } else {
                          return 0; // pair is not listed
                      }
              
                      uint rate = conversionRatesContract.getRate(token, blockNumber, buy, 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 buy;
                      if (srcToken == ETH_TOKEN_ADDRESS) {
                          buy = int(destAmount);
                          token = destToken;
                      } else {
                          buy = -1 * int(srcAmount);
                          token = srcToken;
                      }
              
                      conversionRatesContract.recordImbalance(
                          token,
                          buy,
                          0,
                          block.number
                      );
              
                      // collect src tokens
                      if (srcToken != ETH_TOKEN_ADDRESS) {
                          require(srcToken.transferFrom(msg.sender, this, srcAmount));
                      }
              
                      // send dest tokens
                      if (destToken == ETH_TOKEN_ADDRESS) {
                          destAddress.transfer(destAmount);
                      } else {
                          require(destToken.transfer(destAddress, destAmount));
                      }
              
                      TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
              
                      return true;
                  }
              }

              File 5 of 10: KyberReserve
              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/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/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/ConversionRatesInterface.sol
              
              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
              
              interface SanityRatesInterface {
                  function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
              }
              
              // File: contracts/KyberReserveInterface.sol
              
              /// @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/KyberReserve.sol
              
              /// @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 6 of 10: RepublicToken
              pragma solidity ^0.4.13;
              
              library SafeMath {
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  if (a == 0) {
                    return 0;
                  }
                  uint256 c = a * b;
                  assert(c / a == b);
                  return c;
                }
              
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // assert(b > 0); // Solidity automatically throws when dividing by 0
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
                }
              
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  assert(b <= a);
                  return a - b;
                }
              
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  assert(c >= a);
                  return c;
                }
              }
              
              contract Ownable {
                address public owner;
              
              
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
              
                /**
                 * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                 * account.
                 */
                function Ownable() public {
                  owner = msg.sender;
                }
              
              
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                  require(msg.sender == owner);
                  _;
                }
              
              
                /**
                 * @dev Allows the current owner to transfer control of the contract to a newOwner.
                 * @param newOwner The address to transfer ownership to.
                 */
                function transferOwnership(address newOwner) public onlyOwner {
                  require(newOwner != address(0));
                  OwnershipTransferred(owner, newOwner);
                  owner = newOwner;
                }
              
              }
              
              contract Pausable is Ownable {
                event Pause();
                event Unpause();
              
                bool public paused = false;
              
              
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 */
                modifier whenNotPaused() {
                  require(!paused);
                  _;
                }
              
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 */
                modifier whenPaused() {
                  require(paused);
                  _;
                }
              
                /**
                 * @dev called by the owner to pause, triggers stopped state
                 */
                function pause() onlyOwner whenNotPaused public {
                  paused = true;
                  Pause();
                }
              
                /**
                 * @dev called by the owner to unpause, returns to normal state
                 */
                function unpause() onlyOwner whenPaused public {
                  paused = false;
                  Unpause();
                }
              }
              
              contract ERC20Basic {
                uint256 public totalSupply;
                function balanceOf(address who) public view returns (uint256);
                function transfer(address to, uint256 value) public returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
              }
              
              contract BasicToken is ERC20Basic {
                using SafeMath for uint256;
              
                mapping(address => uint256) balances;
              
                /**
                * @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) {
                  require(_to != address(0));
                  require(_value <= balances[msg.sender]);
              
                  // SafeMath.sub will throw if there is not enough balance.
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  Transfer(msg.sender, _to, _value);
                  return true;
                }
              
                /**
                * @dev Gets the balance of the specified address.
                * @param _owner The address to query the the balance of.
                * @return An uint256 representing the amount owned by the passed address.
                */
                function balanceOf(address _owner) public view returns (uint256 balance) {
                  return balances[_owner];
                }
              
              }
              
              contract ERC20 is ERC20Basic {
                function allowance(address owner, address spender) public view returns (uint256);
                function transferFrom(address from, address to, uint256 value) public returns (bool);
                function approve(address spender, uint256 value) public returns (bool);
                event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              contract StandardToken is ERC20, BasicToken {
              
                mapping (address => mapping (address => uint256)) internal allowed;
              
              
                /**
                 * @dev Transfer tokens from one address to another
                 * @param _from address The address which you want to send tokens from
                 * @param _to address The address which you want to transfer to
                 * @param _value uint256 the amount of tokens to be transferred
                 */
                function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
                  require(_to != address(0));
                  require(_value <= balances[_from]);
                  require(_value <= allowed[_from][msg.sender]);
              
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
                  Transfer(_from, _to, _value);
                  return true;
                }
              
                /**
                 * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                 *
                 * Beware that changing an allowance with this method brings the risk that someone may use both the old
                 * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
                 * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                 * @param _spender The address which will spend the funds.
                 * @param _value The amount of tokens to be spent.
                 */
                function approve(address _spender, uint256 _value) public returns (bool) {
                  allowed[msg.sender][_spender] = _value;
                  Approval(msg.sender, _spender, _value);
                  return true;
                }
              
                /**
                 * @dev Function to check the amount of tokens that an owner allowed to a spender.
                 * @param _owner address The address which owns the funds.
                 * @param _spender address The address which will spend the funds.
                 * @return A uint256 specifying the amount of tokens still available for the spender.
                 */
                function allowance(address _owner, address _spender) public view returns (uint256) {
                  return allowed[_owner][_spender];
                }
              
                /**
                 * approve should be called when allowed[_spender] == 0. To increment
                 * allowed value is better to use this function to avoid 2 calls (and wait until
                 * the first transaction is mined)
                 * From MonolithDAO Token.sol
                 */
                function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
                  allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
                function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
                  uint oldValue = allowed[msg.sender][_spender];
                  if (_subtractedValue > oldValue) {
                    allowed[msg.sender][_spender] = 0;
                  } else {
                    allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
                  }
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
              }
              
              contract BurnableToken is StandardToken {
              
                  event Burn(address indexed burner, uint256 value);
              
                  /**
                   * @dev Burns a specific amount of tokens.
                   * @param _value The amount of token to be burned.
                   */
                  function burn(uint256 _value) public {
                      require(_value > 0);
                      require(_value <= balances[msg.sender]);
                      // no need to require value <= totalSupply, since that would imply the
                      // sender's balance is greater than the totalSupply, which *should* be an assertion failure
              
                      address burner = msg.sender;
                      balances[burner] = balances[burner].sub(_value);
                      totalSupply = totalSupply.sub(_value);
                      Burn(burner, _value);
                  }
              }
              
              contract PausableToken is StandardToken, Pausable {
              
                function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
                  return super.transfer(_to, _value);
                }
              
                function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {
                  return super.transferFrom(_from, _to, _value);
                }
              
                function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
                  return super.approve(_spender, _value);
                }
              
                function increaseApproval(address _spender, uint _addedValue) public whenNotPaused returns (bool success) {
                  return super.increaseApproval(_spender, _addedValue);
                }
              
                function decreaseApproval(address _spender, uint _subtractedValue) public whenNotPaused returns (bool success) {
                  return super.decreaseApproval(_spender, _subtractedValue);
                }
              }
              
              contract RepublicToken is PausableToken, BurnableToken {
              
                  string public constant name = "Republic Token";
                  string public constant symbol = "REN";
                  uint8 public constant decimals = 18;
                  uint256 public constant INITIAL_SUPPLY = 1000000000 * 10**uint256(decimals);
                  
                  /**
                  * @dev RepublicToken Constructor
                  */
              
                  function RepublicToken() public {
                      totalSupply = INITIAL_SUPPLY;   
                      balances[msg.sender] = INITIAL_SUPPLY;
                  }
              
                  function transferTokens(address beneficiary, uint256 amount) public onlyOwner returns (bool) {
                      require(amount > 0);
              
                      balances[owner] = balances[owner].sub(amount);
                      balances[beneficiary] = balances[beneficiary].add(amount);
                      Transfer(owner, beneficiary, amount);
              
                      return true;
                  }
              }

              File 7 of 10: 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 8 of 10: ConversionRates
              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);
              }
              
              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);
              }
              
              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;
                          }
                      }
                  }
              }
              
              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);
                  }
              }
              
              contract VolumeImbalanceRecorder is Withdrawable {
              
                  uint constant internal SLIDING_WINDOW_SIZE = 5;
                  uint constant internal POW_2_64 = 2 ** 64;
              
                  struct TokenControlInfo {
                      uint minimalRecordResolution; // can be roughly 1 cent
                      uint maxPerBlockImbalance; // in twei resolution
                      uint maxTotalImbalance; // max total imbalance (between rate updates)
                                          // before halting trade
                  }
              
                  mapping(address => TokenControlInfo) internal tokenControlInfo;
              
                  struct TokenImbalanceData {
                      int  lastBlockBuyUnitsImbalance;
                      uint lastBlock;
              
                      int  totalBuyUnitsImbalance;
                      uint lastRateUpdateBlock;
                  }
              
                  mapping(address => mapping(uint=>uint)) public tokenImbalanceData;
              
                  function VolumeImbalanceRecorder(address _admin) public {
                      require(_admin != address(0));
                      admin = _admin;
                  }
              
                  function setTokenControlInfo(
                      ERC20 token,
                      uint minimalRecordResolution,
                      uint maxPerBlockImbalance,
                      uint maxTotalImbalance
                  )
                      public
                      onlyAdmin
                  {
                      tokenControlInfo[token] =
                          TokenControlInfo(
                              minimalRecordResolution,
                              maxPerBlockImbalance,
                              maxTotalImbalance
                          );
                  }
              
                  function getTokenControlInfo(ERC20 token) public view returns(uint, uint, uint) {
                      return (tokenControlInfo[token].minimalRecordResolution,
                              tokenControlInfo[token].maxPerBlockImbalance,
                              tokenControlInfo[token].maxTotalImbalance);
                  }
              
                  function addImbalance(
                      ERC20 token,
                      int buyAmount,
                      uint rateUpdateBlock,
                      uint currentBlock
                  )
                      internal
                  {
                      uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE;
                      int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution));
              
                      int prevImbalance = 0;
              
                      TokenImbalanceData memory currentBlockData =
                          decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]);
              
                      // first scenario - this is not the first tx in the current block
                      if (currentBlockData.lastBlock == currentBlock) {
                          if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) {
                              // just increase imbalance
                              currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;
                              currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount;
                          } else {
                              // imbalance was changed in the middle of the block
                              prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock);
                              currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;
                              currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;
                              currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);
                          }
                      } else {
                          // first tx in the current block
                          int currentBlockImbalance;
                          (prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock);
              
                          currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount;
                          currentBlockData.lastBlock = uint(currentBlock);
                          currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);
                          currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;
                      }
              
                      tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData);
                  }
              
                  function setGarbageToVolumeRecorder(ERC20 token) internal {
                      for (uint i = 0; i < SLIDING_WINDOW_SIZE; i++) {
                          tokenImbalanceData[token][i] = 0x1;
                      }
                  }
              
                  function getImbalanceInRange(ERC20 token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) {
                      // check the imbalance in the sliding window
                      require(startBlock <= endBlock);
              
                      buyImbalance = 0;
              
                      for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) {
                          TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);
              
                          if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) {
                              buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance);
                          }
                      }
                  }
              
                  function getImbalanceSinceRateUpdate(ERC20 token, uint rateUpdateBlock, uint currentBlock)
                      internal view
                      returns(int buyImbalance, int currentBlockImbalance)
                  {
                      buyImbalance = 0;
                      currentBlockImbalance = 0;
                      uint latestBlock = 0;
                      int imbalanceInRange = 0;
                      uint startBlock = rateUpdateBlock;
                      uint endBlock = currentBlock;
              
                      for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) {
                          TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);
              
                          if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) {
                              imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance;
                          }
              
                          if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue;
                          if (perBlockData.lastBlock < latestBlock) continue;
              
                          latestBlock = perBlockData.lastBlock;
                          buyImbalance = perBlockData.totalBuyUnitsImbalance;
                          if (uint(perBlockData.lastBlock) == currentBlock) {
                              currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance;
                          }
                      }
              
                      if (buyImbalance == 0) {
                          buyImbalance = imbalanceInRange;
                      }
                  }
              
                  function getImbalance(ERC20 token, uint rateUpdateBlock, uint currentBlock)
                      internal view
                      returns(int totalImbalance, int currentBlockImbalance)
                  {
              
                      int resolution = int(tokenControlInfo[token].minimalRecordResolution);
              
                      (totalImbalance, currentBlockImbalance) =
                          getImbalanceSinceRateUpdate(
                              token,
                              rateUpdateBlock,
                              currentBlock);
              
                      totalImbalance *= resolution;
                      currentBlockImbalance *= resolution;
                  }
              
                  function getMaxPerBlockImbalance(ERC20 token) internal view returns(uint) {
                      return tokenControlInfo[token].maxPerBlockImbalance;
                  }
              
                  function getMaxTotalImbalance(ERC20 token) internal view returns(uint) {
                      return tokenControlInfo[token].maxTotalImbalance;
                  }
              
                  function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) {
                      // check for overflows
                      require(data.lastBlockBuyUnitsImbalance < int(POW_2_64 / 2));
                      require(data.lastBlockBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2));
                      require(data.lastBlock < POW_2_64);
                      require(data.totalBuyUnitsImbalance < int(POW_2_64 / 2));
                      require(data.totalBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2));
                      require(data.lastRateUpdateBlock < POW_2_64);
              
                      // do encoding
                      uint result = uint(data.lastBlockBuyUnitsImbalance) & (POW_2_64 - 1);
                      result |= data.lastBlock * POW_2_64;
                      result |= (uint(data.totalBuyUnitsImbalance) & (POW_2_64 - 1)) * POW_2_64 * POW_2_64;
                      result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64;
              
                      return result;
                  }
              
                  function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) {
                      TokenImbalanceData memory data;
              
                      data.lastBlockBuyUnitsImbalance = int(int64(input & (POW_2_64 - 1)));
                      data.lastBlock = uint(uint64((input / POW_2_64) & (POW_2_64 - 1)));
                      data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) & (POW_2_64 - 1)));
                      data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64))));
              
                      return data;
                  }
              }
              
              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
                  }
              }
              
              contract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils {
              
                  // bps - basic rate steps. one step is 1 / 10000 of the rate.
                  struct StepFunction {
                      int[] x; // quantity for each step. Quantity of each step includes previous steps.
                      int[] y; // rate change per quantity step  in bps.
                  }
              
                  struct TokenData {
                      bool listed;  // was added to reserve
                      bool enabled; // whether trade is enabled
              
                      // position in the compact data
                      uint compactDataArrayIndex;
                      uint compactDataFieldIndex;
              
                      // rate data. base and changes according to quantity and reserve balance.
                      // generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction.
                      uint baseBuyRate;  // in PRECISION units. see KyberConstants
                      uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate
                      StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate.
                      StepFunction sellRateQtyStepFunction;// in bps. higher the qua
                      StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate.
                      StepFunction sellRateImbalanceStepFunction;
                  }
              
                  /*
                  this is the data for tokenRatesCompactData
                  but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write
                  so we represent it as bytes32 and do the byte tricks ourselves.
                  struct TokenRatesCompactData {
                      bytes14 buy;  // change buy rate of token from baseBuyRate in 10 bps
                      bytes14 sell; // change sell rate of token from baseSellRate in 10 bps
              
                      uint32 blockNumber;
                  } */
                  uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks
                  ERC20[] internal listedTokens;
                  mapping(address=>TokenData) internal tokenData;
                  bytes32[] internal tokenRatesCompactData;
                  uint public numTokensInCurrentCompactData = 0;
                  address public reserveContract;
                  uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14;
                  uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA));
                  uint constant internal MAX_STEPS_IN_FUNCTION = 10;
                  int  constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B %
                  int  constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100%
              
                  function ConversionRates(address _admin) public VolumeImbalanceRecorder(_admin)
                      { } // solhint-disable-line no-empty-blocks
              
                  function addToken(ERC20 token) public onlyAdmin {
              
                      require(!tokenData[token].listed);
                      tokenData[token].listed = true;
                      listedTokens.push(token);
              
                      if (numTokensInCurrentCompactData == 0) {
                          tokenRatesCompactData.length++; // add new structure
                      }
              
                      tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1;
                      tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData;
              
                      numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA;
              
                      setGarbageToVolumeRecorder(token);
              
                      setDecimals(token);
                  }
              
                  function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator {
              
                      require(buy.length == sell.length);
                      require(indices.length == buy.length);
                      require(blockNumber <= 0xFFFFFFFF);
              
                      uint bytes14Offset = BYTES_14_OFFSET;
              
                      for (uint i = 0; i < indices.length; i++) {
                          require(indices[i] < tokenRatesCompactData.length);
                          uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset));
                          tokenRatesCompactData[indices[i]] = bytes32(data);
                      }
                  }
              
                  function setBaseRate(
                      ERC20[] tokens,
                      uint[] baseBuy,
                      uint[] baseSell,
                      bytes14[] buy,
                      bytes14[] sell,
                      uint blockNumber,
                      uint[] indices
                  )
                      public
                      onlyOperator
                  {
                      require(tokens.length == baseBuy.length);
                      require(tokens.length == baseSell.length);
                      require(sell.length == buy.length);
                      require(sell.length == indices.length);
              
                      for (uint ind = 0; ind < tokens.length; ind++) {
                          require(tokenData[tokens[ind]].listed);
                          tokenData[tokens[ind]].baseBuyRate = baseBuy[ind];
                          tokenData[tokens[ind]].baseSellRate = baseSell[ind];
                      }
              
                      setCompactData(buy, sell, blockNumber, indices);
                  }
              
                  function setQtyStepFunction(
                      ERC20 token,
                      int[] xBuy,
                      int[] yBuy,
                      int[] xSell,
                      int[] ySell
                  )
                      public
                      onlyOperator
                  {
                      require(xBuy.length == yBuy.length);
                      require(xSell.length == ySell.length);
                      require(xBuy.length <= MAX_STEPS_IN_FUNCTION);
                      require(xSell.length <= MAX_STEPS_IN_FUNCTION);
                      require(tokenData[token].listed);
              
                      tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy);
                      tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell);
                  }
              
                  function setImbalanceStepFunction(
                      ERC20 token,
                      int[] xBuy,
                      int[] yBuy,
                      int[] xSell,
                      int[] ySell
                  )
                      public
                      onlyOperator
                  {
                      require(xBuy.length == yBuy.length);
                      require(xSell.length == ySell.length);
                      require(xBuy.length <= MAX_STEPS_IN_FUNCTION);
                      require(xSell.length <= MAX_STEPS_IN_FUNCTION);
                      require(tokenData[token].listed);
              
                      tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy);
                      tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell);
                  }
              
                  function setValidRateDurationInBlocks(uint duration) public onlyAdmin {
                      validRateDurationInBlocks = duration;
                  }
              
                  function enableTokenTrade(ERC20 token) public onlyAdmin {
                      require(tokenData[token].listed);
                      require(tokenControlInfo[token].minimalRecordResolution != 0);
                      tokenData[token].enabled = true;
                  }
              
                  function disableTokenTrade(ERC20 token) public onlyAlerter {
                      require(tokenData[token].listed);
                      tokenData[token].enabled = false;
                  }
              
                  function setReserveAddress(address reserve) public onlyAdmin {
                      reserveContract = reserve;
                  }
              
                  function recordImbalance(
                      ERC20 token,
                      int buyAmount,
                      uint rateUpdateBlock,
                      uint currentBlock
                  )
                      public
                  {
                      require(msg.sender == reserveContract);
              
                      if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token);
              
                      return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock);
                  }
              
                  /* solhint-disable function-max-lines */
                  function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) {
                      // check if trade is enabled
                      if (!tokenData[token].enabled) return 0;
                      if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set
              
                      // get rate update block
                      bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];
              
                      uint updateRateBlock = getLast4Bytes(compactData);
                      if (currentBlockNumber >= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired
                      // check imbalance
                      int totalImbalance;
                      int blockImbalance;
                      (totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber);
              
                      // calculate actual rate
                      int imbalanceQty;
                      int extraBps;
                      int8 rateUpdate;
                      uint rate;
              
                      if (buy) {
                          // start with base rate
                          rate = tokenData[token].baseBuyRate;
              
                          // add rate update
                          rateUpdate = getRateByteFromCompactData(compactData, token, true);
                          extraBps = int(rateUpdate) * 10;
                          rate = addBps(rate, extraBps);
              
                          // compute token qty
                          qty = getTokenQty(token, rate, qty);
                          imbalanceQty = int(qty);
                          totalImbalance += imbalanceQty;
              
                          // add qty overhead
                          extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty));
                          rate = addBps(rate, extraBps);
              
                          // add imbalance overhead
                          extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance);
                          rate = addBps(rate, extraBps);
                      } else {
                          // start with base rate
                          rate = tokenData[token].baseSellRate;
              
                          // add rate update
                          rateUpdate = getRateByteFromCompactData(compactData, token, false);
                          extraBps = int(rateUpdate) * 10;
                          rate = addBps(rate, extraBps);
              
                          // compute token qty
                          imbalanceQty = -1 * int(qty);
                          totalImbalance += imbalanceQty;
              
                          // add qty overhead
                          extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty));
                          rate = addBps(rate, extraBps);
              
                          // add imbalance overhead
                          extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance);
                          rate = addBps(rate, extraBps);
                      }
              
                      if (abs(totalImbalance) >= getMaxTotalImbalance(token)) return 0;
                      if (abs(blockImbalance + imbalanceQty) >= getMaxPerBlockImbalance(token)) return 0;
              
                      return rate;
                  }
                  /* solhint-enable function-max-lines */
              
                  function getBasicRate(ERC20 token, bool buy) public view returns(uint) {
                      if (buy)
                          return tokenData[token].baseBuyRate;
                      else
                          return tokenData[token].baseSellRate;
                  }
              
                  function getCompactData(ERC20 token) public view returns(uint, uint, byte, byte) {
                      require(tokenData[token].listed);
              
                      uint arrayIndex = tokenData[token].compactDataArrayIndex;
                      uint fieldOffset = tokenData[token].compactDataFieldIndex;
              
                      return (
                          arrayIndex,
                          fieldOffset,
                          byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)),
                          byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false))
                      );
                  }
              
                  function getTokenBasicData(ERC20 token) public view returns(bool, bool) {
                      return (tokenData[token].listed, tokenData[token].enabled);
                  }
              
                  /* solhint-disable code-complexity */
                  function getStepFunctionData(ERC20 token, uint command, uint param) public view returns(int) {
                      if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length);
                      if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param];
                      if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length);
                      if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param];
              
                      if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length);
                      if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param];
                      if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length);
                      if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param];
              
                      if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length);
                      if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param];
                      if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length);
                      if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param];
              
                      if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length);
                      if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param];
                      if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length);
                      if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param];
              
                      revert();
                  }
                  /* solhint-enable code-complexity */
              
                  function getRateUpdateBlock(ERC20 token) public view returns(uint) {
                      bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];
                      return getLast4Bytes(compactData);
                  }
              
                  function getListedTokens() public view returns(ERC20[]) {
                      return listedTokens;
                  }
              
                  function getTokenQty(ERC20 token, uint ethQty, uint rate) internal view returns(uint) {
                      uint dstDecimals = getDecimals(token);
                      uint srcDecimals = ETH_DECIMALS;
              
                      return calcDstQty(ethQty, srcDecimals, dstDecimals, rate);
                  }
              
                  function getLast4Bytes(bytes32 b) internal pure returns(uint) {
                      // cannot trust compiler with not turning bit operations into EXP opcode
                      return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET);
                  }
              
                  function getRateByteFromCompactData(bytes32 data, ERC20 token, bool buy) internal view returns(int8) {
                      uint fieldOffset = tokenData[token].compactDataFieldIndex;
                      uint byteOffset;
                      if (buy)
                          byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset;
                      else
                          byteOffset = 4 + fieldOffset;
              
                      return int8(data[byteOffset]);
                  }
              
                  function executeStepFunction(StepFunction f, int x) internal pure returns(int) {
                      uint len = f.y.length;
                      for (uint ind = 0; ind < len; ind++) {
                          if (x <= f.x[ind]) return f.y[ind];
                      }
              
                      return f.y[len-1];
                  }
              
                  function addBps(uint rate, int bps) internal pure returns(uint) {
                      require(rate <= MAX_RATE);
                      require(bps >= MIN_BPS_ADJUSTMENT);
                      require(bps <= MAX_BPS_ADJUSTMENT);
              
                      uint maxBps = 100 * 100;
                      return (rate * uint(int(maxBps) + bps)) / maxBps;
                  }
              
                  function abs(int x) internal pure returns(uint) {
                      if (x < 0)
                          return uint(-1 * x);
                      else
                          return uint(x);
                  }
              }

              File 9 of 10: SanityRates
              pragma solidity 0.4.18;
              
              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);
              }
              
              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;
                          }
                      }
                  }
              }
              
              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
                  }
              }
              
              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);
                  }
              }
              
              interface SanityRatesInterface {
                  function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
              }
              
              contract SanityRates is SanityRatesInterface, Withdrawable, Utils {
                  mapping(address=>uint) public tokenRate;
                  mapping(address=>uint) public reasonableDiffInBps;
              
                  function SanityRates(address _admin) public {
                      require(_admin != address(0));
                      admin = _admin;
                  }
              
                  function setReasonableDiff(ERC20[] srcs, uint[] diff) public onlyAdmin {
                      require(srcs.length == diff.length);
                      for (uint i = 0; i < srcs.length; i++) {
                          require(diff[i] <= 100 * 100);
                          reasonableDiffInBps[srcs[i]] = diff[i];
                      }
                  }
              
                  function setSanityRates(ERC20[] srcs, uint[] rates) public onlyOperator {
                      require(srcs.length == rates.length);
              
                      for (uint i = 0; i < srcs.length; i++) {
                          require(rates[i] <= MAX_RATE);
                          tokenRate[srcs[i]] = rates[i];
                      }
                  }
              
                  function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint) {
                      if (src != ETH_TOKEN_ADDRESS && dest != ETH_TOKEN_ADDRESS) return 0;
              
                      uint rate;
                      address token;
                      if (src == ETH_TOKEN_ADDRESS) {
                          rate = (PRECISION*PRECISION)/tokenRate[dest];
                          token = dest;
                      } else {
                          rate = tokenRate[src];
                          token = src;
                      }
              
                      return rate * (10000 + reasonableDiffInBps[token])/10000;
                  }
              }

              File 10 of 10: LiquidityConversionRates
              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/ConversionRatesInterface.sol
              
              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/LiquidityFormula.sol
              
              contract UtilMath {
                  uint public constant BIG_NUMBER = (uint(1)<<uint(200));
              
                  function checkMultOverflow(uint x, uint y) public pure returns(bool) {
                      if (y == 0) return false;
                      return (((x*y) / y) != x);
                  }
              
                  function compactFraction(uint p, uint q, uint precision) public pure returns (uint, uint) {
                      if (q < precision * precision) return (p, q);
                      return compactFraction(p/precision, q/precision, precision);
                  }
              
                  /* solhint-disable code-complexity */
                  function exp(uint p, uint q, uint precision) public pure returns (uint) {
                      uint n = 0;
                      uint nFact = 1;
                      uint currentP = 1;
                      uint currentQ = 1;
              
                      uint sum = 0;
                      uint prevSum = 0;
              
                      while (true) {
                          if (checkMultOverflow(currentP, precision)) return sum;
                          if (checkMultOverflow(currentQ, nFact)) return sum;
              
                          sum += (currentP * precision) / (currentQ * nFact);
              
                          if (sum == prevSum) return sum;
                          prevSum = sum;
              
                          n++;
              
                          if (checkMultOverflow(currentP, p)) return sum;
                          if (checkMultOverflow(currentQ, q)) return sum;
                          if (checkMultOverflow(nFact, n)) return sum;
              
                          currentP *= p;
                          currentQ *= q;
                          nFact *= n;
              
                          (currentP, currentQ) = compactFraction(currentP, currentQ, precision);
                      }
                  }
                  /* solhint-enable code-complexity */
              
                  function countLeadingZeros(uint p, uint q) public pure returns (uint) {
                      uint denomator = (uint(1)<<255);
                      for (int i = 255; i >= 0; i--) {
                          if ((q*denomator)/denomator != q) {
                              // overflow
                              denomator = denomator/2;
                              continue;
                          }
                          if (p/(q*denomator) > 0) return uint(i);
                          denomator = denomator/2;
                      }
              
                      return uint(-1);
                  }
              
                  // log2 for a number that it in [1,2)
                  function log2ForSmallNumber(uint x, uint numPrecisionBits) public pure returns (uint) {
                      uint res = 0;
                      uint one = (uint(1)<<numPrecisionBits);
                      uint two = 2 * one;
                      uint addition = one;
              
                      require((x >= one) && (x <= two));
                      require(numPrecisionBits < 125);
              
                      for (uint i = numPrecisionBits; i > 0; i--) {
                          x = (x*x) / one;
                          addition = addition/2;
                          if (x >= two) {
                              x = x/2;
                              res += addition;
                          }
                      }
              
                      return res;
                  }
              
                  function logBase2 (uint p, uint q, uint numPrecisionBits) public pure returns (uint) {
                      uint n = 0;
                      uint precision = (uint(1)<<numPrecisionBits);
              
                      if (p > q) {
                          n = countLeadingZeros(p, q);
                      }
              
                      require(!checkMultOverflow(p, precision));
                      require(!checkMultOverflow(n, precision));
                      require(!checkMultOverflow(uint(1)<<n, q));
              
                      uint y = p * precision / (q * (uint(1)<<n));
                      uint log2Small = log2ForSmallNumber(y, numPrecisionBits);
              
                      require(n*precision <= BIG_NUMBER);
                      require(log2Small <= BIG_NUMBER);
              
                      return n * precision + log2Small;
                  }
              
                  function ln(uint p, uint q, uint numPrecisionBits) public pure returns (uint) {
                      uint ln2Numerator   = 6931471805599453094172;
                      uint ln2Denomerator = 10000000000000000000000;
              
                      uint log2x = logBase2(p, q, numPrecisionBits);
              
                      require(!checkMultOverflow(ln2Numerator, log2x));
              
                      return ln2Numerator * log2x / ln2Denomerator;
                  }
              }
              
              
              contract LiquidityFormula is UtilMath {
                  function pE(uint r, uint pMIn, uint e, uint precision) public pure returns (uint) {
                      uint expRE = exp(r*e, precision*precision, precision);
                      require(!checkMultOverflow(expRE, pMIn));
                      return pMIn*expRE / precision;
                  }
              
                  function deltaTFunc(uint r, uint pMIn, uint e, uint deltaE, uint precision) public pure returns (uint) {
                      uint pe = pE(r, pMIn, e, precision);
                      uint rpe = r * pe;
                      uint erdeltaE = exp(r*deltaE, precision*precision, precision);
              
                      require(erdeltaE >= precision);
                      require(!checkMultOverflow(erdeltaE - precision, precision));
                      require(!checkMultOverflow((erdeltaE - precision)*precision, precision));
                      require(!checkMultOverflow((erdeltaE - precision)*precision*precision, precision));
                      require(!checkMultOverflow(rpe, erdeltaE));
                      require(!checkMultOverflow(r, pe));
              
                      return (erdeltaE - precision) * precision * precision * precision / (rpe*erdeltaE);
                  }
              
                  function deltaEFunc(uint r, uint pMIn, uint e, uint deltaT, uint precision, uint numPrecisionBits)
                      public pure
                      returns (uint)
                  {
                      uint pe = pE(r, pMIn, e, precision);
                      uint rpe = r * pe;
                      uint lnPart = ln(precision*precision + rpe*deltaT/precision, precision*precision, numPrecisionBits);
              
                      require(!checkMultOverflow(r, pe));
                      require(!checkMultOverflow(precision, precision));
                      require(!checkMultOverflow(rpe, deltaT));
                      require(!checkMultOverflow(lnPart, precision));
              
                      return lnPart * precision / r;
                  }
              }
              
              // 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/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/LiquidityConversionRates.sol
              
              contract LiquidityConversionRates is ConversionRatesInterface, LiquidityFormula, Withdrawable, Utils {
                  ERC20 public token;
                  address public reserveContract;
              
                  uint public numFpBits;
                  uint public formulaPrecision;
              
                  uint public rInFp;
                  uint public pMinInFp;
              
                  uint public maxEthCapBuyInFp;
                  uint public maxEthCapSellInFp;
                  uint public maxQtyInFp;
              
                  uint public feeInBps;
                  uint public collectedFeesInTwei = 0;
              
                  uint public maxBuyRateInPrecision;
                  uint public minBuyRateInPrecision;
                  uint public maxSellRateInPrecision;
                  uint public minSellRateInPrecision;
              
                  function LiquidityConversionRates(address _admin, ERC20 _token) public {
                      transferAdminQuickly(_admin);
                      token = _token;
                      setDecimals(token);
                      require(getDecimals(token) <= MAX_DECIMALS);
                  }
              
                  event ReserveAddressSet(address reserve);
              
                  function setReserveAddress(address reserve) public onlyAdmin {
                      reserveContract = reserve;
                      ReserveAddressSet(reserve);
                  }
              
                  event LiquidityParamsSet(
                      uint rInFp,
                      uint pMinInFp,
                      uint numFpBits,
                      uint maxCapBuyInFp,
                      uint maxEthCapSellInFp,
                      uint feeInBps,
                      uint formulaPrecision,
                      uint maxQtyInFp,
                      uint maxBuyRateInPrecision,
                      uint minBuyRateInPrecision,
                      uint maxSellRateInPrecision,
                      uint minSellRateInPrecision
                  );
              
                  function setLiquidityParams(
                      uint _rInFp,
                      uint _pMinInFp,
                      uint _numFpBits,
                      uint _maxCapBuyInWei,
                      uint _maxCapSellInWei,
                      uint _feeInBps,
                      uint _maxTokenToEthRateInPrecision,
                      uint _minTokenToEthRateInPrecision
                  ) public onlyAdmin {
              
                      require(_numFpBits < 256);
                      require(formulaPrecision <= MAX_QTY);
                      require(_feeInBps < 10000);
                      require(_minTokenToEthRateInPrecision < _maxTokenToEthRateInPrecision);
              
                      rInFp = _rInFp;
                      pMinInFp = _pMinInFp;
                      formulaPrecision = uint(1)<<_numFpBits;
                      maxQtyInFp = fromWeiToFp(MAX_QTY);
                      numFpBits = _numFpBits;
                      maxEthCapBuyInFp = fromWeiToFp(_maxCapBuyInWei);
                      maxEthCapSellInFp = fromWeiToFp(_maxCapSellInWei);
                      feeInBps = _feeInBps;
                      maxBuyRateInPrecision = PRECISION * PRECISION / _minTokenToEthRateInPrecision;
                      minBuyRateInPrecision = PRECISION * PRECISION / _maxTokenToEthRateInPrecision;
                      maxSellRateInPrecision = _maxTokenToEthRateInPrecision;
                      minSellRateInPrecision = _minTokenToEthRateInPrecision;
              
                      LiquidityParamsSet(
                          rInFp,
                          pMinInFp,
                          numFpBits,
                          maxEthCapBuyInFp,
                          maxEthCapSellInFp,
                          feeInBps,
                          formulaPrecision,
                          maxQtyInFp,
                          maxBuyRateInPrecision,
                          minBuyRateInPrecision,
                          maxSellRateInPrecision,
                          minSellRateInPrecision
                      );
                  }
              
                  function recordImbalance(
                      ERC20 conversionToken,
                      int buyAmountInTwei,
                      uint rateUpdateBlock,
                      uint currentBlock
                  )
                      public
                  {
                      conversionToken;
                      rateUpdateBlock;
                      currentBlock;
              
                      require(msg.sender == reserveContract);
                      if (buyAmountInTwei > 0) {
                          // Buy case
                          collectedFeesInTwei += calcCollectedFee(abs(buyAmountInTwei));
                      } else {
                          // Sell case
                          collectedFeesInTwei += abs(buyAmountInTwei) * feeInBps / 10000;
                      }
                  }
              
                  event CollectedFeesReset(uint resetFeesInTwei);
              
                  function resetCollectedFees() public onlyAdmin {
                      uint resetFeesInTwei = collectedFeesInTwei;
                      collectedFeesInTwei = 0;
              
                      CollectedFeesReset(resetFeesInTwei);
                  }
              
                  function getRate(
                          ERC20 conversionToken,
                          uint currentBlockNumber,
                          bool buy,
                          uint qtyInSrcWei
                  ) public view returns(uint) {
              
                      currentBlockNumber;
              
                      require(qtyInSrcWei <= MAX_QTY);
                      uint eInFp = fromWeiToFp(reserveContract.balance);
                      uint rateInPrecision = getRateWithE(conversionToken, buy, qtyInSrcWei, eInFp);
                      require(rateInPrecision <= MAX_RATE);
                      return rateInPrecision;
                  }
              
                  function getRateWithE(ERC20 conversionToken, bool buy, uint qtyInSrcWei, uint eInFp) public view returns(uint) {
                      uint deltaEInFp;
                      uint sellInputTokenQtyInFp;
                      uint deltaTInFp;
                      uint rateInPrecision;
              
                      require(qtyInSrcWei <= MAX_QTY);
                      require(eInFp <= maxQtyInFp);
                      if (conversionToken != token) return 0;
              
                      if (buy) {
                          // ETH goes in, token goes out
                          deltaEInFp = fromWeiToFp(qtyInSrcWei);
                          if (deltaEInFp > maxEthCapBuyInFp) return 0;
              
                          if (deltaEInFp == 0) {
                              rateInPrecision = buyRateZeroQuantity(eInFp);
                          } else {
                              rateInPrecision = buyRate(eInFp, deltaEInFp);
                          }
                      } else {
                          sellInputTokenQtyInFp = fromTweiToFp(qtyInSrcWei);
                          deltaTInFp = valueAfterReducingFee(sellInputTokenQtyInFp);
                          if (deltaTInFp == 0) {
                              rateInPrecision = sellRateZeroQuantity(eInFp);
                              deltaEInFp = 0;
                          } else {
                              (rateInPrecision, deltaEInFp) = sellRate(eInFp, sellInputTokenQtyInFp, deltaTInFp);
                          }
              
                          if (deltaEInFp > maxEthCapSellInFp) return 0;
                      }
              
                      rateInPrecision = rateAfterValidation(rateInPrecision, buy);
                      return rateInPrecision;
                  }
              
                  function rateAfterValidation(uint rateInPrecision, bool buy) public view returns(uint) {
                      uint minAllowRateInPrecision;
                      uint maxAllowedRateInPrecision;
              
                      if (buy) {
                          minAllowRateInPrecision = minBuyRateInPrecision;
                          maxAllowedRateInPrecision = maxBuyRateInPrecision;
                      } else {
                          minAllowRateInPrecision = minSellRateInPrecision;
                          maxAllowedRateInPrecision = maxSellRateInPrecision;
                      }
              
                      if ((rateInPrecision > maxAllowedRateInPrecision) || (rateInPrecision < minAllowRateInPrecision)) {
                          return 0;
                      } else if (rateInPrecision > MAX_RATE) {
                          return 0;
                      } else {
                          return rateInPrecision;
                      }
                  }
              
                  function buyRate(uint eInFp, uint deltaEInFp) public view returns(uint) {
                      uint deltaTInFp = deltaTFunc(rInFp, pMinInFp, eInFp, deltaEInFp, formulaPrecision);
                      require(deltaTInFp <= maxQtyInFp);
                      deltaTInFp = valueAfterReducingFee(deltaTInFp);
                      return deltaTInFp * PRECISION / deltaEInFp;
                  }
              
                  function buyRateZeroQuantity(uint eInFp) public view returns(uint) {
                      uint ratePreReductionInPrecision = formulaPrecision * PRECISION / pE(rInFp, pMinInFp, eInFp, formulaPrecision);
                      return valueAfterReducingFee(ratePreReductionInPrecision);
                  }
              
                  function sellRate(
                      uint eInFp,
                      uint sellInputTokenQtyInFp,
                      uint deltaTInFp
                  ) public view returns(uint rateInPrecision, uint deltaEInFp) {
                      deltaEInFp = deltaEFunc(rInFp, pMinInFp, eInFp, deltaTInFp, formulaPrecision, numFpBits);
                      require(deltaEInFp <= maxQtyInFp);
                      rateInPrecision = deltaEInFp * PRECISION / sellInputTokenQtyInFp;
                  }
              
                  function sellRateZeroQuantity(uint eInFp) public view returns(uint) {
                      uint ratePreReductionInPrecision = pE(rInFp, pMinInFp, eInFp, formulaPrecision) * PRECISION / formulaPrecision;
                      return valueAfterReducingFee(ratePreReductionInPrecision);
                  }
              
                  function fromTweiToFp(uint qtyInTwei) public view returns(uint) {
                      require(qtyInTwei <= MAX_QTY);
                      return qtyInTwei * formulaPrecision / (10 ** getDecimals(token));
                  }
              
                  function fromWeiToFp(uint qtyInwei) public view returns(uint) {
                      require(qtyInwei <= MAX_QTY);
                      return qtyInwei * formulaPrecision / (10 ** ETH_DECIMALS);
                  }
              
                  function valueAfterReducingFee(uint val) public view returns(uint) {
                      require(val <= BIG_NUMBER);
                      return ((10000 - feeInBps) * val) / 10000;
                  }
              
                  function calcCollectedFee(uint val) public view returns(uint) {
                      require(val <= MAX_QTY);
                      return val * feeInBps / (10000 - feeInBps);
                  }
              
                  function abs(int val) public pure returns(uint) {
                      if (val < 0) {
                          return uint(val * (-1));
                      } else {
                          return uint(val);
                      }
                  }
              
              }