<commit_before>const fs = require('fs-extra');
const path = require('path');
const _ = require('lodash');
const urljoin = require('url-join');

// There is a warning if the .env file is missing
// This is fine in a production setting, where settings
// are loaded from the env and not from a file
require('dotenv').load({ path: path.join(__dirname, '../../.env') });

const deployConfig = require('../../.s3-website.json');

const hotPort = parseInt(process.env.ASSETS_PORT, 10) || 8080;

const devRelativeOutput  = '/';
const prodRelativeOutput = '/';

const devOutput  = path.join(__dirname, '../../build/dev', devRelativeOutput);
const prodOutput = path.join(__dirname, '../../build/prod', prodRelativeOutput);

// const prodAssetsUrl = ''; // Set this to the url where the assets will be deployed.
                          // If you want the paths to be relative to the deploy then leave this
                          // value as an empty string. This value could also be a CDN or
                          // it could be the ssl version of your S3 bucket ie:
                          // https://s3.amazonaws.com/' + deployConfig.domain;

const prodAssetsUrl = `https://s3.amazonaws.com/${deployConfig.domain}`;

const devAssetsUrl = `${process.env.ASSETS_URL}:${hotPort}`;

// Get a list of all directories in the apps directory.
// These will be used to generate the entries for webpack
const appsDir = path.join(__dirname, '../apps/');

const buildSuffix = '_bundle.js';

const htmlOptions = { // Options for building html files
  truncateSummaryAt: 1000,
  buildExtensions: ['.html', '.htm', '.md', '.markdown'], // file types to build (others will just be copied)
  markdownExtensions: ['.md', '.markdown'], // file types to process markdown
};

// -----------------------------------------------------------------------------
// Main paths for the application. Includes production and development paths.
// -----------------------------------------------------------------------------
const paths = {
  devRelativeOutput,
  prodRelativeOutput,
  devOutput,
  prodOutput,
  prodAssetsUrl,
  devAssetsUrl,
  appsDir,
};

// -----------------------------------------------------------------------------
// Helper function to generate full template paths for the given app
// -----------------------------------------------------------------------------
function templateDirs(app, dirs) {
  return _.map(dirs, templateDir => path.join(app.htmlPath, templateDir));
}

// -----------------------------------------------------------------------------
// Helper to determine if we should do a production build or not
// -----------------------------------------------------------------------------
function isProduction(stage) {
  return stage === 'production' || stage === 'staging';
}

// -----------------------------------------------------------------------------
// Generates a path with the app name if needed
// -----------------------------------------------------------------------------
function withNameIfRequired(name, relativeOutput, options) {
  if (!options.onlyPack && !options.appPerPort) {
    return path.join(relativeOutput, name);
  }
  return relativeOutput;
}

// -----------------------------------------------------------------------------
// Generates the main paths used for output
// -----------------------------------------------------------------------------
function outputPaths(name, port, options) {

  let rootOutputPath = devOutput;
  let outputPath = options.onlyPack ? devOutput : path.join(devOutput, name);
  // Public path indicates where the assets will be served from. In dev this will likely be
  // localhost or a local domain. In production this could be a CDN. In development this will
  // point to whatever public url is serving dev assets.
  let publicPath = urljoin(devAssetsUrl, withNameIfRequired(name, devRelativeOutput, options));

  if (isProduction(options.stage)) {
    rootOutputPath = prodOutput;
    outputPath = options.onlyPack ? prodOutput : path.join(prodOutput, name);
    publicPath = urljoin(prodAssetsUrl, withNameIfRequired(name, prodRelativeOutput, options));
  }

  return {
    rootOutputPath,
    outputPath,
    publicPath
  };
}

// -----------------------------------------------------------------------------
// Generate settings needed for webpack
// -----------------------------------------------------------------------------
function webpackSettings(name, file, appPath, port, options) {
  return {
    name,
    file,
    path: appPath,
    shouldLint: options.shouldLint,
    stage: options.stage,
    production: isProduction(options.stage),
    buildSuffix,
    port,
  };
}

// -----------------------------------------------------------------------------
// Generate all settings needed for a given application
// -----------------------------------------------------------------------------
function appSettings(name, port, options) {

  const appPath = path.join(appsDir, name);
  const htmlPath = path.join(appPath, 'html');
  const staticPath = path.join(appPath, 'static');

  const app = _.merge({
    htmlPath,
    staticPath,
    templateData: {}, // Object that will be passed to every page as it is rendered
    templateMap: {}, // Used to specify specific templates on a per file basis
    htmlOptions,
  }, webpackSettings(name, 'app.jsx', appPath, port, options),
     outputPaths(name, port, options));

  app.templateDirs = templateDirs(app, ['layouts']);
  return {
    [name] : app
  };
}

// -----------------------------------------------------------------------------
// Iterate a given directory to generate app or webpack settings
// -----------------------------------------------------------------------------
function iterateDirAndPorts(dir, options, cb) {
  let port = options.port;
  return fs.readdirSync(dir)
    .filter(file => fs.statSync(path.join(dir, file)).isDirectory())
    .reduce((result, appName) => {
      const app = cb(appName, port, options);
      port = options.appPerPort ? port + 1 : options.port;
      return _.merge(result, app);
    }, {});
}

// -----------------------------------------------------------------------------
// Generates an app setting for all applications found in the client directory
// -----------------------------------------------------------------------------
function apps(options) {
  return iterateDirAndPorts(appsDir, options, appSettings);
}

module.exports = {
  paths,
  hotPort,
  outputPaths,
  apps,
  isProduction
};
<commit_msg>Dynamically set port per app<commit_after>const fs = require('fs-extra');
const path = require('path');
const _ = require('lodash');
const urljoin = require('url-join');

// There is a warning if the .env file is missing
// This is fine in a production setting, where settings
// are loaded from the env and not from a file
require('dotenv').load({ path: path.join(__dirname, '../../.env') });

const deployConfig = require('../../.s3-website.json');

const hotPort = parseInt(process.env.ASSETS_PORT, 10) || 8080;

const devRelativeOutput  = '/';
const prodRelativeOutput = '/';

const devOutput  = path.join(__dirname, '../../build/dev', devRelativeOutput);
const prodOutput = path.join(__dirname, '../../build/prod', prodRelativeOutput);

// const prodAssetsUrl = ''; // Set this to the url where the assets will be deployed.
                          // If you want the paths to be relative to the deploy then leave this
                          // value as an empty string. This value could also be a CDN or
                          // it could be the ssl version of your S3 bucket ie:
                          // https://s3.amazonaws.com/' + deployConfig.domain;

const prodAssetsUrl = `https://s3.amazonaws.com/${deployConfig.domain}`;

const devAssetsUrl = process.env.ASSETS_URL;

// Get a list of all directories in the apps directory.
// These will be used to generate the entries for webpack
const appsDir = path.join(__dirname, '../apps/');

const buildSuffix = '_bundle.js';

const htmlOptions = { // Options for building html files
  truncateSummaryAt: 1000,
  buildExtensions: ['.html', '.htm', '.md', '.markdown'], // file types to build (others will just be copied)
  markdownExtensions: ['.md', '.markdown'], // file types to process markdown
};

// -----------------------------------------------------------------------------
// Main paths for the application. Includes production and development paths.
// -----------------------------------------------------------------------------
const paths = {
  devRelativeOutput,
  prodRelativeOutput,
  devOutput,
  prodOutput,
  prodAssetsUrl,
  devAssetsUrl,
  appsDir,
};

// -----------------------------------------------------------------------------
// Helper function to generate full template paths for the given app
// -----------------------------------------------------------------------------
function templateDirs(app, dirs) {
  return _.map(dirs, templateDir => path.join(app.htmlPath, templateDir));
}

// -----------------------------------------------------------------------------
// Helper to determine if we should do a production build or not
// -----------------------------------------------------------------------------
function isProduction(stage) {
  return stage === 'production' || stage === 'staging';
}

// -----------------------------------------------------------------------------
// Generates a path with the app name if needed
// -----------------------------------------------------------------------------
function withNameIfRequired(name, relativeOutput, options) {
  if (!options.onlyPack && !options.appPerPort) {
    return path.join(relativeOutput, name);
  }
  return relativeOutput;
}

// -----------------------------------------------------------------------------
// Generates the main paths used for output
// -----------------------------------------------------------------------------
function outputPaths(name, port, options) {

  let rootOutputPath = devOutput;
  let outputPath = options.onlyPack ? devOutput : path.join(devOutput, name);
  // Public path indicates where the assets will be served from. In dev this will likely be
  // localhost or a local domain. In production this could be a CDN. In development this will
  // point to whatever public url is serving dev assets.

  let publicPath;

  if (isProduction(options.stage)) {
    rootOutputPath = prodOutput;
    outputPath = options.onlyPack ? prodOutput : path.join(prodOutput, name);
    publicPath = urljoin(prodAssetsUrl, withNameIfRequired(name, prodRelativeOutput, options));
  } else {
    let devUrl = devAssetsUrl;
    // Include the port if we are running on localhost
    if (_.includes(['localhost', '0.0.0.0', '127.0.0.1'], devAssetsUrl)) {
      devUrl = `${devAssetsUrl}:${port}`;
    }
    publicPath = urljoin(devUrl, withNameIfRequired(name, devRelativeOutput, options));
  }

  return {
    rootOutputPath,
    outputPath,
    publicPath
  };
}

