ETH Price: $1,972.73 (-4.90%)

Transaction Decoder

Block:
11394199 at Dec-05-2020 06:15:07 PM +UTC
Transaction Fee:
0.001586897164426943 ETH $3.13
Gas Used:
122,069 Gas / 13.000001347 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x00000000...0A3f67E9a
(MEV Bot: 0x000...E9a)
0x0000002C...8481A9dE0
0.575686084890956348 Eth
Nonce: 44055
0.574099187726529405 Eth
Nonce: 44056
0.001586897164426943
(Babel Pool)
2,938.49686084542175286 Eth2,938.498447742586179803 Eth0.001586897164426943

Execution Trace

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

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

            File 3 of 5: UniBrightToken
            pragma solidity 0.4.15;
            
            contract RegistryICAPInterface {
                function parse(bytes32 _icap) constant returns(address, bytes32, bool);
                function institutions(bytes32 _institution) constant returns(address);
            }
            
            contract EToken2Interface {
                function registryICAP() constant returns(RegistryICAPInterface);
                function baseUnit(bytes32 _symbol) constant returns(uint8);
                function description(bytes32 _symbol) constant returns(string);
                function owner(bytes32 _symbol) constant returns(address);
                function isOwner(address _owner, bytes32 _symbol) constant returns(bool);
                function totalSupply(bytes32 _symbol) constant returns(uint);
                function balanceOf(address _holder, bytes32 _symbol) constant returns(uint);
                function isLocked(bytes32 _symbol) constant returns(bool);
                function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) returns(bool);
                function reissueAsset(bytes32 _symbol, uint _value) returns(bool);
                function revokeAsset(bytes32 _symbol, uint _value) returns(bool);
                function setProxy(address _address, bytes32 _symbol) returns(bool);
                function lockAsset(bytes32 _symbol) returns(bool);
                function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
                function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) returns(bool);
                function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint);
                function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(bool);
            }
            
            contract AssetInterface {
                function _performTransferWithReference(address _to, uint _value, string _reference, address _sender) returns(bool);
                function _performTransferToICAPWithReference(bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
                function _performApprove(address _spender, uint _value, address _sender) returns(bool);    
                function _performTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
                function _performTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
                function _performGeneric(bytes, address) payable {
                    revert();
                }
            }
            
            contract ERC20Interface {
                event Transfer(address indexed from, address indexed to, uint256 value);
                event Approval(address indexed from, address indexed spender, uint256 value);
            
                function totalSupply() constant returns(uint256 supply);
                function balanceOf(address _owner) constant returns(uint256 balance);
                function transfer(address _to, uint256 _value) returns(bool success);
                function transferFrom(address _from, address _to, uint256 _value) returns(bool success);
                function approve(address _spender, uint256 _value) returns(bool success);
                function allowance(address _owner, address _spender) constant returns(uint256 remaining);
            
                // function symbol() constant returns(string);
                function decimals() constant returns(uint8);
                // function name() constant returns(string);
            }
            
            contract AssetProxyInterface {
                function _forwardApprove(address _spender, uint _value, address _sender) returns(bool);
                function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
                function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
                function balanceOf(address _owner) constant returns(uint);
            }
            
            contract Bytes32 {
                function _bytes32(string _input) internal constant returns(bytes32 result) {
                    assembly {
                        result := mload(add(_input, 32))
                    }
                }
            }
            
            contract ReturnData {
                function _returnReturnData(bool _success) internal {
                    assembly {
                        let returndatastart := msize()
                        mstore(0x40, add(returndatastart, returndatasize))
                        returndatacopy(returndatastart, 0, returndatasize)
                        switch _success case 0 { revert(returndatastart, returndatasize) } default { return(returndatastart, returndatasize) }
                    }
                }
            
                function _assemblyCall(address _destination, uint _value, bytes _data) internal returns(bool success) {
                    assembly {
                        success := call(div(mul(gas, 63), 64), _destination, _value, add(_data, 32), mload(_data), 0, 0)
                    }
                }
            }
            
            /**
             * @title EToken2 Asset Proxy.
             *
             * Proxy implements ERC20 interface and acts as a gateway to a single EToken2 asset.
             * Proxy adds etoken2Symbol and caller(sender) when forwarding requests to EToken2.
             * Every request that is made by caller first sent to the specific asset implementation
             * contract, which then calls back to be forwarded onto EToken2.
             *
             * Calls flow: Caller ->
             *             Proxy.func(...) ->
             *             Asset._performFunc(..., Caller.address) ->
             *             Proxy._forwardFunc(..., Caller.address) ->
             *             Platform.proxyFunc(..., symbol, Caller.address)
             *
             * Generic call flow: Caller ->
             *             Proxy.unknownFunc(...) ->
             *             Asset._performGeneric(..., Caller.address) ->
             *             Asset.unknownFunc(...)
             *
             * Asset implementation contract is mutable, but each user have an option to stick with
             * old implementation, through explicit decision made in timely manner, if he doesn't agree
             * with new rules.
             * Each user have a possibility to upgrade to latest asset contract implementation, without the
             * possibility to rollback.
             *
             * Note: all the non constant functions return false instead of throwing in case if state change
             * didn't happen yet.
             */
            contract AssetProxy is ERC20Interface, AssetProxyInterface, Bytes32, ReturnData {
                // Assigned EToken2, immutable.
                EToken2Interface public etoken2;
            
                // Assigned symbol, immutable.
                bytes32 public etoken2Symbol;
            
                // Assigned name, immutable. For UI.
                string public name;
                string public symbol;
            
                /**
                 * Sets EToken2 address, assigns symbol and name.
                 *
                 * Can be set only once.
                 *
                 * @param _etoken2 EToken2 contract address.
                 * @param _symbol assigned symbol.
                 * @param _name assigned name.
                 *
                 * @return success.
                 */
                function init(EToken2Interface _etoken2, string _symbol, string _name) returns(bool) {
                    if (address(etoken2) != 0x0) {
                        return false;
                    }
                    etoken2 = _etoken2;
                    etoken2Symbol = _bytes32(_symbol);
                    name = _name;
                    symbol = _symbol;
                    return true;
                }
            
                /**
                 * Only EToken2 is allowed to call.
                 */
                modifier onlyEToken2() {
                    if (msg.sender == address(etoken2)) {
                        _;
                    }
                }
            
                /**
                 * Only current asset owner is allowed to call.
                 */
                modifier onlyAssetOwner() {
                    if (etoken2.isOwner(msg.sender, etoken2Symbol)) {
                        _;
                    }
                }
            
                /**
                 * Returns asset implementation contract for current caller.
                 *
                 * @return asset implementation contract.
                 */
                function _getAsset() internal returns(AssetInterface) {
                    return AssetInterface(getVersionFor(msg.sender));
                }
            
                function recoverTokens(uint _value) onlyAssetOwner() returns(bool) {
                    return this.transferWithReference(msg.sender, _value, 'Tokens recovery');
                }
            
                /**
                 * Returns asset total supply.
                 *
                 * @return asset total supply.
                 */
                function totalSupply() constant returns(uint) {
                    return etoken2.totalSupply(etoken2Symbol);
                }
            
                /**
                 * Returns asset balance for a particular holder.
                 *
                 * @param _owner holder address.
                 *
                 * @return holder balance.
                 */
                function balanceOf(address _owner) constant returns(uint) {
                    return etoken2.balanceOf(_owner, etoken2Symbol);
                }
            
                /**
                 * Returns asset allowance from one holder to another.
                 *
                 * @param _from holder that allowed spending.
                 * @param _spender holder that is allowed to spend.
                 *
                 * @return holder to spender allowance.
                 */
                function allowance(address _from, address _spender) constant returns(uint) {
                    return etoken2.allowance(_from, _spender, etoken2Symbol);
                }
            
                /**
                 * Returns asset decimals.
                 *
                 * @return asset decimals.
                 */
                function decimals() constant returns(uint8) {
                    return etoken2.baseUnit(etoken2Symbol);
                }
            
                /**
                 * Transfers asset balance from the caller to specified receiver.
                 *
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 *
                 * @return success.
                 */
                function transfer(address _to, uint _value) returns(bool) {
                    return transferWithReference(_to, _value, '');
                }
            
                /**
                 * Transfers asset balance from the caller to specified receiver adding specified comment.
                 * Resolves asset implementation contract for the caller and forwards there arguments along with
                 * the caller address.
                 *
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 *
                 * @return success.
                 */
                function transferWithReference(address _to, uint _value, string _reference) returns(bool) {
                    return _getAsset()._performTransferWithReference(_to, _value, _reference, msg.sender);
                }
            
                /**
                 * Transfers asset balance from the caller to specified ICAP.
                 *
                 * @param _icap recipient ICAP to give to.
                 * @param _value amount to transfer.
                 *
                 * @return success.
                 */
                function transferToICAP(bytes32 _icap, uint _value) returns(bool) {
                    return transferToICAPWithReference(_icap, _value, '');
                }
            
                /**
                 * Transfers asset balance from the caller to specified ICAP adding specified comment.
                 * Resolves asset implementation contract for the caller and forwards there arguments along with
                 * the caller address.
                 *
                 * @param _icap recipient ICAP to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 *
                 * @return success.
                 */
                function transferToICAPWithReference(bytes32 _icap, uint _value, string _reference) returns(bool) {
                    return _getAsset()._performTransferToICAPWithReference(_icap, _value, _reference, msg.sender);
                }
            
                /**
                 * Prforms allowance transfer of asset balance between holders.
                 *
                 * @param _from holder address to take from.
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 *
                 * @return success.
                 */
                function transferFrom(address _from, address _to, uint _value) returns(bool) {
                    return transferFromWithReference(_from, _to, _value, '');
                }
            
                /**
                 * Prforms allowance transfer of asset balance between holders adding specified comment.
                 * Resolves asset implementation contract for the caller and forwards there arguments along with
                 * the caller address.
                 *
                 * @param _from holder address to take from.
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 *
                 * @return success.
                 */
                function transferFromWithReference(address _from, address _to, uint _value, string _reference) returns(bool) {
                    return _getAsset()._performTransferFromWithReference(_from, _to, _value, _reference, msg.sender);
                }
            
                /**
                 * Performs transfer call on the EToken2 by the name of specified sender.
                 *
                 * Can only be called by asset implementation contract assigned to sender.
                 *
                 * @param _from holder address to take from.
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 * @param _sender initial caller.
                 *
                 * @return success.
                 */
                function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
                    return etoken2.proxyTransferFromWithReference(_from, _to, _value, etoken2Symbol, _reference, _sender);
                }
            
                /**
                 * Prforms allowance transfer of asset balance between holders.
                 *
                 * @param _from holder address to take from.
                 * @param _icap recipient ICAP address to give to.
                 * @param _value amount to transfer.
                 *
                 * @return success.
                 */
                function transferFromToICAP(address _from, bytes32 _icap, uint _value) returns(bool) {
                    return transferFromToICAPWithReference(_from, _icap, _value, '');
                }
            
                /**
                 * Prforms allowance transfer of asset balance between holders adding specified comment.
                 * Resolves asset implementation contract for the caller and forwards there arguments along with
                 * the caller address.
                 *
                 * @param _from holder address to take from.
                 * @param _icap recipient ICAP address to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 *
                 * @return success.
                 */
                function transferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference) returns(bool) {
                    return _getAsset()._performTransferFromToICAPWithReference(_from, _icap, _value, _reference, msg.sender);
                }
            
                /**
                 * Performs allowance transfer to ICAP call on the EToken2 by the name of specified sender.
                 *
                 * Can only be called by asset implementation contract assigned to sender.
                 *
                 * @param _from holder address to take from.
                 * @param _icap recipient ICAP address to give to.
                 * @param _value amount to transfer.
                 * @param _reference transfer comment to be included in a EToken2's Transfer event.
                 * @param _sender initial caller.
                 *
                 * @return success.
                 */
                function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
                    return etoken2.proxyTransferFromToICAPWithReference(_from, _icap, _value, _reference, _sender);
                }
            
                /**
                 * Sets asset spending allowance for a specified spender.
                 * Resolves asset implementation contract for the caller and forwards there arguments along with
                 * the caller address.
                 *
                 * @param _spender holder address to set allowance to.
                 * @param _value amount to allow.
                 *
                 * @return success.
                 */
                function approve(address _spender, uint _value) returns(bool) {
                    return _getAsset()._performApprove(_spender, _value, msg.sender);
                }
            
                /**
                 * Performs allowance setting call on the EToken2 by the name of specified sender.
                 *
                 * Can only be called by asset implementation contract assigned to sender.
                 *
                 * @param _spender holder address to set allowance to.
                 * @param _value amount to allow.
                 * @param _sender initial caller.
                 *
                 * @return success.
                 */
                function _forwardApprove(address _spender, uint _value, address _sender) onlyImplementationFor(_sender) returns(bool) {
                    return etoken2.proxyApprove(_spender, _value, etoken2Symbol, _sender);
                }
            
                /**
                 * Emits ERC20 Transfer event on this contract.
                 *
                 * Can only be, and, called by assigned EToken2 when asset transfer happens.
                 */
                function emitTransfer(address _from, address _to, uint _value) onlyEToken2() {
                    Transfer(_from, _to, _value);
                }
            
                /**
                 * Emits ERC20 Approval event on this contract.
                 *
                 * Can only be, and, called by assigned EToken2 when asset allowance set happens.
                 */
                function emitApprove(address _from, address _spender, uint _value) onlyEToken2() {
                    Approval(_from, _spender, _value);
                }
            
                /**
                 * Resolves asset implementation contract for the caller and forwards there transaction data,
                 * along with the value. This allows for proxy interface growth.
                 */
                function () payable {
                    _getAsset()._performGeneric.value(msg.value)(msg.data, msg.sender);
                    _returnReturnData(true);
                }
            
                // Interface functions to allow specifying ICAP addresses as strings.
                function transferToICAP(string _icap, uint _value) returns(bool) {
                    return transferToICAPWithReference(_icap, _value, '');
                }
            
                function transferToICAPWithReference(string _icap, uint _value, string _reference) returns(bool) {
                    return transferToICAPWithReference(_bytes32(_icap), _value, _reference);
                }
            
                function transferFromToICAP(address _from, string _icap, uint _value) returns(bool) {
                    return transferFromToICAPWithReference(_from, _icap, _value, '');
                }
            
                function transferFromToICAPWithReference(address _from, string _icap, uint _value, string _reference) returns(bool) {
                    return transferFromToICAPWithReference(_from, _bytes32(_icap), _value, _reference);
                }
            
                /**
                 * Indicates an upgrade freeze-time start, and the next asset implementation contract.
                 */
                event UpgradeProposal(address newVersion);
            
                // Current asset implementation contract address.
                address latestVersion;
            
                // Proposed next asset implementation contract address.
                address pendingVersion;
            
                // Upgrade freeze-time start.
                uint pendingVersionTimestamp;
            
                // Timespan for users to review the new implementation and make decision.
                uint constant UPGRADE_FREEZE_TIME = 3 days;
            
                // Asset implementation contract address that user decided to stick with.
                // 0x0 means that user uses latest version.
                mapping(address => address) userOptOutVersion;
            
                /**
                 * Only asset implementation contract assigned to sender is allowed to call.
                 */
                modifier onlyImplementationFor(address _sender) {
                    if (getVersionFor(_sender) == msg.sender) {
                        _;
                    }
                }
            
                /**
                 * Returns asset implementation contract address assigned to sender.
                 *
                 * @param _sender sender address.
                 *
                 * @return asset implementation contract address.
                 */
                function getVersionFor(address _sender) constant returns(address) {
                    return userOptOutVersion[_sender] == 0 ? latestVersion : userOptOutVersion[_sender];
                }
            
                /**
                 * Returns current asset implementation contract address.
                 *
                 * @return asset implementation contract address.
                 */
                function getLatestVersion() constant returns(address) {
                    return latestVersion;
                }
            
                /**
                 * Returns proposed next asset implementation contract address.
                 *
                 * @return asset implementation contract address.
                 */
                function getPendingVersion() constant returns(address) {
                    return pendingVersion;
                }
            
                /**
                 * Returns upgrade freeze-time start.
                 *
                 * @return freeze-time start.
                 */
                function getPendingVersionTimestamp() constant returns(uint) {
                    return pendingVersionTimestamp;
                }
            
                /**
                 * Propose next asset implementation contract address.
                 *
                 * Can only be called by current asset owner.
                 *
                 * Note: freeze-time should not be applied for the initial setup.
                 *
                 * @param _newVersion asset implementation contract address.
                 *
                 * @return success.
                 */
                function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
                    // Should not already be in the upgrading process.
                    if (pendingVersion != 0x0) {
                        return false;
                    }
                    // New version address should be other than 0x0.
                    if (_newVersion == 0x0) {
                        return false;
                    }
                    // Don't apply freeze-time for the initial setup.
                    if (latestVersion == 0x0) {
                        latestVersion = _newVersion;
                        return true;
                    }
                    pendingVersion = _newVersion;
                    pendingVersionTimestamp = now;
                    UpgradeProposal(_newVersion);
                    return true;
                }
            
                /**
                 * Cancel the pending upgrade process.
                 *
                 * Can only be called by current asset owner.
                 *
                 * @return success.
                 */
                function purgeUpgrade() onlyAssetOwner() returns(bool) {
                    if (pendingVersion == 0x0) {
                        return false;
                    }
                    delete pendingVersion;
                    delete pendingVersionTimestamp;
                    return true;
                }
            
                /**
                 * Finalize an upgrade process setting new asset implementation contract address.
                 *
                 * Can only be called after an upgrade freeze-time.
                 *
                 * @return success.
                 */
                function commitUpgrade() returns(bool) {
                    if (pendingVersion == 0x0) {
                        return false;
                    }
                    if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
                        return false;
                    }
                    latestVersion = pendingVersion;
                    delete pendingVersion;
                    delete pendingVersionTimestamp;
                    return true;
                }
            
                /**
                 * Disagree with proposed upgrade, and stick with current asset implementation
                 * until further explicit agreement to upgrade.
                 *
                 * @return success.
                 */
                function optOut() returns(bool) {
                    if (userOptOutVersion[msg.sender] != 0x0) {
                        return false;
                    }
                    userOptOutVersion[msg.sender] = latestVersion;
                    return true;
                }
            
                /**
                 * Implicitly agree to upgrade to current and future asset implementation upgrades,
                 * until further explicit disagreement.
                 *
                 * @return success.
                 */
                function optIn() returns(bool) {
                    delete userOptOutVersion[msg.sender];
                    return true;
                }
            
                // Backwards compatibility.
                function multiAsset() constant returns(EToken2Interface) {
                    return etoken2;
                }
            }
            
            contract UniBrightToken is AssetProxy {
                function change(string _symbol, string _name) onlyAssetOwner() returns(bool) {
                    if (etoken2.isLocked(etoken2Symbol)) {
                        return false;
                    }
                    name = _name;
                    symbol = _symbol;
                    return true;
                }
            }

            File 4 of 5: EToken2
            // This software is a subject to Ambisafe License Agreement.
            // No use or distribution is allowed without written permission from Ambisafe.
            // https://ambisafe.com/terms.pdf
            
            pragma solidity 0.4.8;
            
            contract Ambi2 {
                function claimFor(address _address, address _owner) returns(bool);
                function hasRole(address _from, bytes32 _role, address _to) constant returns(bool);
                function isOwner(address _node, address _owner) constant returns(bool);
            }
            
            contract Ambi2Enabled {
                Ambi2 ambi2;
            
                modifier onlyRole(bytes32 _role) {
                    if (address(ambi2) != 0x0 && ambi2.hasRole(this, _role, msg.sender)) {
                        _;
                    }
                }
            
                // Perform only after claiming the node, or claim in the same tx.
                function setupAmbi2(Ambi2 _ambi2) returns(bool) {
                    if (address(ambi2) != 0x0) {
                        return false;
                    }
            
                    ambi2 = _ambi2;
                    return true;
                }
            }
            
            contract Ambi2EnabledFull is Ambi2Enabled {
                // Setup and claim atomically.
                function setupAmbi2(Ambi2 _ambi2) returns(bool) {
                    if (address(ambi2) != 0x0) {
                        return false;
                    }
                    if (!_ambi2.claimFor(this, msg.sender) && !_ambi2.isOwner(this, msg.sender)) {
                        return false;
                    }
            
                    ambi2 = _ambi2;
                    return true;
                }
            }
            
            contract RegistryICAPInterface {
                function parse(bytes32 _icap) constant returns(address, bytes32, bool);
                function institutions(bytes32 _institution) constant returns(address);
            }
            
            contract Cosigner {
                function consumeOperation(bytes32 _opHash, uint _required) returns(bool);
            }
            
            contract Emitter {
                function emitTransfer(address _from, address _to, bytes32 _symbol, uint _value, string _reference);
                function emitTransferToICAP(address _from, address _to, bytes32 _icap, uint _value, string _reference);
                function emitIssue(bytes32 _symbol, uint _value, address _by);
                function emitRevoke(bytes32 _symbol, uint _value, address _by);
                function emitOwnershipChange(address _from, address _to, bytes32 _symbol);
                function emitApprove(address _from, address _spender, bytes32 _symbol, uint _value);
                function emitRecovery(address _from, address _to, address _by);
                function emitError(bytes32 _message);
                function emitChange(bytes32 _symbol);
            }
            
            contract Proxy {
                function emitTransfer(address _from, address _to, uint _value);
                function emitApprove(address _from, address _spender, uint _value);
            }
            
            /**
             * @title EToken2.
             *
             * The official Ambisafe assets platform powering all kinds of tokens.
             * EToken2 uses EventsHistory contract to keep events, so that in case it needs to be redeployed
             * at some point, all the events keep appearing at the same place.
             *
             * Every asset is meant to be used through a proxy contract. Only one proxy contract have access
             * rights for a particular asset.
             *
             * Features: assets issuance, transfers, allowances, supply adjustments, lost wallet access recovery.
             *           cosignature check, ICAP.
             *
             * Note: all the non constant functions return false instead of throwing in case if state change
             * didn't happen yet.
             */
            contract EToken2 is Ambi2EnabledFull {
                mapping(bytes32 => bool) switches;
            
                function isEnabled(bytes32 _switch) constant returns(bool) {
                    return switches[_switch];
                }
            
                function enableSwitch(bytes32 _switch) onlyRole('issuance') returns(bool) {
                    switches[_switch] = true;
                    return true;
                }
            
                modifier checkEnabledSwitch(bytes32 _switch) {
                    if (!isEnabled(_switch)) {
                        _error('Feature is disabled');
                    } else {
                        _;
                    }
                }
            
                enum Features { Issue, TransferWithReference, Revoke, ChangeOwnership, Allowances, ICAP }
            
                // Structure of a particular asset.
                struct Asset {
                    uint owner;                       // Asset's owner id.
                    uint totalSupply;                 // Asset's total supply.
                    string name;                      // Asset's name, for information purposes.
                    string description;               // Asset's description, for information purposes.
                    bool isReissuable;                // Indicates if asset have dynamic of fixed supply.
                    uint8 baseUnit;                   // Proposed number of decimals.
                    bool isLocked;                    // Are changes still allowed.
                    mapping(uint => Wallet) wallets;  // Holders wallets.
                }
            
                // Structure of an asset holder wallet for particular asset.
                struct Wallet {
                    uint balance;
                    mapping(uint => uint) allowance;
                }
            
                // Structure of an asset holder.
                struct Holder {
                    address addr;                    // Current address of the holder.
                    Cosigner cosigner;               // Cosigner contract for 2FA and recovery.
                    mapping(address => bool) trust;  // Addresses that are trusted with recovery proocedure.
                }
            
                // Iterable mapping pattern is used for holders.
                uint public holdersCount;
                mapping(uint => Holder) public holders;
            
                // This is an access address mapping. Many addresses may have access to a single holder.
                mapping(address => uint) holderIndex;
            
                // Asset symbol to asset mapping.
                mapping(bytes32 => Asset) public assets;
            
                // Asset symbol to asset proxy mapping.
                mapping(bytes32 => address) public proxies;
            
                // ICAP registry contract.
                RegistryICAPInterface public registryICAP;
            
                // Should use interface of the emitter, but address of events history.
                Emitter public eventsHistory;
            
                /**
                 * Emits Error event with specified error message.
                 *
                 * Should only be used if no state changes happened.
                 *
                 * @param _message error message.
                 */
                function _error(bytes32 _message) internal {
                    eventsHistory.emitError(_message);
                }
            
                /**
                 * Sets EventsHstory contract address.
                 *
                 * Can be set only once, and only by contract owner.
                 *
                 * @param _eventsHistory EventsHistory contract address.
                 *
                 * @return success.
                 */
                function setupEventsHistory(Emitter _eventsHistory) onlyRole('setup') returns(bool) {
                    if (address(eventsHistory) != 0) {
                        return false;
                    }
                    eventsHistory = _eventsHistory;
                    return true;
                }
            
                /**
                 * Sets RegistryICAP contract address.
                 *
                 * Can be set only once, and only by contract owner.
                 *
                 * @param _registryICAP RegistryICAP contract address.
                 *
                 * @return success.
                 */
                function setupRegistryICAP(RegistryICAPInterface _registryICAP) onlyRole('setup') returns(bool) {
                    if (address(registryICAP) != 0) {
                        return false;
                    }
                    registryICAP = _registryICAP;
                    return true;
                }
            
                /**
                 * Emits Error if called not by asset owner.
                 */
                modifier onlyOwner(bytes32 _symbol) {
                    if (_isSignedOwner(_symbol)) {
                        _;
                    } else {
                        _error('Only owner: access denied');
                    }
                }
            
                /**
                 * Emits Error if called not by asset proxy.
                 */
                modifier onlyProxy(bytes32 _symbol) {
                    if (_isProxy(_symbol)) {
                        _;
                    } else {
                        _error('Only proxy: access denied');
                    }
                }
            
                /**
                 * Emits Error if _from doesn't trust _to.
                 */
                modifier checkTrust(address _from, address _to) {
                    if (isTrusted(_from, _to)) {
                        _;
                    } else {
                        _error('Only trusted: access denied');
                    }
                }
            
                function _isSignedOwner(bytes32 _symbol) internal checkSigned(getHolderId(msg.sender), 1) returns(bool) {
                    return isOwner(msg.sender, _symbol);
                }
            
                /**
                 * Check asset existance.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset existance.
                 */
                function isCreated(bytes32 _symbol) constant returns(bool) {
                    return assets[_symbol].owner != 0;
                }
            
                function isLocked(bytes32 _symbol) constant returns(bool) {
                    return assets[_symbol].isLocked;
                }
            
                /**
                 * Returns asset decimals.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset decimals.
                 */
                function baseUnit(bytes32 _symbol) constant returns(uint8) {
                    return assets[_symbol].baseUnit;
                }
            
                /**
                 * Returns asset name.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset name.
                 */
                function name(bytes32 _symbol) constant returns(string) {
                    return assets[_symbol].name;
                }
            
                /**
                 * Returns asset description.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset description.
                 */
                function description(bytes32 _symbol) constant returns(string) {
                    return assets[_symbol].description;
                }
            
                /**
                 * Returns asset reissuability.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset reissuability.
                 */
                function isReissuable(bytes32 _symbol) constant returns(bool) {
                    return assets[_symbol].isReissuable;
                }
            
                /**
                 * Returns asset owner address.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset owner address.
                 */
                function owner(bytes32 _symbol) constant returns(address) {
                    return holders[assets[_symbol].owner].addr;
                }
            
                /**
                 * Check if specified address has asset owner rights.
                 *
                 * @param _owner address to check.
                 * @param _symbol asset symbol.
                 *
                 * @return owner rights availability.
                 */
                function isOwner(address _owner, bytes32 _symbol) constant returns(bool) {
                    return isCreated(_symbol) && (assets[_symbol].owner == getHolderId(_owner));
                }
            
                /**
                 * Returns asset total supply.
                 *
                 * @param _symbol asset symbol.
                 *
                 * @return asset total supply.
                 */
                function totalSupply(bytes32 _symbol) constant returns(uint) {
                    return assets[_symbol].totalSupply;
                }
            
                /**
                 * Returns asset balance for current address of a particular holder.
                 *
                 * @param _holder holder address.
                 * @param _symbol asset symbol.
                 *
                 * @return holder balance.
                 */
                function balanceOf(address _holder, bytes32 _symbol) constant returns(uint) {
                    uint holderId = getHolderId(_holder);
                    return holders[holderId].addr == _holder ? _balanceOf(holderId, _symbol) : 0;
                }
            
                /**
                 * Returns asset balance for a particular holder id.
                 *
                 * @param _holderId holder id.
                 * @param _symbol asset symbol.
                 *
                 * @return holder balance.
                 */
                function _balanceOf(uint _holderId, bytes32 _symbol) constant internal returns(uint) {
                    return assets[_symbol].wallets[_holderId].balance;
                }
            
                /**
                 * Returns current address for a particular holder id.
                 *
                 * @param _holderId holder id.
                 *
                 * @return holder address.
                 */
                function _address(uint _holderId) constant internal returns(address) {
                    return holders[_holderId].addr;
                }
            
                function _isProxy(bytes32 _symbol) constant internal returns(bool) {
                    return proxies[_symbol] == msg.sender;
                }
            
                /**
                 * Sets Proxy contract address for a particular asset.
                 *
                 * Can be set only once for each asset, and only by contract owner.
                 *
                 * @param _address Proxy contract address.
                 * @param _symbol asset symbol.
                 *
                 * @return success.
                 */
                function setProxy(address _address, bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
                    if (proxies[_symbol] != 0x0 && assets[_symbol].isLocked) {
                        return false;
                    }
                    proxies[_symbol] = _address;
                    return true;
                }
            
                /**
                 * Transfers asset balance between holders wallets.
                 *
                 * @param _fromId holder id to take from.
                 * @param _toId holder id to give to.
                 * @param _value amount to transfer.
                 * @param _symbol asset symbol.
                 */
                function _transferDirect(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
                    assets[_symbol].wallets[_fromId].balance -= _value;
                    assets[_symbol].wallets[_toId].balance += _value;
                }
            
                /**
                 * Transfers asset balance between holders wallets.
                 *
                 * Performs sanity checks and takes care of allowances adjustment.
                 *
                 * @param _fromId holder id to take from.
                 * @param _toId holder id to give to.
                 * @param _value amount to transfer.
                 * @param _symbol asset symbol.
                 * @param _reference transfer comment to be included in a Transfer event.
                 * @param _senderId transfer initiator holder id.
                 *
                 * @return success.
                 */
                function _transfer(uint _fromId, uint _toId, uint _value, bytes32 _symbol, string _reference, uint _senderId) internal checkSigned(_senderId, 1) returns(bool) {
                    // Should not allow to send to oneself.
                    if (_fromId == _toId) {
                        _error('Cannot send to oneself');
                        return false;
                    }
                    // Should have positive value.
                    if (_value == 0) {
                        _error('Cannot send 0 value');
                        return false;
                    }
                    // Should have enough balance.
                    if (_balanceOf(_fromId, _symbol) < _value) {
                        _error('Insufficient balance');
                        return false;
                    }
                    // Should allow references.
                    if (bytes(_reference).length > 0 && !isEnabled(sha3(_symbol, Features.TransferWithReference))) {
                        _error('References feature is disabled');
                        return false;
                    }
                    // Should have enough allowance.
                    if (_fromId != _senderId && _allowance(_fromId, _senderId, _symbol) < _value) {
                        _error('Not enough allowance');
                        return false;
                    }
                    // Adjust allowance.
                    if (_fromId != _senderId) {
                        assets[_symbol].wallets[_fromId].allowance[_senderId] -= _value;
                    }
                    _transferDirect(_fromId, _toId, _value, _symbol);
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitTransfer(_address(_fromId), _address(_toId), _symbol, _value, _reference);
                    _proxyTransferEvent(_fromId, _toId, _value, _symbol);
                    return true;
                }
            
                // Feature and proxy checks done internally due to unknown symbol when the function is called.
                function _transferToICAP(uint _fromId, bytes32 _icap, uint _value, string _reference, uint _senderId) internal returns(bool) {
                    var (to, symbol, success) = registryICAP.parse(_icap);
                    if (!success) {
                        _error('ICAP is not registered');
                        return false;
                    }
                    if (!isEnabled(sha3(symbol, Features.ICAP))) {
                        _error('ICAP feature is disabled');
                        return false;
                    }
                    if (!_isProxy(symbol)) {
                        _error('Only proxy: access denied');
                        return false;
                    }
                    uint toId = _createHolderId(to);
                    if (!_transfer(_fromId, toId, _value, symbol, _reference, _senderId)) {
                        return false;
                    }
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitTransferToICAP(_address(_fromId), _address(toId), _icap, _value, _reference);
                    return true;
                }
            
                function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool) {
                    return _transferToICAP(getHolderId(_from), _icap, _value, _reference, getHolderId(_sender));
                }
            
                /**
                 * Ask asset Proxy contract to emit ERC20 compliant Transfer event.
                 *
                 * @param _fromId holder id to take from.
                 * @param _toId holder id to give to.
                 * @param _value amount to transfer.
                 * @param _symbol asset symbol.
                 */
                function _proxyTransferEvent(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
                    if (proxies[_symbol] != 0x0) {
                        // Internal Out Of Gas/Throw: revert this transaction too;
                        // Recursive Call: safe, all changes already made.
                        Proxy(proxies[_symbol]).emitTransfer(_address(_fromId), _address(_toId), _value);
                    }
                }
            
                /**
                 * Returns holder id for the specified address.
                 *
                 * @param _holder holder address.
                 *
                 * @return holder id.
                 */
                function getHolderId(address _holder) constant returns(uint) {
                    return holderIndex[_holder];
                }
            
                /**
                 * Returns holder id for the specified address, creates it if needed.
                 *
                 * @param _holder holder address.
                 *
                 * @return holder id.
                 */
                function _createHolderId(address _holder) internal returns(uint) {
                    uint holderId = holderIndex[_holder];
                    if (holderId == 0) {
                        holderId = ++holdersCount;
                        holders[holderId].addr = _holder;
                        holderIndex[_holder] = holderId;
                    }
                    return holderId;
                }
            
                /**
                 * Issues new asset token on the platform.
                 *
                 * Tokens issued with this call go straight to contract owner.
                 * Each symbol can be issued only once, and only by contract owner.
                 *
                 * _isReissuable is included in checkEnabledSwitch because it should be
                 * explicitly allowed before issuing new asset.
                 *
                 * @param _symbol asset symbol.
                 * @param _value amount of tokens to issue immediately.
                 * @param _name name of the asset.
                 * @param _description description for the asset.
                 * @param _baseUnit number of decimals.
                 * @param _isReissuable dynamic or fixed supply.
                 *
                 * @return success.
                 */
                function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) checkEnabledSwitch(sha3(_symbol, _isReissuable, Features.Issue)) returns(bool) {
                    // Should have positive value if supply is going to be fixed.
                    if (_value == 0 && !_isReissuable) {
                        _error('Cannot issue 0 value fixed asset');
                        return false;
                    }
                    // Should not be issued yet.
                    if (isCreated(_symbol)) {
                        _error('Asset already issued');
                        return false;
                    }
                    uint holderId = _createHolderId(msg.sender);
            
                    assets[_symbol] = Asset(holderId, _value, _name, _description, _isReissuable, _baseUnit, false);
                    assets[_symbol].wallets[holderId].balance = _value;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitIssue(_symbol, _value, _address(holderId));
                    return true;
                }
            
                function changeAsset(bytes32 _symbol, string _name, string _description, uint8 _baseUnit) onlyOwner(_symbol) returns(bool) {
                    if (isLocked(_symbol)) {
                        _error('Asset is locked');
                        return false;
                    }
                    assets[_symbol].name = _name;
                    assets[_symbol].description = _description;
                    assets[_symbol].baseUnit = _baseUnit;
                    eventsHistory.emitChange(_symbol);
                    return true;
                }
            
                function lockAsset(bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
                    if (isLocked(_symbol)) {
                        _error('Asset is locked');
                        return false;
                    }
                    assets[_symbol].isLocked = true;
                    return true;
                }
            
                /**
                 * Issues additional asset tokens if the asset have dynamic supply.
                 *
                 * Tokens issued with this call go straight to asset owner.
                 * Can only be called by asset owner.
                 *
                 * @param _symbol asset symbol.
                 * @param _value amount of additional tokens to issue.
                 *
                 * @return success.
                 */
                function reissueAsset(bytes32 _symbol, uint _value) onlyOwner(_symbol) returns(bool) {
                    // Should have positive value.
                    if (_value == 0) {
                        _error('Cannot reissue 0 value');
                        return false;
                    }
                    Asset asset = assets[_symbol];
                    // Should have dynamic supply.
                    if (!asset.isReissuable) {
                        _error('Cannot reissue fixed asset');
                        return false;
                    }
                    // Resulting total supply should not overflow.
                    if (asset.totalSupply + _value < asset.totalSupply) {
                        _error('Total supply overflow');
                        return false;
                    }
                    uint holderId = getHolderId(msg.sender);
                    asset.wallets[holderId].balance += _value;
                    asset.totalSupply += _value;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitIssue(_symbol, _value, _address(holderId));
                    _proxyTransferEvent(0, holderId, _value, _symbol);
                    return true;
                }
            
                /**
                 * Destroys specified amount of senders asset tokens.
                 *
                 * @param _symbol asset symbol.
                 * @param _value amount of tokens to destroy.
                 *
                 * @return success.
                 */
                function revokeAsset(bytes32 _symbol, uint _value) checkEnabledSwitch(sha3(_symbol, Features.Revoke)) checkSigned(getHolderId(msg.sender), 1) returns(bool) {
                    // Should have positive value.
                    if (_value == 0) {
                        _error('Cannot revoke 0 value');
                        return false;
                    }
                    Asset asset = assets[_symbol];
                    uint holderId = getHolderId(msg.sender);
                    // Should have enough tokens.
                    if (asset.wallets[holderId].balance < _value) {
                        _error('Not enough tokens to revoke');
                        return false;
                    }
                    asset.wallets[holderId].balance -= _value;
                    asset.totalSupply -= _value;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitRevoke(_symbol, _value, _address(holderId));
                    _proxyTransferEvent(holderId, 0, _value, _symbol);
                    return true;
                }
            
                /**
                 * Passes asset ownership to specified address.
                 *
                 * Only ownership is changed, balances are not touched.
                 * Can only be called by asset owner.
                 *
                 * @param _symbol asset symbol.
                 * @param _newOwner address to become a new owner.
                 *
                 * @return success.
                 */
                function changeOwnership(bytes32 _symbol, address _newOwner) checkEnabledSwitch(sha3(_symbol, Features.ChangeOwnership)) onlyOwner(_symbol) returns(bool) {
                    Asset asset = assets[_symbol];
                    uint newOwnerId = _createHolderId(_newOwner);
                    // Should pass ownership to another holder.
                    if (asset.owner == newOwnerId) {
                        _error('Cannot pass ownership to oneself');
                        return false;
                    }
                    address oldOwner = _address(asset.owner);
                    asset.owner = newOwnerId;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitOwnershipChange(oldOwner, _address(newOwnerId), _symbol);
                    return true;
                }
            
                function setCosignerAddress(Cosigner _cosigner) checkSigned(_createHolderId(msg.sender), 1) returns(bool) {
                    if (!_checkSigned(_cosigner, getHolderId(msg.sender), 1)) {
                        _error('Invalid cosigner');
                        return false;
                    }
                    holders[_createHolderId(msg.sender)].cosigner = _cosigner;
                    return true;
                }
            
                function isCosignerSet(uint _holderId) constant returns(bool) {
                    return address(holders[_holderId].cosigner) != 0x0;
                }
            
                function _checkSigned(Cosigner _cosigner, uint _holderId, uint _required) internal returns(bool) {
                    return _cosigner.consumeOperation(sha3(msg.data, _holderId), _required);
                }
            
                modifier checkSigned(uint _holderId, uint _required) {
                    if (!isCosignerSet(_holderId) || _checkSigned(holders[_holderId].cosigner, _holderId, _required)) {
                        _;
                    } else {
                        _error('Cosigner: access denied');
                    }
                }
            
                /**
                 * Check if specified holder trusts an address with recovery procedure.
                 *
                 * @param _from truster.
                 * @param _to trustee.
                 *
                 * @return trust existance.
                 */
                function isTrusted(address _from, address _to) constant returns(bool) {
                    return holders[getHolderId(_from)].trust[_to];
                }
            
                /**
                 * Trust an address to perform recovery procedure for the caller.
                 *
                 * @param _to trustee.
                 *
                 * @return success.
                 */
                function trust(address _to) returns(bool) {
                    uint fromId = _createHolderId(msg.sender);
                    // Should trust to another address.
                    if (fromId == getHolderId(_to)) {
                        _error('Cannot trust to oneself');
                        return false;
                    }
                    // Should trust to yet untrusted.
                    if (isTrusted(msg.sender, _to)) {
                        _error('Already trusted');
                        return false;
                    }
                    holders[fromId].trust[_to] = true;
                    return true;
                }
            
                /**
                 * Revoke trust to perform recovery procedure from an address.
                 *
                 * @param _to trustee.
                 *
                 * @return success.
                 */
                function distrust(address _to) checkTrust(msg.sender, _to) returns(bool) {
                    holders[getHolderId(msg.sender)].trust[_to] = false;
                    return true;
                }
            
                /**
                 * Perform recovery procedure.
                 *
                 * This function logic is actually more of an grantAccess(uint _holderId, address _to).
                 * It grants another address access to recovery subject wallets.
                 * Can only be called by trustee of recovery subject.
                 * If cosigning is enabled, should have atleast 2 confirmations.
                 *
                 * @dev Deprecated. Backward compatibility.
                 *
                 * @param _from holder address to recover from.
                 * @param _to address to grant access to.
                 *
                 * @return success.
                 */
                function recover(address _from, address _to) checkTrust(_from, msg.sender) returns(bool) {
                    return _grantAccess(getHolderId(_from), _to);
                }
            
                /**
                 * Perform recovery procedure.
                 *
                 * This function logic is actually more of an grantAccess(uint _holderId, address _to).
                 * It grants another address access to subject holder wallets.
                 * Can only be called if pre-confirmed by atleast 2 cosign oracles.
                 *
                 * @param _from holder address to recover from.
                 * @param _to address to grant access to.
                 *
                 * @return success.
                 */
                function grantAccess(address _from, address _to) returns(bool) {
                    if (!isCosignerSet(getHolderId(_from))) {
                        _error('Cosigner not set');
                        return false;
                    }
                    return _grantAccess(getHolderId(_from), _to);
                }
            
                function _grantAccess(uint _fromId, address _to) internal checkSigned(_fromId, 2) returns(bool) {
                    // Should recover to previously unused address.
                    if (getHolderId(_to) != 0) {
                        _error('Should recover to new address');
                        return false;
                    }
                    // We take current holder address because it might not equal _from.
                    // It is possible to recover from any old holder address, but event should have the current one.
                    address from = holders[_fromId].addr;
                    holders[_fromId].addr = _to;
                    holderIndex[_to] = _fromId;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitRecovery(from, _to, msg.sender);
                    return true;
                }
            
                /**
                 * Sets asset spending allowance for a specified spender.
                 *
                 * Note: to revoke allowance, one needs to set allowance to 0.
                 *
                 * @param _spenderId holder id to set allowance for.
                 * @param _value amount to allow.
                 * @param _symbol asset symbol.
                 * @param _senderId approve initiator holder id.
                 *
                 * @return success.
                 */
                function _approve(uint _spenderId, uint _value, bytes32 _symbol, uint _senderId) internal checkEnabledSwitch(sha3(_symbol, Features.Allowances)) checkSigned(_senderId, 1) returns(bool) {
                    // Asset should exist.
                    if (!isCreated(_symbol)) {
                        _error('Asset is not issued');
                        return false;
                    }
                    // Should allow to another holder.
                    if (_senderId == _spenderId) {
                        _error('Cannot approve to oneself');
                        return false;
                    }
                    assets[_symbol].wallets[_senderId].allowance[_spenderId] = _value;
                    // Internal Out Of Gas/Throw: revert this transaction too;
                    // Recursive Call: safe, all changes already made.
                    eventsHistory.emitApprove(_address(_senderId), _address(_spenderId), _symbol, _value);
                    if (proxies[_symbol] != 0x0) {
                        // Internal Out Of Gas/Throw: revert this transaction too;
                        // Recursive Call: safe, all changes already made.
                        Proxy(proxies[_symbol]).emitApprove(_address(_senderId), _address(_spenderId), _value);
                    }
                    return true;
                }
            
                /**
                 * Sets asset spending allowance for a specified spender.
                 *
                 * Can only be called by asset proxy.
                 *
                 * @param _spender holder address to set allowance to.
                 * @param _value amount to allow.
                 * @param _symbol asset symbol.
                 * @param _sender approve initiator address.
                 *
                 * @return success.
                 */
                function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) onlyProxy(_symbol) returns(bool) {
                    return _approve(_createHolderId(_spender), _value, _symbol, _createHolderId(_sender));
                }
            
                /**
                 * Returns asset allowance from one holder to another.
                 *
                 * @param _from holder that allowed spending.
                 * @param _spender holder that is allowed to spend.
                 * @param _symbol asset symbol.
                 *
                 * @return holder to spender allowance.
                 */
                function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint) {
                    return _allowance(getHolderId(_from), getHolderId(_spender), _symbol);
                }
            
                /**
                 * Returns asset allowance from one holder to another.
                 *
                 * @param _fromId holder id that allowed spending.
                 * @param _toId holder id that is allowed to spend.
                 * @param _symbol asset symbol.
                 *
                 * @return holder to spender allowance.
                 */
                function _allowance(uint _fromId, uint _toId, bytes32 _symbol) constant internal returns(uint) {
                    return assets[_symbol].wallets[_fromId].allowance[_toId];
                }
            
                /**
                 * Prforms allowance transfer of asset balance between holders wallets.
                 *
                 * Can only be called by asset proxy.
                 *
                 * @param _from holder address to take from.
                 * @param _to holder address to give to.
                 * @param _value amount to transfer.
                 * @param _symbol asset symbol.
                 * @param _reference transfer comment to be included in a Transfer event.
                 * @param _sender allowance transfer initiator address.
                 *
                 * @return success.
                 */
                function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) onlyProxy(_symbol) returns(bool) {
                    return _transfer(getHolderId(_from), _createHolderId(_to), _value, _symbol, _reference, getHolderId(_sender));
                }
            }

            File 5 of 5: UniswapV2Pair
            // File: contracts/interfaces/IUniswapV2Pair.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2ERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2ERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/libraries/SafeMath.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            // File: contracts/UniswapV2ERC20.sol
            
            pragma solidity =0.5.16;
            
            
            
            contract UniswapV2ERC20 is IUniswapV2ERC20 {
                using SafeMath for uint;
            
                string public constant name = 'Uniswap V2';
                string public constant symbol = 'UNI-V2';
                uint8 public constant decimals = 18;
                uint  public totalSupply;
                mapping(address => uint) public balanceOf;
                mapping(address => mapping(address => uint)) public allowance;
            
                bytes32 public DOMAIN_SEPARATOR;
                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                mapping(address => uint) public nonces;
            
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                constructor() public {
                    uint chainId;
                    assembly {
                        chainId := chainid
                    }
                    DOMAIN_SEPARATOR = keccak256(
                        abi.encode(
                            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                            keccak256(bytes(name)),
                            keccak256(bytes('1')),
                            chainId,
                            address(this)
                        )
                    );
                }
            
                function _mint(address to, uint value) internal {
                    totalSupply = totalSupply.add(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(address(0), to, value);
                }
            
                function _burn(address from, uint value) internal {
                    balanceOf[from] = balanceOf[from].sub(value);
                    totalSupply = totalSupply.sub(value);
                    emit Transfer(from, address(0), value);
                }
            
                function _approve(address owner, address spender, uint value) private {
                    allowance[owner][spender] = value;
                    emit Approval(owner, spender, value);
                }
            
                function _transfer(address from, address to, uint value) private {
                    balanceOf[from] = balanceOf[from].sub(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(from, to, value);
                }
            
                function approve(address spender, uint value) external returns (bool) {
                    _approve(msg.sender, spender, value);
                    return true;
                }
            
                function transfer(address to, uint value) external returns (bool) {
                    _transfer(msg.sender, to, value);
                    return true;
                }
            
                function transferFrom(address from, address to, uint value) external returns (bool) {
                    if (allowance[from][msg.sender] != uint(-1)) {
                        allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                    }
                    _transfer(from, to, value);
                    return true;
                }
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                    bytes32 digest = keccak256(
                        abi.encodePacked(
                            '\x19\x01',
                            DOMAIN_SEPARATOR,
                            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                        )
                    );
                    address recoveredAddress = ecrecover(digest, v, r, s);
                    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                    _approve(owner, spender, value);
                }
            }
            
            // File: contracts/libraries/Math.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing various math operations
            
            library Math {
                function min(uint x, uint y) internal pure returns (uint z) {
                    z = x < y ? x : y;
                }
            
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint y) internal pure returns (uint z) {
                    if (y > 3) {
                        z = y;
                        uint x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                    } else if (y != 0) {
                        z = 1;
                    }
                }
            }
            
            // File: contracts/libraries/UQ112x112.sol
            
            pragma solidity =0.5.16;
            
            // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
            
            // range: [0, 2**112 - 1]
            // resolution: 1 / 2**112
            
            library UQ112x112 {
                uint224 constant Q112 = 2**112;
            
                // encode a uint112 as a UQ112x112
                function encode(uint112 y) internal pure returns (uint224 z) {
                    z = uint224(y) * Q112; // never overflows
                }
            
                // divide a UQ112x112 by a uint112, returning a UQ112x112
                function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                    z = x / uint224(y);
                }
            }
            
            // File: contracts/interfaces/IERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            // File: contracts/interfaces/IUniswapV2Factory.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2Callee.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Callee {
                function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
            }
            
            // File: contracts/UniswapV2Pair.sol
            
            pragma solidity =0.5.16;
            
            
            
            
            
            
            
            
            contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                using SafeMath  for uint;
                using UQ112x112 for uint224;
            
                uint public constant MINIMUM_LIQUIDITY = 10**3;
                bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
            
                address public factory;
                address public token0;
                address public token1;
            
                uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
            
                uint public price0CumulativeLast;
                uint public price1CumulativeLast;
                uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
            
                uint private unlocked = 1;
                modifier lock() {
                    require(unlocked == 1, 'UniswapV2: LOCKED');
                    unlocked = 0;
                    _;
                    unlocked = 1;
                }
            
                function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                    _reserve0 = reserve0;
                    _reserve1 = reserve1;
                    _blockTimestampLast = blockTimestampLast;
                }
            
                function _safeTransfer(address token, address to, uint value) private {
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                }
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                constructor() public {
                    factory = msg.sender;
                }
            
                // called once by the factory at time of deployment
                function initialize(address _token0, address _token1) external {
                    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                    token0 = _token0;
                    token1 = _token1;
                }
            
                // update reserves and, on the first call per block, price accumulators
                function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                        // * never overflows, and + overflow is desired
                        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                    }
                    reserve0 = uint112(balance0);
                    reserve1 = uint112(balance1);
                    blockTimestampLast = blockTimestamp;
                    emit Sync(reserve0, reserve1);
                }
            
                // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                    address feeTo = IUniswapV2Factory(factory).feeTo();
                    feeOn = feeTo != address(0);
                    uint _kLast = kLast; // gas savings
                    if (feeOn) {
                        if (_kLast != 0) {
                            uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                            uint rootKLast = Math.sqrt(_kLast);
                            if (rootK > rootKLast) {
                                uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                uint denominator = rootK.mul(5).add(rootKLast);
                                uint liquidity = numerator / denominator;
                                if (liquidity > 0) _mint(feeTo, liquidity);
                            }
                        }
                    } else if (_kLast != 0) {
                        kLast = 0;
                    }
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function mint(address to) external lock returns (uint liquidity) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    uint balance0 = IERC20(token0).balanceOf(address(this));
                    uint balance1 = IERC20(token1).balanceOf(address(this));
                    uint amount0 = balance0.sub(_reserve0);
                    uint amount1 = balance1.sub(_reserve1);
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    if (_totalSupply == 0) {
                        liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                       _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                    } else {
                        liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                    }
                    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                    _mint(to, liquidity);
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Mint(msg.sender, amount0, amount1);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function burn(address to) external lock returns (uint amount0, uint amount1) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    address _token0 = token0;                                // gas savings
                    address _token1 = token1;                                // gas savings
                    uint balance0 = IERC20(_token0).balanceOf(address(this));
                    uint balance1 = IERC20(_token1).balanceOf(address(this));
                    uint liquidity = balanceOf[address(this)];
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                    _burn(address(this), liquidity);
                    _safeTransfer(_token0, to, amount0);
                    _safeTransfer(_token1, to, amount1);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Burn(msg.sender, amount0, amount1, to);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
            
                    uint balance0;
                    uint balance1;
                    { // scope for _token{0,1}, avoids stack too deep errors
                    address _token0 = token0;
                    address _token1 = token1;
                    require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                    if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                    if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
                    }
                    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                    { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                    }
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                }
            
                // force balances to match reserves
                function skim(address to) external lock {
                    address _token0 = token0; // gas savings
                    address _token1 = token1; // gas savings
                    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                }
            
                // force reserves to match balances
                function sync() external lock {
                    _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                }
            }