ETH Price: $1,896.88 (-1.68%)
Gas: 0.46 Gwei

Transaction Decoder

Block:
10100930 at May-20-2020 05:25:04 AM +UTC
Transaction Fee:
0.008954484 ETH $16.99
Gas Used:
407,022 Gas / 22 Gwei

Emitted Events:

154 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000018f80de8783f6b158ae18423c3d5b9e19617072c, 0x00000000000000000000000065bf64ff5f51272f729bdcd7acfb00677ced86cd, 0000000000000000000000000000000000000000000000008ac7230489e80000 )
155 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000065bf64ff5f51272f729bdcd7acfb00677ced86cd, 0x00000000000000000000000056178a0d5f301baf6cf3e1cd53d9863437345bf9, 0000000000000000000000000000000000000000000000008ac7230489e80000 )
156 KyberNetwork.EtherReceival( sender=KyberReserve, amount=46695479930351170 )
157 KyberReserve.TradeExecute( origin=KyberNetwork, src=AdminUpgradeabilityProxy, srcAmount=10000000000000000000, destToken=0xEeeeeEee...eeeeeEEeE, destAmount=46695479930351170, destAddress=KyberNetwork )
158 FeeBurner.AssignFeeToWallet( reserve=KyberReserve, wallet=0x3fFFF2F4...AC2Ea501b, walletFee=6002501268923718 )
159 FeeBurner.AssignBurnFees( reserve=KyberReserve, burnFee=14005836294155342 )
160 KyberNetwork.KyberTrade( trader=[Sender] 0x18f80de8783f6b158ae18423c3d5b9e19617072c, src=AdminUpgradeabilityProxy, dest=0xEeeeeEee...eeeeeEEeE, srcAmount=10000000000000000000, dstAmount=46695479930351170, destAddress=[Sender] 0x18f80de8783f6b158ae18423c3d5b9e19617072c, ethWeiValue=46695479930351170, reserve1=KyberReserve, reserve2=0x00000000...000000000, hint=0x5045524D )
161 KyberNetworkProxy.ExecuteTrade( trader=[Sender] 0x18f80de8783f6b158ae18423c3d5b9e19617072c, src=AdminUpgradeabilityProxy, dest=0xEeeeeEee...eeeeeEEeE, actualSrcAmount=10000000000000000000, actualDestAmount=46695479930351170 )

Account State Difference:

  Address   Before After State Difference Code
(zhizhu.top)
1,403.799701683005220633 Eth1,403.808656167005220633 Eth0.008954484
0x18f80De8...19617072C
0.03856191691628298 Eth
Nonce: 63
0.07630291284663415 Eth
Nonce: 64
0.03774099593035117
0x65bF64Ff...77ced86Cd
(Kyber: Contract)
0x7a337007...5552d4F7D
(Kyber: Reserve 2)
817.051415924626908835 Eth817.004720444696557665 Eth0.04669547993035117
0x8007aa43...E5fF626d1
(Kyber: Fee Burner)
0x8E870D67...d388289E1
0xFB80Bfa1...deeb10483

Execution Trace