// -----------------------------------------------------------------------------
// Generate settings needed for webpack
// -----------------------------------------------------------------------------
function webpackSettings(name, file, appPath, port, options) {
  return {
    name,
    file,
    path: appPath,
    shouldLint: options.shouldLint,
    stage: options.stage,
    production: isProduction(options.stage),
    buildSuffix,
    port,
  };
}

// -----------------------------------------------------------------------------
// Generate all settings needed for a given application
// -----------------------------------------------------------------------------
function appSettings(name, port, options) {

  const appPath = path.join(appsDir, name);
  const htmlPath = path.join(appPath, 'html');
  const staticPath = path.join(appPath, 'static');

  const app = _.merge({
    htmlPath,
    staticPath,
    templateData: {}, // Object that will be passed to every page as it is rendered
    templateMap: {}, // Used to specify specific templates on a per file basis
    htmlOptions,
  }, webpackSettings(name, 'app.jsx', appPath, port, options),
     outputPaths(name, port, options));

  app.templateDirs = templateDirs(app, ['layouts']);
  return {
    [name] : app
  };
}

// -----------------------------------------------------------------------------
// Iterate a given directory to generate app or webpack settings
// -----------------------------------------------------------------------------
function iterateDirAndPorts(dir, options, cb) {
  let port = options.port;
  return fs.readdirSync(dir)
    .filter(file => fs.statSync(path.join(dir, file)).isDirectory())
    .reduce((result, appName) => {
      const app = cb(appName, port, options);
      port = options.appPerPort ? port + 1 : options.port;
      return _.merge(result, app);
    }, {});
}

// -----------------------------------------------------------------------------
// Generates an app setting for all applications found in the client directory
// -----------------------------------------------------------------------------
function apps(options) {
  return iterateDirAndPorts(appsDir, options, appSettings);
}

module.exports = {
  paths,
  hotPort,
  outputPaths,
  apps,
  isProduction
};
<|endoftext|>


