ETH Price: $2,077.33 (-2.04%)

Transaction Decoder

Block:
9631796 at Mar-08-2020 04:21:35 PM +UTC
Transaction Fee:
0.002176299999891185 ETH $4.52
Gas Used:
108,815 Gas / 19.999999999 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x0f3c5696...F2Cc4B45e
0.171956514199110471 Eth
Nonce: 15994
0.169780214199219286 Eth
Nonce: 15995
0.002176299999891185
(Spark Pool)
77.283200293413284472 Eth77.285376593413175657 Eth0.002176299999891185

Execution Trace

MEV Bot: 0x860...F66.c89e4361( )
  • 0xd0e863512d438d3c48536e5b7d65c51a7890f9f0.689c49c0( )
    • KyberReserve.getConversionRate( src=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, dest=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, srcQty=3007833657413099049, blockNumber=9631796 ) => ( 7558627906896342358847 )
      • ConversionRates.getRate( token=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03, currentBlockNumber=9631796, buy=True, qty=3007833657413099049 ) => ( 7558627906896342358847 )
      • EthLendToken.balanceOf( _owner=0x63825c174ab367968EC60f061753D3bbD36A0D8F ) => ( 64627119821967801550039 )
      • SanityRates.getSanityRate( src=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, dest=0x80fB784B7eD66730e8b1DBd9820aFD29931aab03 ) => ( 8461538461538461538461 )
      • EthLendToken.STATICCALL( )
      • Vyper_contract.getTokenToEthInputPrice( tokens_sold=22735095422224742957218 ) => ( out=2959843449324895016 )
        • Vyper_contract.getTokenToEthInputPrice( tokens_sold=22735095422224742957218 ) => ( out=2959843449324895016 )
          • EthLendToken.balanceOf( _owner=0xcaA7e4656f6A2B59f5f99c745F91AB26D1210DCe ) => ( 706142536958663779141099 )
            File 1 of 6: 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 2 of 6: 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 3 of 6: EthLendToken
            pragma solidity ^0.4.16;
            
            contract SafeMath {
                 function safeMul(uint a, uint b) internal returns (uint) {
                      uint c = a * b;
                      assert(a == 0 || c / a == b);
                      return c;
                 }
            
                 function safeSub(uint a, uint b) internal returns (uint) {
                      assert(b <= a);
                      return a - b;
                 }
            
                 function safeAdd(uint a, uint b) internal returns (uint) {
                      uint c = a + b;
                      assert(c>=a && c>=b);
                      return c;
                 }
            }
            
            // Standard token interface (ERC 20)
            // https://github.com/ethereum/EIPs/issues/20
            contract Token is SafeMath {
                 // Functions:
                 /// @return total amount of tokens
                 function totalSupply() constant returns (uint256 supply);
            
                 /// @param _owner The address from which the balance will be retrieved
                 /// @return The balance
                 function balanceOf(address _owner) constant returns (uint256 balance);
            
                 /// @notice send `_value` token to `_to` from `msg.sender`
                 /// @param _to The address of the recipient
                 /// @param _value The amount of token to be transferred
                 function transfer(address _to, uint256 _value) returns(bool);
            
                 /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
                 /// @param _from The address of the sender
                 /// @param _to The address of the recipient
                 /// @param _value The amount of token to be transferred
                 /// @return Whether the transfer was successful or not
                 function transferFrom(address _from, address _to, uint256 _value) returns(bool);
            
                 /// @notice `msg.sender` approves `_addr` to spend `_value` tokens
                 /// @param _spender The address of the account able to transfer the tokens
                 /// @param _value The amount of wei to be approved for transfer
                 /// @return Whether the approval was successful or not
                 function approve(address _spender, uint256 _value) returns (bool success);
            
                 /// @param _owner The address of the account owning tokens
                 /// @param _spender The address of the account able to transfer the tokens
                 /// @return Amount of remaining tokens allowed to spent
                 function allowance(address _owner, address _spender) constant returns (uint256 remaining);
            
                 // Events:
                 event Transfer(address indexed _from, address indexed _to, uint256 _value);
                 event Approval(address indexed _owner, address indexed _spender, uint256 _value);
            }
            
            contract StdToken is Token {
                 // Fields:
                 mapping(address => uint256) balances;
                 mapping (address => mapping (address => uint256)) allowed;
                 uint public supply = 0;
            
                 // Functions:
                 function transfer(address _to, uint256 _value) returns(bool) {
                      require(balances[msg.sender] >= _value);
                      require(balances[_to] + _value > balances[_to]);
            
                      balances[msg.sender] = safeSub(balances[msg.sender],_value);
                      balances[_to] = safeAdd(balances[_to],_value);
            
                      Transfer(msg.sender, _to, _value);
                      return true;
                 }
            
                 function transferFrom(address _from, address _to, uint256 _value) returns(bool){
                      require(balances[_from] >= _value);
                      require(allowed[_from][msg.sender] >= _value);
                      require(balances[_to] + _value > balances[_to]);
            
                      balances[_to] = safeAdd(balances[_to],_value);
                      balances[_from] = safeSub(balances[_from],_value);
                      allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender],_value);
            
                      Transfer(_from, _to, _value);
                      return true;
                 }
            
                 function totalSupply() constant returns (uint256) {
                      return supply;
                 }
            
                 function balanceOf(address _owner) constant returns (uint256) {
                      return balances[_owner];
                 }
            
                 function approve(address _spender, uint256 _value) returns (bool) {
                      // To change the approve amount you first have to reduce the addresses`
                      //  allowance to zero by calling `approve(_spender, 0)` if it is not
                      //  already 0 to mitigate the race condition described here:
                      //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                      require((_value == 0) || (allowed[msg.sender][_spender] == 0));
            
                      allowed[msg.sender][_spender] = _value;
                      Approval(msg.sender, _spender, _value);
            
                      return true;
                 }
            
                 function allowance(address _owner, address _spender) constant returns (uint256) {
                      return allowed[_owner][_spender];
                 }
            }
            
            contract EthLendToken is StdToken
            {
            /// Fields:
                string public constant name = "EthLend Token";
                string public constant symbol = "LEND";
                uint public constant decimals = 18;
            
                // this includes DEVELOPERS_BONUS
                uint public constant TOTAL_SUPPLY = 1300000000 * (1 ether / 1 wei);
                uint public constant DEVELOPERS_BONUS = 300000000 * (1 ether / 1 wei);
            
                uint public constant PRESALE_PRICE = 30000;  // per 1 Ether
                uint public constant PRESALE_MAX_ETH = 2000;
                // 60 mln tokens sold during presale
                uint public constant PRESALE_TOKEN_SUPPLY_LIMIT = PRESALE_PRICE * PRESALE_MAX_ETH * (1 ether / 1 wei);
            
                uint public constant ICO_PRICE1 = 27500;     // per 1 Ether
                uint public constant ICO_PRICE2 = 26250;     // per 1 Ether
                uint public constant ICO_PRICE3 = 25000;     // per 1 Ether
            
                // 1bln - this includes presale tokens
                uint public constant TOTAL_SOLD_TOKEN_SUPPLY_LIMIT = 1000000000* (1 ether / 1 wei);
            
                enum State{
                   Init,
                   Paused,
            
                   PresaleRunning,
                   PresaleFinished,
            
                   ICORunning,
                   ICOFinished
                }
            
                State public currentState = State.Init;
                bool public enableTransfers = false;
            
                address public teamTokenBonus = 0;
            
                // Gathered funds can be withdrawn only to escrow's address.
                address public escrow = 0;
            
                // Token manager has exclusive priveleges to call administrative
                // functions on this contract.
                address public tokenManager = 0;
            
                uint public presaleSoldTokens = 0;
                uint public icoSoldTokens = 0;
                uint public totalSoldTokens = 0;
            
            /// Modifiers:
                modifier onlyTokenManager()
                {
                    require(msg.sender==tokenManager); 
                    _; 
                }
            
                modifier onlyInState(State state)
                {
                    require(state==currentState); 
                    _; 
                }
            
            /// Events:
                event LogBuy(address indexed owner, uint value);
                event LogBurn(address indexed owner, uint value);
            
            /// Functions:
                /// @dev Constructor
                /// @param _tokenManager Token manager address.
                function EthLendToken(address _tokenManager, address _escrow, address _teamTokenBonus) 
                {
                    tokenManager = _tokenManager;
                    teamTokenBonus = _teamTokenBonus;
                    escrow = _escrow;
            
                    // send team bonus immediately
                    uint teamBonus = DEVELOPERS_BONUS;
                    balances[_teamTokenBonus] += teamBonus;
                    supply+= teamBonus;
            
                    assert(PRESALE_TOKEN_SUPPLY_LIMIT==60000000 * (1 ether / 1 wei));
                    assert(TOTAL_SOLD_TOKEN_SUPPLY_LIMIT==1000000000 * (1 ether / 1 wei));
                }
            
                function buyTokens() public payable
                {
                    require(currentState==State.PresaleRunning || currentState==State.ICORunning);
            
                    if(currentState==State.PresaleRunning){
                        return buyTokensPresale();
                    }else{
                        return buyTokensICO();
                    }
                }
            
                function buyTokensPresale() public payable onlyInState(State.PresaleRunning)
                {
                    // min - 1 ETH
                    require(msg.value >= (1 ether / 1 wei));
                    uint newTokens = msg.value * PRESALE_PRICE;
            
                    require(presaleSoldTokens + newTokens <= PRESALE_TOKEN_SUPPLY_LIMIT);
            
                    balances[msg.sender] += newTokens;
                    supply+= newTokens;
                    presaleSoldTokens+= newTokens;
                    totalSoldTokens+= newTokens;
            
                    LogBuy(msg.sender, newTokens);
                }
            
                function buyTokensICO() public payable onlyInState(State.ICORunning)
                {
                    // min - 0.01 ETH
                    require(msg.value >= ((1 ether / 1 wei) / 100));
                    uint newTokens = msg.value * getPrice();
            
                    require(totalSoldTokens + newTokens <= TOTAL_SOLD_TOKEN_SUPPLY_LIMIT);
            
                    balances[msg.sender] += newTokens;
                    supply+= newTokens;
                    icoSoldTokens+= newTokens;
                    totalSoldTokens+= newTokens;
            
                    LogBuy(msg.sender, newTokens);
                }
            
                function getPrice()constant returns(uint)
                {
                    if(currentState==State.ICORunning){
                         if(icoSoldTokens<(200000000 * (1 ether / 1 wei))){
                              return ICO_PRICE1;
                         }
                         
                         if(icoSoldTokens<(300000000 * (1 ether / 1 wei))){
                              return ICO_PRICE2;
                         }
            
                         return ICO_PRICE3;
                    }else{
                         return PRESALE_PRICE;
                    }
                }
            
                function setState(State _nextState) public onlyTokenManager
                {
                    //setState() method call shouldn't be entertained after ICOFinished
                    require(currentState != State.ICOFinished);
                    
                    currentState = _nextState;
                    // enable/disable transfers
                    //enable transfers only after ICOFinished, disable otherwise
                    enableTransfers = (currentState==State.ICOFinished);
                }
            
                function withdrawEther() public onlyTokenManager
                {
                    if(this.balance > 0) 
                    {
                        require(escrow.send(this.balance));
                    }
                }
            
            /// Overrides:
                function transfer(address _to, uint256 _value) returns(bool){
                    require(enableTransfers);
                    return super.transfer(_to,_value);
                }
            
                function transferFrom(address _from, address _to, uint256 _value) returns(bool){
                    require(enableTransfers);
                    return super.transferFrom(_from,_to,_value);
                }
            
                function approve(address _spender, uint256 _value) returns (bool) {
                    require(enableTransfers);
                    return super.approve(_spender,_value);
                }
            
            /// Setters/getters
                function setTokenManager(address _mgr) public onlyTokenManager
                {
                    tokenManager = _mgr;
                }
            
                // Default fallback function
                function() payable 
                {
                    buyTokens();
                }
            }

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

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