KyberNetworkProxy.tradeWithHint( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, srcAmount=10000000000000000000, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x18f80De8783f6B158Ae18423C3D5B9e19617072C, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=4529461553244063, walletId=0x3fFFF2F4f6C0831FAC59534694ACd14AC2Ea501b, hint=0x5045524D ) => ( 46695479930351170 )
  • AdminUpgradeabilityProxy.70a08231( )
    • PAXImplementationV2.balanceOf( _addr=0x18f80De8783f6B158Ae18423C3D5B9e19617072C ) => ( 490000000000000000000 )
    • AdminUpgradeabilityProxy.23b872dd( )
      • PAXImplementationV2.transferFrom( _from=0x18f80De8783f6B158Ae18423C3D5B9e19617072C, _to=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _value=10000000000000000000 ) => ( True )
      • KyberNetwork.tradeWithHint( trader=0x18f80De8783f6B158Ae18423C3D5B9e19617072C, src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, srcAmount=10000000000000000000, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x18f80De8783f6B158Ae18423C3D5B9e19617072C, maxDestAmount=57896044618658097711785492504343953926634992332820282019728792003956564819968, minConversionRate=4529461553244063, walletId=0x3fFFF2F4f6C0831FAC59534694ACd14AC2Ea501b, hint=0x5045524D ) => ( 46695479930351170 )
        • AdminUpgradeabilityProxy.70a08231( )
          • PAXImplementationV2.balanceOf( _addr=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd ) => ( 10000000000000160895 )
          • KyberReserve.getConversionRate( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=10000000000000000000, blockNumber=10100930 ) => ( 4661316764926632 )
            • ConversionRates.getRate( token=0x8E870D67F660D95d5be530380D0eC0bd388289E1, currentBlockNumber=10100930, buy=False, qty=10000000000000000000 ) => ( 4661316764926632 )
            • AdminUpgradeabilityProxy.CALL( )
              • PAXImplementationV2.DELEGATECALL( )
              • SanityRates.getSanityRate( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ) => ( 5202913631633714 )
              • KyberReserve.getConversionRate( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=10000000000000000000, blockNumber=10100930 ) => ( 4669547993035117 )
                • ConversionRates.getRate( token=0x8E870D67F660D95d5be530380D0eC0bd388289E1, currentBlockNumber=10100930, buy=False, qty=10000000000000000000 ) => ( 4669547993035117 )
                • AdminUpgradeabilityProxy.CALL( )
                  • PAXImplementationV2.DELEGATECALL( )
                  • KyberReserve.getConversionRate( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, srcQty=10000000000000000000, blockNumber=10100930 ) => ( 4667029786566904 )
                    • ConversionRates.getRate( token=0x8E870D67F660D95d5be530380D0eC0bd388289E1, currentBlockNumber=10100930, buy=False, qty=10000000000000000000 ) => ( 4667029786566904 )
                    • SanityRates.getSanityRate( src=0x8E870D67F660D95d5be530380D0eC0bd388289E1, dest=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ) => ( 5326356118301173 )
                    • KyberReserve.trade( srcToken=0x8E870D67F660D95d5be530380D0eC0bd388289E1, srcAmount=10000000000000000000, destToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, destAddress=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, conversionRate=4669547993035117, validate=True ) => ( True )
                      • AdminUpgradeabilityProxy.CALL( )
                        • PAXImplementationV2.DELEGATECALL( )
                        • ConversionRates.recordImbalance( token=0x8E870D67F660D95d5be530380D0eC0bd388289E1, buyAmount=-10000000000000000000, rateUpdateBlock=0, currentBlock=10100930 )
                        • AdminUpgradeabilityProxy.23b872dd( )
                          • PAXImplementationV2.transferFrom( _from=0x65bF64Ff5f51272f729BDcD7AcFB00677ced86Cd, _to=0x56178a0d5F301bAf6CF3e1Cd53d9863437345Bf9, _value=10000000000000000000 ) => ( True )
                          • ETH 0.04669547993035117 KyberNetwork.CALL( )
                          • ETH 0.04669547993035117 0x18f80de8783f6b158ae18423c3d5b9e19617072c.CALL( )
                          • FeeBurner.handleFees( tradeWeiAmount=46695479930351170, reserve=0x7a3370075a54B187d7bD5DceBf0ff2B5552d4F7D, wallet=0x3fFFF2F4f6C0831FAC59534694ACd14AC2Ea501b ) => ( True )
                          • AdminUpgradeabilityProxy.70a08231( )
                            • PAXImplementationV2.balanceOf( _addr=0x18f80De8783f6B158Ae18423C3D5B9e19617072C ) => ( 480000000000000000000 )
                              File 1 of 13: 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 13: AdminUpgradeabilityProxy
                              pragma solidity ^0.4.24;
                              
                              // File: contracts/zeppelin/Proxy.sol
                              
                              /**
                               * @title Proxy
                               * @dev Implements delegation of calls to other contracts, with proper
                               * forwarding of return values and bubbling of failures.
                               * It defines a fallback function that delegates all calls to the address
                               * returned by the abstract _implementation() internal function.
                               */
                              contract Proxy {
                                  /**
                                   * @dev Fallback function.
                                   * Implemented entirely in `_fallback`.
                                   */
                                  function () payable external {
                                      _fallback();
                                  }
                              
                                  /**
                                   * @return The Address of the implementation.
                                   */
                                  function _implementation() internal view returns (address);
                              
                                  /**
                                   * @dev Delegates execution to an implementation contract.
                                   * This is a low level function that doesn't return to its internal call site.
                                   * It will return to the external caller whatever the implementation returns.
                                   * @param implementation Address to delegate.
                                   */
                                  function _delegate(address implementation) internal {
                                      assembly {
                                      // Copy msg.data. We take full control of memory in this inline assembly
                                      // block because it will not return to Solidity code. We overwrite the
                                      // Solidity scratch pad at memory position 0.
                                          calldatacopy(0, 0, calldatasize)
                              
                                      // Call the implementation.
                                      // out and outsize are 0 because we don't know the size yet.
                                          let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0)
                              
                                      // Copy the returned data.
                                          returndatacopy(0, 0, returndatasize)
                              
                                          switch result
                                          // delegatecall returns 0 on error.
                                          case 0 { revert(0, returndatasize) }
                                          default { return(0, returndatasize) }
                                      }
                                  }
                              
                                  /**
                                   * @dev Function that is run as the first thing in the fallback function.
                                   * Can be redefined in derived contracts to add functionality.
                                   * Redefinitions must call super._willFallback().
                                   */
                                  function _willFallback() internal {
                                  }
                              
                                  /**
                                   * @dev fallback implementation.
                                   * Extracted to enable manual triggering.
                                   */
                                  function _fallback() internal {
                                      _willFallback();
                                      _delegate(_implementation());
                                  }
                              }
                              
                              // File: contracts/zeppelin/AddressUtils.sol
                              
                              /**
                               * Utility library of inline functions on addresses
                               */
                              library AddressUtils {
                              
                                  /**
                                   * Returns whether the target address is a contract
                                   * @dev This function will return false if invoked during the constructor of a contract,
                                   * as the code is not actually created until after the constructor finishes.
                                   * @param addr address to check
                                   * @return whether the target address is a contract
                                   */
                                  function isContract(address addr) internal view returns (bool) {
                                      uint256 size;
                                      // XXX Currently there is no better way to check if there is a contract in an address
                                      // than to check the size of the code at that address.
                                      // See https://ethereum.stackexchange.com/a/14016/36603
                                      // for more details about how this works.
                                      // TODO Check this again before the Serenity release, because all addresses will be
                                      // contracts then.
                                      // solium-disable-next-line security/no-inline-assembly
                                      assembly { size := extcodesize(addr) }
                                      return size > 0;
                                  }
                              
                              }
                              
                              // File: contracts/zeppelin/UpgradeabilityProxy.sol
                              
                              /**
                               * @title UpgradeabilityProxy
                               * @dev This contract implements a proxy that allows to change the
                               * implementation address to which it will delegate.
                               * Such a change is called an implementation upgrade.
                               */
                              contract UpgradeabilityProxy is Proxy {
                                  /**
                                   * @dev Emitted when the implementation is upgraded.
                                   * @param implementation Address of the new implementation.
                                   */
                                  event Upgraded(address implementation);
                              
                                  /**
                                   * @dev Storage slot with the address of the current implementation.
                                   * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                                   * validated in the constructor.
                                   */
                                  bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
                              
                                  /**
                                   * @dev Contract constructor.
                                   * @param _implementation Address of the initial implementation.
                                   */
                                  constructor(address _implementation) public {
                                      assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
                              
                                      _setImplementation(_implementation);
                                  }
                              
                                  /**
                                   * @dev Returns the current implementation.
                                   * @return Address of the current implementation
                                   */
                                  function _implementation() internal view returns (address impl) {
                                      bytes32 slot = IMPLEMENTATION_SLOT;
                                      assembly {
                                          impl := sload(slot)
                                      }
                                  }
                              
                                  /**
                                   * @dev Upgrades the proxy to a new implementation.
                                   * @param newImplementation Address of the new implementation.
                                   */
                                  function _upgradeTo(address newImplementation) internal {
                                      _setImplementation(newImplementation);
                                      emit Upgraded(newImplementation);
                                  }
                              
                                  /**
                                   * @dev Sets the implementation address of the proxy.
                                   * @param newImplementation Address of the new implementation.
                                   */
                                  function _setImplementation(address newImplementation) private {
                                      require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
                              
                                      bytes32 slot = IMPLEMENTATION_SLOT;
                              
                                      assembly {
                                          sstore(slot, newImplementation)
                                      }
                                  }
                              }
                              
                              // File: contracts/zeppelin/AdminUpgradeabilityProxy.sol
                              
                              /**
                               * @title AdminUpgradeabilityProxy
                               * @dev This contract combines an upgradeability proxy with an authorization
                               * mechanism for administrative tasks.
                               * All external functions in this contract must be guarded by the
                               * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                               * feature proposal that would enable this to be done automatically.
                               */
                              contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                                  /**
                                   * @dev Emitted when the administration has been transferred.
                                   * @param previousAdmin Address of the previous admin.
                                   * @param newAdmin Address of the new admin.
                                   */
                                  event AdminChanged(address previousAdmin, address newAdmin);
                              
                                  /**
                                   * @dev Storage slot with the admin of the contract.
                                   * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                                   * validated in the constructor.
                                   */
                                  bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
                              
                                  /**
                                   * @dev Modifier to check whether the `msg.sender` is the admin.
                                   * If it is, it will run the function. Otherwise, it will delegate the call
                                   * to the implementation.
                                   */
                                  modifier ifAdmin() {
                                      if (msg.sender == _admin()) {
                                          _;
                                      } else {
                                          _fallback();
                                      }
                                  }
                              
                                  /**
                                   * Contract constructor.
                                   * It sets the `msg.sender` as the proxy administrator.
                                   * @param _implementation address of the initial implementation.
                                   */
                                  constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                                      assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                              
                                      _setAdmin(msg.sender);
                                  }
                              
                                  /**
                                   * @return The address of the proxy admin.
                                   */
                                  function admin() external view ifAdmin returns (address) {
                                      return _admin();
                                  }
                              
                                  /**
                                   * @return The address of the implementation.
                                   */
                                  function implementation() external view ifAdmin returns (address) {
                                      return _implementation();
                                  }
                              
                                  /**
                                   * @dev Changes the admin of the proxy.
                                   * Only the current admin can call this function.
                                   * @param newAdmin Address to transfer proxy administration to.
                                   */
                                  function changeAdmin(address newAdmin) external ifAdmin {
                                      require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                                      emit AdminChanged(_admin(), newAdmin);
                                      _setAdmin(newAdmin);
                                  }
                              
                                  /**
                                   * @dev Upgrade the backing implementation of the proxy.
                                   * Only the admin can call this function.
                                   * @param newImplementation Address of the new implementation.
                                   */
                                  function upgradeTo(address newImplementation) external ifAdmin {
                                      _upgradeTo(newImplementation);
                                  }
                              
                                  /**
                                   * @dev Upgrade the backing implementation of the proxy and call a function
                                   * on the new implementation.
                                   * This is useful to initialize the proxied contract.
                                   * @param newImplementation Address of the new implementation.
                                   * @param data Data to send as msg.data in the low level call.
                                   * It should include the signature and the parameters of the function to be
                                   * called, as described in
                                   * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                                   */
                                  function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                                      _upgradeTo(newImplementation);
                                      require(address(this).call.value(msg.value)(data));
                                  }
                              
                                  /**
                                   * @return The admin slot.
                                   */
                                  function _admin() internal view returns (address adm) {
                                      bytes32 slot = ADMIN_SLOT;
                                      assembly {
                                          adm := sload(slot)
                                      }
                                  }
                              
                                  /**
                                   * @dev Sets the address of the proxy admin.
                                   * @param newAdmin Address of the new proxy admin.
                                   */
                                  function _setAdmin(address newAdmin) internal {
                                      bytes32 slot = ADMIN_SLOT;
                              
                                      assembly {
                                          sstore(slot, newAdmin)
                                      }
                                  }
                              
                                  /**
                                   * @dev Only fall back when the sender is not the admin.
                                   */
                                  function _willFallback() internal {
                                      require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                                      super._willFallback();
                                  }
                              }

                              File 3 of 13: 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 4 of 13: KyberReserve
                              pragma solidity ^0.4.13;
                              
                              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);
                              }
                              
                              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 SanityRatesInterface {
                                  function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
                              }
                              
                              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);
                                  }
                              }
                              
                              contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {
                              
                                  address public kyberNetwork;
                                  bool public tradeEnabled;
                                  ConversionRatesInterface public conversionRatesContract;
                                  SanityRatesInterface public sanityRatesContract;
                                  mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
                                  mapping(address=>address) public tokenWallet;
                              
                                  function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
                                      require(_admin != address(0));
                                      require(_ratesContract != address(0));
                                      require(_kyberNetwork != address(0));
                                      kyberNetwork = _kyberNetwork;
                                      conversionRatesContract = _ratesContract;
                                      admin = _admin;
                                      tradeEnabled = true;
                                  }
                              
                                  event DepositToken(ERC20 token, uint amount);
                              
                                  function() public payable {
                                      DepositToken(ETH_TOKEN_ADDRESS, msg.value);
                                  }
                              
                                  event TradeExecute(
                                      address indexed origin,
                                      address src,
                                      uint srcAmount,
                                      address destToken,
                                      uint destAmount,
                                      address destAddress
                                  );
                              
                                  function trade(
                                      ERC20 srcToken,
                                      uint srcAmount,
                                      ERC20 destToken,
                                      address destAddress,
                                      uint conversionRate,
                                      bool validate
                                  )
                                      public
                                      payable
                                      returns(bool)
                                  {
                                      require(tradeEnabled);
                                      require(msg.sender == kyberNetwork);
                              
                                      require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
                              
                                      return true;
                                  }
                              
                                  event TradeEnabled(bool enable);
                              
                                  function enableTrade() public onlyAdmin returns(bool) {
                                      tradeEnabled = true;
                                      TradeEnabled(true);
                              
                                      return true;
                                  }
                              
                                  function disableTrade() public onlyAlerter returns(bool) {
                                      tradeEnabled = false;
                                      TradeEnabled(false);
                              
                                      return true;
                                  }
                              
                                  event WithdrawAddressApproved(ERC20 token, address addr, bool approve);
                              
                                  function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
                                      approvedWithdrawAddresses[keccak256(token, addr)] = approve;
                                      WithdrawAddressApproved(token, addr, approve);
                              
                                      setDecimals(token);
                                      if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) {
                                          tokenWallet[token] = this; // by default
                                          require(token.approve(this, 2 ** 255));
                                      }
                                  }
                              
                                  event NewTokenWallet(ERC20 token, address wallet);
                              
                                  function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {
                                      require(wallet != address(0x0));
                                      tokenWallet[token] = wallet;
                                      NewTokenWallet(token, wallet);
                                  }
                              
                                  event WithdrawFunds(ERC20 token, uint amount, address destination);
                              
                                  function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
                                      require(approvedWithdrawAddresses[keccak256(token, destination)]);
                              
                                      if (token == ETH_TOKEN_ADDRESS) {
                                          destination.transfer(amount);
                                      } else {
                                          require(token.transferFrom(tokenWallet[token], destination, amount));
                                      }
                              
                                      WithdrawFunds(token, amount, destination);
                              
                                      return true;
                                  }
                              
                                  event SetContractAddresses(address network, address rate, address sanity);
                              
                                  function setContracts(
                                      address _kyberNetwork,
                                      ConversionRatesInterface _conversionRates,
                                      SanityRatesInterface _sanityRates
                                  )
                                      public
                                      onlyAdmin
                                  {
                                      require(_kyberNetwork != address(0));
                                      require(_conversionRates != address(0));
                              
                                      kyberNetwork = _kyberNetwork;
                                      conversionRatesContract = _conversionRates;
                                      sanityRatesContract = _sanityRates;
                              
                                      SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
                                  }
                              
                                  ////////////////////////////////////////////////////////////////////////////
                                  /// status functions ///////////////////////////////////////////////////////
                                  ////////////////////////////////////////////////////////////////////////////
                                  function getBalance(ERC20 token) public view returns(uint) {
                                      if (token == ETH_TOKEN_ADDRESS)
                                          return this.balance;
                                      else {
                                          address wallet = tokenWallet[token];
                                          uint balanceOfWallet = token.balanceOf(wallet);
                                          uint allowanceOfWallet = token.allowance(wallet, this);
                              
                                          return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;
                                      }
                                  }
                              
                                  function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
                                      uint dstDecimals = getDecimals(dest);
                                      uint srcDecimals = getDecimals(src);
                              
                                      return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
                                  }
                              
                                  function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
                                      uint dstDecimals = getDecimals(dest);
                                      uint srcDecimals = getDecimals(src);
                              
                                      return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
                                  }
                              
                                  function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
                                      ERC20 token;
                                      bool  isBuy;
                              
                                      if (!tradeEnabled) return 0;
                              
                                      if (ETH_TOKEN_ADDRESS == src) {
                                          isBuy = true;
                                          token = dest;
                                      } else if (ETH_TOKEN_ADDRESS == dest) {
                                          isBuy = false;
                                          token = src;
                                      } else {
                                          return 0; // pair is not listed
                                      }
                              
                                      uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);
                                      uint destQty = getDestQty(src, dest, srcQty, rate);
                              
                                      if (getBalance(dest) < destQty) return 0;
                              
                                      if (sanityRatesContract != address(0)) {
                                          uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
                                          if (rate > sanityRate) return 0;
                                      }
                              
                                      return rate;
                                  }
                              
                                  /// @dev do a trade
                                  /// @param srcToken Src token
                                  /// @param srcAmount Amount of src token
                                  /// @param destToken Destination token
                                  /// @param destAddress Destination address to send tokens to
                                  /// @param validate If true, additional validations are applicable
                                  /// @return true iff trade is successful
                                  function doTrade(
                                      ERC20 srcToken,
                                      uint srcAmount,
                                      ERC20 destToken,
                                      address destAddress,
                                      uint conversionRate,
                                      bool validate
                                  )
                                      internal
                                      returns(bool)
                                  {
                                      // can skip validation if done at kyber network level
                                      if (validate) {
                                          require(conversionRate > 0);
                                          if (srcToken == ETH_TOKEN_ADDRESS)
                                              require(msg.value == srcAmount);
                                          else
                                              require(msg.value == 0);
                                      }
                              
                                      uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
                                      // sanity check
                                      require(destAmount > 0);
                              
                                      // add to imbalance
                                      ERC20 token;
                                      int tradeAmount;
                                      if (srcToken == ETH_TOKEN_ADDRESS) {
                                          tradeAmount = int(destAmount);
                                          token = destToken;
                                      } else {
                                          tradeAmount = -1 * int(srcAmount);
                                          token = srcToken;
                                      }
                              
                                      conversionRatesContract.recordImbalance(
                                          token,
                                          tradeAmount,
                                          0,
                                          block.number
                                      );
                              
                                      // collect src tokens
                                      if (srcToken != ETH_TOKEN_ADDRESS) {
                                          require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));
                                      }
                              
                                      // send dest tokens
                                      if (destToken == ETH_TOKEN_ADDRESS) {
                                          destAddress.transfer(destAmount);
                                      } else {
                                          require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));
                                      }
                              
                                      TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
                              
                                      return true;
                                  }
                              }

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

                              File 6 of 13: PAXImplementationV2
                              /**
                               *Submitted for verification at Etherscan.io on 2020-02-03
                              */
                              
                              // File: contracts/zeppelin/SafeMath.sol
                              
                              pragma solidity ^0.4.24;
                              
                              
                              /**
                               * @title SafeMath
                               * @dev Math operations with safety checks that throw on error
                               */
                              library SafeMath {
                                  /**
                                  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                                  */
                                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                      require(b <= a);
                                      uint256 c = a - b;
                              
                                      return c;
                                  }
                              
                                  /**
                                  * @dev Adds two numbers, reverts on overflow.
                                  */
                                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                      uint256 c = a + b;
                                      require(c >= a);
                              
                                      return c;
                                  }
                              }
                              
                              // File: contracts/PAXImplementationV2.sol
                              
                              pragma solidity ^0.4.24;
                              pragma experimental "v0.5.0";
                              
                              
                              
                              /**
                               * @title PAXImplementationV2
                               * @dev this contract is a Pausable ERC20 token with Burn and Mint
                               * controlled by a central SupplyController. By implementing PaxosImplementation
                               * this contract also includes external methods for setting
                               * a new implementation contract for the Proxy.
                               * NOTE: The storage defined here will actually be held in the Proxy
                               * contract and all calls to this contract should be made through
                               * the proxy, including admin actions done as owner or supplyController.
                               * Any call to transfer against this contract should fail
                               * with insufficient funds since no tokens will be issued there.
                               */
                              contract PAXImplementationV2 {
                              
                                  /**
                                   * MATH
                                   */
                              
                                  using SafeMath for uint256;
                              
                                  /**
                                   * DATA
                                   */
                              
                                  // INITIALIZATION DATA
                                  bool private initialized = false;
                              
                                  // ERC20 BASIC DATA
                                  mapping(address => uint256) internal balances;
                                  uint256 internal totalSupply_;
                                  string public constant name = "Paxos Standard"; // solium-disable-line
                                  string public constant symbol = "PAX"; // solium-disable-line uppercase
                                  uint8 public constant decimals = 18; // solium-disable-line uppercase
                              
                                  // ERC20 DATA
                                  mapping(address => mapping(address => uint256)) internal allowed;
                              
                                  // OWNER DATA PART 1
                                  address public owner;
                              
                                  // PAUSABILITY DATA
                                  bool public paused = false;
                              
                                  // ASSET PROTECTION DATA
                                  address public assetProtectionRole;
                                  mapping(address => bool) internal frozen;
                              
                                  // SUPPLY CONTROL DATA
                                  address public supplyController;
                              
                                  // OWNER DATA PART 2
                                  address public proposedOwner;
                              
                                  // DELEGATED TRANSFER DATA
                                  address public betaDelegateWhitelister;
                                  mapping(address => bool) internal betaDelegateWhitelist;
                                  mapping(address => uint256) internal nextSeqs;
                                  // EIP191 header for EIP712 prefix
                                  string constant internal EIP191_HEADER = "\x19\x01";
                                  // Hash of the EIP712 Domain Separator Schema
                                  bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(
                                      "EIP712Domain(string name,address verifyingContract)"
                                  );
                                  bytes32 constant internal EIP712_DELEGATED_TRANSFER_SCHEMA_HASH = keccak256(
                                      "BetaDelegatedTransfer(address to,uint256 value,uint256 fee,uint256 seq,uint256 deadline)"
                                  );
                                  // Hash of the EIP712 Domain Separator data
                                  // solhint-disable-next-line var-name-mixedcase
                                  bytes32 public EIP712_DOMAIN_HASH;
                              
                                  /**
                                   * EVENTS
                                   */
                              
                                  // ERC20 BASIC EVENTS
                                  event Transfer(address indexed from, address indexed to, uint256 value);
                              
                                  // ERC20 EVENTS
                                  event Approval(
                                      address indexed owner,
                                      address indexed spender,
                                      uint256 value
                                  );
                              
                                  // OWNABLE EVENTS
                                  event OwnershipTransferProposed(
                                      address indexed currentOwner,
                                      address indexed proposedOwner
                                  );
                                  event OwnershipTransferDisregarded(
                                      address indexed oldProposedOwner
                                  );
                                  event OwnershipTransferred(
                                      address indexed oldOwner,
                                      address indexed newOwner
                                  );
                              
                                  // PAUSABLE EVENTS
                                  event Pause();
                                  event Unpause();
                              
                                  // ASSET PROTECTION EVENTS
                                  event AddressFrozen(address indexed addr);
                                  event AddressUnfrozen(address indexed addr);
                                  event FrozenAddressWiped(address indexed addr);
                                  event AssetProtectionRoleSet (
                                      address indexed oldAssetProtectionRole,
                                      address indexed newAssetProtectionRole
                                  );
                              
                                  // SUPPLY CONTROL EVENTS
                                  event SupplyIncreased(address indexed to, uint256 value);
                                  event SupplyDecreased(address indexed from, uint256 value);
                                  event SupplyControllerSet(
                                      address indexed oldSupplyController,
                                      address indexed newSupplyController
                                  );
                              
                                  // DELEGATED TRANSFER EVENTS
                                  event BetaDelegatedTransfer(
                                      address indexed from, address indexed to, uint256 value, uint256 seq, uint256 fee
                                  );
                                  event BetaDelegateWhitelisterSet(
                                      address indexed oldWhitelister,
                                      address indexed newWhitelister
                                  );
                                  event BetaDelegateWhitelisted(address indexed newDelegate);
                                  event BetaDelegateUnwhitelisted(address indexed oldDelegate);
                              
                                  /**
                                   * FUNCTIONALITY
                                   */
                              
                                  // INITIALIZATION FUNCTIONALITY
                              
                                  /**
                                   * @dev sets 0 initials tokens, the owner, and the supplyController.
                                   * this serves as the constructor for the proxy but compiles to the
                                   * memory model of the Implementation contract.
                                   */
                                  function initialize() public {
                                      require(!initialized, "already initialized");
                                      owner = msg.sender;
                                      assetProtectionRole = address(0);
                                      totalSupply_ = 0;
                                      supplyController = msg.sender;
                                      initialized = true;
                                  }
                              
                                  /**
                                   * The constructor is used here to ensure that the implementation
                                   * contract is initialized. An uncontrolled implementation
                                   * contract might lead to misleading state
                                   * for users who accidentally interact with it.
                                   */
                                  constructor() public {
                                      initialize();
                                      pause();
                                      // Added in V2
                                      initializeDomainSeparator();
                                  }
                              
                                  /**
                                   * @dev To be called when upgrading the contract using upgradeAndCall to add delegated transfers
                                   */
                                  function initializeDomainSeparator() public {
                                      // hash the name context with the contract address
                                      EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(// solium-disable-line
                                              EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
                                              keccak256(bytes(name)),
                                              bytes32(address(this))
                                          ));
                                      proposedOwner = address(0);
                                  }
                              
                                  // ERC20 BASIC FUNCTIONALITY
                              
                                  /**
                                  * @dev Total number of tokens in existence
                                  */
                                  function totalSupply() public view returns (uint256) {
                                      return totalSupply_;
                                  }
                              
                                  /**
                                  * @dev Transfer token to a specified address from msg.sender
                                  * Note: the use of Safemath ensures that _value is nonnegative.
                                  * @param _to The address to transfer to.
                                  * @param _value The amount to be transferred.
                                  */
                                  function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
                                      require(_to != address(0), "cannot transfer to address zero");
                                      require(!frozen[_to] && !frozen[msg.sender], "address frozen");
                                      require(_value <= balances[msg.sender], "insufficient funds");
                              
                                      balances[msg.sender] = balances[msg.sender].sub(_value);
                                      balances[_to] = balances[_to].add(_value);
                                      emit Transfer(msg.sender, _to, _value);
                                      return true;
                                  }
                              
                                  /**
                                  * @dev Gets the balance of the specified address.
                                  * @param _addr The address to query the the balance of.
                                  * @return An uint256 representing the amount owned by the passed address.
                                  */
                                  function balanceOf(address _addr) public view returns (uint256) {
                                      return balances[_addr];
                                  }
                              
                                  // ERC20 FUNCTIONALITY
                              
                                  /**
                                   * @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
                                  whenNotPaused
                                  returns (bool)
                                  {
                                      require(_to != address(0), "cannot transfer to address zero");
                                      require(!frozen[_to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
                                      require(_value <= balances[_from], "insufficient funds");
                                      require(_value <= allowed[_from][msg.sender], "insufficient allowance");
                              
                                      balances[_from] = balances[_from].sub(_value);
                                      balances[_to] = balances[_to].add(_value);
                                      allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
                                      emit 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 whenNotPaused returns (bool) {
                                      require(!frozen[_spender] && !frozen[msg.sender], "address frozen");
                                      allowed[msg.sender][_spender] = _value;
                                      emit 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];
                                  }
                              
                                  // OWNER FUNCTIONALITY
                              
                                  /**
                                   * @dev Throws if called by any account other than the owner.
                                   */
                                  modifier onlyOwner() {
                                      require(msg.sender == owner, "onlyOwner");
                                      _;
                                  }
                              
                                  /**
                                       * @dev Allows the current owner to begin transferring control of the contract to a proposedOwner
                                       * @param _proposedOwner The address to transfer ownership to.
                                       */
                                  function proposeOwner(address _proposedOwner) public onlyOwner {
                                      require(_proposedOwner != address(0), "cannot transfer ownership to address zero");
                                      require(msg.sender != _proposedOwner, "caller already is owner");
                                      proposedOwner = _proposedOwner;
                                      emit OwnershipTransferProposed(owner, proposedOwner);
                                  }
                                  /**
                                   * @dev Allows the current owner or proposed owner to cancel transferring control of the contract to a proposedOwner
                                   */
                                  function disregardProposeOwner() public {
                                      require(msg.sender == proposedOwner || msg.sender == owner, "only proposedOwner or owner");
                                      require(proposedOwner != address(0), "can only disregard a proposed owner that was previously set");
                                      address _oldProposedOwner = proposedOwner;
                                      proposedOwner = address(0);
                                      emit OwnershipTransferDisregarded(_oldProposedOwner);
                                  }
                                  /**
                                   * @dev Allows the proposed owner to complete transferring control of the contract to the proposedOwner.
                                   */
                                  function claimOwnership() public {
                                      require(msg.sender == proposedOwner, "onlyProposedOwner");
                                      address _oldOwner = owner;
                                      owner = proposedOwner;
                                      proposedOwner = address(0);
                                      emit OwnershipTransferred(_oldOwner, owner);
                                  }
                              
                                  /**
                                   * @dev Reclaim all PAX at the contract address.
                                   * This sends the PAX tokens that this contract add holding to the owner.
                                   * Note: this is not affected by freeze constraints.
                                   */
                                  function reclaimPAX() external onlyOwner {
                                      uint256 _balance = balances[this];
                                      balances[this] = 0;
                                      balances[owner] = balances[owner].add(_balance);
                                      emit Transfer(this, owner, _balance);
                                  }
                              
                                  // PAUSABILITY FUNCTIONALITY
                              
                                  /**
                                   * @dev Modifier to make a function callable only when the contract is not paused.
                                   */
                                  modifier whenNotPaused() {
                                      require(!paused, "whenNotPaused");
                                      _;
                                  }
                              
                                  /**
                                   * @dev called by the owner to pause, triggers stopped state
                                   */
                                  function pause() public onlyOwner {
                                      require(!paused, "already paused");
                                      paused = true;
                                      emit Pause();
                                  }
                              
                                  /**
                                   * @dev called by the owner to unpause, returns to normal state
                                   */
                                  function unpause() public onlyOwner {
                                      require(paused, "already unpaused");
                                      paused = false;
                                      emit Unpause();
                                  }
                              
                                  // ASSET PROTECTION FUNCTIONALITY
                              
                                  /**
                                   * @dev Sets a new asset protection role address.
                                   * @param _newAssetProtectionRole The new address allowed to freeze/unfreeze addresses and seize their tokens.
                                   */
                                  function setAssetProtectionRole(address _newAssetProtectionRole) public {
                                      require(msg.sender == assetProtectionRole || msg.sender == owner, "only assetProtectionRole or Owner");
                                      emit AssetProtectionRoleSet(assetProtectionRole, _newAssetProtectionRole);
                                      assetProtectionRole = _newAssetProtectionRole;
                                  }
                              
                                  modifier onlyAssetProtectionRole() {
                                      require(msg.sender == assetProtectionRole, "onlyAssetProtectionRole");
                                      _;
                                  }
                              
                                  /**
                                   * @dev Freezes an address balance from being transferred.
                                   * @param _addr The new address to freeze.
                                   */
                                  function freeze(address _addr) public onlyAssetProtectionRole {
                                      require(!frozen[_addr], "address already frozen");
                                      frozen[_addr] = true;
                                      emit AddressFrozen(_addr);
                                  }
                              
                                  /**
                                   * @dev Unfreezes an address balance allowing transfer.
                                   * @param _addr The new address to unfreeze.
                                   */
                                  function unfreeze(address _addr) public onlyAssetProtectionRole {
                                      require(frozen[_addr], "address already unfrozen");
                                      frozen[_addr] = false;
                                      emit AddressUnfrozen(_addr);
                                  }
                              
                                  /**
                                   * @dev Wipes the balance of a frozen address, burning the tokens
                                   * and setting the approval to zero.
                                   * @param _addr The new frozen address to wipe.
                                   */
                                  function wipeFrozenAddress(address _addr) public onlyAssetProtectionRole {
                                      require(frozen[_addr], "address is not frozen");
                                      uint256 _balance = balances[_addr];
                                      balances[_addr] = 0;
                                      totalSupply_ = totalSupply_.sub(_balance);
                                      emit FrozenAddressWiped(_addr);
                                      emit SupplyDecreased(_addr, _balance);
                                      emit Transfer(_addr, address(0), _balance);
                                  }
                              
                                  /**
                                  * @dev Gets whether the address is currently frozen.
                                  * @param _addr The address to check if frozen.
                                  * @return A bool representing whether the given address is frozen.
                                  */
                                  function isFrozen(address _addr) public view returns (bool) {
                                      return frozen[_addr];
                                  }
                              
                                  // SUPPLY CONTROL FUNCTIONALITY
                              
                                  /**
                                   * @dev Sets a new supply controller address.
                                   * @param _newSupplyController The address allowed to burn/mint tokens to control supply.
                                   */
                                  function setSupplyController(address _newSupplyController) public {
                                      require(msg.sender == supplyController || msg.sender == owner, "only SupplyController or Owner");
                                      require(_newSupplyController != address(0), "cannot set supply controller to address zero");
                                      emit SupplyControllerSet(supplyController, _newSupplyController);
                                      supplyController = _newSupplyController;
                                  }
                              
                                  modifier onlySupplyController() {
                                      require(msg.sender == supplyController, "onlySupplyController");
                                      _;
                                  }
                              
                                  /**
                                   * @dev Increases the total supply by minting the specified number of tokens to the supply controller account.
                                   * @param _value The number of tokens to add.
                                   * @return A boolean that indicates if the operation was successful.
                                   */
                                  function increaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
                                      totalSupply_ = totalSupply_.add(_value);
                                      balances[supplyController] = balances[supplyController].add(_value);
                                      emit SupplyIncreased(supplyController, _value);
                                      emit Transfer(address(0), supplyController, _value);
                                      return true;
                                  }
                              
                                  /**
                                   * @dev Decreases the total supply by burning the specified number of tokens from the supply controller account.
                                   * @param _value The number of tokens to remove.
                                   * @return A boolean that indicates if the operation was successful.
                                   */
                                  function decreaseSupply(uint256 _value) public onlySupplyController returns (bool success) {
                                      require(_value <= balances[supplyController], "not enough supply");
                                      balances[supplyController] = balances[supplyController].sub(_value);
                                      totalSupply_ = totalSupply_.sub(_value);
                                      emit SupplyDecreased(supplyController, _value);
                                      emit Transfer(supplyController, address(0), _value);
                                      return true;
                                  }
                              
                                  // DELEGATED TRANSFER FUNCTIONALITY
                              
                                  /**
                                   * @dev returns the next seq for a target address.
                                   * The transactor must submit nextSeqOf(transactor) in the next transaction for it to be valid.
                                   * Note: that the seq context is specific to this smart contract.
                                   * @param target The target address.
                                   * @return the seq.
                                   */
                                  //
                                  function nextSeqOf(address target) public view returns (uint256) {
                                      return nextSeqs[target];
                                  }
                              
                                  /**
                                   * @dev Performs a transfer on behalf of the from address, identified by its signature on the delegatedTransfer msg.
                                   * Splits a signature byte array into r,s,v for convenience.
                                   * @param sig the signature of the delgatedTransfer msg.
                                   * @param to The address to transfer to.
                                   * @param value The amount to be transferred.
                                   * @param fee an optional ERC20 fee paid to the executor of betaDelegatedTransfer by the from address.
                                   * @param seq a sequencing number included by the from address specific to this contract to protect from replays.
                                   * @param deadline a block number after which the pre-signed transaction has expired.
                                   * @return A boolean that indicates if the operation was successful.
                                   */
                                  function betaDelegatedTransfer(
                                      bytes sig, address to, uint256 value, uint256 fee, uint256 seq, uint256 deadline
                                  ) public returns (bool) {
                                      require(sig.length == 65, "signature should have length 65");
                                      bytes32 r;
                                      bytes32 s;
                                      uint8 v;
                                      assembly {
                                          r := mload(add(sig, 32))
                                          s := mload(add(sig, 64))
                                          v := byte(0, mload(add(sig, 96)))
                                      }
                                      require(_betaDelegatedTransfer(r, s, v, to, value, fee, seq, deadline), "failed transfer");
                                      return true;
                                  }
                              
                                  /**
                                   * @dev Performs a transfer on behalf of the from address, identified by its signature on the betaDelegatedTransfer msg.
                                   * Note: both the delegate and transactor sign in the fees. The transactor, however,
                                   * has no control over the gas price, and therefore no control over the transaction time.
                                   * Beta prefix chosen to avoid a name clash with an emerging standard in ERC865 or elsewhere.
                                   * Internal to the contract - see betaDelegatedTransfer and betaDelegatedTransferBatch.
                                   * @param r the r signature of the delgatedTransfer msg.
                                   * @param s the s signature of the delgatedTransfer msg.
                                   * @param v the v signature of the delgatedTransfer msg.
                                   * @param to The address to transfer to.
                                   * @param value The amount to be transferred.
                                   * @param fee an optional ERC20 fee paid to the delegate of betaDelegatedTransfer by the from address.
                                   * @param seq a sequencing number included by the from address specific to this contract to protect from replays.
                                   * @param deadline a block number after which the pre-signed transaction has expired.
                                   * @return A boolean that indicates if the operation was successful.
                                   */
                                  function _betaDelegatedTransfer(
                                      bytes32 r, bytes32 s, uint8 v, address to, uint256 value, uint256 fee, uint256 seq, uint256 deadline
                                  ) internal whenNotPaused returns (bool) {
                                      require(betaDelegateWhitelist[msg.sender], "Beta feature only accepts whitelisted delegates");
                                      require(value > 0 || fee > 0, "cannot transfer zero tokens with zero fee");
                                      require(block.number <= deadline, "transaction expired");
                                      // prevent sig malleability from ecrecover()
                                      require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "signature incorrect");
                                      require(v == 27 || v == 28, "signature incorrect");
                              
                                      // EIP712 scheme: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
                                      bytes32 delegatedTransferHash = keccak256(abi.encodePacked(// solium-disable-line
                                              EIP712_DELEGATED_TRANSFER_SCHEMA_HASH, bytes32(to), value, fee, seq, deadline
                                          ));
                                      bytes32 hash = keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, delegatedTransferHash));
                                      address _from = ecrecover(hash, v, r, s);
                              
                                      require(_from != address(0), "error determining from address from signature");
                                      require(to != address(0), "canno use address zero");
                                      require(!frozen[to] && !frozen[_from] && !frozen[msg.sender], "address frozen");
                                      require(value.add(fee) <= balances[_from], "insufficient fund");
                                      require(nextSeqs[_from] == seq, "incorrect seq");
                              
                                      nextSeqs[_from] = nextSeqs[_from].add(1);
                                      balances[_from] = balances[_from].sub(value.add(fee));
                                      if (fee != 0) {
                                          balances[msg.sender] = balances[msg.sender].add(fee);
                                          emit Transfer(_from, msg.sender, fee);
                                      }
                                      balances[to] = balances[to].add(value);
                                      emit Transfer(_from, to, value);
                              
                                      emit BetaDelegatedTransfer(_from, to, value, seq, fee);
                                      return true;
                                  }
                              
                                  /**
                                   * @dev Performs an atomic batch of transfers on behalf of the from addresses, identified by their signatures.
                                   * Lack of nested array support in arguments requires all arguments to be passed as equal size arrays where
                                   * delegated transfer number i is the combination of all arguments at index i
                                   * @param r the r signatures of the delgatedTransfer msg.
                                   * @param s the s signatures of the delgatedTransfer msg.
                                   * @param v the v signatures of the delgatedTransfer msg.
                                   * @param to The addresses to transfer to.
                                   * @param value The amounts to be transferred.
                                   * @param fee optional ERC20 fees paid to the delegate of betaDelegatedTransfer by the from address.
                                   * @param seq sequencing numbers included by the from address specific to this contract to protect from replays.
                                   * @param deadline block numbers after which the pre-signed transactions have expired.
                                   * @return A boolean that indicates if the operation was successful.
                                   */
                                  function betaDelegatedTransferBatch(
                                      bytes32[] r, bytes32[] s, uint8[] v, address[] to, uint256[] value, uint256[] fee, uint256[] seq, uint256[] deadline
                                  ) public returns (bool) {
                                      require(r.length == s.length && r.length == v.length && r.length == to.length && r.length == value.length, "length mismatch");
                                      require(r.length == fee.length && r.length == seq.length && r.length == deadline.length, "length mismatch");
                              
                                      for (uint i = 0; i < r.length; i++) {
                                          require(
                                              _betaDelegatedTransfer(r[i], s[i], v[i], to[i], value[i], fee[i], seq[i], deadline[i]),
                                              "failed transfer"
                                          );
                                      }
                                      return true;
                                  }
                              
                                  /**
                                  * @dev Gets whether the address is currently whitelisted for betaDelegateTransfer.
                                  * @param _addr The address to check if whitelisted.
                                  * @return A bool representing whether the given address is whitelisted.
                                  */
                                  function isWhitelistedBetaDelegate(address _addr) public view returns (bool) {
                                      return betaDelegateWhitelist[_addr];
                                  }
                              
                                  /**
                                   * @dev Sets a new betaDelegate whitelister.
                                   * @param _newWhitelister The address allowed to whitelist betaDelegates.
                                   */
                                  function setBetaDelegateWhitelister(address _newWhitelister) public {
                                      require(msg.sender == betaDelegateWhitelister || msg.sender == owner, "only Whitelister or Owner");
                                      betaDelegateWhitelister = _newWhitelister;
                                      emit BetaDelegateWhitelisterSet(betaDelegateWhitelister, _newWhitelister);
                                  }
                              
                                  modifier onlyBetaDelegateWhitelister() {
                                      require(msg.sender == betaDelegateWhitelister, "onlyBetaDelegateWhitelister");
                                      _;
                                  }
                              
                                  /**
                                   * @dev Whitelists an address to allow calling BetaDelegatedTransfer.
                                   * @param _addr The new address to whitelist.
                                   */
                                  function whitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister {
                                      require(!betaDelegateWhitelist[_addr], "delegate already whitelisted");
                                      betaDelegateWhitelist[_addr] = true;
                                      emit BetaDelegateWhitelisted(_addr);
                                  }
                              
                                  /**
                                   * @dev Unwhitelists an address to disallow calling BetaDelegatedTransfer.
                                   * @param _addr The new address to whitelist.
                                   */
                                  function unwhitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister {
                                      require(betaDelegateWhitelist[_addr], "delegate not whitelisted");
                                      betaDelegateWhitelist[_addr] = false;
                                      emit BetaDelegateUnwhitelisted(_addr);
                                  }
                              }

                              File 7 of 13: 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 8 of 13: 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 13: 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 13: 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 11 of 13: KyberReserve
                              {"ConversionRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ConversionRatesInterface {\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public;\n\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);\n}\n"},"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"KyberReserve.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../ERC20Interface.sol\";\nimport \"../Utils.sol\";\nimport \"../Withdrawable.sol\";\nimport \"../ConversionRatesInterface.sol\";\nimport \"../SanityRatesInterface.sol\";\nimport \"../KyberReserveInterface.sol\";\n\n\n/// @title Kyber Reserve contract\ncontract KyberReserve is KyberReserveInterface, Withdrawable, Utils {\n\n    address public kyberNetwork;\n    bool public tradeEnabled;\n    ConversionRatesInterface public conversionRatesContract;\n    SanityRatesInterface public sanityRatesContract;\n    mapping(bytes32=\u003ebool) public approvedWithdrawAddresses; // sha3(token,address)=\u003ebool\n    mapping(address=\u003eaddress) public tokenWallet;\n\n    function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {\n        require(_admin != address(0));\n        require(_ratesContract != address(0));\n        require(_kyberNetwork != address(0));\n        kyberNetwork = _kyberNetwork;\n        conversionRatesContract = _ratesContract;\n        admin = _admin;\n        tradeEnabled = true;\n    }\n\n    event DepositToken(ERC20 token, uint amount);\n\n    function() public payable {\n        DepositToken(ETH_TOKEN_ADDRESS, msg.value);\n    }\n\n    event TradeExecute(\n        address indexed origin,\n        address src,\n        uint srcAmount,\n        address destToken,\n        uint destAmount,\n        address destAddress\n    );\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        require(tradeEnabled);\n        require(msg.sender == kyberNetwork);\n\n        require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));\n\n        return true;\n    }\n\n    event TradeEnabled(bool enable);\n\n    function enableTrade() public onlyAdmin returns(bool) {\n        tradeEnabled = true;\n        TradeEnabled(true);\n\n        return true;\n    }\n\n    function disableTrade() public onlyAlerter returns(bool) {\n        tradeEnabled = false;\n        TradeEnabled(false);\n\n        return true;\n    }\n\n    event WithdrawAddressApproved(ERC20 token, address addr, bool approve);\n\n    function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {\n        approvedWithdrawAddresses[keccak256(token, addr)] = approve;\n        WithdrawAddressApproved(token, addr, approve);\n\n        setDecimals(token);\n        if ((tokenWallet[token] == address(0x0)) \u0026\u0026 (token != ETH_TOKEN_ADDRESS)) {\n            tokenWallet[token] = this; // by default\n            require(token.approve(this, 2 ** 255));\n        }\n    }\n\n    event NewTokenWallet(ERC20 token, address wallet);\n\n    function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {\n        require(wallet != address(0x0));\n        tokenWallet[token] = wallet;\n        NewTokenWallet(token, wallet);\n    }\n\n    event WithdrawFunds(ERC20 token, uint amount, address destination);\n\n    function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {\n        require(approvedWithdrawAddresses[keccak256(token, destination)]);\n\n        if (token == ETH_TOKEN_ADDRESS) {\n            destination.transfer(amount);\n        } else {\n            require(token.transferFrom(tokenWallet[token], destination, amount));\n        }\n\n        WithdrawFunds(token, amount, destination);\n\n        return true;\n    }\n\n    event SetContractAddresses(address network, address rate, address sanity);\n\n    function setContracts(\n        address _kyberNetwork,\n        ConversionRatesInterface _conversionRates,\n        SanityRatesInterface _sanityRates\n    )\n        public\n        onlyAdmin\n    {\n        require(_kyberNetwork != address(0));\n        require(_conversionRates != address(0));\n\n        kyberNetwork = _kyberNetwork;\n        conversionRatesContract = _conversionRates;\n        sanityRatesContract = _sanityRates;\n\n        SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);\n    }\n\n    ////////////////////////////////////////////////////////////////////////////\n    /// status functions ///////////////////////////////////////////////////////\n    ////////////////////////////////////////////////////////////////////////////\n    function getBalance(ERC20 token) public view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS)\n            return this.balance;\n        else {\n            address wallet = tokenWallet[token];\n            uint balanceOfWallet = token.balanceOf(wallet);\n            uint allowanceOfWallet = token.allowance(wallet, this);\n\n            return (balanceOfWallet \u003c allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;\n        }\n    }\n\n    function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {\n        uint dstDecimals = getDecimals(dest);\n        uint srcDecimals = getDecimals(src);\n\n        return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);\n    }\n\n    function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {\n        uint dstDecimals = getDecimals(dest);\n        uint srcDecimals = getDecimals(src);\n\n        return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);\n    }\n\n    function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {\n        ERC20 token;\n        bool  isBuy;\n\n        if (!tradeEnabled) return 0;\n\n        if (ETH_TOKEN_ADDRESS == src) {\n            isBuy = true;\n            token = dest;\n        } else if (ETH_TOKEN_ADDRESS == dest) {\n            isBuy = false;\n            token = src;\n        } else {\n            return 0; // pair is not listed\n        }\n\n        uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);\n        uint destQty = getDestQty(src, dest, srcQty, rate);\n\n        if (getBalance(dest) \u003c destQty) return 0;\n\n        if (sanityRatesContract != address(0)) {\n            uint sanityRate = sanityRatesContract.getSanityRate(src, dest);\n            if (rate \u003e sanityRate) return 0;\n        }\n\n        return rate;\n    }\n\n    /// @dev do a trade\n    /// @param srcToken Src token\n    /// @param srcAmount Amount of src token\n    /// @param destToken Destination token\n    /// @param destAddress Destination address to send tokens to\n    /// @param validate If true, additional validations are applicable\n    /// @return true iff trade is successful\n    function doTrade(\n        ERC20 srcToken,\n        uint srcAmount,\n        ERC20 destToken,\n        address destAddress,\n        uint conversionRate,\n        bool validate\n    )\n        internal\n        returns(bool)\n    {\n        // can skip validation if done at kyber network level\n        if (validate) {\n            require(conversionRate \u003e 0);\n            if (srcToken == ETH_TOKEN_ADDRESS)\n                require(msg.value == srcAmount);\n            else\n                require(msg.value == 0);\n        }\n\n        uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);\n        // sanity check\n        require(destAmount \u003e 0);\n\n        // add to imbalance\n        ERC20 token;\n        int tradeAmount;\n        if (srcToken == ETH_TOKEN_ADDRESS) {\n            tradeAmount = int(destAmount);\n            token = destToken;\n        } else {\n            tradeAmount = -1 * int(srcAmount);\n            token = srcToken;\n        }\n\n        conversionRatesContract.recordImbalance(\n            token,\n            tradeAmount,\n            0,\n            block.number\n        );\n\n        // collect src tokens\n        if (srcToken != ETH_TOKEN_ADDRESS) {\n            require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));\n        }\n\n        // send dest tokens\n        if (destToken == ETH_TOKEN_ADDRESS) {\n            destAddress.transfer(destAmount);\n        } else {\n            require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));\n        }\n\n        TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);\n\n        return true;\n    }\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"},"SanityRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\ninterface SanityRatesInterface {\n    function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}

                              File 12 of 13: ConversionRates
                              {"ConversionRates.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../../ERC20Interface.sol\";\nimport \"../../Utils.sol\";\nimport \"../../ConversionRatesInterface.sol\";\nimport \"../VolumeImbalanceRecorder.sol\";\n\n\ncontract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils {\n\n    // bps - basic rate steps. one step is 1 / 10000 of the rate.\n    struct StepFunction {\n        int[] x; // quantity for each step. Quantity of each step includes previous steps.\n        int[] y; // rate change per quantity step  in bps.\n    }\n\n    struct TokenData {\n        bool listed;  // was added to reserve\n        bool enabled; // whether trade is enabled\n\n        // position in the compact data\n        uint compactDataArrayIndex;\n        uint compactDataFieldIndex;\n\n        // rate data. base and changes according to quantity and reserve balance.\n        // generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction.\n        uint baseBuyRate;  // in PRECISION units. see KyberConstants\n        uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate\n        StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate.\n        StepFunction sellRateQtyStepFunction;// in bps. higher the qua\n        StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate.\n        StepFunction sellRateImbalanceStepFunction;\n    }\n\n    /*\n    this is the data for tokenRatesCompactData\n    but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write\n    so we represent it as bytes32 and do the byte tricks ourselves.\n    struct TokenRatesCompactData {\n        bytes14 buy;  // change buy rate of token from baseBuyRate in 10 bps\n        bytes14 sell; // change sell rate of token from baseSellRate in 10 bps\n\n        uint32 blockNumber;\n    } */\n    uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks\n    ERC20[] internal listedTokens;\n    mapping(address=\u003eTokenData) internal tokenData;\n    bytes32[] internal tokenRatesCompactData;\n    uint public numTokensInCurrentCompactData = 0;\n    address public reserveContract;\n    uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14;\n    uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA));\n    uint constant internal MAX_STEPS_IN_FUNCTION = 10;\n    int  constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B %\n    int  constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100%\n\n    function ConversionRates(address _admin) public VolumeImbalanceRecorder(_admin)\n        { } // solhint-disable-line no-empty-blocks\n\n    function addToken(ERC20 token) public onlyAdmin {\n\n        require(!tokenData[token].listed);\n        tokenData[token].listed = true;\n        listedTokens.push(token);\n\n        if (numTokensInCurrentCompactData == 0) {\n            tokenRatesCompactData.length++; // add new structure\n        }\n\n        tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1;\n        tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData;\n\n        numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA;\n\n        setGarbageToVolumeRecorder(token);\n\n        setDecimals(token);\n    }\n\n    function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator {\n\n        require(buy.length == sell.length);\n        require(indices.length == buy.length);\n        require(blockNumber \u003c= 0xFFFFFFFF);\n\n        uint bytes14Offset = BYTES_14_OFFSET;\n\n        for (uint i = 0; i \u003c indices.length; i++) {\n            require(indices[i] \u003c tokenRatesCompactData.length);\n            uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset));\n            tokenRatesCompactData[indices[i]] = bytes32(data);\n        }\n    }\n\n    function setBaseRate(\n        ERC20[] tokens,\n        uint[] baseBuy,\n        uint[] baseSell,\n        bytes14[] buy,\n        bytes14[] sell,\n        uint blockNumber,\n        uint[] indices\n    )\n        public\n        onlyOperator\n    {\n        require(tokens.length == baseBuy.length);\n        require(tokens.length == baseSell.length);\n        require(sell.length == buy.length);\n        require(sell.length == indices.length);\n\n        for (uint ind = 0; ind \u003c tokens.length; ind++) {\n            require(tokenData[tokens[ind]].listed);\n            tokenData[tokens[ind]].baseBuyRate = baseBuy[ind];\n            tokenData[tokens[ind]].baseSellRate = baseSell[ind];\n        }\n\n        setCompactData(buy, sell, blockNumber, indices);\n    }\n\n    function setQtyStepFunction(\n        ERC20 token,\n        int[] xBuy,\n        int[] yBuy,\n        int[] xSell,\n        int[] ySell\n    )\n        public\n        onlyOperator\n    {\n        require(xBuy.length == yBuy.length);\n        require(xSell.length == ySell.length);\n        require(xBuy.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(xSell.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(tokenData[token].listed);\n\n        tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy);\n        tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell);\n    }\n\n    function setImbalanceStepFunction(\n        ERC20 token,\n        int[] xBuy,\n        int[] yBuy,\n        int[] xSell,\n        int[] ySell\n    )\n        public\n        onlyOperator\n    {\n        require(xBuy.length == yBuy.length);\n        require(xSell.length == ySell.length);\n        require(xBuy.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(xSell.length \u003c= MAX_STEPS_IN_FUNCTION);\n        require(tokenData[token].listed);\n\n        tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy);\n        tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell);\n    }\n\n    function setValidRateDurationInBlocks(uint duration) public onlyAdmin {\n        validRateDurationInBlocks = duration;\n    }\n\n    function enableTokenTrade(ERC20 token) public onlyAdmin {\n        require(tokenData[token].listed);\n        require(tokenControlInfo[token].minimalRecordResolution != 0);\n        tokenData[token].enabled = true;\n    }\n\n    function disableTokenTrade(ERC20 token) public onlyAlerter {\n        require(tokenData[token].listed);\n        tokenData[token].enabled = false;\n    }\n\n    function setReserveAddress(address reserve) public onlyAdmin {\n        reserveContract = reserve;\n    }\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public\n    {\n        require(msg.sender == reserveContract);\n\n        if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token);\n\n        return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock);\n    }\n\n    /* solhint-disable function-max-lines */\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) {\n        // check if trade is enabled\n        if (!tokenData[token].enabled) return 0;\n        if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set\n\n        // get rate update block\n        bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];\n\n        uint updateRateBlock = getLast4Bytes(compactData);\n        if (currentBlockNumber \u003e= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired\n        // check imbalance\n        int totalImbalance;\n        int blockImbalance;\n        (totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber);\n\n        // calculate actual rate\n        int imbalanceQty;\n        int extraBps;\n        int8 rateUpdate;\n        uint rate;\n\n        if (buy) {\n            // start with base rate\n            rate = tokenData[token].baseBuyRate;\n\n            // add rate update\n            rateUpdate = getRateByteFromCompactData(compactData, token, true);\n            extraBps = int(rateUpdate) * 10;\n            rate = addBps(rate, extraBps);\n\n            // compute token qty\n            qty = getTokenQty(token, rate, qty);\n            imbalanceQty = int(qty);\n            totalImbalance += imbalanceQty;\n\n            // add qty overhead\n            extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty));\n            rate = addBps(rate, extraBps);\n\n            // add imbalance overhead\n            extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance);\n            rate = addBps(rate, extraBps);\n        } else {\n            // start with base rate\n            rate = tokenData[token].baseSellRate;\n\n            // add rate update\n            rateUpdate = getRateByteFromCompactData(compactData, token, false);\n            extraBps = int(rateUpdate) * 10;\n            rate = addBps(rate, extraBps);\n\n            // compute token qty\n            imbalanceQty = -1 * int(qty);\n            totalImbalance += imbalanceQty;\n\n            // add qty overhead\n            extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty));\n            rate = addBps(rate, extraBps);\n\n            // add imbalance overhead\n            extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance);\n            rate = addBps(rate, extraBps);\n        }\n\n        if (abs(totalImbalance) \u003e= getMaxTotalImbalance(token)) return 0;\n        if (abs(blockImbalance + imbalanceQty) \u003e= getMaxPerBlockImbalance(token)) return 0;\n\n        return rate;\n    }\n    /* solhint-enable function-max-lines */\n\n    function getBasicRate(ERC20 token, bool buy) public view returns(uint) {\n        if (buy)\n            return tokenData[token].baseBuyRate;\n        else\n            return tokenData[token].baseSellRate;\n    }\n\n    function getCompactData(ERC20 token) public view returns(uint, uint, byte, byte) {\n        require(tokenData[token].listed);\n\n        uint arrayIndex = tokenData[token].compactDataArrayIndex;\n        uint fieldOffset = tokenData[token].compactDataFieldIndex;\n\n        return (\n            arrayIndex,\n            fieldOffset,\n            byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)),\n            byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false))\n        );\n    }\n\n    function getTokenBasicData(ERC20 token) public view returns(bool, bool) {\n        return (tokenData[token].listed, tokenData[token].enabled);\n    }\n\n    /* solhint-disable code-complexity */\n    function getStepFunctionData(ERC20 token, uint command, uint param) public view returns(int) {\n        if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length);\n        if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param];\n        if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length);\n        if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param];\n\n        if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length);\n        if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param];\n        if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length);\n        if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param];\n\n        if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length);\n        if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param];\n        if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length);\n        if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param];\n\n        if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length);\n        if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param];\n        if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length);\n        if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param];\n\n        revert();\n    }\n    /* solhint-enable code-complexity */\n\n    function getRateUpdateBlock(ERC20 token) public view returns(uint) {\n        bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex];\n        return getLast4Bytes(compactData);\n    }\n\n    function getListedTokens() public view returns(ERC20[]) {\n        return listedTokens;\n    }\n\n    function getTokenQty(ERC20 token, uint ethQty, uint rate) internal view returns(uint) {\n        uint dstDecimals = getDecimals(token);\n        uint srcDecimals = ETH_DECIMALS;\n\n        return calcDstQty(ethQty, srcDecimals, dstDecimals, rate);\n    }\n\n    function getLast4Bytes(bytes32 b) internal pure returns(uint) {\n        // cannot trust compiler with not turning bit operations into EXP opcode\n        return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET);\n    }\n\n    function getRateByteFromCompactData(bytes32 data, ERC20 token, bool buy) internal view returns(int8) {\n        uint fieldOffset = tokenData[token].compactDataFieldIndex;\n        uint byteOffset;\n        if (buy)\n            byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset;\n        else\n            byteOffset = 4 + fieldOffset;\n\n        return int8(data[byteOffset]);\n    }\n\n    function executeStepFunction(StepFunction f, int x) internal pure returns(int) {\n        uint len = f.y.length;\n        for (uint ind = 0; ind \u003c len; ind++) {\n            if (x \u003c= f.x[ind]) return f.y[ind];\n        }\n\n        return f.y[len-1];\n    }\n\n    function addBps(uint rate, int bps) internal pure returns(uint) {\n        require(rate \u003c= MAX_RATE);\n        require(bps \u003e= MIN_BPS_ADJUSTMENT);\n        require(bps \u003c= MAX_BPS_ADJUSTMENT);\n\n        uint maxBps = 100 * 100;\n        return (rate * uint(int(maxBps) + bps)) / maxBps;\n    }\n\n    function abs(int x) internal pure returns(uint) {\n        if (x \u003c 0)\n            return uint(-1 * x);\n        else\n            return uint(x);\n    }\n}\n"},"ConversionRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ConversionRatesInterface {\n\n    function recordImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        public;\n\n    function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);\n}\n"},"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n    function totalSupply() public view returns (uint supply);\n    function balanceOf(address _owner) public view returns (uint balance);\n    function transfer(address _to, uint _value) public returns (bool success);\n    function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n    function approve(address _spender, uint _value) public returns (bool success);\n    function allowance(address _owner, address _spender) public view returns (uint remaining);\n    function decimals() public view returns(uint digits);\n    event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"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"},"VolumeImbalanceRecorder.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../ERC20Interface.sol\";\nimport \"../Withdrawable.sol\";\n\n\ncontract VolumeImbalanceRecorder is Withdrawable {\n\n    uint constant internal SLIDING_WINDOW_SIZE = 5;\n    uint constant internal POW_2_64 = 2 ** 64;\n\n    struct TokenControlInfo {\n        uint minimalRecordResolution; // can be roughly 1 cent\n        uint maxPerBlockImbalance; // in twei resolution\n        uint maxTotalImbalance; // max total imbalance (between rate updates)\n                            // before halting trade\n    }\n\n    mapping(address =\u003e TokenControlInfo) internal tokenControlInfo;\n\n    struct TokenImbalanceData {\n        int  lastBlockBuyUnitsImbalance;\n        uint lastBlock;\n\n        int  totalBuyUnitsImbalance;\n        uint lastRateUpdateBlock;\n    }\n\n    mapping(address =\u003e mapping(uint=\u003euint)) public tokenImbalanceData;\n\n    function VolumeImbalanceRecorder(address _admin) public {\n        require(_admin != address(0));\n        admin = _admin;\n    }\n\n    function setTokenControlInfo(\n        ERC20 token,\n        uint minimalRecordResolution,\n        uint maxPerBlockImbalance,\n        uint maxTotalImbalance\n    )\n        public\n        onlyAdmin\n    {\n        tokenControlInfo[token] =\n            TokenControlInfo(\n                minimalRecordResolution,\n                maxPerBlockImbalance,\n                maxTotalImbalance\n            );\n    }\n\n    function getTokenControlInfo(ERC20 token) public view returns(uint, uint, uint) {\n        return (tokenControlInfo[token].minimalRecordResolution,\n                tokenControlInfo[token].maxPerBlockImbalance,\n                tokenControlInfo[token].maxTotalImbalance);\n    }\n\n    function addImbalance(\n        ERC20 token,\n        int buyAmount,\n        uint rateUpdateBlock,\n        uint currentBlock\n    )\n        internal\n    {\n        uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE;\n        int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution));\n\n        int prevImbalance = 0;\n\n        TokenImbalanceData memory currentBlockData =\n            decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]);\n\n        // first scenario - this is not the first tx in the current block\n        if (currentBlockData.lastBlock == currentBlock) {\n            if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) {\n                // just increase imbalance\n                currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;\n                currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount;\n            } else {\n                // imbalance was changed in the middle of the block\n                prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock);\n                currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;\n                currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount;\n                currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);\n            }\n        } else {\n            // first tx in the current block\n            int currentBlockImbalance;\n            (prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock);\n\n            currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount;\n            currentBlockData.lastBlock = uint(currentBlock);\n            currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock);\n            currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount;\n        }\n\n        tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData);\n    }\n\n    function setGarbageToVolumeRecorder(ERC20 token) internal {\n        for (uint i = 0; i \u003c SLIDING_WINDOW_SIZE; i++) {\n            tokenImbalanceData[token][i] = 0x1;\n        }\n    }\n\n    function getImbalanceInRange(ERC20 token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) {\n        // check the imbalance in the sliding window\n        require(startBlock \u003c= endBlock);\n\n        buyImbalance = 0;\n\n        for (uint windowInd = 0; windowInd \u003c SLIDING_WINDOW_SIZE; windowInd++) {\n            TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);\n\n            if (perBlockData.lastBlock \u003c= endBlock \u0026\u0026 perBlockData.lastBlock \u003e= startBlock) {\n                buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance);\n            }\n        }\n    }\n\n    function getImbalanceSinceRateUpdate(ERC20 token, uint rateUpdateBlock, uint currentBlock)\n        internal view\n        returns(int buyImbalance, int currentBlockImbalance)\n    {\n        buyImbalance = 0;\n        currentBlockImbalance = 0;\n        uint latestBlock = 0;\n        int imbalanceInRange = 0;\n        uint startBlock = rateUpdateBlock;\n        uint endBlock = currentBlock;\n\n        for (uint windowInd = 0; windowInd \u003c SLIDING_WINDOW_SIZE; windowInd++) {\n            TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]);\n\n            if (perBlockData.lastBlock \u003c= endBlock \u0026\u0026 perBlockData.lastBlock \u003e= startBlock) {\n                imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance;\n            }\n\n            if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue;\n            if (perBlockData.lastBlock \u003c latestBlock) continue;\n\n            latestBlock = perBlockData.lastBlock;\n            buyImbalance = perBlockData.totalBuyUnitsImbalance;\n            if (uint(perBlockData.lastBlock) == currentBlock) {\n                currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance;\n            }\n        }\n\n        if (buyImbalance == 0) {\n            buyImbalance = imbalanceInRange;\n        }\n    }\n\n    function getImbalance(ERC20 token, uint rateUpdateBlock, uint currentBlock)\n        internal view\n        returns(int totalImbalance, int currentBlockImbalance)\n    {\n\n        int resolution = int(tokenControlInfo[token].minimalRecordResolution);\n\n        (totalImbalance, currentBlockImbalance) =\n            getImbalanceSinceRateUpdate(\n                token,\n                rateUpdateBlock,\n                currentBlock);\n\n        totalImbalance *= resolution;\n        currentBlockImbalance *= resolution;\n    }\n\n    function getMaxPerBlockImbalance(ERC20 token) internal view returns(uint) {\n        return tokenControlInfo[token].maxPerBlockImbalance;\n    }\n\n    function getMaxTotalImbalance(ERC20 token) internal view returns(uint) {\n        return tokenControlInfo[token].maxTotalImbalance;\n    }\n\n    function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) {\n        // check for overflows\n        require(data.lastBlockBuyUnitsImbalance \u003c int(POW_2_64 / 2));\n        require(data.lastBlockBuyUnitsImbalance \u003e int(-1 * int(POW_2_64) / 2));\n        require(data.lastBlock \u003c POW_2_64);\n        require(data.totalBuyUnitsImbalance \u003c int(POW_2_64 / 2));\n        require(data.totalBuyUnitsImbalance \u003e int(-1 * int(POW_2_64) / 2));\n        require(data.lastRateUpdateBlock \u003c POW_2_64);\n\n        // do encoding\n        uint result = uint(data.lastBlockBuyUnitsImbalance) \u0026 (POW_2_64 - 1);\n        result |= data.lastBlock * POW_2_64;\n        result |= (uint(data.totalBuyUnitsImbalance) \u0026 (POW_2_64 - 1)) * POW_2_64 * POW_2_64;\n        result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64;\n\n        return result;\n    }\n\n    function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) {\n        TokenImbalanceData memory data;\n\n        data.lastBlockBuyUnitsImbalance = int(int64(input \u0026 (POW_2_64 - 1)));\n        data.lastBlock = uint(uint64((input / POW_2_64) \u0026 (POW_2_64 - 1)));\n        data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) \u0026 (POW_2_64 - 1)));\n        data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64))));\n\n        return data;\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 13 of 13: SanityRates
                              {"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"},"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"},"SanityRates.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./Utils.sol\";\nimport \"./SanityRatesInterface.sol\";\n\n\ncontract SanityRates is SanityRatesInterface, Withdrawable, Utils {\n    mapping(address=\u003euint) public tokenRate;\n    mapping(address=\u003euint) public reasonableDiffInBps;\n\n    function SanityRates(address _admin) public {\n        require(_admin != address(0));\n        admin = _admin;\n    }\n\n    function setReasonableDiff(ERC20[] srcs, uint[] diff) public onlyAdmin {\n        require(srcs.length == diff.length);\n        for (uint i = 0; i \u003c srcs.length; i++) {\n            require(diff[i] \u003c= 100 * 100);\n            reasonableDiffInBps[srcs[i]] = diff[i];\n        }\n    }\n\n    function setSanityRates(ERC20[] srcs, uint[] rates) public onlyOperator {\n        require(srcs.length == rates.length);\n\n        for (uint i = 0; i \u003c srcs.length; i++) {\n            require(rates[i] \u003c= MAX_RATE);\n            tokenRate[srcs[i]] = rates[i];\n        }\n    }\n\n    function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint) {\n        if (src != ETH_TOKEN_ADDRESS \u0026\u0026 dest != ETH_TOKEN_ADDRESS) return 0;\n\n        uint rate;\n        address token;\n        if (src == ETH_TOKEN_ADDRESS) {\n            rate = (PRECISION*PRECISION)/tokenRate[dest];\n            token = dest;\n        } else {\n            rate = tokenRate[src];\n            token = src;\n        }\n\n        return rate * (10000 + reasonableDiffInBps[token])/10000;\n    }\n}\n"},"SanityRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\ninterface SanityRatesInterface {\n    function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n    ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n    uint  constant internal PRECISION = (10**18);\n    uint  constant internal MAX_QTY   = (10**28); // 10B tokens\n    uint  constant internal MAX_RATE  = (PRECISION * 10**6); // up to 1M tokens per ETH\n    uint  constant internal MAX_DECIMALS = 18;\n    uint  constant internal ETH_DECIMALS = 18;\n    mapping(address=\u003euint) internal decimals;\n\n    function setDecimals(ERC20 token) internal {\n        if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n        else decimals[token] = token.decimals();\n    }\n\n    function getDecimals(ERC20 token) internal view returns(uint) {\n        if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n        uint tokenDecimals = decimals[token];\n        // technically, there might be token with decimals 0\n        // moreover, very possible that old tokens have decimals 0\n        // these tokens will just have higher gas fees.\n        if(tokenDecimals == 0) return token.decimals();\n\n        return tokenDecimals;\n    }\n\n    function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(srcQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n\n        if (dstDecimals \u003e= srcDecimals) {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n        } else {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n        }\n    }\n\n    function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n        require(dstQty \u003c= MAX_QTY);\n        require(rate \u003c= MAX_RATE);\n        \n        //source quantity is rounded up. to avoid dest quantity being too low.\n        uint numerator;\n        uint denominator;\n        if (srcDecimals \u003e= dstDecimals) {\n            require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n            denominator = rate;\n        } else {\n            require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n            numerator = (PRECISION * dstQty);\n            denominator = (rate * (10**(dstDecimals - srcDecimals)));\n        }\n        return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n    }\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n    event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw all ERC20 compatible tokens\n     * @param token ERC20 The address of the token contract\n     */\n    function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n        require(token.transfer(sendTo, amount));\n        TokenWithdraw(token, amount, sendTo);\n    }\n\n    event EtherWithdraw(uint amount, address sendTo);\n\n    /**\n     * @dev Withdraw Ethers\n     */\n    function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n        sendTo.transfer(amount);\n        EtherWithdraw(amount, sendTo);\n    }\n}\n"}}