<commit_before>"use strict";

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class bter extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bter',
            'name': 'Bter',
            'countries': [ 'VG', 'CN' ], // British Virgin Islands, China
            'version': '2',
            // obsolete metainfo interface
            'hasCORS': false,
            'hasFetchTickers': true,
            'hasWithdraw': true,
            // new metainfo interface
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'withdraw': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27980479-cfa3188c-6387-11e7-8191-93fc4184ba5c.jpg',
                'api': {
                    'public': 'https://data.bter.com/api',
                    'private': 'https://api.bter.com/api',
                },
                'www': 'https://bter.com',
                'doc': 'https://bter.com/api2',
                'fees': 'https://bter.com/fee',
            },
            'api': {
                'public': {
                    'get': [
                        'pairs',
                        'marketinfo',
                        'marketlist',
                        'tickers',
                        'ticker/{id}',
                        'orderBook/{id}',
                        'trade/{id}',
                        'tradeHistory/{id}',
                        'tradeHistory/{id}/{tid}',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'depositAddress',
                        'newAddress',
                        'depositsWithdrawals',
                        'buy',
                        'sell',
                        'cancelOrder',
                        'cancelAllOrders',
                        'getOrder',
                        'openOrders',
                        'tradeHistory',
                        'withdraw',
                    ],
                },
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetMarketinfo ();
        let markets = this.safeValue (response, 'pairs');
        if (!markets)
            throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response');
        let result = [];
        for (let i = 0; i < markets.length; i++) {
            let market = markets[i];
            let keys = Object.keys (market);
            let id = keys[0];
            let details = market[id];
            let [ base, quote ] = id.split ('_');
            base = base.toUpperCase ();
            quote = quote.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let precision = {
                'amount': details['decimal_places'],
                'price': details['decimal_places'],
            };
            let amountLimits = {
                'min': details['min_amount'],
                'max': undefined,
            };
            let priceLimits = {
                'min': undefined,
                'max': undefined,
            };
            let limits = {
                'amount': amountLimits,
                'price': priceLimits,
            };
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
                'maker': details['fee'] / 100,
                'taker': details['fee'] / 100,
                'precision': precision,
                'limits': limits,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balance = await this.privatePostBalances ();
        let result = { 'info': balance };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let code = this.commonCurrencyCode (currency);
            let account = this.account ();
            if ('available' in balance) {
                if (currency in balance['available']) {
                    account['free'] = parseFloat (balance['available'][currency]);
                }
            }
            if ('locked' in balance) {
                if (currency in balance['locked']) {
                    account['used'] = parseFloat (balance['locked'][currency]);
                }
            }
            account['total'] = this.sum (account['free'], account['used']);
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderBookId (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        let result = this.parseOrderBook (orderbook);
        result['asks'] = this.sortBy (result['asks'], 0);
        return result;
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high24hr']),
            'low': parseFloat (ticker['low24hr']),
            'bid': parseFloat (ticker['highestBid']),
            'ask': parseFloat (ticker['lowestAsk']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last']),
            'change': parseFloat (ticker['percentChange']),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['quoteVolume']),
            'quoteVolume': parseFloat (ticker['baseVolume']),
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let result = {};
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let ticker = tickers[id];
            let market = undefined;
            if (symbol in this.markets)
                market = this.markets[symbol];
            if (id in this.markets_by_id)
                market = this.markets_by_id[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTickerId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        let timestamp = this.parse8601 (trade['date']);
        return {
            'id': trade['tradeID'],
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['type'],
            'price': trade['rate'],
            'amount': this.safeFloat (trade, 'amount'),
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTradeHistoryId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type == 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let method = 'privatePost' + this.capitalize (side);
        let order = {
            'currencyPair': this.marketId (symbol),
            'rate': price,
            'amount': amount,
        };
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['orderNumber'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({ 'orderNumber': id });
    }

    async withdraw (currency, amount, address, params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostWithdraw (this.extend ({
            'currency': currency.toLowerCase (),
            'amount': amount,
            'address': address, // Address must exist in you AddressBook in security settings
        }, params));
        return {
            'info': response,
            'id': undefined,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let prefix = (api == 'private') ? (api + '/') : '';
        let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api == 'public') {
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let request = { 'nonce': nonce };
            body = this.urlencode (this.extend (request, query));
            let signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512');
            headers = {
                'Key': this.apiKey,
                'Sign': signature,
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('result' in response)
            if (response['result'] != 'true')
                throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
}
<commit_msg>bter withdraw tag and linting #1313<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class bter extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'bter',
            'name': 'Bter',
            'countries': [ 'VG', 'CN' ], // British Virgin Islands, China
            'version': '2',
            // obsolete metainfo interface
            'hasCORS': false,
            'hasFetchTickers': true,
            'hasWithdraw': true,
            // new metainfo interface
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'withdraw': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27980479-cfa3188c-6387-11e7-8191-93fc4184ba5c.jpg',
                'api': {
                    'public': 'https://data.bter.com/api',
                    'private': 'https://api.bter.com/api',
                },
                'www': 'https://bter.com',
                'doc': 'https://bter.com/api2',
                'fees': 'https://bter.com/fee',
            },
            'api': {
                'public': {
                    'get': [
                        'pairs',
                        'marketinfo',
                        'marketlist',
                        'tickers',
                        'ticker/{id}',
                        'orderBook/{id}',
                        'trade/{id}',
                        'tradeHistory/{id}',
                        'tradeHistory/{id}/{tid}',
                    ],
                },
                'private': {
                    'post': [
                        'balances',
                        'depositAddress',
                        'newAddress',
                        'depositsWithdrawals',
                        'buy',
                        'sell',
                        'cancelOrder',
                        'cancelAllOrders',
                        'getOrder',
                        'openOrders',
                        'tradeHistory',
                        'withdraw',
                    ],
                },
            },
        });
    }

    async fetchMarkets () {
        let response = await this.publicGetMarketinfo ();
        let markets = this.safeValue (response, 'pairs');
        if (!markets)
            throw new ExchangeError (this.id + ' fetchMarkets got an unrecognized response');
        let result = [];
        for (let i = 0; i < markets.length; i++) {
            let market = markets[i];
            let keys = Object.keys (market);
            let id = keys[0];
            let details = market[id];
            let [ base, quote ] = id.split ('_');
            base = base.toUpperCase ();
            quote = quote.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let precision = {
                'amount': details['decimal_places'],
                'price': details['decimal_places'],
            };
            let amountLimits = {
                'min': details['min_amount'],
                'max': undefined,
            };
            let priceLimits = {
                'min': undefined,
                'max': undefined,
            };
            let limits = {
                'amount': amountLimits,
                'price': priceLimits,
            };
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
                'maker': details['fee'] / 100,
                'taker': details['fee'] / 100,
                'precision': precision,
                'limits': limits,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balance = await this.privatePostBalances ();
        let result = { 'info': balance };
        let currencies = Object.keys (this.currencies);
        for (let i = 0; i < currencies.length; i++) {
            let currency = currencies[i];
            let code = this.commonCurrencyCode (currency);
            let account = this.account ();
            if ('available' in balance) {
                if (currency in balance['available']) {
                    account['free'] = parseFloat (balance['available'][currency]);
                }
            }
            if ('locked' in balance) {
                if (currency in balance['locked']) {
                    account['used'] = parseFloat (balance['locked'][currency]);
                }
            }
            account['total'] = this.sum (account['free'], account['used']);
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderBookId (this.extend ({
            'id': this.marketId (symbol),
        }, params));
        let result = this.parseOrderBook (orderbook);
        result['asks'] = this.sortBy (result['asks'], 0);
        return result;
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.milliseconds ();
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['high24hr']),
            'low': parseFloat (ticker['low24hr']),
            'bid': parseFloat (ticker['highestBid']),
            'ask': parseFloat (ticker['lowestAsk']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last']),
            'change': parseFloat (ticker['percentChange']),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['quoteVolume']),
            'quoteVolume': parseFloat (ticker['baseVolume']),
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTickers (params);
        let result = {};
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let [ baseId, quoteId ] = id.split ('_');
            let base = baseId.toUpperCase ();
            let quote = quoteId.toUpperCase ();
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let ticker = tickers[id];
            let market = undefined;
            if (symbol in this.markets)
                market = this.markets[symbol];
            if (id in this.markets_by_id)
                market = this.markets_by_id[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTickerId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        let timestamp = this.parse8601 (trade['date']);
        return {
            'id': trade['tradeID'],
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'type': undefined,
            'side': trade['type'],
            'price': trade['rate'],
            'amount': this.safeFloat (trade, 'amount'),
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetTradeHistoryId (this.extend ({
            'id': market['id'],
        }, params));
        return this.parseTrades (response['data'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market')
            throw new ExchangeError (this.id + ' allows limit orders only');
        await this.loadMarkets ();
        let method = 'privatePost' + this.capitalize (side);
        let order = {
            'currencyPair': this.marketId (symbol),
            'rate': price,
            'amount': amount,
        };
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['orderNumber'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({ 'orderNumber': id });
    }

    async withdraw (currency, amount, address, tag = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privatePostWithdraw (this.extend ({
            'currency': currency.toLowerCase (),
            'amount': amount,
            'address': address, // Address must exist in you AddressBook in security settings
        }, params));
        return {
            'info': response,
            'id': undefined,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let prefix = (api === 'private') ? (api + '/') : '';
        let url = this.urls['api'][api] + this.version + '/1/' + prefix + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length)
                url += '?' + this.urlencode (query);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let request = { 'nonce': nonce };
            body = this.urlencode (this.extend (request, query));
            let signature = this.hmac (this.encode (body), this.encode (this.secret), 'sha512');
            headers = {
                'Key': this.apiKey,
                'Sign': signature,
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('result' in response)
            if (response['result'] !== 'true')
                throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
}
<|endoftext|>


<commit_before>if (enyo && enyo.version) {
	enyo.version.onyx = "2.3.1-pre.1";
}
<commit_msg>Update version string to 2.3.0-rc.3<commit_after>if (enyo && enyo.version) {
	enyo.version.onyx = "2.3.0-rc.3";
}
<|endoftext|>


<commit_before>
const RG = require('../src/rg');
RG.Component = require('../src/component');

const DebugGame = function(fact, parser) {
    const _fact = fact;
    const _parser = parser;

    this.create = function(obj, game, player) {
        const sqrPerItem = obj.sqrPerItem;
        const level = _fact.createLastBattle(game, obj);

        const spirit = new RG.Actor.Spirit('Wolf spirit');
        spirit.get('Stats').setStrength(500);
        level.addActor(spirit, 2, 1);

        const gem = new RG.Item.SpiritGem('Lesser gem');
        level.addItem(gem);

        const pickaxe = _parser.createActualObj('items', 'Pick-axe');
        level.addItem(pickaxe, 2, 2);

        const poison = _parser.createActualObj('items',
            'Potion of frost poison');
        poison.count = 5;
        level.addItem(poison, 2, 2);
        const curePoison = _parser.createActualObj('items',
            'Potion of cure poison');
        level.addItem(curePoison, 3, 2);

        const rifle = _parser.createActualObj('items', 'Rifle');
        const ammo = _parser.createActualObj('items', 'Steel bullet');
        ammo.setCount(100);
        level.addItem(rifle, 1, 1);
        level.addItem(ammo, 1, 1);

        // Test for shops
        const keeper = _parser.createActualObj('actors', 'shopkeeper');
        const gold = new RG.Item.GoldCoin();
        gold.count = 50;
        keeper.getInvEq().addItem(gold);
        level.addActor(keeper, 2, 2);
        const shopElem = new RG.Element.Shop();
        const shopCell = level.getMap().getCell(3, 3);
        shopCell.setProp('elements', shopElem);
        const soldItem = _parser.createActualObj('items', 'Ruby glass sword');
        soldItem.add('Unpaid', new RG.Component.Unpaid());
        shopCell.setProp('items', soldItem);
        shopElem.setShopkeeper(keeper);

        const numFree = level.getMap().getFree().length;
        const itemsPerLevel = Math.round(numFree / sqrPerItem);

        const itemConf = {
            itemsPerLevel,
            func: (item) => (item.value <= 2500),
            maxValue: 2500,
            food: () => true,
            gold: () => false
        };
        _fact.addNRandItems(level, _parser, itemConf);

        const cols = level.getMap().cols;
        const rows = level.getMap().rows;

        const boss = _parser.createActor('Thabba, Son of Ice');
        level.addActor(boss, cols - 2, rows - 2);

        const cryomancer = _parser.createActor('Cryomancer');
        level.addActor(cryomancer, 1, rows - 2);

        const spiritPot = _parser.createActualObj(
            'items', 'Potion of spirit form');
        player.getInvEq().addItem(spiritPot);

        const potStr = _parser.createItem('Potion of strength');
        player.getInvEq().addItem(potStr);

        // BladeMaster components
        player.add('Attacker', new RG.Component.Attacker());
        player.add('Defender', new RG.Component.Defender());
        player.add('MasterEquipper', new RG.Component.MasterEquipper());
        player.add('BiDirStrike', new RG.Component.BiDirStrike());
        player.add('CounterAttack', new RG.Component.BiDirStrike());

        // Marksman components
        player.add(new RG.Component.ThroughShot());

        const winCond = new RG.Game.WinCondition('Kill a keeper');
        winCond.addActorKilled(keeper);

        game.addPlayer(player);

        const eq = player.getInvEq().getEquipment();
        eq.addSlot('spiritgem', new RG.Inv.EquipSlot(eq, 'spiritgem'));
        const gem1 = _parser.createItem('Lesser spirit gem');
        const gem2 = _parser.createItem('Greater spirit gem');
        player.getInvEq().addItem(gem1);
        player.getInvEq().addItem(gem2);
        player.add(new RG.Component.SpiritItemCrafter());

        const exploreElem = new RG.Element.Exploration();
        exploreElem.setExp(100);
        level.addElement(exploreElem, 1, 20);

        const trainer = this.createTrainer();
        level.addActor(trainer, 1, 2);

        const coins = new RG.Item.GoldCoin();
        coins.count = 600;
        player.getInvEq().addItem(coins);

        if (!player.getBook()) {
            const spellbook = new RG.Spell.SpellBook(player);
            player.setBook(spellbook);
            RG.Spell.addAllSpells(this._spellbook);
            player.add(new RG.Component.SpellPower());
            player.get('SpellPower').setPP(100);
        }

        return game;
    };

    this.createTrainer = function() {
        const human = _parser.createActor('fighter');
        human.setName('Old trainer');
        const trainComp = new RG.Component.Trainer();
        trainComp.getChatObj().setTrainer(human);
        human.add(trainComp);
        return human;
    };
};

module.exports = DebugGame;
<commit_msg>Added Spawner for testing.<commit_after>
const RG = require('../src/rg');
RG.Component = require('../src/component');

const DebugGame = function(fact, parser) {
    const _fact = fact;
    const _parser = parser;

    this.create = function(obj, game, player) {
        const sqrPerItem = obj.sqrPerItem;
        const level = _fact.createLastBattle(game, obj);

        const spirit = new RG.Actor.Spirit('Wolf spirit');
        spirit.get('Stats').setStrength(500);
        level.addActor(spirit, 2, 1);

        const gem = new RG.Item.SpiritGem('Lesser gem');
        level.addItem(gem);

        const pickaxe = _parser.createActualObj('items', 'Pick-axe');
        level.addItem(pickaxe, 2, 2);

        const poison = _parser.createActualObj('items',
            'Potion of frost poison');
        poison.count = 5;
        level.addItem(poison, 2, 2);
        const curePoison = _parser.createActualObj('items',
            'Potion of cure poison');
        level.addItem(curePoison, 3, 2);

        const rifle = _parser.createActualObj('items', 'Rifle');
        const ammo = _parser.createActualObj('items', 'Steel bullet');
        ammo.setCount(100);
        level.addItem(rifle, 1, 1);
        level.addItem(ammo, 1, 1);

        // Test for shops
        const keeper = _parser.createActualObj('actors', 'shopkeeper');
        const gold = new RG.Item.GoldCoin();
        gold.count = 50;
        keeper.getInvEq().addItem(gold);
        level.addActor(keeper, 2, 2);
        const shopElem = new RG.Element.Shop();
        const shopCell = level.getMap().getCell(3, 3);
        shopCell.setProp('elements', shopElem);
        const soldItem = _parser.createActualObj('items', 'Ruby glass sword');
        soldItem.add('Unpaid', new RG.Component.Unpaid());
        shopCell.setProp('items', soldItem);
        shopElem.setShopkeeper(keeper);

        const numFree = level.getMap().getFree().length;
        const itemsPerLevel = Math.round(numFree / sqrPerItem);

        const itemConf = {
            itemsPerLevel,
            func: (item) => (item.value <= 2500),
            maxValue: 2500,
            food: () => true,
            gold: () => false
        };
        _fact.addNRandItems(level, _parser, itemConf);

        const cols = level.getMap().cols;
        const rows = level.getMap().rows;

        const boss = _parser.createActor('Thabba, Son of Ice');
        level.addActor(boss, cols - 2, rows - 2);

        const cryomancer = _parser.createActor('Cryomancer');
        level.addActor(cryomancer, 1, rows - 2);

        const spiritPot = _parser.createActualObj(
            'items', 'Potion of spirit form');
        player.getInvEq().addItem(spiritPot);

        const potStr = _parser.createItem('Potion of strength');
        player.getInvEq().addItem(potStr);

        // BladeMaster components
        player.add('Attacker', new RG.Component.Attacker());
        player.add('Defender', new RG.Component.Defender());
        player.add('MasterEquipper', new RG.Component.MasterEquipper());
        player.add('BiDirStrike', new RG.Component.BiDirStrike());
        player.add('CounterAttack', new RG.Component.BiDirStrike());

        // Marksman components
        player.add(new RG.Component.ThroughShot());

        const winCond = new RG.Game.WinCondition('Kill a keeper');
        winCond.addActorKilled(keeper);

        game.addPlayer(player);

        const eq = player.getInvEq().getEquipment();
        eq.addSlot('spiritgem', new RG.Inv.EquipSlot(eq, 'spiritgem'));
        const gem1 = _parser.createItem('Lesser spirit gem');
        const gem2 = _parser.createItem('Greater spirit gem');
        player.getInvEq().addItem(gem1);
        player.getInvEq().addItem(gem2);
        player.add(new RG.Component.SpiritItemCrafter());

        const exploreElem = new RG.Element.Exploration();
        exploreElem.setExp(100);
        level.addElement(exploreElem, 1, 20);

        const trainer = this.createTrainer();
        level.addActor(trainer, 1, 2);

        const coins = new RG.Item.GoldCoin();
        coins.count = 600;
        player.getInvEq().addItem(coins);

        if (!player.getBook()) {
            const spellbook = new RG.Spell.SpellBook(player);
            player.setBook(spellbook);
            RG.Spell.addAllSpells(this._spellbook);
            player.add(new RG.Component.SpellPower());
            player.get('SpellPower').setPP(100);
        }

        const vActor = new RG.Actor.Virtual('spawner');
        const spawnBrain = new RG.Brain.Spawner(vActor);
        vActor.setBrain(spawnBrain);
        level.addActor(vActor, 0, 0);

        return game;
    };

    this.createTrainer = function() {
        const human = _parser.createActor('fighter');
        human.setName('Old trainer');
        const trainComp = new RG.Component.Trainer();
        trainComp.getChatObj().setTrainer(human);
        human.add(trainComp);
        return human;
    };
};

module.exports = DebugGame;
<|endoftext|>


<commit_before>/** @namespace moon */
if (enyo && enyo.version) {
	enyo.version.moonstone = "2.5.1-pre.8";
}
<commit_msg>Update version string to 2.5.1-pre.9<commit_after>/** @namespace moon */
if (enyo && enyo.version) {
	enyo.version.moonstone = "2.5.1-pre.9";
}
<|endoftext|>


<commit_before>const async = require('async');
import { __ } from 'embark-i18n';
const Web3 = require('web3');
const util = require('util');

const { blockchain: blockchainConstants } = require('embark-core/constants');
import { dappPath, hexToNumber } from 'embark-utils';
import { getAddressToContract, getTransactionParams } from './transactionUtils';
export { getAddressToContract, getTransactionParams };

const Transaction = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');

const LISTENED_METHODS = [
  blockchainConstants.transactionMethods.eth_call,
  blockchainConstants.transactionMethods.eth_getTransactionReceipt,
  blockchainConstants.transactionMethods.eth_sendTransaction,
  blockchainConstants.transactionMethods.eth_sendRawTransaction
];

export default class TransactionLogger {
  constructor(embark, _options) {
    this.embark = embark;
    this.logger = embark.logger;
    this.events = embark.events;
    this.fs = embark.fs;
    this.addressToContract = [];
    this.contractsConfig = embark.config.contractsConfig;
    this.contractsDeployed = false;
    this.outputDone = false;
    this.logFile = dappPath(".embark", "contractLogs.json.txt");
    this.transactions = {};

    this._listenForLogRequests();
    this._registerAPI();

    this.events.on("contracts:log", this._saveLog.bind(this));
    this.events.on('outputDone', () => {
      this.outputDone = true;
    });

    this.embark.registerActionForEvent("deployment:deployContracts:afterAll", { priority: 38 }, (params, cb) => {
      this.contractsDeployed = true;
      this._getContractsList((contractsList) => {
        this.addressToContract = getAddressToContract(contractsList, this.addressToContract);
        cb(null, params);
      });
    });

    this.writeLogFile = async.cargo((tasks, callback) => {
      let appendThis = '';
      tasks.forEach(task => {
        appendThis += `"${new Date().getTime()}":${JSON.stringify(task)},\n`;
      });
      this.fs.appendFile(this.logFile, appendThis, (err) => {
        if (err) {
          this.logger.error('Error writing to the log file', err.message);
          this.logger.trace(err);
        }
        callback();
      });
    });
  }

  get web3() {
    return (async () => {
      if (!this._web3) {
        const provider = await this.events.request2("blockchain:client:provider", "ethereum");
        this._web3 = new Web3(provider);
      }
      return this._web3;
    })();
  }

  get web3Accounts() {
    return (async () => {
      if (!this._web3Accounts) {
        const web3 = await this.web3;
        this._web3Accounts = await web3.eth.getAccounts();
      }
      return this._web3Accounts;
    })();
  }

  _getContractsList(callback) {
    this.events.request("contracts:list", (err, contractsList) => {
      if (err) {
        this.logger.error(__("no contracts found"));
        return callback();
      }
      callback(contractsList);
    });
  }

  _listenForLogRequests() {
    this.events.on('deploy:contract:receipt', receipt => {
      this.events.emit('contracts:log', {
        name: receipt.className,
        functionName: 'constructor',
        paramString: '',
        address: receipt.contractAddress,
        status: receipt.status ? '0x1' : '0x0',
        gasUsed: receipt.gasUsed,
        blockNumber: receipt.blockNumber,
        transactionHash: receipt.transactionHash
      });
    });

    this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this));
  }

  async _onLogRequest(args) {
    /*eslint complexity: ["error", 22]*/
    const method = args.request.method;
    if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) {
      return;
    }

    if (method === blockchainConstants.transactionMethods.eth_sendTransaction) {
      // We just gather data and wait for the receipt
      this.transactions[args.response.result] = {
        address: args.request.params[0].to,
        data: args.request.params[0].data,
        value: args.request.params[0].value,
        txHash: args.response.result
      };
      return;
    } else if (method === blockchainConstants.transactionMethods.eth_sendRawTransaction) {
      const rawData = Buffer.from(ethUtil.stripHexPrefix(args.request.params[0]), 'hex');
      const tx = new Transaction(rawData, 'hex');
      this.transactions[args.response.result] = {
        address: '0x' + tx.to.toString('hex'),
        data: '0x' + tx.data.toString('hex')
      };
      return;
    }

    let dataObject;
    if (method === blockchainConstants.transactionMethods.eth_getTransactionReceipt) {
      dataObject = args.response.result;
      if (!dataObject) {
        return;
      }
      if (this.transactions[args.response.result.transactionHash]) {
        // This is the normal case. If we don't get here, it's because we missed a TX
        dataObject = Object.assign(dataObject, this.transactions[args.response.result.transactionHash]);
        delete this.transactions[args.response.result.transactionHash]; // No longer needed
      } else {
        // Was not a eth_getTransactionReceipt in the context of a transaction
        return;
      }
    } else {
      dataObject = args.request.params[0];
    }
    const { to: address, data } = dataObject;
    if (!address) {
      // It's a deployment
      return;
    }

    const accounts = await this.web3Accounts;

    if (accounts.map(account => account.toLowerCase()).includes(address.toLowerCase())) {
      const web3 = await this.web3;
      const value = web3.utils.fromWei(web3.utils.hexToNumberString(dataObject.value));
      return this.logger.info(`Blockchain>`.underline + ` transferring ${value} ETH from ${dataObject.from} to ${address}`.bold);
    }

    const contract = this.addressToContract[address];
    if (!contract) {
      this.logger.debug(`Contract log for unknown contract: ${util.inspect(args)}`);
      return this._getContractsList((contractsList) => {
        this.addressToContract = getAddressToContract(contractsList, this.addressToContract);
      });
    }

    const { name, silent } = contract;
    if (silent && !this.outputDone) {
      return;
    }

    let functionName, paramString;
    if (!data) {
      // We missed the TX
      functionName = 'unknown';
      paramString = 'unknown';
    } else {
      const txParams = getTransactionParams(contract, data);
      functionName = txParams.functionName;
      paramString = txParams.paramString;
    }

    if (method === blockchainConstants.transactionMethods.eth_call) {
      const log = Object.assign({}, args, { name, functionName, paramString });
      log.status = '0x1';
      return this.events.emit('contracts:log', log);
    }

    let { transactionHash, blockNumber, gasUsed, status } = args.response.result;
    let reason;
    if (status !== '0x0' && status !== '0x1') {
      status = !status ? '0x0' : '0x1';
    }

    if (status === '0x0') {
      const web3 = await this.web3;
      const tx = await web3.eth.getTransaction(transactionHash);
      if (tx) {
        const code = await web3.eth.call(tx, tx.blockNumber);
        // Convert to Ascii and remove the useless bytes around the revert message
        reason = web3.utils.hexToAscii('0x' + code.substring(138)).toString().replace(/[^\x20-\x7E]/g, '');
      }
    }

    gasUsed = hexToNumber(gasUsed);
    blockNumber = hexToNumber(blockNumber);
    const log = Object.assign({}, args, { name, functionName, paramString, gasUsed, blockNumber, reason, status, transactionHash });

    this.events.emit('contracts:log', log);
    this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}${reason ? ` | reason: "${reason}"` : ''}`);
    this.events.emit('blockchain:tx', {
      name,
      functionName,
      paramString,
      transactionHash,
      gasUsed,
      blockNumber,
      status,
      reason
    });
  }

  _registerAPI() {
    const apiRoute = '/embark-api/contracts/logs';
    this.embark.registerAPICall(
      'ws',
      apiRoute,
      (ws, _req) => {
        this.events.on('contracts:log', (log) => {
          ws.send(JSON.stringify(log), () => {
          });
        });
      }
    );

    this.embark.registerAPICall(
      'get',
      apiRoute,
      (req, res) => {
        res.send(JSON.stringify(this._getLogs()));
      }
    );
  }

  _getLogs() {
    const data = this._readLogs();
    return Object.values(data).reverse();
  }

  _saveLog(log) {
    this.writeLogFile.push(log);
  }

  _readLogs() {
    this.fs.ensureFileSync(this.logFile);
    try {
      let data = this.fs.readFileSync(this.logFile).toString();

      if (!data) {
        return {};
      }

      // remove last comma and add braces around
      data = `{${data.substring(0, data.length - 2)}}`;
      return JSON.parse(data);
    } catch (error) {
      this.logger.error('Error reading contract log file', error.message);
      this.logger.trace(error);
      return {};
    }
  }
}
<commit_msg>refactor(transaction-logger): change saveLog to be async<commit_after>const async = require('async');
import { __ } from 'embark-i18n';
const Web3 = require('web3');
const util = require('util');

const { blockchain: blockchainConstants } = require('embark-core/constants');
import { dappPath, hexToNumber } from 'embark-utils';
import { getAddressToContract, getTransactionParams } from './transactionUtils';
export { getAddressToContract, getTransactionParams };

const Transaction = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');

const LISTENED_METHODS = [
  blockchainConstants.transactionMethods.eth_call,
  blockchainConstants.transactionMethods.eth_getTransactionReceipt,
  blockchainConstants.transactionMethods.eth_sendTransaction,
  blockchainConstants.transactionMethods.eth_sendRawTransaction
];

export default class TransactionLogger {
  constructor(embark, _options) {
    this.embark = embark;
    this.logger = embark.logger;
    this.events = embark.events;
    this.fs = embark.fs;
    this.addressToContract = [];
    this.contractsConfig = embark.config.contractsConfig;
    this.contractsDeployed = false;
    this.outputDone = false;
    this.logFile = dappPath(".embark", "contractLogs.json.txt");
    this.transactions = {};

    this._listenForLogRequests();
    this._registerAPI();

    this.events.on("contracts:log", this._saveLog.bind(this));
    this.events.on('outputDone', () => {
      this.outputDone = true;
    });

    this.embark.registerActionForEvent("deployment:deployContracts:afterAll", { priority: 38 }, (params, cb) => {
      this.contractsDeployed = true;
      this._getContractsList((contractsList) => {
        this.addressToContract = getAddressToContract(contractsList, this.addressToContract);
        cb(null, params);
      });
    });

    this.writeLogFile = async.cargo((tasks, callback) => {
      let appendThis = '';
      tasks.forEach(task => {
        appendThis += `"${new Date().getTime()}":${JSON.stringify(task)},\n`;
      });
      this.fs.appendFile(this.logFile, appendThis, (err) => {
        if (err) {
          this.logger.error('Error writing to the log file', err.message);
          this.logger.trace(err);
        }
        callback();
      });
    });
  }

  get web3() {
    return (async () => {
      if (!this._web3) {
        const provider = await this.events.request2("blockchain:client:provider", "ethereum");
        this._web3 = new Web3(provider);
      }
      return this._web3;
    })();
  }

  get web3Accounts() {
    return (async () => {
      if (!this._web3Accounts) {
        const web3 = await this.web3;
        this._web3Accounts = await web3.eth.getAccounts();
      }
      return this._web3Accounts;
    })();
  }

  _getContractsList(callback) {
    this.events.request("contracts:list", (err, contractsList) => {
      if (err) {
        this.logger.error(__("no contracts found"));
        return callback();
      }
      callback(contractsList);
    });
  }

  _listenForLogRequests() {
    this.events.on('deploy:contract:receipt', receipt => {
      this.events.emit('contracts:log', {
        name: receipt.className,
        functionName: 'constructor',
        paramString: '',
        address: receipt.contractAddress,
        status: receipt.status ? '0x1' : '0x0',
        gasUsed: receipt.gasUsed,
        blockNumber: receipt.blockNumber,
        transactionHash: receipt.transactionHash
      });
    });

    this.events.on('blockchain:proxy:response', this._onLogRequest.bind(this));
  }

  async _onLogRequest(args) {
    /*eslint complexity: ["error", 22]*/
    const method = args.request.method;
    if (!this.contractsDeployed || !LISTENED_METHODS.includes(method)) {
      return;
    }

    if (method === blockchainConstants.transactionMethods.eth_sendTransaction) {
      // We just gather data and wait for the receipt
      this.transactions[args.response.result] = {
        address: args.request.params[0].to,
        data: args.request.params[0].data,
        value: args.request.params[0].value,
        txHash: args.response.result
      };
      return;
    } else if (method === blockchainConstants.transactionMethods.eth_sendRawTransaction) {
      const rawData = Buffer.from(ethUtil.stripHexPrefix(args.request.params[0]), 'hex');
      const tx = new Transaction(rawData, 'hex');
      this.transactions[args.response.result] = {
        address: '0x' + tx.to.toString('hex'),
        data: '0x' + tx.data.toString('hex')
      };
      return;
    }

    let dataObject;
    if (method === blockchainConstants.transactionMethods.eth_getTransactionReceipt) {
      dataObject = args.response.result;
      if (!dataObject) {
        return;
      }
      if (this.transactions[args.response.result.transactionHash]) {
        // This is the normal case. If we don't get here, it's because we missed a TX
        dataObject = Object.assign(dataObject, this.transactions[args.response.result.transactionHash]);
        delete this.transactions[args.response.result.transactionHash]; // No longer needed
      } else {
        // Was not a eth_getTransactionReceipt in the context of a transaction
        return;
      }
    } else {
      dataObject = args.request.params[0];
    }
    const { to: address, data } = dataObject;
    if (!address) {
      // It's a deployment
      return;
    }

    const accounts = await this.web3Accounts;

    if (accounts.map(account => account.toLowerCase()).includes(address.toLowerCase())) {
      const web3 = await this.web3;
      const value = web3.utils.fromWei(web3.utils.hexToNumberString(dataObject.value));
      return this.logger.info(`Blockchain>`.underline + ` transferring ${value} ETH from ${dataObject.from} to ${address}`.bold);
    }

    const contract = this.addressToContract[address];
    if (!contract) {
      this.logger.debug(`Contract log for unknown contract: ${util.inspect(args)}`);
      return this._getContractsList((contractsList) => {
        this.addressToContract = getAddressToContract(contractsList, this.addressToContract);
      });
    }

    const { name, silent } = contract;
    if (silent && !this.outputDone) {
      return;
    }

    let functionName, paramString;
    if (!data) {
      // We missed the TX
      functionName = 'unknown';
      paramString = 'unknown';
    } else {
      const txParams = getTransactionParams(contract, data);
      functionName = txParams.functionName;
      paramString = txParams.paramString;
    }

    if (method === blockchainConstants.transactionMethods.eth_call) {
      const log = Object.assign({}, args, { name, functionName, paramString });
      log.status = '0x1';
      return this.events.emit('contracts:log', log);
    }

    let { transactionHash, blockNumber, gasUsed, status } = args.response.result;
    let reason;
    if (status !== '0x0' && status !== '0x1') {
      status = !status ? '0x0' : '0x1';
    }

    if (status === '0x0') {
      const web3 = await this.web3;
      const tx = await web3.eth.getTransaction(transactionHash);
      if (tx) {
        const code = await web3.eth.call(tx, tx.blockNumber);
        // Convert to Ascii and remove the useless bytes around the revert message
        reason = web3.utils.hexToAscii('0x' + code.substring(138)).toString().replace(/[^\x20-\x7E]/g, '');
      }
    }

    gasUsed = hexToNumber(gasUsed);
    blockNumber = hexToNumber(blockNumber);
    const log = Object.assign({}, args, { name, functionName, paramString, gasUsed, blockNumber, reason, status, transactionHash });

    this.events.emit('contracts:log', log);
    this.logger.info(`Blockchain>`.underline + ` ${name}.${functionName}(${paramString})`.bold + ` | ${transactionHash} | gas:${gasUsed} | blk:${blockNumber} | status:${status}${reason ? ` | reason: "${reason}"` : ''}`);
    this.events.emit('blockchain:tx', {
      name,
      functionName,
      paramString,
      transactionHash,
      gasUsed,
      blockNumber,
      status,
      reason
    });
  }

  _registerAPI() {
    const apiRoute = '/embark-api/contracts/logs';
    this.embark.registerAPICall(
      'ws',
      apiRoute,
      (ws, _req) => {
        this.events.on('contracts:log', (log) => {
          ws.send(JSON.stringify(log), () => {
          });
        });
      }
    );

    this.embark.registerAPICall(
      'get',
      apiRoute,
      async (req, res) => {
        res.send(JSON.stringify(await this._getLogs()));
      }
    );
  }

  async _getLogs() {
    const data = await this._readLogs();
    return Object.values(data).reverse();
  }

  _saveLog(log) {
    this.writeLogFile.push(log);
  }

  async _readLogs() {
    return new Promise(async resolve => {
      try {
        await this.fs.ensureFile(this.logFile);
        this.fs.readFile(this.logFile, (err, data) => {
          if (err) {
            throw (new Error(err));
          }
          data = data.toString();
          if (!data) {
            return resolve({});
          }

          // remove last comma and add braces around
          data = `{${data.substring(0, data.length - 2)}}`;

          resolve(JSON.parse(data));
        });
      } catch (error) {
        this.logger.error('Error reading contract log file', error.message);
        this.logger.trace(error);
        resolve({});
      }
    });
  }
}
<|endoftext|>


<commit_before>'use strict';

// ----------------------------------------------------------------------------

const huobipro = require ('./huobipro.js');

// ----------------------------------------------------------------------------

module.exports = class cdax extends huobipro {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'cdax',
            'name': 'CDAX',
            'countries': [ 'RU' ],
            'hostname': 'cdax.io',
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/102157692-fd406280-3e90-11eb-8d46-4511b617cd17.jpg',
                'api': {
                    'ws': {
                        'api': {
                            'public': 'wss://{hostname}/api/ws',
                            'private': 'wss://{hostname}/api/ws/v2',
                        },
                    },
                    'market': 'https://{hostname}/api',
                    'public': 'https://{hostname}/api',
                    'private': 'https://{hostname}/api',
                    'v2Public': 'https://{hostname}/api',
                    'v2Private': 'https://{hostname}/api',
                },
                'www': 'https://cdax.io',
                'referral': 'https://cdax.io/invite?invite_code=esc74',
                'doc': 'https://github.com/cloudapidoc/API_Docs',
                'fees': 'https://cdax.io/about/fee',
            },
        });
    }
};
<commit_msg>cdax reverted to v1<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const ccxt = require ('ccxt');
const { ExchangeError } = require ('ccxt/js/base/errors');
const { ArrayCache, ArrayCacheByTimestamp } = require ('./base/Cache');

// ----------------------------------------------------------------------------

module.exports = class cdax extends ccxt.cdax {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'cdax',
            'name': 'CDAX',
            'countries': [ 'RU' ],
            'hostname': 'cdax.io',
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/102157692-fd406280-3e90-11eb-8d46-4511b617cd17.jpg',
                'api': {
                    'ws': {
                        'api': {
                            'public': 'wss://{hostname}/api/ws',
                            'private': 'wss://{hostname}/api/ws/v2',
                        },
                    },
                    'market': 'https://{hostname}/api',
                    'public': 'https://{hostname}/api',
                    'private': 'https://{hostname}/api',
                    'v2Public': 'https://{hostname}/api',
                    'v2Private': 'https://{hostname}/api',
                },
                'www': 'https://cdax.io',
                'referral': 'https://cdax.io/invite?invite_code=esc74',
                'doc': 'https://github.com/cloudapidoc/API_Docs',
                'fees': 'https://cdax.io/about/fee',
            },
        });
    }

    requestId () {
        const requestId = this.sum (this.safeInteger (this.options, 'requestId', 0), 1);
        this.options['requestId'] = requestId;
        return requestId.toString ();
    }

    async watchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        // only supports a limit of 150 at this time
        const messageHash = 'market.' + market['id'] + '.detail';
        const api = this.safeString (this.options, 'api', 'api');
        const hostname = { 'hostname': this.hostname };
        const url = this.implodeParams (this.urls['api']['ws'][api]['public'], hostname);
        const requestId = this.requestId ();
        const request = {
            'sub': messageHash,
            'id': requestId,
        };
        const subscription = {
            'id': requestId,
            'messageHash': messageHash,
            'symbol': symbol,
            'params': params,
        };
        return await this.watch (url, messageHash, this.extend (request, params), messageHash, subscription);
    }

    handleTicker (client, message) {
        //
        //     {
        //         ch: 'market.btcusdt.detail',
        //         ts: 1583494163784,
        //         tick: {
        //             id: 209988464418,
        //             low: 8988,
        //             high: 9155.41,
        //             open: 9078.91,
        //             close: 9136.46,
        //             vol: 237813910.5928412,
        //             amount: 26184.202558551195,
        //             version: 209988464418,
        //             count: 265673
        //         }
        //     }
        //
        const tick = this.safeValue (message, 'tick', {});
        const ch = this.safeString (message, 'ch');
        const parts = ch.split ('.');
        const marketId = this.safeString (parts, 1);
        const market = this.safeMarket (marketId);
        const ticker = this.parseTicker (tick, market);
        const timestamp = this.safeValue (message, 'ts');
        ticker['timestamp'] = timestamp;
        ticker['datetime'] = this.iso8601 (timestamp);
        const symbol = ticker['symbol'];
        this.tickers[symbol] = ticker;
        client.resolve (ticker, ch);
        return message;
    }

    async watchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        // only supports a limit of 150 at this time
        const messageHash = 'market.' + market['id'] + '.trade.detail';
        const api = this.safeString (this.options, 'api', 'api');
        const hostname = { 'hostname': this.hostname };
        const url = this.implodeParams (this.urls['api']['ws'][api]['public'], hostname);
        const requestId = this.requestId ();
        const request = {
            'sub': messageHash,
            'id': requestId,
        };
        const subscription = {
            'id': requestId,
            'messageHash': messageHash,
            'symbol': symbol,
            'params': params,
        };
        const trades = await this.watch (url, messageHash, this.extend (request, params), messageHash, subscription);
        if (this.newUpdates) {
            limit = trades.getLimit (symbol, limit);
        }
        return this.filterBySinceLimit (trades, since, limit, 'timestamp', true);
    }

    handleTrades (client, message) {
        //
        //     {
        //         ch: "market.btcusdt.trade.detail",
        //         ts: 1583495834011,
        //         tick: {
        //             id: 105004645372,
        //             ts: 1583495833751,
        //             data: [
        //                 {
        //                     id: 1.050046453727319e+22,
        //                     ts: 1583495833751,
        //                     tradeId: 102090727790,
        //                     amount: 0.003893,
        //                     price: 9150.01,
        //                     direction: "sell"
        //                 }
        //             ]
        //         }
        //     }
        //
        const tick = this.safeValue (message, 'tick', {});
        const data = this.safeValue (tick, 'data', {});
        const ch = this.safeString (message, 'ch');
        const parts = ch.split ('.');
        const marketId = this.safeString (parts, 1);
        const market = this.safeMarket (marketId);
        const symbol = market['symbol'];
        let tradesCache = this.safeValue (this.trades, symbol);
        if (tradesCache === undefined) {
            const limit = this.safeInteger (this.options, 'tradesLimit', 1000);
            tradesCache = new ArrayCache (limit);
            this.trades[symbol] = tradesCache;
        }
        for (let i = 0; i < data.length; i++) {
            const trade = this.parseTrade (data[i], market);
            tradesCache.append (trade);
        }
        client.resolve (tradesCache, ch);
        return message;
    }

    async watchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const interval = this.timeframes[timeframe];
        const messageHash = 'market.' + market['id'] + '.kline.' + interval;
        const api = this.safeString (this.options, 'api', 'api');
        const hostname = { 'hostname': this.hostname };
        const url = this.implodeParams (this.urls['api']['ws'][api]['public'], hostname);
        const requestId = this.requestId ();
        const request = {
            'sub': messageHash,
            'id': requestId,
        };
        const subscription = {
            'id': requestId,
            'messageHash': messageHash,
            'symbol': symbol,
            'timeframe': timeframe,
            'params': params,
        };
        const ohlcv = await this.watch (url, messageHash, this.extend (request, params), messageHash, subscription);
        if (this.newUpdates) {
            limit = ohlcv.getLimit (symbol, limit);
        }
        return this.filterBySinceLimit (ohlcv, since, limit, 0, true);
    }

    handleOHLCV (client, message) {
        //
        //     {
        //         ch: 'market.btcusdt.kline.1min',
        //         ts: 1583501786794,
        //         tick: {
        //             id: 1583501760,
        //             open: 9094.5,
        //             close: 9094.51,
        //             low: 9094.5,
        //             high: 9094.51,
        //             amount: 0.44639786263800907,
        //             vol: 4059.76919054,
        //             count: 16
        //         }
        //     }
        //
        const ch = this.safeString (message, 'ch');
        const parts = ch.split ('.');
        const marketId = this.safeString (parts, 1);
        const market = this.safeMarket (marketId);
        const symbol = market['symbol'];
        const interval = this.safeString (parts, 3);
        const timeframe = this.findTimeframe (interval);
        this.ohlcvs[symbol] = this.safeValue (this.ohlcvs, symbol, {});
        let stored = this.safeValue (this.ohlcvs[symbol], timeframe);
        if (stored === undefined) {
            const limit = this.safeInteger (this.options, 'OHLCVLimit', 1000);
            stored = new ArrayCacheByTimestamp (limit);
            this.ohlcvs[symbol][timeframe] = stored;
        }
        const tick = this.safeValue (message, 'tick');
        const parsed = this.parseOHLCV (tick, market);
        stored.append (parsed);
        client.resolve (stored, ch);
    }

    async watchOrderBook (symbol, limit = undefined, params = {}) {
        if ((limit !== undefined) && (limit !== 150)) {
            throw new ExchangeError (this.id + ' watchOrderBook accepts limit = 150 only');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        // only supports a limit of 150 at this time
        limit = (limit === undefined) ? 150 : limit;
        const messageHash = 'market.' + market['id'] + '.mbp.' + limit.toString ();
        const api = this.safeString (this.options, 'api', 'api');
        const hostname = { 'hostname': this.hostname };
        const url = this.implodeParams (this.urls['api']['ws'][api]['public'], hostname);
        const requestId = this.requestId ();
        const request = {
            'sub': messageHash,
            'id': requestId,
        };
        const subscription = {
            'id': requestId,
            'messageHash': messageHash,
            'symbol': symbol,
            'limit': limit,
            'params': params,
            'method': this.handleOrderBookSubscription,
        };
        const orderbook = await this.watch (url, messageHash, this.extend (request, params), messageHash, subscription);
        return orderbook.limit (limit);
    }

    handleOrderBookSnapshot (client, message, subscription) {
        //
        //     {
        //         id: 1583473663565,
        //         rep: 'market.btcusdt.mbp.150',
        //         status: 'ok',
        //         data: {
        //             seqNum: 104999417756,
        //             bids: [
        //                 [9058.27, 0],
        //                 [9058.43, 0],
        //                 [9058.99, 0],
        //             ],
        //             asks: [
        //                 [9084.27, 0.2],
        //                 [9085.69, 0],
        //                 [9085.81, 0],
        //             ]
        //         }
        //     }
        //
        const symbol = this.safeString (subscription, 'symbol');
        const messageHash = this.safeString (subscription, 'messageHash');
        const orderbook = this.orderbooks[symbol];
        const data = this.safeValue (message, 'data');
        const snapshot = this.parseOrderBook (data, symbol);
        snapshot['nonce'] = this.safeInteger (data, 'seqNum');
        orderbook.reset (snapshot);
        // unroll the accumulated deltas
        const messages = orderbook.cache;
        for (let i = 0; i < messages.length; i++) {
            const message = messages[i];
            this.handleOrderBookMessage (client, message, orderbook);
        }
        this.orderbooks[symbol] = orderbook;
        client.resolve (orderbook, messageHash);
    }

    async watchOrderBookSnapshot (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        const limit = this.safeInteger (subscription, 'limit');
        const params = this.safeValue (subscription, 'params');
        const messageHash = this.safeString (subscription, 'messageHash');
        const api = this.safeString (this.options, 'api', 'api');
        const hostname = { 'hostname': this.hostname };
        const url = this.implodeParams (this.urls['api']['ws'][api]['public'], hostname);
        const requestId = this.requestId ();
        const request = {
            'req': messageHash,
            'id': requestId,
        };
        // this is a temporary subscription by a specific requestId
        // it has a very short lifetime until the snapshot is received over ws
        const snapshotSubscription = {
            'id': requestId,
            'messageHash': messageHash,
            'symbol': symbol,
            'limit': limit,
            'params': params,
            'method': this.handleOrderBookSnapshot,
        };
        const orderbook = await this.watch (url, requestId, request, requestId, snapshotSubscription);
        return orderbook.limit (limit);
    }

    handleDelta (bookside, delta) {
        const price = this.safeFloat (delta, 0);
        const amount = this.safeFloat (delta, 1);
        bookside.store (price, amount);
    }

    handleDeltas (bookside, deltas) {
        for (let i = 0; i < deltas.length; i++) {
            this.handleDelta (bookside, deltas[i]);
        }
    }

    handleOrderBookMessage (client, message, orderbook) {
        //
        //     {
        //         ch: "market.btcusdt.mbp.150",
        //         ts: 1583472025885,
        //         tick: {
        //             seqNum: 104998984994,
        //             prevSeqNum: 104998984977,
        //             bids: [
        //                 [9058.27, 0],
        //                 [9058.43, 0],
        //                 [9058.99, 0],
        //             ],
        //             asks: [
        //                 [9084.27, 0.2],
        //                 [9085.69, 0],
        //                 [9085.81, 0],
        //             ]
        //         }
        //     }
        //
        const tick = this.safeValue (message, 'tick', {});
        const seqNum = this.safeInteger (tick, 'seqNum');
        const prevSeqNum = this.safeInteger (tick, 'prevSeqNum');
        if ((prevSeqNum <= orderbook['nonce']) && (seqNum > orderbook['nonce'])) {
            const asks = this.safeValue (tick, 'asks', []);
            const bids = this.safeValue (tick, 'bids', []);
            this.handleDeltas (orderbook['asks'], asks);
            this.handleDeltas (orderbook['bids'], bids);
            orderbook['nonce'] = seqNum;
            const timestamp = this.safeInteger (message, 'ts');
            orderbook['timestamp'] = timestamp;
            orderbook['datetime'] = this.iso8601 (timestamp);
        }
        return orderbook;
    }

    handleOrderBook (client, message) {
        //
        // deltas
        //
        //     {
        //         ch: "market.btcusdt.mbp.150",
        //         ts: 1583472025885,
        //         tick: {
        //             seqNum: 104998984994,
        //             prevSeqNum: 104998984977,
        //             bids: [
        //                 [9058.27, 0],
        //                 [9058.43, 0],
        //                 [9058.99, 0],
        //             ],
        //             asks: [
        //                 [9084.27, 0.2],
        //                 [9085.69, 0],
        //                 [9085.81, 0],
        //             ]
        //         }
        //     }
        //
        const messageHash = this.safeString (message, 'ch');
        const ch = this.safeValue (message, 'ch');
        const parts = ch.split ('.');
        const marketId = this.safeString (parts, 1);
        const symbol = this.safeSymbol (marketId);
        const orderbook = this.orderbooks[symbol];
        if (orderbook['nonce'] === undefined) {
            orderbook.cache.push (message);
        } else {
            this.handleOrderBookMessage (client, message, orderbook);
            client.resolve (orderbook, messageHash);
        }
    }

    handleOrderBookSubscription (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        const limit = this.safeInteger (subscription, 'limit');
        if (symbol in this.orderbooks) {
            delete this.orderbooks[symbol];
        }
        this.orderbooks[symbol] = this.orderBook ({}, limit);
        // watch the snapshot in a separate async call
        this.spawn (this.watchOrderBookSnapshot, client, message, subscription);
    }

    handleSubscriptionStatus (client, message) {
        //
        //     {
        //         "id": 1583414227,
        //         "status": "ok",
        //         "subbed": "market.btcusdt.mbp.150",
        //         "ts": 1583414229143
        //     }
        //
        const id = this.safeString (message, 'id');
        const subscriptionsById = this.indexBy (client.subscriptions, 'id');
        const subscription = this.safeValue (subscriptionsById, id);
        if (subscription !== undefined) {
            const method = this.safeValue (subscription, 'method');
            if (method !== undefined) {
                return method.call (this, client, message, subscription);
            }
            // clean up
            if (id in client.subscriptions) {
                delete client.subscriptions[id];
            }
        }
        return message;
    }

    handleSystemStatus (client, message) {
        //
        // todo: answer the question whether handleSystemStatus should be renamed
        // and unified as handleStatus for any usage pattern that
        // involves system status and maintenance updates
        //
        //     {
        //         id: '1578090234088', // connectId
        //         type: 'welcome',
        //     }
        //
        return message;
    }

    handleSubject (client, message) {
        //
        //     {
        //         ch: "market.btcusdt.mbp.150",
        //         ts: 1583472025885,
        //         tick: {
        //             seqNum: 104998984994,
        //             prevSeqNum: 104998984977,
        //             bids: [
        //                 [9058.27, 0],
        //                 [9058.43, 0],
        //                 [9058.99, 0],
        //             ],
        //             asks: [
        //                 [9084.27, 0.2],
        //                 [9085.69, 0],
        //                 [9085.81, 0],
        //             ]
        //         }
        //     }
        //
        const ch = this.safeValue (message, 'ch');
        const parts = ch.split ('.');
        const type = this.safeString (parts, 0);
        if (type === 'market') {
            const methodName = this.safeString (parts, 2);
            const methods = {
                'mbp': this.handleOrderBook,
                'detail': this.handleTicker,
                'trade': this.handleTrades,
                'kline': this.handleOHLCV,
                // ...
            };
            const method = this.safeValue (methods, methodName);
            if (method === undefined) {
                return message;
            } else {
                return method.call (this, client, message);
            }
        }
    }

    async pong (client, message) {
        //
        //     { ping: 1583491673714 }
        //
        await client.send ({ 'pong': this.safeInteger (message, 'ping') });
    }

    handlePing (client, message) {
        this.spawn (this.pong, client, message);
    }

    handleErrorMessage (client, message) {
        //
        //     {
        //         ts: 1586323747018,
        //         status: 'error',
        //         'err-code': 'bad-request',
        //         'err-msg': 'invalid mbp.150.symbol linkusdt',
        //         id: '2'
        //     }
        //
        const status = this.safeString (message, 'status');
        if (status === 'error') {
            const id = this.safeString (message, 'id');
            const subscriptionsById = this.indexBy (client.subscriptions, 'id');
            const subscription = this.safeValue (subscriptionsById, id);
            if (subscription !== undefined) {
                const errorCode = this.safeString (message, 'err-code');
                try {
                    this.throwExactlyMatchedException (this.exceptions['exact'], errorCode, this.json (message));
                } catch (e) {
                    const messageHash = this.safeString (subscription, 'messageHash');
                    client.reject (e, messageHash);
                    client.reject (e, id);
                    if (id in client.subscriptions) {
                        delete client.subscriptions[id];
                    }
                }
            }
            return false;
        }
        return message;
    }

    handleMessage (client, message) {
        if (this.handleErrorMessage (client, message)) {
            //
            //     {"id":1583414227,"status":"ok","subbed":"market.btcusdt.mbp.150","ts":1583414229143}
            //
            if ('id' in message) {
                this.handleSubscriptionStatus (client, message);
            } else if ('ch' in message) {
                // route by channel aka topic aka subject
                this.handleSubject (client, message);
            } else if ('ping' in message) {
                this.handlePing (client, message);
            }
        }
    }
};
<|endoftext|>


<commit_before>import assign from 'lodash/object/assign';
import difference from 'lodash/array/difference';

export default function createPrototypeProxy() {
  let proxy = {};
  let current = null;
  let mountedInstances = [];

  function proxyMethod(key) {
    return function () {
      if (typeof current[key] === 'function') {
        return current[key].apply(this, arguments);
      }
    };
  }

  function proxiedComponentWillMount() {
    mountedInstances.push(this);
    if (typeof current.componentWillMount === 'function') {
      return current.componentWillMount.apply(this, arguments);
    }
  }

  function proxiedComponentWillUnmount() {
    mountedInstances.splice(mountedInstances.indexOf(this), 1);
    if (typeof current.componentWillUnmount === 'function') {
      return current.componentWillUnmount.apply(this, arguments);
    }
  }

  function update(next) {
    // Save current source of truth
    current = next;

    const nextKeys = Object.getOwnPropertyNames(next);
    const currentKeys = Object.getOwnPropertyNames(proxy);
    const addedKeys = difference(nextKeys, currentKeys);
    const removedKeys = difference(currentKeys, nextKeys);

    removedKeys.forEach(key => {
      delete proxy[key];
    });
    addedKeys.forEach(key => {
      if (typeof next[key] === 'function') {
        proxy[key] = proxyMethod(key);
      }
    });

    // Track mounted instances so we can forceUpdate() them later
    proxy.componentWillMount = proxiedComponentWillMount;
    proxy.componentWillUnmount = proxiedComponentWillUnmount;

    // Woodoo to appease Babel and React
    proxy.__proto__ = next;
    proxy.__reactAutoBindMap = {};
  }

  function get() {
    return proxy;
  }

  function getMountedInstances() {
    return mountedInstances;
  }

  return {
    update,
    get,
    getMountedInstances
  };
};<commit_msg>Copy properties to fix the React "approved method" warning<commit_after>import assign from 'lodash/object/assign';
import difference from 'lodash/array/difference';

export default function createPrototypeProxy() {
  let proxy = {};
  let current = null;
  let mountedInstances = [];

  function proxyMethod(key) {
    return function () {
      if (typeof current[key] === 'function') {
        return current[key].apply(this, arguments);
      }
    };
  }

  function proxiedComponentWillMount() {
    mountedInstances.push(this);
    if (typeof current.componentWillMount === 'function') {
      return current.componentWillMount.apply(this, arguments);
    }
  }

  function proxiedComponentWillUnmount() {
    mountedInstances.splice(mountedInstances.indexOf(this), 1);
    if (typeof current.componentWillUnmount === 'function') {
      return current.componentWillUnmount.apply(this, arguments);
    }
  }

  function update(next) {
    // Save current source of truth
    current = next;

    const nextKeys = Object.getOwnPropertyNames(next);
    const currentKeys = Object.getOwnPropertyNames(proxy);
    const addedKeys = difference(nextKeys, currentKeys);
    const removedKeys = difference(currentKeys, nextKeys);

    removedKeys.forEach(key => {
      delete proxy[key];
    });
    addedKeys.forEach(key => {
      if (typeof next[key] === 'function') {
        // Wrap the original function
        proxy[key] = proxyMethod(key);
        // Copy properties of the original function, if any
        assign(proxy[key], next[key]);
      }
    });

    // Track mounted instances so we can forceUpdate() them later
    proxy.componentWillMount = proxiedComponentWillMount;
    proxy.componentWillUnmount = proxiedComponentWillUnmount;

    // Woodoo to appease Babel and React
    proxy.__proto__ = next;
    proxy.__reactAutoBindMap = {};
  }

  function get() {
    return proxy;
  }

  function getMountedInstances() {
    return mountedInstances;
  }

  return {
    update,
    get,
    getMountedInstances
  };
};<|endoftext|>


<commit_before>if (enyo && enyo.version) {
	enyo.version.canvas = "2.3.0-rc.4";
}
<commit_msg>Update for 2.3.0-rc.5<commit_after>if (enyo && enyo.version) {
	enyo.version.canvas = "2.3.0-rc.5";
}
<|endoftext|>