Add connect to monorepo
This commit is contained in:
parent
5bd8e172c9
commit
655b0636fa
1
packages/connect/README.md
Normal file
1
packages/connect/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
This repository contains a Javascript library that makes it easy to interact with Relayers that conform to the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api)
|
68
packages/connect/package.json
Normal file
68
packages/connect/package.json
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"name": "@0xproject/connect",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "A javascript library for interacting with the standard relayer api",
|
||||||
|
"keywords": [
|
||||||
|
"0x-connect",
|
||||||
|
"0xproject",
|
||||||
|
"ethereum",
|
||||||
|
"tokens",
|
||||||
|
"exchange"
|
||||||
|
],
|
||||||
|
"main": "lib/src/index.js",
|
||||||
|
"types": "lib/src/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"clean": "shx rm -rf _bundles lib test_temp",
|
||||||
|
"copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
|
||||||
|
"lint": "tslint src/**/*.ts test/**/*.ts",
|
||||||
|
"prepublishOnly": "run-p build",
|
||||||
|
"run_mocha": "mocha lib/test/**/*_test.js",
|
||||||
|
"test": "run-s clean build copy_test_fixtures run_mocha",
|
||||||
|
"test:circleci": "yarn test"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/0xProject/0x.js.git"
|
||||||
|
},
|
||||||
|
"author": "Brandon Millman",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/0xProject/0x.js/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
|
||||||
|
"dependencies": {
|
||||||
|
"@0xproject/assert": "0.0.4",
|
||||||
|
"@0xproject/json-schemas": "0.6.7",
|
||||||
|
"0x.js": "~0.25.1",
|
||||||
|
"bignumber.js": "~4.1.0",
|
||||||
|
"isomorphic-fetch": "^2.2.1",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"query-string": "^5.0.1",
|
||||||
|
"websocket": "^1.0.25"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@0xproject/tslint-config": "0.1.0",
|
||||||
|
"@types/fetch-mock": "^5.12.1",
|
||||||
|
"@types/lodash": "^4.14.77",
|
||||||
|
"@types/mocha": "^2.2.42",
|
||||||
|
"@types/query-string": "^5.0.1",
|
||||||
|
"@types/websocket": "^0.0.34",
|
||||||
|
"chai": "^4.0.1",
|
||||||
|
"chai-as-promised": "^7.1.0",
|
||||||
|
"chai-as-promised-typescript-typings": "0.0.3",
|
||||||
|
"chai-typescript-typings": "^0.0.1",
|
||||||
|
"copyfiles": "^1.2.0",
|
||||||
|
"dirty-chai": "^2.0.1",
|
||||||
|
"fetch-mock": "^5.13.1",
|
||||||
|
"mocha": "^4.0.0",
|
||||||
|
"npm-run-all": "^4.0.2",
|
||||||
|
"shx": "^0.2.2",
|
||||||
|
"tslint": "5.8.0",
|
||||||
|
"typescript": "~2.6.1",
|
||||||
|
"web3-typescript-typings": "^0.7.1"
|
||||||
|
}
|
||||||
|
}
|
6
packages/connect/src/globals.d.ts
vendored
Normal file
6
packages/connect/src/globals.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
declare module 'dirty-chai';
|
||||||
|
|
||||||
|
declare module '*.json' {
|
||||||
|
const value: any;
|
||||||
|
export default value;
|
||||||
|
}
|
171
packages/connect/src/http_client.ts
Normal file
171
packages/connect/src/http_client.ts
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import 'isomorphic-fetch';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
import * as queryString from 'query-string';
|
||||||
|
import {assert} from '@0xproject/assert';
|
||||||
|
import {schemas} from '@0xproject/json-schemas';
|
||||||
|
import {SignedOrder} from '0x.js';
|
||||||
|
import {
|
||||||
|
Client,
|
||||||
|
FeesRequest,
|
||||||
|
FeesResponse,
|
||||||
|
OrderbookRequest,
|
||||||
|
OrderbookResponse,
|
||||||
|
OrdersRequest,
|
||||||
|
TokenPairsItem,
|
||||||
|
TokenPairsRequest,
|
||||||
|
} from './types';
|
||||||
|
import {schemas as clientSchemas} from './schemas/schemas';
|
||||||
|
import {typeConverters} from './utils/type_converters';
|
||||||
|
|
||||||
|
interface RequestOptions {
|
||||||
|
params?: object;
|
||||||
|
payload?: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RequestType {
|
||||||
|
Get = 'GET',
|
||||||
|
Post = 'POST',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class includes all the functionality related to interacting with a set of HTTP endpoints
|
||||||
|
* that implement the standard relayer API v0
|
||||||
|
*/
|
||||||
|
export class HttpClient implements Client {
|
||||||
|
private apiEndpointUrl: string;
|
||||||
|
/**
|
||||||
|
* Instantiates a new HttpClient instance
|
||||||
|
* @param url The base url for making API calls
|
||||||
|
* @return An instance of HttpClient
|
||||||
|
*/
|
||||||
|
constructor(url: string) {
|
||||||
|
assert.isHttpUrl('url', url);
|
||||||
|
this.apiEndpointUrl = url;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve token pair info from the API
|
||||||
|
* @param request A TokenPairsRequest instance describing specific token information
|
||||||
|
* to retrieve
|
||||||
|
* @return The resulting TokenPairsItems that match the request
|
||||||
|
*/
|
||||||
|
public async getTokenPairsAsync(request?: TokenPairsRequest): Promise<TokenPairsItem[]> {
|
||||||
|
if (!_.isUndefined(request)) {
|
||||||
|
assert.doesConformToSchema('request', request, clientSchemas.relayerTokenPairsRequestSchema);
|
||||||
|
}
|
||||||
|
const requestOpts = {
|
||||||
|
params: request,
|
||||||
|
};
|
||||||
|
const tokenPairs = await this._requestAsync('/token_pairs', RequestType.Get, requestOpts);
|
||||||
|
assert.doesConformToSchema(
|
||||||
|
'tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
|
||||||
|
_.each(tokenPairs, (tokenPair: object) => {
|
||||||
|
typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
|
||||||
|
'tokenA.minAmount',
|
||||||
|
'tokenA.maxAmount',
|
||||||
|
'tokenB.minAmount',
|
||||||
|
'tokenB.maxAmount',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
return tokenPairs;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve orders from the API
|
||||||
|
* @param request An OrdersRequest instance describing specific orders to retrieve
|
||||||
|
* @return The resulting SignedOrders that match the request
|
||||||
|
*/
|
||||||
|
public async getOrdersAsync(request?: OrdersRequest): Promise<SignedOrder[]> {
|
||||||
|
if (!_.isUndefined(request)) {
|
||||||
|
assert.doesConformToSchema('request', request, clientSchemas.relayerOrdersRequestSchema);
|
||||||
|
}
|
||||||
|
const requestOpts = {
|
||||||
|
params: request,
|
||||||
|
};
|
||||||
|
const orders = await this._requestAsync(`/orders`, RequestType.Get, requestOpts);
|
||||||
|
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
|
||||||
|
_.each(orders, (order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve a specific order from the API
|
||||||
|
* @param orderHash An orderHash generated from the desired order
|
||||||
|
* @return The SignedOrder that matches the supplied orderHash
|
||||||
|
*/
|
||||||
|
public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
|
||||||
|
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
|
||||||
|
const order = await this._requestAsync(`/order/${orderHash}`, RequestType.Get);
|
||||||
|
assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
|
||||||
|
typeConverters.convertOrderStringFieldsToBigNumber(order);
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve an orderbook from the API
|
||||||
|
* @param request An OrderbookRequest instance describing the specific orderbook to retrieve
|
||||||
|
* @return The resulting OrderbookResponse that matches the request
|
||||||
|
*/
|
||||||
|
public async getOrderbookAsync(request: OrderbookRequest): Promise<OrderbookResponse> {
|
||||||
|
assert.doesConformToSchema('request', request, clientSchemas.relayerOrderBookRequestSchema);
|
||||||
|
const requestOpts = {
|
||||||
|
params: request,
|
||||||
|
};
|
||||||
|
const orderBook = await this._requestAsync('/orderbook', RequestType.Get, requestOpts);
|
||||||
|
assert.doesConformToSchema('orderBook', orderBook, schemas.relayerApiOrderBookResponseSchema);
|
||||||
|
typeConverters.convertOrderbookStringFieldsToBigNumber(orderBook);
|
||||||
|
return orderBook;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve fee information from the API
|
||||||
|
* @param request A FeesRequest instance describing the specific fees to retrieve
|
||||||
|
* @return The resulting FeesResponse that matches the request
|
||||||
|
*/
|
||||||
|
public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
|
||||||
|
assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
|
||||||
|
typeConverters.convertBigNumberFieldsToStrings(request, [
|
||||||
|
'makerTokenAmount',
|
||||||
|
'takerTokenAmount',
|
||||||
|
'expirationUnixTimestampSec',
|
||||||
|
'salt',
|
||||||
|
]);
|
||||||
|
const requestOpts = {
|
||||||
|
payload: request,
|
||||||
|
};
|
||||||
|
const fees = await this._requestAsync('/fees', RequestType.Post, requestOpts);
|
||||||
|
assert.doesConformToSchema('fees', fees, schemas.relayerApiFeesResponseSchema);
|
||||||
|
typeConverters.convertStringsFieldsToBigNumbers(fees, ['makerFee', 'takerFee']);
|
||||||
|
return fees;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Submit a signed order to the API
|
||||||
|
* @param signedOrder A SignedOrder instance to submit
|
||||||
|
*/
|
||||||
|
public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> {
|
||||||
|
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
|
||||||
|
const requestOpts = {
|
||||||
|
payload: signedOrder,
|
||||||
|
};
|
||||||
|
await this._requestAsync('/order', RequestType.Post, requestOpts);
|
||||||
|
}
|
||||||
|
private async _requestAsync(path: string, requestType: RequestType, requestOptions?: RequestOptions): Promise<any> {
|
||||||
|
const params = _.get(requestOptions, 'params');
|
||||||
|
const payload = _.get(requestOptions, 'payload');
|
||||||
|
let query = '';
|
||||||
|
if (!_.isUndefined(params) && !_.isEmpty(params)) {
|
||||||
|
const stringifiedParams = queryString.stringify(params);
|
||||||
|
query = `?${stringifiedParams}`;
|
||||||
|
}
|
||||||
|
const url = `${this.apiEndpointUrl}/v0${path}${query}`;
|
||||||
|
const headers = new Headers({
|
||||||
|
'content-type': 'application/json',
|
||||||
|
});
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: requestType,
|
||||||
|
body: payload,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error(response.statusText);
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
15
packages/connect/src/index.ts
Normal file
15
packages/connect/src/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export {HttpClient} from './http_client';
|
||||||
|
export {WebSocketOrderbookChannel} from './ws_orderbook_channel';
|
||||||
|
export {
|
||||||
|
Client,
|
||||||
|
FeesRequest,
|
||||||
|
FeesResponse,
|
||||||
|
OrderbookChannel,
|
||||||
|
OrderbookChannelHandler,
|
||||||
|
OrderbookChannelSubscriptionOpts,
|
||||||
|
OrderbookRequest,
|
||||||
|
OrderbookResponse,
|
||||||
|
OrdersRequest,
|
||||||
|
TokenPairsItem,
|
||||||
|
TokenPairsRequest,
|
||||||
|
} from './types';
|
@ -0,0 +1,8 @@
|
|||||||
|
export const relayerOrderBookRequestSchema = {
|
||||||
|
id: '/RelayerOrderBookRequest',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
baseTokenAddress: {$ref: '/Address'},
|
||||||
|
quoteTokenAddress: {$ref: '/Address'},
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
export const relayerOrderBookRequestSchema = {
|
||||||
|
id: '/RelayerOrderBookRequest',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
baseTokenAddress: {$ref: '/Address'},
|
||||||
|
quoteTokenAddress: {$ref: '/Address'},
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
export const relayerOrdersRequestSchema = {
|
||||||
|
id: '/RelayerOrdersRequest',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
exchangeContractAddress: {$ref: '/Address'},
|
||||||
|
tokenAddress: {$ref: '/Address'},
|
||||||
|
makerTokenAddress: {$ref: '/Address'},
|
||||||
|
takerTokenAddress: {$ref: '/Address'},
|
||||||
|
tokenA: {$ref: '/Address'},
|
||||||
|
tokenB: {$ref: '/Address'},
|
||||||
|
maker: {$ref: '/Address'},
|
||||||
|
taker: {$ref: '/Address'},
|
||||||
|
trader: {$ref: '/Address'},
|
||||||
|
feeRecipient: {$ref: '/Address'},
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
export const relayerTokenPairsRequestSchema = {
|
||||||
|
id: '/RelayerTokenPairsRequest',
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
tokenA: {$ref: '/Address'},
|
||||||
|
tokenB: {$ref: '/Address'},
|
||||||
|
},
|
||||||
|
};
|
15
packages/connect/src/schemas/schemas.ts
Normal file
15
packages/connect/src/schemas/schemas.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import {
|
||||||
|
relayerOrderBookRequestSchema,
|
||||||
|
} from './relayer_orderbook_request_schema';
|
||||||
|
import {
|
||||||
|
relayerOrdersRequestSchema,
|
||||||
|
} from './relayer_orders_request_schema';
|
||||||
|
import {
|
||||||
|
relayerTokenPairsRequestSchema,
|
||||||
|
} from './relayer_token_pairs_request_schema';
|
||||||
|
|
||||||
|
export const schemas = {
|
||||||
|
relayerOrderBookRequestSchema,
|
||||||
|
relayerOrdersRequestSchema,
|
||||||
|
relayerTokenPairsRequestSchema,
|
||||||
|
};
|
120
packages/connect/src/types.ts
Normal file
120
packages/connect/src/types.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import {SignedOrder} from '0x.js';
|
||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
|
||||||
|
export interface Client {
|
||||||
|
getTokenPairsAsync: (request?: TokenPairsRequest) => Promise<TokenPairsItem[]>;
|
||||||
|
getOrdersAsync: (request?: OrdersRequest) => Promise<SignedOrder[]>;
|
||||||
|
getOrderAsync: (orderHash: string) => Promise<SignedOrder>;
|
||||||
|
getOrderbookAsync: (request: OrderbookRequest) => Promise<OrderbookResponse>;
|
||||||
|
getFeesAsync: (request: FeesRequest) => Promise<FeesResponse>;
|
||||||
|
submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderbookChannel {
|
||||||
|
subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler) => void;
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderbookChannelHandler {
|
||||||
|
onSnapshot: (channel: OrderbookChannel, snapshot: OrderbookResponse) => void;
|
||||||
|
onUpdate: (channel: OrderbookChannel, order: SignedOrder) => void;
|
||||||
|
onError: (channel: OrderbookChannel, err: Error) => void;
|
||||||
|
onClose: (channel: OrderbookChannel) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OrderbookChannelMessage =
|
||||||
|
SnapshotOrderbookChannelMessage |
|
||||||
|
UpdateOrderbookChannelMessage |
|
||||||
|
UnknownOrderbookChannelMessage;
|
||||||
|
|
||||||
|
export enum OrderbookChannelMessageTypes {
|
||||||
|
Snapshot = 'snapshot',
|
||||||
|
Update = 'update',
|
||||||
|
Unknown = 'unknown',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SnapshotOrderbookChannelMessage {
|
||||||
|
type: OrderbookChannelMessageTypes.Snapshot;
|
||||||
|
payload: OrderbookResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateOrderbookChannelMessage {
|
||||||
|
type: OrderbookChannelMessageTypes.Update;
|
||||||
|
payload: SignedOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnknownOrderbookChannelMessage {
|
||||||
|
type: OrderbookChannelMessageTypes.Unknown;
|
||||||
|
payload: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* baseTokenAddress: The address of token designated as the baseToken in the currency pair calculation of price
|
||||||
|
* quoteTokenAddress: The address of token designated as the quoteToken in the currency pair calculation of price
|
||||||
|
* snapshot: If true, a snapshot of the orderbook will be sent before the updates to the orderbook
|
||||||
|
* limit: Maximum number of bids and asks in orderbook snapshot
|
||||||
|
*/
|
||||||
|
export interface OrderbookChannelSubscriptionOpts {
|
||||||
|
baseTokenAddress: string;
|
||||||
|
quoteTokenAddress: string;
|
||||||
|
snapshot: boolean;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenPairsRequest {
|
||||||
|
tokenA?: string;
|
||||||
|
tokenB?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenPairsItem {
|
||||||
|
tokenA: TokenTradeInfo;
|
||||||
|
tokenB: TokenTradeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenTradeInfo {
|
||||||
|
address: string;
|
||||||
|
minAmount: BigNumber;
|
||||||
|
maxAmount: BigNumber;
|
||||||
|
precision: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrdersRequest {
|
||||||
|
exchangeContractAddress?: string;
|
||||||
|
tokenAddress?: string;
|
||||||
|
makerTokenAddress?: string;
|
||||||
|
takerTokenAddress?: string;
|
||||||
|
tokenA?: string;
|
||||||
|
tokenB?: string;
|
||||||
|
maker?: string;
|
||||||
|
taker?: string;
|
||||||
|
trader?: string;
|
||||||
|
feeRecipient?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderbookRequest {
|
||||||
|
baseTokenAddress: string;
|
||||||
|
quoteTokenAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderbookResponse {
|
||||||
|
bids: SignedOrder[];
|
||||||
|
asks: SignedOrder[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FeesRequest {
|
||||||
|
exchangeContractAddress: string;
|
||||||
|
maker: string;
|
||||||
|
taker: string;
|
||||||
|
makerTokenAddress: string;
|
||||||
|
takerTokenAddress: string;
|
||||||
|
makerTokenAmount: BigNumber;
|
||||||
|
takerTokenAmount: BigNumber;
|
||||||
|
expirationUnixTimestampSec: BigNumber;
|
||||||
|
salt: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FeesResponse {
|
||||||
|
feeRecipient: string;
|
||||||
|
makerFee: BigNumber;
|
||||||
|
takerFee: BigNumber;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
import {SignedOrder} from '0x.js';
|
||||||
|
import {assert} from '@0xproject/assert';
|
||||||
|
import {schemas} from '@0xproject/json-schemas';
|
||||||
|
import {
|
||||||
|
OrderbookChannelMessage,
|
||||||
|
OrderbookChannelMessageTypes,
|
||||||
|
} from '../types';
|
||||||
|
import {typeConverters} from './type_converters';
|
||||||
|
|
||||||
|
export const orderbookChannelMessageParsers = {
|
||||||
|
parser(utf8Data: string): OrderbookChannelMessage {
|
||||||
|
const messageObj = JSON.parse(utf8Data);
|
||||||
|
const type: string = _.get(messageObj, 'type');
|
||||||
|
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
|
||||||
|
switch (type) {
|
||||||
|
case (OrderbookChannelMessageTypes.Snapshot): {
|
||||||
|
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
|
||||||
|
const orderbook = messageObj.payload;
|
||||||
|
typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook);
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
payload: orderbook,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case (OrderbookChannelMessageTypes.Update): {
|
||||||
|
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
|
||||||
|
const order = messageObj.payload;
|
||||||
|
typeConverters.convertOrderStringFieldsToBigNumber(order);
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
payload: order,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {
|
||||||
|
type: OrderbookChannelMessageTypes.Unknown,
|
||||||
|
payload: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
31
packages/connect/src/utils/type_converters.ts
Normal file
31
packages/connect/src/utils/type_converters.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
|
||||||
|
// TODO: convert all of these to non-mutating, pure functions
|
||||||
|
export const typeConverters = {
|
||||||
|
convertOrderbookStringFieldsToBigNumber(orderbook: object): void {
|
||||||
|
_.each(orderbook, (orders: object[]) => {
|
||||||
|
_.each(orders, (order: object) => this.convertOrderStringFieldsToBigNumber(order));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
convertOrderStringFieldsToBigNumber(order: object): void {
|
||||||
|
this.convertStringsFieldsToBigNumbers(order, [
|
||||||
|
'makerTokenAmount',
|
||||||
|
'takerTokenAmount',
|
||||||
|
'makerFee',
|
||||||
|
'takerFee',
|
||||||
|
'expirationUnixTimestampSec',
|
||||||
|
'salt',
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
convertBigNumberFieldsToStrings(obj: object, fields: string[]): void {
|
||||||
|
_.each(fields, field => {
|
||||||
|
_.update(obj, field, (value: BigNumber) => value.toString());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
convertStringsFieldsToBigNumbers(obj: object, fields: string[]): void {
|
||||||
|
_.each(fields, field => {
|
||||||
|
_.update(obj, field, (value: string) => new BigNumber(value));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
127
packages/connect/src/ws_orderbook_channel.ts
Normal file
127
packages/connect/src/ws_orderbook_channel.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as WebSocket from 'websocket';
|
||||||
|
import {assert} from '@0xproject/assert';
|
||||||
|
import {schemas} from '@0xproject/json-schemas';
|
||||||
|
import {SignedOrder} from '0x.js';
|
||||||
|
import {
|
||||||
|
OrderbookChannel,
|
||||||
|
OrderbookChannelHandler,
|
||||||
|
OrderbookChannelMessageTypes,
|
||||||
|
OrderbookChannelSubscriptionOpts,
|
||||||
|
} from './types';
|
||||||
|
import {orderbookChannelMessageParsers} from './utils/orderbook_channel_message_parsers';
|
||||||
|
|
||||||
|
enum ConnectionEventType {
|
||||||
|
Close = 'close',
|
||||||
|
Error = 'error',
|
||||||
|
Message = 'message',
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ClientEventType {
|
||||||
|
Connect = 'connect',
|
||||||
|
ConnectFailed = 'connectFailed',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class includes all the functionality related to interacting with a websocket endpoint
|
||||||
|
* that implements the standard relayer API v0
|
||||||
|
*/
|
||||||
|
export class WebSocketOrderbookChannel implements OrderbookChannel {
|
||||||
|
private apiEndpointUrl: string;
|
||||||
|
private client: WebSocket.client;
|
||||||
|
private connectionIfExists?: WebSocket.connection;
|
||||||
|
/**
|
||||||
|
* Instantiates a new WebSocketOrderbookChannel instance
|
||||||
|
* @param url The base url for making API calls
|
||||||
|
* @return An instance of WebSocketOrderbookChannel
|
||||||
|
*/
|
||||||
|
constructor(url: string) {
|
||||||
|
assert.isUri('url', url);
|
||||||
|
this.apiEndpointUrl = url;
|
||||||
|
this.client = new WebSocket.client();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Subscribe to orderbook snapshots and updates from the websocket
|
||||||
|
* @param subscriptionOpts An OrderbookChannelSubscriptionOpts instance describing which
|
||||||
|
* token pair to subscribe to
|
||||||
|
* @param handler An OrderbookChannelHandler instance that responds to various
|
||||||
|
* channel updates
|
||||||
|
*/
|
||||||
|
public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
|
||||||
|
assert.doesConformToSchema(
|
||||||
|
'subscriptionOpts', subscriptionOpts, schemas.relayerApiOrderbookChannelSubscribePayload);
|
||||||
|
assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
|
||||||
|
assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
|
||||||
|
assert.isFunction('handler.onError', _.get(handler, 'onError'));
|
||||||
|
assert.isFunction('handler.onClose', _.get(handler, 'onClose'));
|
||||||
|
const subscribeMessage = {
|
||||||
|
type: 'subscribe',
|
||||||
|
channel: 'orderbook',
|
||||||
|
payload: subscriptionOpts,
|
||||||
|
};
|
||||||
|
this._getConnection((error, connection) => {
|
||||||
|
if (!_.isUndefined(error)) {
|
||||||
|
handler.onError(this, error);
|
||||||
|
} else if (!_.isUndefined(connection) && connection.connected) {
|
||||||
|
connection.on(ConnectionEventType.Error, wsError => {
|
||||||
|
handler.onError(this, wsError);
|
||||||
|
});
|
||||||
|
connection.on(ConnectionEventType.Close, () => {
|
||||||
|
handler.onClose(this);
|
||||||
|
});
|
||||||
|
connection.on(ConnectionEventType.Message, message => {
|
||||||
|
this._handleWebSocketMessage(message, handler);
|
||||||
|
});
|
||||||
|
connection.sendUTF(JSON.stringify(subscribeMessage));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Close the websocket and stop receiving updates
|
||||||
|
*/
|
||||||
|
public close() {
|
||||||
|
if (!_.isUndefined(this.connectionIfExists)) {
|
||||||
|
this.connectionIfExists.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _getConnection(callback: (error?: Error, connection?: WebSocket.connection) => void) {
|
||||||
|
if (!_.isUndefined(this.connectionIfExists) && this.connectionIfExists.connected) {
|
||||||
|
callback(undefined, this.connectionIfExists);
|
||||||
|
} else {
|
||||||
|
this.client.on(ClientEventType.Connect, connection => {
|
||||||
|
this.connectionIfExists = connection;
|
||||||
|
callback(undefined, this.connectionIfExists);
|
||||||
|
});
|
||||||
|
this.client.on(ClientEventType.ConnectFailed, error => {
|
||||||
|
callback(error, undefined);
|
||||||
|
});
|
||||||
|
this.client.connect(this.apiEndpointUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _handleWebSocketMessage(message: WebSocket.IMessage, handler: OrderbookChannelHandler): void {
|
||||||
|
if (!_.isUndefined(message.utf8Data)) {
|
||||||
|
try {
|
||||||
|
const utf8Data = message.utf8Data;
|
||||||
|
const parserResult = orderbookChannelMessageParsers.parser(utf8Data);
|
||||||
|
const type = parserResult.type;
|
||||||
|
switch (parserResult.type) {
|
||||||
|
case (OrderbookChannelMessageTypes.Snapshot): {
|
||||||
|
handler.onSnapshot(this, parserResult.payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (OrderbookChannelMessageTypes.Update): {
|
||||||
|
handler.onUpdate(this, parserResult.payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
handler.onError(this, new Error(`Message has missing a type parameter: ${utf8Data}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
handler.onError(this, error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handler.onError(this, new Error(`Message does not contain utf8Data`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
packages/connect/test/fixtures/standard_relayer_api/fees.json
vendored
Normal file
5
packages/connect/test/fixtures/standard_relayer_api/fees.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"feeRecipient": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"makerFee": "10000000000000000",
|
||||||
|
"takerFee": "30000000000000000"
|
||||||
|
}
|
8
packages/connect/test/fixtures/standard_relayer_api/fees.ts
vendored
Normal file
8
packages/connect/test/fixtures/standard_relayer_api/fees.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
import {FeesResponse} from '../../../src/types';
|
||||||
|
|
||||||
|
export const feesResponse: FeesResponse = {
|
||||||
|
feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
makerFee: new BigNumber('10000000000000000'),
|
||||||
|
takerFee: new BigNumber('30000000000000000'),
|
||||||
|
};
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
|
||||||
|
"makerFee": "100000000000000",
|
||||||
|
"takerFee": "200000000000000",
|
||||||
|
"makerTokenAmount": "10000000000000000",
|
||||||
|
"takerTokenAmount": "20000000000000000",
|
||||||
|
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
|
||||||
|
"salt": "256",
|
||||||
|
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
|
||||||
|
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
|
||||||
|
"expirationUnixTimestampSec": "42",
|
||||||
|
"ecSignature": {
|
||||||
|
"v": 27,
|
||||||
|
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
|
||||||
|
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
|
||||||
|
export const orderResponse = {
|
||||||
|
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
makerFee: new BigNumber('100000000000000'),
|
||||||
|
takerFee: new BigNumber('200000000000000'),
|
||||||
|
makerTokenAmount: new BigNumber('10000000000000000'),
|
||||||
|
takerTokenAmount: new BigNumber('20000000000000000'),
|
||||||
|
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
salt: new BigNumber('256'),
|
||||||
|
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
|
||||||
|
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
|
||||||
|
expirationUnixTimestampSec: new BigNumber('42'),
|
||||||
|
ecSignature: {
|
||||||
|
v: 27,
|
||||||
|
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
||||||
|
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
||||||
|
},
|
||||||
|
};
|
44
packages/connect/test/fixtures/standard_relayer_api/orderbook.json
vendored
Normal file
44
packages/connect/test/fixtures/standard_relayer_api/orderbook.json
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"bids": [
|
||||||
|
{
|
||||||
|
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
|
||||||
|
"makerFee": "100000000000000",
|
||||||
|
"takerFee": "200000000000000",
|
||||||
|
"makerTokenAmount": "10000000000000000",
|
||||||
|
"takerTokenAmount": "20000000000000000",
|
||||||
|
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
|
||||||
|
"salt": "256",
|
||||||
|
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
|
||||||
|
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
|
||||||
|
"expirationUnixTimestampSec": "42",
|
||||||
|
"ecSignature": {
|
||||||
|
"v": 27,
|
||||||
|
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
|
||||||
|
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"asks": [
|
||||||
|
{
|
||||||
|
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
|
||||||
|
"makerFee": "100000000000000",
|
||||||
|
"takerFee": "200000000000000",
|
||||||
|
"makerTokenAmount": "10000000000000000",
|
||||||
|
"takerTokenAmount": "20000000000000000",
|
||||||
|
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
|
||||||
|
"salt": "256",
|
||||||
|
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da",
|
||||||
|
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093",
|
||||||
|
"expirationUnixTimestampSec": "42",
|
||||||
|
"ecSignature": {
|
||||||
|
"v": 27,
|
||||||
|
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
|
||||||
|
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
46
packages/connect/test/fixtures/standard_relayer_api/orderbook.ts
vendored
Normal file
46
packages/connect/test/fixtures/standard_relayer_api/orderbook.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
|
||||||
|
export const orderbookResponse = {
|
||||||
|
bids: [
|
||||||
|
{
|
||||||
|
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
makerFee: new BigNumber('100000000000000'),
|
||||||
|
takerFee: new BigNumber('200000000000000'),
|
||||||
|
makerTokenAmount: new BigNumber('10000000000000000'),
|
||||||
|
takerTokenAmount: new BigNumber('20000000000000000'),
|
||||||
|
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
salt: new BigNumber('256'),
|
||||||
|
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
|
||||||
|
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
|
||||||
|
expirationUnixTimestampSec: new BigNumber('42'),
|
||||||
|
ecSignature: {
|
||||||
|
v: 27,
|
||||||
|
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
||||||
|
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
asks: [
|
||||||
|
{
|
||||||
|
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
makerFee: new BigNumber('100000000000000'),
|
||||||
|
takerFee: new BigNumber('200000000000000'),
|
||||||
|
makerTokenAmount: new BigNumber('10000000000000000'),
|
||||||
|
takerTokenAmount: new BigNumber('20000000000000000'),
|
||||||
|
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
salt: new BigNumber('256'),
|
||||||
|
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da',
|
||||||
|
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
|
||||||
|
expirationUnixTimestampSec: new BigNumber('42'),
|
||||||
|
ecSignature: {
|
||||||
|
v: 27,
|
||||||
|
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
||||||
|
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
21
packages/connect/test/fixtures/standard_relayer_api/orders.json
vendored
Normal file
21
packages/connect/test/fixtures/standard_relayer_api/orders.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
|
||||||
|
"makerFee": "100000000000000",
|
||||||
|
"takerFee": "200000000000000",
|
||||||
|
"makerTokenAmount": "10000000000000000",
|
||||||
|
"takerTokenAmount": "20000000000000000",
|
||||||
|
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990",
|
||||||
|
"salt": "256",
|
||||||
|
"feeRecipient": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"exchangeContractAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
|
||||||
|
"expirationUnixTimestampSec": "42",
|
||||||
|
"ecSignature": {
|
||||||
|
"v": 27,
|
||||||
|
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
|
||||||
|
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
23
packages/connect/test/fixtures/standard_relayer_api/orders.ts
vendored
Normal file
23
packages/connect/test/fixtures/standard_relayer_api/orders.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
|
||||||
|
export const ordersResponse = [
|
||||||
|
{
|
||||||
|
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
makerFee: new BigNumber('100000000000000'),
|
||||||
|
takerFee: new BigNumber('200000000000000'),
|
||||||
|
makerTokenAmount: new BigNumber('10000000000000000'),
|
||||||
|
takerTokenAmount: new BigNumber('20000000000000000'),
|
||||||
|
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
salt: new BigNumber('256'),
|
||||||
|
feeRecipient: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
exchangeContractAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
expirationUnixTimestampSec: new BigNumber('42'),
|
||||||
|
ecSignature: {
|
||||||
|
v: 27,
|
||||||
|
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
||||||
|
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
17
packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts
vendored
Normal file
17
packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import * as orderbookJSON from './orderbook.json';
|
||||||
|
|
||||||
|
const orderbookJsonString = JSON.stringify(orderbookJSON);
|
||||||
|
|
||||||
|
export const snapshotOrderbookChannelMessage = `{
|
||||||
|
"type": "snapshot",
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": ${orderbookJsonString}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const malformedSnapshotOrderbookChannelMessage = `{
|
||||||
|
"type": "snapshot",
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": {}
|
||||||
|
}`;
|
16
packages/connect/test/fixtures/standard_relayer_api/token_pairs.json
vendored
Normal file
16
packages/connect/test/fixtures/standard_relayer_api/token_pairs.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"tokenA": {
|
||||||
|
"address": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
|
||||||
|
"minAmount": "0",
|
||||||
|
"maxAmount": "10000000000000000000",
|
||||||
|
"precision": 5
|
||||||
|
},
|
||||||
|
"tokenB": {
|
||||||
|
"address": "0xef7fff64389b814a946f3e92105513705ca6b990",
|
||||||
|
"minAmount": "0",
|
||||||
|
"maxAmount": "50000000000000000000",
|
||||||
|
"precision": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
19
packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts
vendored
Normal file
19
packages/connect/test/fixtures/standard_relayer_api/token_pairs.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
import {TokenPairsItem} from '../../../src/types';
|
||||||
|
|
||||||
|
export const tokenPairsResponse: TokenPairsItem[] = [
|
||||||
|
{
|
||||||
|
tokenA: {
|
||||||
|
address: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
minAmount: new BigNumber(0),
|
||||||
|
maxAmount: new BigNumber('10000000000000000000'),
|
||||||
|
precision: 5,
|
||||||
|
},
|
||||||
|
tokenB: {
|
||||||
|
address: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
minAmount: new BigNumber(0),
|
||||||
|
maxAmount: new BigNumber('50000000000000000000'),
|
||||||
|
precision: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
10
packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts
vendored
Normal file
10
packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import * as orderResponseJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
|
||||||
|
|
||||||
|
const orderJSONString = JSON.stringify(orderResponseJSON);
|
||||||
|
|
||||||
|
export const unknownOrderbookChannelMessage = `{
|
||||||
|
"type": "superGoodUpdate",
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": ${orderJSONString}
|
||||||
|
}`;
|
17
packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts
vendored
Normal file
17
packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import * as orderResponseJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
|
||||||
|
|
||||||
|
const orderJSONString = JSON.stringify(orderResponseJSON);
|
||||||
|
|
||||||
|
export const updateOrderbookChannelMessage = `{
|
||||||
|
"type": "update",
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": ${orderJSONString}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const malformedUpdateOrderbookChannelMessage = `{
|
||||||
|
"type": "update",
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": {}
|
||||||
|
}`;
|
130
packages/connect/test/http_client_test.ts
Normal file
130
packages/connect/test/http_client_test.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import * as dirtyChai from 'dirty-chai';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import * as chaiAsPromised from 'chai-as-promised';
|
||||||
|
import * as fetchMock from 'fetch-mock';
|
||||||
|
import {BigNumber} from 'bignumber.js';
|
||||||
|
import {HttpClient} from '../src/index';
|
||||||
|
import {feesResponse} from './fixtures/standard_relayer_api/fees';
|
||||||
|
import {
|
||||||
|
orderResponse,
|
||||||
|
} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
|
||||||
|
import {ordersResponse} from './fixtures/standard_relayer_api/orders';
|
||||||
|
import {tokenPairsResponse} from './fixtures/standard_relayer_api/token_pairs';
|
||||||
|
import {orderbookResponse} from './fixtures/standard_relayer_api/orderbook';
|
||||||
|
import * as feesResponseJSON from './fixtures/standard_relayer_api/fees.json';
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
|
||||||
|
import * as ordersResponseJSON from './fixtures/standard_relayer_api/orders.json';
|
||||||
|
import * as tokenPairsResponseJSON from './fixtures/standard_relayer_api/token_pairs.json';
|
||||||
|
import * as orderbookJSON from './fixtures/standard_relayer_api/orderbook.json';
|
||||||
|
|
||||||
|
chai.config.includeStack = true;
|
||||||
|
chai.use(dirtyChai);
|
||||||
|
chai.use(chaiAsPromised);
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
describe('HttpClient', () => {
|
||||||
|
const relayUrl = 'https://example.com';
|
||||||
|
const relayerClient = new HttpClient(relayUrl);
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
describe('#getTokenPairsAsync', () => {
|
||||||
|
const url = `${relayUrl}/v0/token_pairs`;
|
||||||
|
it('gets token pairs', async () => {
|
||||||
|
fetchMock.get(url, tokenPairsResponseJSON);
|
||||||
|
const tokenPairs = await relayerClient.getTokenPairsAsync();
|
||||||
|
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
|
||||||
|
});
|
||||||
|
it('gets specfic token pairs for request', async () => {
|
||||||
|
const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
|
||||||
|
const tokenPairsRequest = {
|
||||||
|
tokenA: tokenAddress,
|
||||||
|
};
|
||||||
|
const urlWithQuery = `${url}?tokenA=${tokenAddress}`;
|
||||||
|
fetchMock.get(urlWithQuery, tokenPairsResponseJSON);
|
||||||
|
const tokenPairs = await relayerClient.getTokenPairsAsync(tokenPairsRequest);
|
||||||
|
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
|
||||||
|
});
|
||||||
|
it('throws an error for invalid JSON response', async () => {
|
||||||
|
fetchMock.get(url, {test: 'dummy'});
|
||||||
|
expect(relayerClient.getTokenPairsAsync()).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getOrdersAsync', () => {
|
||||||
|
const url = `${relayUrl}/v0/orders`;
|
||||||
|
it('gets orders', async () => {
|
||||||
|
fetchMock.get(url, ordersResponseJSON);
|
||||||
|
const orders = await relayerClient.getOrdersAsync();
|
||||||
|
expect(orders).to.be.deep.equal(ordersResponse);
|
||||||
|
});
|
||||||
|
it('gets specfic orders for request', async () => {
|
||||||
|
const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
|
||||||
|
const ordersRequest = {
|
||||||
|
tokenA: tokenAddress,
|
||||||
|
};
|
||||||
|
const urlWithQuery = `${url}?tokenA=${tokenAddress}`;
|
||||||
|
fetchMock.get(urlWithQuery, ordersResponseJSON);
|
||||||
|
const orders = await relayerClient.getOrdersAsync(ordersRequest);
|
||||||
|
expect(orders).to.be.deep.equal(ordersResponse);
|
||||||
|
});
|
||||||
|
it('throws an error for invalid JSON response', async () => {
|
||||||
|
fetchMock.get(url, {test: 'dummy'});
|
||||||
|
expect(relayerClient.getOrdersAsync()).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getOrderAsync', () => {
|
||||||
|
const orderHash = '0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
|
||||||
|
const url = `${relayUrl}/v0/order/${orderHash}`;
|
||||||
|
it('gets order', async () => {
|
||||||
|
fetchMock.get(url, orderResponseJSON);
|
||||||
|
const order = await relayerClient.getOrderAsync(orderHash);
|
||||||
|
expect(order).to.be.deep.equal(orderResponse);
|
||||||
|
});
|
||||||
|
it('throws an error for invalid JSON response', async () => {
|
||||||
|
fetchMock.get(url, {test: 'dummy'});
|
||||||
|
expect(relayerClient.getOrderAsync(orderHash)).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getOrderBookAsync', () => {
|
||||||
|
const request = {
|
||||||
|
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
};
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
const url = `${relayUrl}/v0/orderbook?baseTokenAddress=${request.baseTokenAddress}"eTokenAddress=${request.quoteTokenAddress}`;
|
||||||
|
it('gets order book', async () => {
|
||||||
|
fetchMock.get(url, orderbookJSON);
|
||||||
|
const orderbook = await relayerClient.getOrderbookAsync(request);
|
||||||
|
expect(orderbook).to.be.deep.equal(orderbookResponse);
|
||||||
|
});
|
||||||
|
it('throws an error for invalid JSON response', async () => {
|
||||||
|
fetchMock.get(url, {test: 'dummy'});
|
||||||
|
expect(relayerClient.getOrderbookAsync(request)).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('#getFeesAsync', () => {
|
||||||
|
const request = {
|
||||||
|
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
|
||||||
|
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
|
||||||
|
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
|
||||||
|
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
makerTokenAmount: new BigNumber('10000000000000000000'),
|
||||||
|
takerTokenAmount: new BigNumber('30000000000000000000'),
|
||||||
|
salt: new BigNumber('256'),
|
||||||
|
expirationUnixTimestampSec: new BigNumber('42'),
|
||||||
|
};
|
||||||
|
const url = `${relayUrl}/v0/fees`;
|
||||||
|
it('gets fees', async () => {
|
||||||
|
fetchMock.post(url, feesResponseJSON);
|
||||||
|
const fees = await relayerClient.getFeesAsync(request);
|
||||||
|
expect(fees).to.be.deep.equal(feesResponse);
|
||||||
|
});
|
||||||
|
it('throws an error for invalid JSON response', async () => {
|
||||||
|
fetchMock.post(url, {test: 'dummy'});
|
||||||
|
expect(relayerClient.getFeesAsync(request)).to.be.rejected();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,66 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import * as dirtyChai from 'dirty-chai';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import {orderbookChannelMessageParsers} from '../src/utils/orderbook_channel_message_parsers';
|
||||||
|
import {
|
||||||
|
snapshotOrderbookChannelMessage,
|
||||||
|
malformedSnapshotOrderbookChannelMessage,
|
||||||
|
} from './fixtures/standard_relayer_api/snapshot_orderbook_channel_message';
|
||||||
|
import {
|
||||||
|
updateOrderbookChannelMessage,
|
||||||
|
malformedUpdateOrderbookChannelMessage,
|
||||||
|
} from './fixtures/standard_relayer_api/update_orderbook_channel_message';
|
||||||
|
import {unknownOrderbookChannelMessage} from './fixtures/standard_relayer_api/unknown_orderbook_channel_message';
|
||||||
|
import {orderbookResponse} from './fixtures/standard_relayer_api/orderbook';
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
import {orderResponse} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
|
||||||
|
|
||||||
|
chai.config.includeStack = true;
|
||||||
|
chai.use(dirtyChai);
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
describe('orderbookChannelMessageParsers', () => {
|
||||||
|
describe('#parser', () => {
|
||||||
|
it('parses snapshot messages', () => {
|
||||||
|
const snapshotMessage = orderbookChannelMessageParsers.parser(snapshotOrderbookChannelMessage);
|
||||||
|
expect(snapshotMessage.type).to.be.equal('snapshot');
|
||||||
|
expect(snapshotMessage.payload).to.be.deep.equal(orderbookResponse);
|
||||||
|
});
|
||||||
|
it('parses update messages', () => {
|
||||||
|
const updateMessage = orderbookChannelMessageParsers.parser(updateOrderbookChannelMessage);
|
||||||
|
expect(updateMessage.type).to.be.equal('update');
|
||||||
|
expect(updateMessage.payload).to.be.deep.equal(orderResponse);
|
||||||
|
});
|
||||||
|
it('returns unknown message for messages with unsupported types', () => {
|
||||||
|
const unknownMessage = orderbookChannelMessageParsers.parser(unknownOrderbookChannelMessage);
|
||||||
|
expect(unknownMessage.type).to.be.equal('unknown');
|
||||||
|
expect(unknownMessage.payload).to.be.undefined();
|
||||||
|
});
|
||||||
|
it('throws when message does not include a type', () => {
|
||||||
|
const typelessMessage = `{
|
||||||
|
"channel": "orderbook",
|
||||||
|
"channelId": 1,
|
||||||
|
"payload": {}
|
||||||
|
}`;
|
||||||
|
const badCall = () => orderbookChannelMessageParsers.parser(typelessMessage);
|
||||||
|
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
|
||||||
|
});
|
||||||
|
it('throws when snapshot message has malformed payload', () => {
|
||||||
|
const badCall = () =>
|
||||||
|
orderbookChannelMessageParsers.parser(malformedSnapshotOrderbookChannelMessage);
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
const errMsg = 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
|
||||||
|
expect(badCall).throws(errMsg);
|
||||||
|
});
|
||||||
|
it('throws when update message has malformed payload', () => {
|
||||||
|
const badCall = () =>
|
||||||
|
orderbookChannelMessageParsers.parser(malformedUpdateOrderbookChannelMessage);
|
||||||
|
expect(badCall).throws(/^Expected message to conform to schema/);
|
||||||
|
});
|
||||||
|
it('throws when input message is not valid JSON', () => {
|
||||||
|
const nonJsonString = 'h93b{sdfs9fsd f';
|
||||||
|
const badCall = () => orderbookChannelMessageParsers.parser(nonJsonString);
|
||||||
|
expect(badCall).throws('Unexpected token h in JSON at position 0');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
46
packages/connect/test/ws_orderbook_channel_test.ts
Normal file
46
packages/connect/test/ws_orderbook_channel_test.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'mocha';
|
||||||
|
import * as dirtyChai from 'dirty-chai';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import {
|
||||||
|
WebSocketOrderbookChannel,
|
||||||
|
} from '../src/index';
|
||||||
|
|
||||||
|
chai.config.includeStack = true;
|
||||||
|
chai.use(dirtyChai);
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
describe('WebSocketOrderbookChannel', () => {
|
||||||
|
const websocketUrl = 'ws://localhost:8080';
|
||||||
|
const orderbookChannel = new WebSocketOrderbookChannel(websocketUrl);
|
||||||
|
const subscriptionOpts = {
|
||||||
|
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||||
|
quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||||
|
snapshot: true,
|
||||||
|
limit: 100,
|
||||||
|
};
|
||||||
|
const emptyOrderbookChannelHandler = {
|
||||||
|
onSnapshot: () => { return; },
|
||||||
|
onUpdate: () => { return; },
|
||||||
|
onError: () => { return; },
|
||||||
|
onClose: () => { return; },
|
||||||
|
};
|
||||||
|
describe('#subscribe', () => {
|
||||||
|
it('throws when subscriptionOpts does not conform to schema', () => {
|
||||||
|
const badSubscribeCall = orderbookChannel.subscribe.bind(
|
||||||
|
orderbookChannel, {}, emptyOrderbookChannelHandler);
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
expect(badSubscribeCall)
|
||||||
|
.throws('Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"');
|
||||||
|
});
|
||||||
|
it('throws when handler has the incorrect members', () => {
|
||||||
|
const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {});
|
||||||
|
expect(badSubscribeCall)
|
||||||
|
.throws('Expected handler.onSnapshot to be of type function, encountered: undefined');
|
||||||
|
});
|
||||||
|
it('does not throw when inputs are of correct types', () => {
|
||||||
|
const goodSubscribeCall = orderbookChannel.subscribe.bind(
|
||||||
|
orderbookChannel, subscriptionOpts, emptyOrderbookChannelHandler);
|
||||||
|
expect(goodSubscribeCall).to.not.throw();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
19
packages/connect/tsconfig.json
Normal file
19
packages/connect/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"lib": [ "es2015", "dom" ],
|
||||||
|
"outDir": "lib",
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*",
|
||||||
|
"./test/**/*",
|
||||||
|
"../../node_modules/chai-as-promised-typescript-typings/index.d.ts",
|
||||||
|
"../../node_modules/chai-typescript-typings/index.d.ts",
|
||||||
|
"../../node_modules/web3-typescript-typings/index.d.ts"
|
||||||
|
]
|
||||||
|
}
|
5
packages/connect/tslint.json
Normal file
5
packages/connect/tslint.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"@0xproject/tslint-config"
|
||||||
|
]
|
||||||
|
}
|
67
yarn.lock
67
yarn.lock
@ -2,6 +2,10 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/fetch-mock@^5.12.1":
|
||||||
|
version "5.12.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-5.12.2.tgz#8c96517ff74303031c65c5da2d99858e34c844d2"
|
||||||
|
|
||||||
"@types/fs-extra@^4.0.0":
|
"@types/fs-extra@^4.0.0":
|
||||||
version "4.0.4"
|
version "4.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.4.tgz#72947e108f2cbeda5ab288a927399fdf6d02bd42"
|
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.4.tgz#72947e108f2cbeda5ab288a927399fdf6d02bd42"
|
||||||
@ -41,7 +45,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/lodash" "*"
|
"@types/lodash" "*"
|
||||||
|
|
||||||
"@types/lodash@*", "@types/lodash@^4.14.37", "@types/lodash@^4.14.64", "@types/lodash@^4.14.78":
|
"@types/lodash@*", "@types/lodash@^4.14.37", "@types/lodash@^4.14.64", "@types/lodash@^4.14.77", "@types/lodash@^4.14.78":
|
||||||
version "4.14.85"
|
version "4.14.85"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.85.tgz#a16fbf942422f6eca5622b6910492c496c35069b"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.85.tgz#a16fbf942422f6eca5622b6910492c496c35069b"
|
||||||
|
|
||||||
@ -61,6 +65,10 @@
|
|||||||
version "8.0.51"
|
version "8.0.51"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
|
||||||
|
|
||||||
|
"@types/query-string@^5.0.1":
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.0.1.tgz#6cb41c724cb1644d56c2d1dae7c7b204e706b39e"
|
||||||
|
|
||||||
"@types/shelljs@^0.7.0":
|
"@types/shelljs@^0.7.0":
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.5.tgz#5834fb7385d1137bd2be5842f2c278ac36a117f4"
|
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.5.tgz#5834fb7385d1137bd2be5842f2c278ac36a117f4"
|
||||||
@ -82,6 +90,12 @@
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45"
|
resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45"
|
||||||
|
|
||||||
|
"@types/websocket@^0.0.34":
|
||||||
|
version "0.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-0.0.34.tgz#25596764cec885eda070fdb6d19cd76fe582747c"
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
JSONStream@^1.0.4:
|
JSONStream@^1.0.4:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a"
|
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a"
|
||||||
@ -2281,6 +2295,14 @@ fast-json-stable-stringify@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
|
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
|
||||||
|
|
||||||
|
fetch-mock@^5.13.1:
|
||||||
|
version "5.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-5.13.1.tgz#955794a77f3d972f1644b9ace65a0fdfd60f1df7"
|
||||||
|
dependencies:
|
||||||
|
glob-to-regexp "^0.3.0"
|
||||||
|
node-fetch "^1.3.3"
|
||||||
|
path-to-regexp "^1.7.0"
|
||||||
|
|
||||||
fetch-ponyfill@^4.0.0:
|
fetch-ponyfill@^4.0.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893"
|
resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893"
|
||||||
@ -2599,6 +2621,10 @@ glob-parent@^3.1.0:
|
|||||||
is-glob "^3.1.0"
|
is-glob "^3.1.0"
|
||||||
path-dirname "^1.0.0"
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
|
glob-to-regexp@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
||||||
|
|
||||||
glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.2:
|
glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.2:
|
||||||
version "7.1.2"
|
version "7.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||||
@ -3140,7 +3166,7 @@ is-text-path@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
text-extensions "^1.0.0"
|
text-extensions "^1.0.0"
|
||||||
|
|
||||||
is-typedarray@~1.0.0:
|
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||||
|
|
||||||
@ -3174,7 +3200,7 @@ isobject@^3.0.0, isobject@^3.0.1:
|
|||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
|
|
||||||
isomorphic-fetch@^2.2.0:
|
isomorphic-fetch@^2.2.0, isomorphic-fetch@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3881,7 +3907,7 @@ mute-stream@0.0.7, mute-stream@~0.0.4:
|
|||||||
version "0.0.7"
|
version "0.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||||
|
|
||||||
nan@^2.0.5, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0:
|
nan@^2.0.5, nan@^2.0.8, nan@^2.2.1, nan@^2.3.0, nan@^2.3.3:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
|
||||||
|
|
||||||
@ -3917,7 +3943,7 @@ node-abi@^2.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^5.4.1"
|
semver "^5.4.1"
|
||||||
|
|
||||||
node-fetch@^1.0.1, node-fetch@~1.7.1:
|
node-fetch@^1.0.1, node-fetch@^1.3.3, node-fetch@~1.7.1:
|
||||||
version "1.7.3"
|
version "1.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4519,6 +4545,14 @@ qs@~6.5.1:
|
|||||||
version "6.5.1"
|
version "6.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
|
||||||
|
|
||||||
|
query-string@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.0.1.tgz#6e2b86fe0e08aef682ecbe86e85834765402bd88"
|
||||||
|
dependencies:
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
querystring-es3@^0.2.0:
|
querystring-es3@^0.2.0:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||||
@ -5279,6 +5313,10 @@ stream-http@^2.3.1:
|
|||||||
to-arraybuffer "^1.0.0"
|
to-arraybuffer "^1.0.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
|
strict-uri-encode@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
|
||||||
string-editor@^0.1.0:
|
string-editor@^0.1.0:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/string-editor/-/string-editor-0.1.2.tgz#f5ff1b5ac4aed7ac6c2fb8de236d1551b20f61d0"
|
resolved "https://registry.yarnpkg.com/string-editor/-/string-editor-0.1.2.tgz#f5ff1b5ac4aed7ac6c2fb8de236d1551b20f61d0"
|
||||||
@ -5667,6 +5705,12 @@ type-detect@^4.0.0:
|
|||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
|
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
|
||||||
|
|
||||||
|
typedarray-to-buffer@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.2.tgz#1017b32d984ff556eba100f501589aba1ace2e04"
|
||||||
|
dependencies:
|
||||||
|
is-typedarray "^1.0.0"
|
||||||
|
|
||||||
typedarray@^0.0.6:
|
typedarray@^0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
@ -5978,6 +6022,15 @@ webpack@^3.0.0, webpack@^3.1.0:
|
|||||||
webpack-sources "^1.0.1"
|
webpack-sources "^1.0.1"
|
||||||
yargs "^8.0.2"
|
yargs "^8.0.2"
|
||||||
|
|
||||||
|
websocket@^1.0.25:
|
||||||
|
version "1.0.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.25.tgz#998ec790f0a3eacb8b08b50a4350026692a11958"
|
||||||
|
dependencies:
|
||||||
|
debug "^2.2.0"
|
||||||
|
nan "^2.3.3"
|
||||||
|
typedarray-to-buffer "^3.1.2"
|
||||||
|
yaeti "^0.0.6"
|
||||||
|
|
||||||
whatwg-fetch@>=0.10.0:
|
whatwg-fetch@>=0.10.0:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
||||||
@ -6094,6 +6147,10 @@ y18n@^3.2.1:
|
|||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||||
|
|
||||||
|
yaeti@^0.0.6:
|
||||||
|
version "0.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||||
|
|
||||||
yallist@^2.1.2:
|
yallist@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user