merge development

This commit is contained in:
Fabio Berger 2018-08-22 23:46:45 +01:00
commit 61255309d2
58 changed files with 999 additions and 809 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "2.0.0-rc.1",
"changes": [
{
"note": "Updated for SRA v2",
"pr": 974
}
]
},
{ {
"timestamp": 1534210131, "timestamp": 1534210131,
"version": "2.0.0", "version": "2.0.0",

View File

@ -44,14 +44,15 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md",
"dependencies": { "dependencies": {
"@0xproject/assert": "^0.2.14", "@0xproject/assert": "^1.0.5",
"@0xproject/json-schemas": "^0.8.3", "@0xproject/json-schemas": "^1.0.1-rc.4",
"@0xproject/types": "^0.8.2", "@0xproject/types": "^1.0.1-rc.4",
"@0xproject/typescript-typings": "^1.0.4", "@0xproject/typescript-typings": "^1.0.4",
"@0xproject/utils": "^1.0.5", "@0xproject/utils": "^1.0.5",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"sinon": "^4.0.0", "sinon": "^4.0.0",
"uuid": "^3.3.2",
"websocket": "^1.0.25" "websocket": "^1.0.25"
}, },
"devDependencies": { "devDependencies": {
@ -61,6 +62,7 @@
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/query-string": "^5.0.1", "@types/query-string": "^5.0.1",
"@types/sinon": "^2.2.2", "@types/sinon": "^2.2.2",
"@types/uuid": "^3.4.3",
"@types/websocket": "^0.0.39", "@types/websocket": "^0.0.39",
"async-child-process": "^1.1.1", "async-child-process": "^1.1.1",
"chai": "^4.0.1", "chai": "^4.0.1",

View File

@ -7,31 +7,25 @@ import * as queryString from 'query-string';
import { schemas as clientSchemas } from './schemas/schemas'; import { schemas as clientSchemas } from './schemas/schemas';
import { import {
APIOrder,
AssetPairsRequestOpts,
AssetPairsResponse,
Client, Client,
FeesRequest, FeeRecipientsResponse,
FeesResponse,
HttpRequestOptions, HttpRequestOptions,
HttpRequestType, HttpRequestType,
OrderbookRequest, OrderbookRequest,
OrderbookResponse, OrderbookResponse,
OrderConfigRequest,
OrderConfigResponse,
OrdersRequestOpts, OrdersRequestOpts,
OrdersResponse,
PagedRequestOpts, PagedRequestOpts,
TokenPairsItem, RequestOpts,
TokenPairsRequestOpts,
} from './types'; } from './types';
import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers'; import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers';
const TRAILING_SLASHES_REGEX = /\/+$/; const TRAILING_SLASHES_REGEX = /\/+$/;
const DEFAULT_PAGED_REQUEST_OPTS: PagedRequestOpts = {
page: 1,
perPage: 100,
};
/**
* This mapping defines how an option property name gets converted into an HTTP request query field
*/
const OPTS_TO_QUERY_FIELD_MAP = {
perPage: 'per_page',
};
/** /**
* This class includes all the functionality related to interacting with a set of HTTP endpoints * This class includes all the functionality related to interacting with a set of HTTP endpoints
@ -47,12 +41,8 @@ export class HttpClient implements Client {
if (_.isUndefined(params) || _.isEmpty(params)) { if (_.isUndefined(params) || _.isEmpty(params)) {
return ''; return '';
} }
// format params into a form the api expects
const formattedParams = _.mapKeys(params, (_value: any, key: string) => {
return _.get(OPTS_TO_QUERY_FIELD_MAP, key, key);
});
// stringify the formatted object // stringify the formatted object
const stringifiedParams = queryString.stringify(formattedParams); const stringifiedParams = queryString.stringify(params);
return `?${stringifiedParams}`; return `?${stringifiedParams}`;
} }
/** /**
@ -65,34 +55,40 @@ export class HttpClient implements Client {
this._apiEndpointUrl = url.replace(TRAILING_SLASHES_REGEX, ''); // remove trailing slashes this._apiEndpointUrl = url.replace(TRAILING_SLASHES_REGEX, ''); // remove trailing slashes
} }
/** /**
* Retrieve token pair info from the API * Retrieve assetData pair info from the API
* @param requestOpts Options specifying token information to retrieve and page information, defaults to { page: 1, perPage: 100 } * @param requestOpts Options specifying assetData information to retrieve, page information, and network id.
* @return The resulting TokenPairsItems that match the request * @return The resulting AssetPairsResponse that match the request
*/ */
public async getTokenPairsAsync(requestOpts?: TokenPairsRequestOpts & PagedRequestOpts): Promise<TokenPairsItem[]> { public async getAssetPairsAsync(
requestOpts?: RequestOpts & AssetPairsRequestOpts & PagedRequestOpts,
): Promise<AssetPairsResponse> {
if (!_.isUndefined(requestOpts)) { if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.tokenPairsRequestOptsSchema); assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.assetPairsRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema); assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
} }
const httpRequestOpts = { const httpRequestOpts = {
params: _.defaults({}, requestOpts, DEFAULT_PAGED_REQUEST_OPTS), params: requestOpts,
}; };
const responseJson = await this._requestAsync('/token_pairs', HttpRequestType.Get, httpRequestOpts); const responseJson = await this._requestAsync('/asset_pairs', HttpRequestType.Get, httpRequestOpts);
const tokenPairs = relayerResponseJsonParsers.parseTokenPairsJson(responseJson); const assetDataPairs = relayerResponseJsonParsers.parseAssetDataPairsJson(responseJson);
return tokenPairs; return assetDataPairs;
} }
/** /**
* Retrieve orders from the API * Retrieve orders from the API
* @param requestOpts Options specifying orders to retrieve and page information, defaults to { page: 1, perPage: 100 } * @param requestOpts Options specifying orders to retrieve and page information, page information, and network id.
* @return The resulting SignedOrders that match the request * @return The resulting OrdersResponse that match the request
*/ */
public async getOrdersAsync(requestOpts?: OrdersRequestOpts & PagedRequestOpts): Promise<SignedOrder[]> { public async getOrdersAsync(
requestOpts?: RequestOpts & OrdersRequestOpts & PagedRequestOpts,
): Promise<OrdersResponse> {
if (!_.isUndefined(requestOpts)) { if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.ordersRequestOptsSchema); assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.ordersRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema); assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
} }
const httpRequestOpts = { const httpRequestOpts = {
params: _.defaults({}, requestOpts, DEFAULT_PAGED_REQUEST_OPTS), params: requestOpts,
}; };
const responseJson = await this._requestAsync(`/orders`, HttpRequestType.Get, httpRequestOpts); const responseJson = await this._requestAsync(`/orders`, HttpRequestType.Get, httpRequestOpts);
const orders = relayerResponseJsonParsers.parseOrdersJson(responseJson); const orders = relayerResponseJsonParsers.parseOrdersJson(responseJson);
@ -101,30 +97,37 @@ export class HttpClient implements Client {
/** /**
* Retrieve a specific order from the API * Retrieve a specific order from the API
* @param orderHash An orderHash generated from the desired order * @param orderHash An orderHash generated from the desired order
* @return The SignedOrder that matches the supplied orderHash * @return The APIOrder that matches the supplied orderHash
*/ */
public async getOrderAsync(orderHash: string): Promise<SignedOrder> { public async getOrderAsync(orderHash: string, requestOpts?: RequestOpts): Promise<APIOrder> {
if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
}
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
const responseJson = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get); const httpRequestOpts = {
const order = relayerResponseJsonParsers.parseOrderJson(responseJson); params: requestOpts,
};
const responseJson = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get, httpRequestOpts);
const order = relayerResponseJsonParsers.parseAPIOrderJson(responseJson);
return order; return order;
} }
/** /**
* Retrieve an orderbook from the API * Retrieve an orderbook from the API
* @param request An OrderbookRequest instance describing the specific orderbook to retrieve * @param request An OrderbookRequest instance describing the specific orderbook to retrieve
* @param requestOpts Options specifying page information, defaults to { page: 1, perPage: 100 } * @param requestOpts Options specifying page information, and network id.
* @return The resulting OrderbookResponse that matches the request * @return The resulting OrderbookResponse that matches the request
*/ */
public async getOrderbookAsync( public async getOrderbookAsync(
request: OrderbookRequest, request: OrderbookRequest,
requestOpts?: PagedRequestOpts, requestOpts?: RequestOpts & PagedRequestOpts,
): Promise<OrderbookResponse> { ): Promise<OrderbookResponse> {
assert.doesConformToSchema('request', request, clientSchemas.orderBookRequestSchema); assert.doesConformToSchema('request', request, clientSchemas.orderBookRequestSchema);
if (!_.isUndefined(requestOpts)) { if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema); assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
} }
const httpRequestOpts = { const httpRequestOpts = {
params: _.defaults({}, request, requestOpts, DEFAULT_PAGED_REQUEST_OPTS), params: _.defaults({}, request, requestOpts),
}; };
const responseJson = await this._requestAsync('/orderbook', HttpRequestType.Get, httpRequestOpts); const responseJson = await this._requestAsync('/orderbook', HttpRequestType.Get, httpRequestOpts);
const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(responseJson); const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(responseJson);
@ -132,28 +135,55 @@ export class HttpClient implements Client {
} }
/** /**
* Retrieve fee information from the API * Retrieve fee information from the API
* @param request A FeesRequest instance describing the specific fees to retrieve * @param request A OrderConfigRequest instance describing the specific fees to retrieve
* @return The resulting FeesResponse that matches the request * @param requestOpts Options specifying network id.
* @return The resulting OrderConfigResponse that matches the request
*/ */
public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> { public async getOrderConfigAsync(
assert.doesConformToSchema('request', request, clientSchemas.feesRequestSchema); request: OrderConfigRequest,
requestOpts?: RequestOpts,
): Promise<OrderConfigResponse> {
if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
}
assert.doesConformToSchema('request', request, clientSchemas.orderConfigRequestSchema);
const httpRequestOpts = { const httpRequestOpts = {
params: requestOpts,
payload: request, payload: request,
}; };
const responseJson = await this._requestAsync('/fees', HttpRequestType.Post, httpRequestOpts); const responseJson = await this._requestAsync('/order_config', HttpRequestType.Post, httpRequestOpts);
const fees = relayerResponseJsonParsers.parseFeesResponseJson(responseJson); const fees = relayerResponseJsonParsers.parseOrderConfigResponseJson(responseJson);
return fees; return fees;
} }
/**
* Retrieve the list of fee recipient addresses used by the relayer.
* @param requestOpts Options specifying page information, and network id.
* @return The resulting FeeRecipientsResponse
*/
public async getFeeRecipientsAsync(requestOpts?: RequestOpts & PagedRequestOpts): Promise<FeeRecipientsResponse> {
if (!_.isUndefined(requestOpts)) {
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.pagedRequestOptsSchema);
assert.doesConformToSchema('requestOpts', requestOpts, clientSchemas.requestOptsSchema);
}
const httpRequestOpts = {
params: requestOpts,
};
const feeRecipients = await this._requestAsync('/fee_recipients', HttpRequestType.Get, httpRequestOpts);
assert.doesConformToSchema('feeRecipients', feeRecipients, schemas.relayerApiFeeRecipientsResponseSchema);
return feeRecipients;
}
/** /**
* Submit a signed order to the API * Submit a signed order to the API
* @param signedOrder A SignedOrder instance to submit * @param signedOrder A SignedOrder instance to submit
* @param requestOpts Options specifying network id.
*/ */
public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> { public async submitOrderAsync(signedOrder: SignedOrder, requestOpts?: RequestOpts): Promise<void> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
const requestOpts = { const httpRequestOpts = {
params: requestOpts,
payload: signedOrder, payload: signedOrder,
}; };
await this._requestAsync('/order', HttpRequestType.Post, requestOpts); await this._requestAsync('/order', HttpRequestType.Post, httpRequestOpts);
} }
private async _requestAsync( private async _requestAsync(
path: string, path: string,

View File

@ -1,19 +1,19 @@
export { HttpClient } from './http_client'; export { HttpClient } from './http_client';
export { orderbookChannelFactory } from './orderbook_channel_factory'; export { ordersChannelFactory } from './orders_channel_factory';
export { export {
Client, Client,
FeesRequest, OrderConfigRequest,
FeesResponse, OrderConfigResponse,
OrderbookChannel, OrdersChannel,
OrderbookChannelHandler, OrdersChannelHandler,
OrderbookChannelSubscriptionOpts, OrdersChannelSubscriptionOpts,
OrderbookRequest, OrderbookRequest,
OrderbookResponse, OrderbookResponse,
OrdersRequestOpts, OrdersRequestOpts,
PagedRequestOpts, PagedRequestOpts,
TokenPairsItem, AssetPairsItem,
TokenPairsRequestOpts, AssetPairsRequestOpts,
TokenTradeInfo, Asset,
} from './types'; } from './types';
export { SignedOrder } from '@0xproject/types'; export { SignedOrder } from '@0xproject/types';

View File

@ -1,32 +0,0 @@
import * as WebSocket from 'websocket';
import { OrderbookChannel, OrderbookChannelHandler } from './types';
import { assert } from './utils/assert';
import { WebSocketOrderbookChannel } from './ws_orderbook_channel';
export const orderbookChannelFactory = {
/**
* Instantiates a new WebSocketOrderbookChannel instance
* @param url The relayer API base WS url you would like to interact with
* @param handler An OrderbookChannelHandler instance that responds to various
* channel updates
* @return An OrderbookChannel Promise
*/
async createWebSocketOrderbookChannelAsync(
url: string,
handler: OrderbookChannelHandler,
): Promise<OrderbookChannel> {
assert.isUri('url', url);
assert.isOrderbookChannelHandler('handler', handler);
return new Promise<OrderbookChannel>((resolve, reject) => {
const client = new WebSocket.w3cwebsocket(url);
client.onopen = () => {
const orderbookChannel = new WebSocketOrderbookChannel(client, handler);
resolve(orderbookChannel);
};
client.onerror = err => {
reject(err);
};
});
},
};

View File

@ -0,0 +1,29 @@
import * as WebSocket from 'websocket';
import { OrdersChannel, OrdersChannelHandler } from './types';
import { assert } from './utils/assert';
import { WebSocketOrdersChannel } from './ws_orders_channel';
export const ordersChannelFactory = {
/**
* Instantiates a new WebSocketOrdersChannel instance
* @param url The relayer API base WS url you would like to interact with
* @param handler An OrdersChannelHandler instance that responds to various
* channel updates
* @return An OrdersChannel Promise
*/
async createWebSocketOrdersChannelAsync(url: string, handler: OrdersChannelHandler): Promise<OrdersChannel> {
assert.isUri('url', url);
assert.isOrdersChannelHandler('handler', handler);
return new Promise<OrdersChannel>((resolve, reject) => {
const client = new WebSocket.w3cwebsocket(url);
client.onopen = () => {
const ordersChannel = new WebSocketOrdersChannel(client, handler);
resolve(ordersChannel);
};
client.onerror = err => {
reject(err);
};
});
},
};

View File

@ -0,0 +1,8 @@
export const assetPairsRequestOptsSchema = {
id: '/AssetPairsRequestOpts',
type: 'object',
properties: {
assetDataA: { $ref: '/hexSchema' },
assetDataB: { $ref: '/hexSchema' },
},
};

View File

@ -1,26 +0,0 @@
export const feesRequestSchema = {
id: '/FeesRequest',
type: 'object',
properties: {
exchangeContractAddress: { $ref: '/Address' },
maker: { $ref: '/Address' },
taker: { $ref: '/Address' },
makerTokenAddress: { $ref: '/Address' },
takerTokenAddress: { $ref: '/Address' },
makerTokenAmount: { $ref: '/Number' },
takerTokenAmount: { $ref: '/Number' },
expirationUnixTimestampSec: { $ref: '/Number' },
salt: { $ref: '/Number' },
},
required: [
'exchangeContractAddress',
'maker',
'taker',
'makerTokenAddress',
'takerTokenAddress',
'makerTokenAmount',
'takerTokenAmount',
'expirationUnixTimestampSec',
'salt',
],
};

View File

@ -0,0 +1,24 @@
export const orderConfigRequestSchema = {
id: '/OrderConfigRequest',
type: 'object',
properties: {
makerAddress: { $ref: '/addressSchema' },
takerAddress: { $ref: '/addressSchema' },
makerAssetAmount: { $ref: '/numberSchema' },
takerAssetAmount: { $ref: '/numberSchema' },
makerAssetData: { $ref: '/hexSchema' },
takerAssetData: { $ref: '/hexSchema' },
exchangeAddress: { $ref: '/addressSchema' },
expirationTimeSeconds: { $ref: '/numberSchema' },
},
required: [
'makerAddress',
'takerAddress',
'makerAssetAmount',
'takerAssetAmount',
'makerAssetData',
'takerAssetData',
'exchangeAddress',
'expirationTimeSeconds',
],
};

View File

@ -2,8 +2,8 @@ export const orderBookRequestSchema = {
id: '/OrderBookRequest', id: '/OrderBookRequest',
type: 'object', type: 'object',
properties: { properties: {
baseTokenAddress: { $ref: '/Address' }, baseAssetData: { $ref: '/hexSchema' },
quoteTokenAddress: { $ref: '/Address' }, quoteAssetData: { $ref: '/hexSchema' },
}, },
required: ['baseTokenAddress', 'quoteTokenAddress'], required: ['baseAssetData', 'quoteAssetData'],
}; };

View File

@ -2,15 +2,18 @@ export const ordersRequestOptsSchema = {
id: '/OrdersRequestOpts', id: '/OrdersRequestOpts',
type: 'object', type: 'object',
properties: { properties: {
exchangeContractAddress: { $ref: '/Address' }, makerAssetProxyId: { $ref: '/hexSchema' },
tokenAddress: { $ref: '/Address' }, takerAssetProxyId: { $ref: '/hexSchema' },
makerTokenAddress: { $ref: '/Address' }, makerAssetAddress: { $ref: '/addressSchema' },
takerTokenAddress: { $ref: '/Address' }, takerAssetAddress: { $ref: '/addressSchema' },
tokenA: { $ref: '/Address' }, exchangeAddress: { $ref: '/addressSchema' },
tokenB: { $ref: '/Address' }, senderAddress: { $ref: '/addressSchema' },
maker: { $ref: '/Address' }, makerAssetData: { $ref: '/hexSchema' },
taker: { $ref: '/Address' }, takerAssetData: { $ref: '/hexSchema' },
trader: { $ref: '/Address' }, traderAssetData: { $ref: '/hexSchema' },
feeRecipient: { $ref: '/Address' }, makerAddress: { $ref: '/addressSchema' },
takerAddress: { $ref: '/addressSchema' },
traderAddress: { $ref: '/addressSchema' },
feeRecipientAddress: { $ref: '/addressSchema' },
}, },
}; };

View File

@ -0,0 +1,7 @@
export const requestOptsSchema = {
id: '/RequestOpts',
type: 'object',
properties: {
networkId: { type: 'number' },
},
};

View File

@ -1,13 +1,15 @@
import { feesRequestSchema } from './fees_request_schema'; import { assetPairsRequestOptsSchema } from './asset_pairs_request_opts_schema';
import { orderConfigRequestSchema } from './order_config_request_schema';
import { orderBookRequestSchema } from './orderbook_request_schema'; import { orderBookRequestSchema } from './orderbook_request_schema';
import { ordersRequestOptsSchema } from './orders_request_opts_schema'; import { ordersRequestOptsSchema } from './orders_request_opts_schema';
import { pagedRequestOptsSchema } from './paged_request_opts_schema'; import { pagedRequestOptsSchema } from './paged_request_opts_schema';
import { tokenPairsRequestOptsSchema } from './token_pairs_request_opts_schema'; import { requestOptsSchema } from './request_opts_schema';
export const schemas = { export const schemas = {
feesRequestSchema, orderConfigRequestSchema,
orderBookRequestSchema, orderBookRequestSchema,
ordersRequestOptsSchema, ordersRequestOptsSchema,
pagedRequestOptsSchema, pagedRequestOptsSchema,
tokenPairsRequestOptsSchema, requestOptsSchema,
assetPairsRequestOptsSchema,
}; };

View File

@ -1,8 +0,0 @@
export const tokenPairsRequestOptsSchema = {
id: '/TokenPairsRequestOpts',
type: 'object',
properties: {
tokenA: { $ref: '/Address' },
tokenB: { $ref: '/Address' },
},
};

View File

@ -2,73 +2,55 @@ import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
export interface Client { export interface Client {
getTokenPairsAsync: (requestOpts?: TokenPairsRequestOpts & PagedRequestOpts) => Promise<TokenPairsItem[]>; getAssetPairsAsync: (
getOrdersAsync: (requestOpts?: OrdersRequestOpts & PagedRequestOpts) => Promise<SignedOrder[]>; requestOpts?: AssetPairsRequestOpts & PagedRequestOpts,
getOrderAsync: (orderHash: string) => Promise<SignedOrder>; ) => Promise<PaginatedCollection<AssetPairsItem>>;
getOrdersAsync: (requestOpts?: OrdersRequestOpts & PagedRequestOpts) => Promise<PaginatedCollection<APIOrder>>;
getOrderAsync: (orderHash: string) => Promise<APIOrder>;
getOrderbookAsync: (request: OrderbookRequest, requestOpts?: PagedRequestOpts) => Promise<OrderbookResponse>; getOrderbookAsync: (request: OrderbookRequest, requestOpts?: PagedRequestOpts) => Promise<OrderbookResponse>;
getFeesAsync: (request: FeesRequest) => Promise<FeesResponse>; getOrderConfigAsync: (request: OrderConfigRequest) => Promise<OrderConfigResponse>;
getFeeRecipientsAsync: (requestOpts?: PagedRequestOpts) => Promise<FeeRecipientsResponse>;
submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>; submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>;
} }
export interface OrderbookChannel { export interface OrdersChannel {
subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts) => void; subscribe: (subscriptionOpts: OrdersChannelSubscriptionOpts) => void;
close: () => void; close: () => void;
} }
/** /**
* baseTokenAddress: The address of token designated as the baseToken in the currency pair calculation of price * baseAssetData: The address of assetData 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 * quoteAssetData: The address of assetData 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 * limit: Maximum number of bids and asks in orderbook snapshot
*/ */
export interface OrderbookChannelSubscriptionOpts { export interface OrdersChannelSubscriptionOpts {
baseTokenAddress: string; baseAssetData: string;
quoteTokenAddress: string; quoteAssetData: string;
snapshot: boolean;
limit: number; limit: number;
} }
export interface OrderbookChannelHandler { export interface OrdersChannelHandler {
onSnapshot: ( onUpdate: (channel: OrdersChannel, subscriptionOpts: OrdersChannelSubscriptionOpts, orders: APIOrder[]) => void;
channel: OrderbookChannel, onError: (channel: OrdersChannel, err: Error, subscriptionOpts?: OrdersChannelSubscriptionOpts) => void;
subscriptionOpts: OrderbookChannelSubscriptionOpts, onClose: (channel: OrdersChannel) => void;
snapshot: OrderbookResponse,
) => void;
onUpdate: (
channel: OrderbookChannel,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
order: SignedOrder,
) => void;
onError: (channel: OrderbookChannel, err: Error, subscriptionOpts?: OrderbookChannelSubscriptionOpts) => void;
onClose: (channel: OrderbookChannel) => void;
} }
export type OrderbookChannelMessage = export type OrdersChannelMessage = UpdateOrdersChannelMessage | UnknownOrdersChannelMessage;
| SnapshotOrderbookChannelMessage
| UpdateOrderbookChannelMessage
| UnknownOrderbookChannelMessage;
export enum OrderbookChannelMessageTypes { export enum OrdersChannelMessageTypes {
Snapshot = 'snapshot',
Update = 'update', Update = 'update',
Unknown = 'unknown', Unknown = 'unknown',
} }
export interface SnapshotOrderbookChannelMessage { export interface UpdateOrdersChannelMessage {
type: OrderbookChannelMessageTypes.Snapshot; type: OrdersChannelMessageTypes.Update;
requestId: number; requestId: string;
payload: OrderbookResponse; payload: APIOrder[];
} }
export interface UpdateOrderbookChannelMessage { export interface UnknownOrdersChannelMessage {
type: OrderbookChannelMessageTypes.Update; type: OrdersChannelMessageTypes.Unknown;
requestId: number; requestId: string;
payload: SignedOrder;
}
export interface UnknownOrderbookChannelMessage {
type: OrderbookChannelMessageTypes.Unknown;
requestId: number;
payload: undefined; payload: undefined;
} }
@ -83,60 +65,86 @@ export enum WebsocketClientEventType {
ConnectFailed = 'connectFailed', ConnectFailed = 'connectFailed',
} }
export interface TokenPairsRequestOpts { export type OrdersResponse = PaginatedCollection<APIOrder>;
tokenA?: string;
tokenB?: string; export interface APIOrder {
order: SignedOrder;
metaData: object;
} }
export interface TokenPairsItem { export interface AssetPairsRequestOpts {
tokenA: TokenTradeInfo; assetDataA?: string;
tokenB: TokenTradeInfo; assetDataB?: string;
} }
export interface TokenTradeInfo { export type AssetPairsResponse = PaginatedCollection<AssetPairsItem>;
address: string;
export interface AssetPairsItem {
assetDataA: Asset;
assetDataB: Asset;
}
export interface Asset {
assetData: string;
minAmount: BigNumber; minAmount: BigNumber;
maxAmount: BigNumber; maxAmount: BigNumber;
precision: number; precision: number;
} }
export interface OrdersRequestOpts { export interface OrdersRequestOpts {
exchangeContractAddress?: string; makerAssetProxyId?: string;
tokenAddress?: string; takerAssetProxyId?: string;
makerTokenAddress?: string; makerAssetAddress?: string;
takerTokenAddress?: string; takerAssetAddress?: string;
maker?: string; exchangeAddress?: string;
taker?: string; senderAddress?: string;
trader?: string; makerAssetData?: string;
feeRecipient?: string; takerAssetData?: string;
makerAddress?: string;
takerAddress?: string;
traderAddress?: string;
feeRecipientAddress?: string;
} }
export interface OrderbookRequest { export interface OrderbookRequest {
baseTokenAddress: string; baseAssetData: string;
quoteTokenAddress: string; quoteAssetData: string;
} }
export interface OrderbookResponse { export interface OrderbookResponse {
bids: SignedOrder[]; bids: PaginatedCollection<APIOrder>;
asks: SignedOrder[]; asks: PaginatedCollection<APIOrder>;
} }
export interface FeesRequest { export interface PaginatedCollection<T> {
exchangeContractAddress: string; total: number;
maker: string; page: number;
taker: string; perPage: number;
makerTokenAddress: string; records: T[];
takerTokenAddress: string;
makerTokenAmount: BigNumber;
takerTokenAmount: BigNumber;
expirationUnixTimestampSec: BigNumber;
salt: BigNumber;
} }
export interface FeesResponse { export interface OrderConfigRequest {
feeRecipient: string; makerAddress: string;
takerAddress: string;
makerAssetAmount: string;
takerAssetAmount: string;
makerAssetData: string;
takerAssetData: string;
exchangeAddress: string;
expirationTimeSeconds: string;
}
export interface OrderConfigResponse {
makerFee: BigNumber; makerFee: BigNumber;
takerFee: BigNumber; takerFee: BigNumber;
feeRecipientAddress: string;
senderAddress: string;
}
export type FeeRecipientsResponse = PaginatedCollection<string>;
export interface RequestOpts {
networkId?: number;
} }
export interface PagedRequestOpts { export interface PagedRequestOpts {

View File

@ -10,15 +10,14 @@ import * as _ from 'lodash';
export const assert = { export const assert = {
...sharedAssert, ...sharedAssert,
isOrderbookChannelSubscriptionOpts(variableName: string, subscriptionOpts: any): void { isOrdersChannelSubscriptionOpts(variableName: string, subscriptionOpts: any): void {
sharedAssert.doesConformToSchema( sharedAssert.doesConformToSchema(
variableName, variableName,
subscriptionOpts, subscriptionOpts,
schemas.relayerApiOrderbookChannelSubscribePayload, schemas.relayerApiOrdersChannelSubscribePayload,
); );
}, },
isOrderbookChannelHandler(variableName: string, handler: any): void { isOrdersChannelHandler(variableName: string, handler: any): void {
sharedAssert.isFunction(`${variableName}.onSnapshot`, _.get(handler, 'onSnapshot'));
sharedAssert.isFunction(`${variableName}.onUpdate`, _.get(handler, 'onUpdate')); sharedAssert.isFunction(`${variableName}.onUpdate`, _.get(handler, 'onUpdate'));
sharedAssert.isFunction(`${variableName}.onError`, _.get(handler, 'onError')); sharedAssert.isFunction(`${variableName}.onError`, _.get(handler, 'onError'));
sharedAssert.isFunction(`${variableName}.onClose`, _.get(handler, 'onClose')); sharedAssert.isFunction(`${variableName}.onClose`, _.get(handler, 'onClose'));

View File

@ -1,43 +0,0 @@
import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash';
import { OrderbookChannelMessage, OrderbookChannelMessageTypes } from '../types';
import { relayerResponseJsonParsers } from './relayer_response_json_parsers';
export const orderbookChannelMessageParser = {
parse(utf8Data: string): OrderbookChannelMessage {
// parse the message
const messageObj = JSON.parse(utf8Data);
// ensure we have a type parameter to switch on
const type: string = _.get(messageObj, 'type');
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
assert.isString('type', type);
// ensure we have a request id for the resulting message
const requestId: number = _.get(messageObj, 'requestId');
assert.assert(!_.isUndefined(requestId), `Message is missing a requestId parameter: ${utf8Data}`);
assert.isNumber('requestId', requestId);
switch (type) {
case OrderbookChannelMessageTypes.Snapshot: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
const orderbookJson = messageObj.payload;
const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
return _.assign(messageObj, { payload: orderbook });
}
case OrderbookChannelMessageTypes.Update: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
const orderJson = messageObj.payload;
const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
return _.assign(messageObj, { payload: order });
}
default: {
return {
type: OrderbookChannelMessageTypes.Unknown,
requestId,
payload: undefined,
};
}
}
},
};

View File

@ -0,0 +1,37 @@
import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash';
import { OrdersChannelMessage, OrdersChannelMessageTypes } from '../types';
import { relayerResponseJsonParsers } from './relayer_response_json_parsers';
export const ordersChannelMessageParser = {
parse(utf8Data: string): OrdersChannelMessage {
// parse the message
const messageObj = JSON.parse(utf8Data);
// ensure we have a type parameter to switch on
const type: string = _.get(messageObj, 'type');
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
assert.isString('type', type);
// ensure we have a request id for the resulting message
const requestId: string = _.get(messageObj, 'requestId');
assert.assert(!_.isUndefined(requestId), `Message is missing a requestId parameter: ${utf8Data}`);
assert.isString('requestId', requestId);
switch (type) {
case OrdersChannelMessageTypes.Update: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrdersChannelUpdateSchema);
const ordersJson = messageObj.payload;
const orders = relayerResponseJsonParsers.parseAPIOrdersJson(ordersJson);
return _.assign(messageObj, { payload: orders });
}
default: {
return {
type: OrdersChannelMessageTypes.Unknown,
requestId,
payload: undefined,
};
}
}
},
};

View File

@ -1,37 +1,49 @@
import { assert } from '@0xproject/assert'; import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas'; import { schemas } from '@0xproject/json-schemas';
import { SignedOrder } from '@0xproject/types';
import { FeesResponse, OrderbookResponse, TokenPairsItem } from '../types'; import {
APIOrder,
AssetPairsItem,
AssetPairsResponse,
OrderbookResponse,
OrderConfigResponse,
OrdersResponse,
} from '../types';
import { typeConverters } from './type_converters'; import { typeConverters } from './type_converters';
export const relayerResponseJsonParsers = { export const relayerResponseJsonParsers = {
parseTokenPairsJson(json: any): TokenPairsItem[] { parseAssetDataPairsJson(json: any): AssetPairsResponse {
assert.doesConformToSchema('tokenPairs', json, schemas.relayerApiTokenPairsResponseSchema); assert.doesConformToSchema('assetDataPairsResponse', json, schemas.relayerApiAssetDataPairsResponseSchema);
return json.map((tokenPair: any) => { return { ...json, records: relayerResponseJsonParsers.parseAssetPairsItemsJson(json.records) };
return typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [ },
'tokenA.minAmount', parseAssetPairsItemsJson(json: any): AssetPairsItem[] {
'tokenA.maxAmount', return json.map((assetDataPair: any) => {
'tokenB.minAmount', return typeConverters.convertStringsFieldsToBigNumbers(assetDataPair, [
'tokenB.maxAmount', 'assetDataA.minAmount',
'assetDataA.maxAmount',
'assetDataB.minAmount',
'assetDataB.maxAmount',
]); ]);
}); });
}, },
parseOrdersJson(json: any): SignedOrder[] { parseOrdersJson(json: any): OrdersResponse {
assert.doesConformToSchema('orders', json, schemas.signedOrdersSchema); assert.doesConformToSchema('relayerApiOrdersResponse', json, schemas.relayerApiOrdersResponseSchema);
return json.map((order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order)); return { ...json, records: relayerResponseJsonParsers.parseAPIOrdersJson(json.records) };
}, },
parseOrderJson(json: any): SignedOrder { parseAPIOrdersJson(json: any): APIOrder[] {
assert.doesConformToSchema('order', json, schemas.signedOrderSchema); return json.map(relayerResponseJsonParsers.parseAPIOrderJson.bind(relayerResponseJsonParsers));
return typeConverters.convertOrderStringFieldsToBigNumber(json); },
parseAPIOrderJson(json: any): APIOrder {
assert.doesConformToSchema('relayerApiOrder', json, schemas.relayerApiOrderSchema);
return typeConverters.convertAPIOrderStringFieldsToBigNumber(json);
}, },
parseOrderbookResponseJson(json: any): OrderbookResponse { parseOrderbookResponseJson(json: any): OrderbookResponse {
assert.doesConformToSchema('orderBook', json, schemas.relayerApiOrderBookResponseSchema); assert.doesConformToSchema('orderBookResponse', json, schemas.relayerApiOrderbookResponseSchema);
return typeConverters.convertOrderbookStringFieldsToBigNumber(json); return typeConverters.convertOrderbookStringFieldsToBigNumber(json);
}, },
parseFeesResponseJson(json: any): FeesResponse { parseOrderConfigResponseJson(json: any): OrderConfigResponse {
assert.doesConformToSchema('fees', json, schemas.relayerApiFeesResponseSchema); assert.doesConformToSchema('orderConfigResponse', json, schemas.relayerApiOrderConfigResponseSchema);
return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']); return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']);
}, },
}; };

View File

@ -1,29 +1,47 @@
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { APIOrder } from '../types';
export const typeConverters = { export const typeConverters = {
convertOrderbookStringFieldsToBigNumber(orderbook: any): any { convertOrderbookStringFieldsToBigNumber(orderbook: any): any {
const bids = _.get(orderbook, 'bids', []); const bids = _.get(orderbook, 'bids', []);
const asks = _.get(orderbook, 'asks', []); const asks = _.get(orderbook, 'asks', []);
return { const convertedBids = {
bids: bids.map((order: any) => typeConverters.convertOrderStringFieldsToBigNumber(order)), ...bids,
asks: asks.map((order: any) => typeConverters.convertOrderStringFieldsToBigNumber(order)), records: bids.records.map((order: any) => typeConverters.convertAPIOrderStringFieldsToBigNumber(order)),
}; };
const convertedAsks = {
...asks,
records: asks.records.map((order: any) => typeConverters.convertAPIOrderStringFieldsToBigNumber(order)),
};
return {
bids: convertedBids,
asks: convertedAsks,
};
},
convertAPIOrderStringFieldsToBigNumber(apiOrder: any): APIOrder {
return { ...apiOrder, order: typeConverters.convertOrderStringFieldsToBigNumber(apiOrder.order) };
}, },
convertOrderStringFieldsToBigNumber(order: any): any { convertOrderStringFieldsToBigNumber(order: any): any {
return typeConverters.convertStringsFieldsToBigNumbers(order, [ return typeConverters.convertStringsFieldsToBigNumbers(order, [
'makerTokenAmount', 'makerAssetAmount',
'takerTokenAmount', 'takerAssetAmount',
'makerFee', 'makerFee',
'takerFee', 'takerFee',
'expirationUnixTimestampSec', 'expirationTimeSeconds',
'salt', 'salt',
]); ]);
}, },
convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any { convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
const result = _.assign({}, obj); const result = _.assign({}, obj);
_.each(fields, field => { _.each(fields, field => {
_.update(result, field, (value: string) => new BigNumber(value)); _.update(result, field, (value: string) => {
if (_.isUndefined(value)) {
throw new Error(`Could not find field '${field}' while converting string fields to BigNumber.`);
}
return new BigNumber(value);
});
}); });
return result; return result;
}, },

View File

@ -1,32 +1,32 @@
import * as _ from 'lodash'; import * as _ from 'lodash';
import { v4 as uuid } from 'uuid';
import * as WebSocket from 'websocket'; import * as WebSocket from 'websocket';
import { import { OrdersChannel, OrdersChannelHandler, OrdersChannelMessageTypes, OrdersChannelSubscriptionOpts } from './types';
OrderbookChannel,
OrderbookChannelHandler,
OrderbookChannelMessageTypes,
OrderbookChannelSubscriptionOpts,
} from './types';
import { assert } from './utils/assert'; import { assert } from './utils/assert';
import { orderbookChannelMessageParser } from './utils/orderbook_channel_message_parser'; import { ordersChannelMessageParser } from './utils/orders_channel_message_parser';
export interface OrdersChannelSubscriptionOptsMap {
[key: string]: OrdersChannelSubscriptionOpts;
}
/** /**
* This class includes all the functionality related to interacting with a websocket endpoint * This class includes all the functionality related to interacting with a websocket endpoint
* that implements the standard relayer API v0 * that implements the standard relayer API v0
*/ */
export class WebSocketOrderbookChannel implements OrderbookChannel { export class WebSocketOrdersChannel implements OrdersChannel {
private readonly _client: WebSocket.w3cwebsocket; private readonly _client: WebSocket.w3cwebsocket;
private readonly _handler: OrderbookChannelHandler; private readonly _handler: OrdersChannelHandler;
private readonly _subscriptionOptsList: OrderbookChannelSubscriptionOpts[] = []; private readonly _subscriptionOptsMap: OrdersChannelSubscriptionOptsMap = {};
/** /**
* Instantiates a new WebSocketOrderbookChannel instance * Instantiates a new WebSocketOrdersChannel instance
* @param client A WebSocket client * @param client A WebSocket client
* @param handler An OrderbookChannelHandler instance that responds to various * @param handler An OrdersChannelHandler instance that responds to various
* channel updates * channel updates
* @return An instance of WebSocketOrderbookChannel * @return An instance of WebSocketOrdersChannel
*/ */
constructor(client: WebSocket.w3cwebsocket, handler: OrderbookChannelHandler) { constructor(client: WebSocket.w3cwebsocket, handler: OrdersChannelHandler) {
assert.isOrderbookChannelHandler('handler', handler); assert.isOrdersChannelHandler('handler', handler);
// set private members // set private members
this._client = client; this._client = client;
this._handler = handler; this._handler = handler;
@ -43,18 +43,18 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
} }
/** /**
* Subscribe to orderbook snapshots and updates from the websocket * Subscribe to orderbook snapshots and updates from the websocket
* @param subscriptionOpts An OrderbookChannelSubscriptionOpts instance describing which * @param subscriptionOpts An OrdersChannelSubscriptionOpts instance describing which
* token pair to subscribe to * assetData pair to subscribe to
*/ */
public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts): void { public subscribe(subscriptionOpts: OrdersChannelSubscriptionOpts): void {
assert.isOrderbookChannelSubscriptionOpts('subscriptionOpts', subscriptionOpts); assert.isOrdersChannelSubscriptionOpts('subscriptionOpts', subscriptionOpts);
assert.assert(this._client.readyState === WebSocket.w3cwebsocket.OPEN, 'WebSocket connection is closed'); assert.assert(this._client.readyState === WebSocket.w3cwebsocket.OPEN, 'WebSocket connection is closed');
this._subscriptionOptsList.push(subscriptionOpts); const requestId = uuid();
// TODO: update requestId management to use UUIDs for v2 this._subscriptionOptsMap[requestId] = subscriptionOpts;
const subscribeMessage = { const subscribeMessage = {
type: 'subscribe', type: 'subscribe',
channel: 'orderbook', channel: 'orders',
requestId: this._subscriptionOptsList.length - 1, requestId,
payload: subscriptionOpts, payload: subscriptionOpts,
}; };
this._client.send(JSON.stringify(subscribeMessage)); this._client.send(JSON.stringify(subscribeMessage));
@ -72,8 +72,8 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
} }
try { try {
const data = message.data; const data = message.data;
const parserResult = orderbookChannelMessageParser.parse(data); const parserResult = ordersChannelMessageParser.parse(data);
const subscriptionOpts = this._subscriptionOptsList[parserResult.requestId]; const subscriptionOpts = this._subscriptionOptsMap[parserResult.requestId];
if (_.isUndefined(subscriptionOpts)) { if (_.isUndefined(subscriptionOpts)) {
this._handler.onError( this._handler.onError(
this, this,
@ -82,11 +82,7 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
return; return;
} }
switch (parserResult.type) { switch (parserResult.type) {
case OrderbookChannelMessageTypes.Snapshot: { case OrdersChannelMessageTypes.Update: {
this._handler.onSnapshot(this, subscriptionOpts, parserResult.payload);
break;
}
case OrderbookChannelMessageTypes.Update: {
this._handler.onUpdate(this, subscriptionOpts, parserResult.payload); this._handler.onUpdate(this, subscriptionOpts, parserResult.payload);
break; break;
} }

View File

@ -0,0 +1,21 @@
{
"total": 43,
"page": 1,
"perPage": 100,
"records": [
{
"assetDataA": {
"minAmount": "0",
"maxAmount": "10000000000000000000",
"precision": 5,
"assetData": "0xf47261b04c32345ced77393b3530b1eed0f346429d"
},
"assetDataB": {
"minAmount": "0",
"maxAmount": "50000000000000000000",
"precision": 5,
"assetData": "0x0257179264389b814a946f3e92105513705ca6b990"
}
}
]
}

View File

@ -0,0 +1,25 @@
import { BigNumber } from '@0xproject/utils';
import { AssetPairsResponse } from '../../../src/types';
export const assetDataPairsResponse: AssetPairsResponse = {
total: 43,
page: 1,
perPage: 100,
records: [
{
assetDataA: {
minAmount: new BigNumber('0'),
maxAmount: new BigNumber('10000000000000000000'),
precision: 5,
assetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
},
assetDataB: {
minAmount: new BigNumber('0'),
maxAmount: new BigNumber('50000000000000000000'),
precision: 5,
assetData: '0x0257179264389b814a946f3e92105513705ca6b990',
},
},
],
};

View File

@ -0,0 +1,10 @@
{
"total": 3,
"page": 1,
"perPage": 10,
"records": [
"0x6ec92694ea172ebc430c30fa31de87620967a082",
"0x9e56625509c2f60af937f23b7b532600390e8c8b",
"0xa2b31dacf30a9c50ca473337c01d8a201ae33e32"
]
}

View File

@ -0,0 +1,12 @@
import { FeeRecipientsResponse } from '../../../src/types';
export const feeRecipientsResponse: FeeRecipientsResponse = {
total: 3,
page: 1,
perPage: 10,
records: [
'0x6ec92694ea172ebc430c30fa31de87620967a082',
'0x9e56625509c2f60af937f23b7b532600390e8c8b',
'0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
],
};

View File

@ -1,5 +0,0 @@
{
"feeRecipient": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
"makerFee": "10000000000000000",
"takerFee": "30000000000000000"
}

View File

@ -1,9 +0,0 @@
import { BigNumber } from '@0xproject/utils';
import { FeesResponse } from '../../../src/types';
export const feesResponse: FeesResponse = {
feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
makerFee: new BigNumber('10000000000000000'),
takerFee: new BigNumber('30000000000000000'),
};

View File

@ -1,19 +1,19 @@
{ {
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b", "order": {
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32", "makerAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
"takerAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"feeRecipientAddress": "0xb046140686d052fff581f63f8136cce132e857da",
"senderAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"makerAssetAmount": "10000000000000000",
"takerAssetAmount": "20000000000000000",
"makerFee": "100000000000000", "makerFee": "100000000000000",
"takerFee": "200000000000000", "takerFee": "200000000000000",
"makerTokenAmount": "10000000000000000", "expirationTimeSeconds": "1532560590",
"takerTokenAmount": "20000000000000000", "salt": "1532559225",
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", "makerAssetData": "0xf47261b04c32345ced77393b3530b1eed0f346429d",
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", "takerAssetData": "0x0257179264389b814a946f3e92105513705ca6b990",
"salt": "256", "exchangeAddress": "0x12459c951127e0c374ff9105dda097662a027093",
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da", "signature": "0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33"
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093", },
"expirationUnixTimestampSec": "42", "metaData": {}
"ecSignature": {
"v": 27,
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
}
} }

View File

@ -1,21 +1,21 @@
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
export const orderResponse = { export const orderResponse = {
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b', order: {
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', makerAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
takerAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
feeRecipientAddress: '0xb046140686d052fff581f63f8136cce132e857da',
senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
makerAssetAmount: new BigNumber('10000000000000000'),
takerAssetAmount: new BigNumber('20000000000000000'),
makerFee: new BigNumber('100000000000000'), makerFee: new BigNumber('100000000000000'),
takerFee: new BigNumber('200000000000000'), takerFee: new BigNumber('200000000000000'),
makerTokenAmount: new BigNumber('10000000000000000'), expirationTimeSeconds: new BigNumber('1532560590'),
takerTokenAmount: new BigNumber('20000000000000000'), salt: new BigNumber('1532559225'),
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', makerAssetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', takerAssetData: '0x0257179264389b814a946f3e92105513705ca6b990',
salt: new BigNumber('256'), exchangeAddress: '0x12459c951127e0c374ff9105dda097662a027093',
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da', signature: '0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
expirationUnixTimestampSec: new BigNumber('42'),
ecSignature: {
v: 27,
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
}, },
metaData: {},
}; };

View File

@ -0,0 +1,6 @@
{
"senderAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"feeRecipientAddress": "0xb046140686d052fff581f63f8136cce132e857da",
"makerFee": "100000000000000",
"takerFee": "200000000000000"
}

View File

@ -0,0 +1,10 @@
import { BigNumber } from '@0xproject/utils';
import { OrderConfigResponse } from '../../../src/types';
export const orderConfigResponse: OrderConfigResponse = {
senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
feeRecipientAddress: '0xb046140686d052fff581f63f8136cce132e857da',
makerFee: new BigNumber('100000000000000'),
takerFee: new BigNumber('200000000000000'),
};

View File

@ -1,44 +1,54 @@
{ {
"bids": [ "bids": {
"total": 325,
"page": 2,
"perPage": 100,
"records": [
{ {
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b", "order": {
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32", "makerAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
"takerAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"feeRecipientAddress": "0xb046140686d052fff581f63f8136cce132e857da",
"senderAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"makerAssetAmount": "10000000000000000",
"takerAssetAmount": "20000000000000000",
"makerFee": "100000000000000", "makerFee": "100000000000000",
"takerFee": "200000000000000", "takerFee": "200000000000000",
"makerTokenAmount": "10000000000000000", "expirationTimeSeconds": "1532560590",
"takerTokenAmount": "20000000000000000", "salt": "1532559225",
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", "makerAssetData": "0xf47261b04c32345ced77393b3530b1eed0f346429d",
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", "takerAssetData": "0x0257179264389b814a946f3e92105513705ca6b990",
"salt": "256", "exchangeAddress": "0x12459c951127e0c374ff9105dda097662a027093",
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da", "signature": "0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33"
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093", },
"expirationUnixTimestampSec": "42", "metaData": {}
"ecSignature": {
"v": 27,
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
} }
} ]
], },
"asks": [ "asks": {
"total": 500,
"page": 2,
"perPage": 100,
"records": [
{ {
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b", "order": {
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32", "makerAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"makerFee": "100000000000000", "takerAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
"takerFee": "200000000000000", "feeRecipientAddress": "0xb046140686d052fff581f63f8136cce132e857da",
"makerTokenAmount": "10000000000000000", "senderAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"takerTokenAmount": "20000000000000000", "makerAssetAmount": "20000000000000000",
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", "takerAssetAmount": "10000000000000000",
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", "makerFee": "200000000000000",
"salt": "256", "takerFee": "100000000000000",
"feeRecipient": "0xb046140686d052fff581f63f8136cce132e857da", "expirationTimeSeconds": "1532560590",
"exchangeContractAddress": "0x12459c951127e0c374ff9105dda097662a027093", "salt": "1532559225",
"expirationUnixTimestampSec": "42", "makerAssetData": "0x0257179264389b814a946f3e92105513705ca6b990",
"ecSignature": { "takerAssetData": "0xf47261b04c32345ced77393b3530b1eed0f346429d",
"v": 27, "exchangeAddress": "0x12459c951127e0c374ff9105dda097662a027093",
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33", "signature": "0x013842a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b3518891"
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254" },
} "metaData": {}
} }
] ]
} }
}

View File

@ -1,46 +1,58 @@
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
export const orderbookResponse = { import { OrderbookResponse } from '../../../src/types';
bids: [
export const orderbookResponse: OrderbookResponse = {
bids: {
total: 325,
page: 2,
perPage: 100,
records: [
{ {
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b', order: {
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', makerAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
takerAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
feeRecipientAddress: '0xb046140686d052fff581f63f8136cce132e857da',
senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
makerAssetAmount: new BigNumber('10000000000000000'),
takerAssetAmount: new BigNumber('20000000000000000'),
makerFee: new BigNumber('100000000000000'), makerFee: new BigNumber('100000000000000'),
takerFee: new BigNumber('200000000000000'), takerFee: new BigNumber('200000000000000'),
makerTokenAmount: new BigNumber('10000000000000000'), expirationTimeSeconds: new BigNumber('1532560590'),
takerTokenAmount: new BigNumber('20000000000000000'), salt: new BigNumber('1532559225'),
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', makerAssetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', takerAssetData: '0x0257179264389b814a946f3e92105513705ca6b990',
salt: new BigNumber('256'), exchangeAddress: '0x12459c951127e0c374ff9105dda097662a027093',
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da', signature: '0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093',
expirationUnixTimestampSec: new BigNumber('42'),
ecSignature: {
v: 27,
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
}, },
metaData: {},
}, },
], ],
asks: [ },
asks: {
total: 500,
page: 2,
perPage: 100,
records: [
{ {
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b', order: {
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', makerAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
makerFee: new BigNumber('100000000000000'), takerAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
takerFee: new BigNumber('200000000000000'), feeRecipientAddress: '0xb046140686d052fff581f63f8136cce132e857da',
makerTokenAmount: new BigNumber('10000000000000000'), senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
takerTokenAmount: new BigNumber('20000000000000000'), makerAssetAmount: new BigNumber('20000000000000000'),
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', takerAssetAmount: new BigNumber('10000000000000000'),
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', makerFee: new BigNumber('200000000000000'),
salt: new BigNumber('256'), takerFee: new BigNumber('100000000000000'),
feeRecipient: '0xb046140686d052fff581f63f8136cce132e857da', expirationTimeSeconds: new BigNumber('1532560590'),
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093', salt: new BigNumber('1532559225'),
expirationUnixTimestampSec: new BigNumber('42'), makerAssetData: '0x0257179264389b814a946f3e92105513705ca6b990',
ecSignature: { takerAssetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
v: 27, exchangeAddress: '0x12459c951127e0c374ff9105dda097662a027093',
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33', signature: '0x013842a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b3518891',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
}, },
metaData: {},
}, },
], ],
},
}; };

View File

@ -1,21 +1,26 @@
[
{ {
"maker": "0x9e56625509c2f60af937f23b7b532600390e8c8b", "total": 984,
"taker": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32", "page": 1,
"perPage": 100,
"records": [
{
"order": {
"makerAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b",
"takerAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"feeRecipientAddress": "0xb046140686d052fff581f63f8136cce132e857da",
"senderAddress": "0xa2b31dacf30a9c50ca473337c01d8a201ae33e32",
"makerAssetAmount": "10000000000000000",
"takerAssetAmount": "20000000000000000",
"makerFee": "100000000000000", "makerFee": "100000000000000",
"takerFee": "200000000000000", "takerFee": "200000000000000",
"makerTokenAmount": "10000000000000000", "expirationTimeSeconds": "1532560590",
"takerTokenAmount": "20000000000000000", "salt": "1532559225",
"makerTokenAddress": "0x323b5d4c32345ced77393b3530b1eed0f346429d", "makerAssetData": "0xf47261b04c32345ced77393b3530b1eed0f346429d",
"takerTokenAddress": "0xef7fff64389b814a946f3e92105513705ca6b990", "takerAssetData": "0x0257179264389b814a946f3e92105513705ca6b990",
"salt": "256", "exchangeAddress": "0x12459c951127e0c374ff9105dda097662a027093",
"feeRecipient": "0x9e56625509c2f60af937f23b7b532600390e8c8b", "signature": "0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33"
"exchangeContractAddress": "0x9e56625509c2f60af937f23b7b532600390e8c8b", },
"expirationUnixTimestampSec": "42", "metaData": {}
"ecSignature": {
"v": 27,
"r": "0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33",
"s": "0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254"
}
} }
] ]
}

View File

@ -1,23 +1,30 @@
import { BigNumber } from '@0xproject/utils'; import { BigNumber } from '@0xproject/utils';
export const ordersResponse = [ import { OrdersResponse } from '../../../src/types';
export const ordersResponse: OrdersResponse = {
total: 984,
page: 1,
perPage: 100,
records: [
{ {
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b', order: {
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', makerAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
takerAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
feeRecipientAddress: '0xb046140686d052fff581f63f8136cce132e857da',
senderAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
makerAssetAmount: new BigNumber('10000000000000000'),
takerAssetAmount: new BigNumber('20000000000000000'),
makerFee: new BigNumber('100000000000000'), makerFee: new BigNumber('100000000000000'),
takerFee: new BigNumber('200000000000000'), takerFee: new BigNumber('200000000000000'),
makerTokenAmount: new BigNumber('10000000000000000'), expirationTimeSeconds: new BigNumber('1532560590'),
takerTokenAmount: new BigNumber('20000000000000000'), salt: new BigNumber('1532559225'),
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', makerAssetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', takerAssetData: '0x0257179264389b814a946f3e92105513705ca6b990',
salt: new BigNumber('256'), exchangeAddress: '0x12459c951127e0c374ff9105dda097662a027093',
feeRecipient: '0x9e56625509c2f60af937f23b7b532600390e8c8b', signature: '0x012761a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
exchangeContractAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
expirationUnixTimestampSec: new BigNumber('42'),
ecSignature: {
v: 27,
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
}, },
metaData: {},
}, },
]; ],
};

View File

@ -1,17 +0,0 @@
import * as orderbookJSON from './orderbook.json';
const orderbookJsonString = JSON.stringify(orderbookJSON);
export const snapshotOrderbookChannelMessage = `{
"type": "snapshot",
"channel": "orderbook",
"requestId": 1,
"payload": ${orderbookJsonString}
}`;
export const malformedSnapshotOrderbookChannelMessage = `{
"type": "snapshot",
"channel": "orderbook",
"requestId": 1,
"payload": {}
}`;

View File

@ -1,16 +0,0 @@
[
{
"tokenA": {
"address": "0x323b5d4c32345ced77393b3530b1eed0f346429d",
"minAmount": "0",
"maxAmount": "10000000000000000000",
"precision": 5
},
"tokenB": {
"address": "0xef7fff64389b814a946f3e92105513705ca6b990",
"minAmount": "0",
"maxAmount": "50000000000000000000",
"precision": 5
}
}
]

View File

@ -1,20 +0,0 @@
import { BigNumber } from '@0xproject/utils';
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,
},
},
];

View File

@ -2,9 +2,9 @@ import * as orderResponseJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50
const orderJSONString = JSON.stringify(orderResponseJSON); const orderJSONString = JSON.stringify(orderResponseJSON);
export const unknownOrderbookChannelMessage = `{ export const unknownOrdersChannelMessage = `{
"type": "superGoodUpdate", "type": "superGoodUpdate",
"channel": "orderbook", "channel": "orderbook",
"requestId": 1, "requestId": "6ce8c5a6-5c46-4027-a44a-51831c77b8a1",
"payload": ${orderJSONString} "payload": [${orderJSONString}]
}`; }`;

View File

@ -1,17 +0,0 @@
import * as orderResponseJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
const orderJSONString = JSON.stringify(orderResponseJSON);
export const updateOrderbookChannelMessage = `{
"type": "update",
"channel": "orderbook",
"requestId": 1,
"payload": ${orderJSONString}
}`;
export const malformedUpdateOrderbookChannelMessage = `{
"type": "update",
"channel": "orderbook",
"requestId": 1,
"payload": {}
}`;

View File

@ -0,0 +1,17 @@
import * as apiOrderJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
const apiOrderJSONString = JSON.stringify(apiOrderJSON);
export const updateOrdersChannelMessage = `{
"type": "update",
"channel": "orders",
"requestId": "5a1ce3a2-22b9-41e6-a615-68077512e9e2",
"payload": [${apiOrderJSONString}]
}`;
export const malformedUpdateOrdersChannelMessage = `{
"type": "update",
"channel": "orders",
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
"payload": {}
}`;

View File

@ -1,4 +1,3 @@
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised'; import * as chaiAsPromised from 'chai-as-promised';
import * as dirtyChai from 'dirty-chai'; import * as dirtyChai from 'dirty-chai';
@ -7,16 +6,18 @@ import 'mocha';
import { HttpClient } from '../src/index'; import { HttpClient } from '../src/index';
import { feesResponse } from './fixtures/standard_relayer_api/fees'; import { assetDataPairsResponse } from './fixtures/standard_relayer_api/asset_pairs';
import * as feesResponseJSON from './fixtures/standard_relayer_api/fees.json'; import * as assetDataPairsResponseJSON from './fixtures/standard_relayer_api/asset_pairs.json';
import { feeRecipientsResponse } from './fixtures/standard_relayer_api/fee_recipients';
import * as feeRecipientsResponseJSON from './fixtures/standard_relayer_api/fee_recipients.json';
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f'; import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json'; import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
import { orderConfigResponse } from './fixtures/standard_relayer_api/order_config';
import * as orderConfigResponseJSON from './fixtures/standard_relayer_api/order_config.json';
import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook'; import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook';
import * as orderbookJSON from './fixtures/standard_relayer_api/orderbook.json'; import * as orderbookJSON from './fixtures/standard_relayer_api/orderbook.json';
import { ordersResponse } from './fixtures/standard_relayer_api/orders'; import { ordersResponse } from './fixtures/standard_relayer_api/orders';
import * as ordersResponseJSON from './fixtures/standard_relayer_api/orders.json'; import * as ordersResponseJSON from './fixtures/standard_relayer_api/orders.json';
import { tokenPairsResponse } from './fixtures/standard_relayer_api/token_pairs';
import * as tokenPairsResponseJSON from './fixtures/standard_relayer_api/token_pairs.json';
chai.config.includeStack = true; chai.config.includeStack = true;
chai.use(dirtyChai); chai.use(dirtyChai);
@ -26,7 +27,7 @@ const expect = chai.expect;
describe('HttpClient', () => { describe('HttpClient', () => {
const relayUrl = 'https://example.com'; const relayUrl = 'https://example.com';
const relayerClient = new HttpClient(relayUrl); const relayerClient = new HttpClient(relayUrl);
afterEach(() => { beforeEach(() => {
fetchMock.restore(); fetchMock.restore();
}); });
describe('#constructor', () => { describe('#constructor', () => {
@ -38,47 +39,47 @@ describe('HttpClient', () => {
expect(sanitizedUrl).to.be.deep.equal(urlWithoutTrailingSlash); expect(sanitizedUrl).to.be.deep.equal(urlWithoutTrailingSlash);
}); });
}); });
describe('#getTokenPairsAsync', () => { describe('#getAssetPairsAsync', () => {
const url = `${relayUrl}/token_pairs`; const url = `${relayUrl}/asset_pairs`;
it('gets token pairs with default options when none are provided', async () => { it('gets assetData pairs with default options when none are provided', async () => {
const urlWithQuery = `${url}?page=1&per_page=100`; fetchMock.get(url, assetDataPairsResponseJSON);
fetchMock.get(urlWithQuery, tokenPairsResponseJSON); const assetDataPairs = await relayerClient.getAssetPairsAsync();
const tokenPairs = await relayerClient.getTokenPairsAsync(); expect(assetDataPairs).to.be.deep.equal(assetDataPairsResponse);
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
}); });
it('gets token pairs with specified request options', async () => { it('gets assetData pairs with specified request options', async () => {
const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d'; const assetData = '0xf47261b04c32345ced77393b3530b1eed0f346429d';
const tokenPairsRequestOpts = { const assetPairsRequestOpts = {
tokenA: tokenAddress, assetDataA: assetData,
page: 3, page: 3,
perPage: 50, perPage: 50,
networkdId: 42,
}; };
const urlWithQuery = `${url}?page=3&per_page=50&tokenA=${tokenAddress}`; const urlWithQuery = `${url}?assetDataA=${assetData}&networkdId=42&page=3&perPage=50`;
fetchMock.get(urlWithQuery, tokenPairsResponseJSON); fetchMock.get(urlWithQuery, assetDataPairsResponseJSON);
const tokenPairs = await relayerClient.getTokenPairsAsync(tokenPairsRequestOpts); const assetDataPairs = await relayerClient.getAssetPairsAsync(assetPairsRequestOpts);
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse); expect(assetDataPairs).to.be.deep.equal(assetDataPairsResponse);
}); });
it('throws an error for invalid JSON response', async () => { it('throws an error for invalid JSON response', async () => {
fetchMock.get(url, { test: 'dummy' }); fetchMock.get(url, { test: 'dummy' });
expect(relayerClient.getTokenPairsAsync()).to.be.rejected(); expect(relayerClient.getAssetPairsAsync()).to.be.rejected();
}); });
}); });
describe('#getOrdersAsync', () => { describe('#getOrdersAsync', () => {
const url = `${relayUrl}/orders`; const url = `${relayUrl}/orders`;
it('gets orders with default options when none are provided', async () => { it('gets orders with default options when none are provided', async () => {
const urlWithQuery = `${url}?page=1&per_page=100`; fetchMock.get(url, ordersResponseJSON);
fetchMock.get(urlWithQuery, ordersResponseJSON);
const orders = await relayerClient.getOrdersAsync(); const orders = await relayerClient.getOrdersAsync();
expect(orders).to.be.deep.equal(ordersResponse); expect(orders).to.be.deep.equal(ordersResponse);
}); });
it('gets orders with specified request options', async () => { it('gets orders with specified request options', async () => {
const tokenAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d'; const assetDataAddress = '0x323b5d4c32345ced77393b3530b1eed0f346429d';
const ordersRequest = { const ordersRequest = {
tokenAddress, assetDataAddress,
page: 3, page: 3,
perPage: 50, perPage: 50,
networkdId: 42,
}; };
const urlWithQuery = `${url}?page=3&per_page=50&tokenAddress=${tokenAddress}`; const urlWithQuery = `${url}?assetDataAddress=${assetDataAddress}&networkdId=42&page=3&perPage=50`;
fetchMock.get(urlWithQuery, ordersResponseJSON); fetchMock.get(urlWithQuery, ordersResponseJSON);
const orders = await relayerClient.getOrdersAsync(ordersRequest); const orders = await relayerClient.getOrdersAsync(ordersRequest);
expect(orders).to.be.deep.equal(ordersResponse); expect(orders).to.be.deep.equal(ordersResponse);
@ -103,26 +104,27 @@ describe('HttpClient', () => {
}); });
describe('#getOrderBookAsync', () => { describe('#getOrderBookAsync', () => {
const request = { const request = {
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', baseAssetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', quoteAssetData: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
}; };
const url = `${relayUrl}/orderbook`; const url = `${relayUrl}/orderbook`;
it('gets orderbook with default page options when none are provided', async () => { it('gets orderbook with default page options when none are provided', async () => {
const urlWithQuery = `${url}?baseTokenAddress=${ const urlWithQuery = `${url}?baseAssetData=${request.baseAssetData}&quoteAssetData=${
request.baseTokenAddress request.quoteAssetData
}&page=1&per_page=100&quoteTokenAddress=${request.quoteTokenAddress}`; }`;
fetchMock.get(urlWithQuery, orderbookJSON); fetchMock.get(urlWithQuery, orderbookJSON);
const orderbook = await relayerClient.getOrderbookAsync(request); const orderbook = await relayerClient.getOrderbookAsync(request);
expect(orderbook).to.be.deep.equal(orderbookResponse); expect(orderbook).to.be.deep.equal(orderbookResponse);
}); });
it('gets orderbook with specified page options', async () => { it('gets orderbook with specified page options', async () => {
const urlWithQuery = `${url}?baseTokenAddress=${ const urlWithQuery = `${url}?baseAssetData=${
request.baseTokenAddress request.baseAssetData
}&page=3&per_page=50&quoteTokenAddress=${request.quoteTokenAddress}`; }&networkId=42&page=3&perPage=50&quoteAssetData=${request.quoteAssetData}`;
fetchMock.get(urlWithQuery, orderbookJSON); fetchMock.get(urlWithQuery, orderbookJSON);
const pagedRequestOptions = { const pagedRequestOptions = {
page: 3, page: 3,
perPage: 50, perPage: 50,
networkId: 42,
}; };
const orderbook = await relayerClient.getOrderbookAsync(request, pagedRequestOptions); const orderbook = await relayerClient.getOrderbookAsync(request, pagedRequestOptions);
expect(orderbook).to.be.deep.equal(orderbookResponse); expect(orderbook).to.be.deep.equal(orderbookResponse);
@ -132,39 +134,59 @@ describe('HttpClient', () => {
expect(relayerClient.getOrderbookAsync(request)).to.be.rejected(); expect(relayerClient.getOrderbookAsync(request)).to.be.rejected();
}); });
}); });
describe('#getFeesAsync', () => { describe('#getOrderConfigAsync', () => {
const request = { const request = {
exchangeContractAddress: '0x12459c951127e0c374ff9105dda097662a027093', makerAddress: '0x9e56625509c2f60af937f23b7b532600390e8c8b',
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b', takerAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
taker: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32', makerAssetAmount: '10000000000000000',
makerTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', takerAssetAmount: '20000000000000000',
takerTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', expirationTimeSeconds: '1532560590',
makerTokenAmount: new BigNumber('10000000000000000000'), makerAssetData: '0xf47261b04c32345ced77393b3530b1eed0f346429d',
takerTokenAmount: new BigNumber('30000000000000000000'), takerAssetData: '0x0257179264389b814a946f3e92105513705ca6b990',
salt: new BigNumber('256'), exchangeAddress: '0x12459c951127e0c374ff9105dda097662a027093',
expirationUnixTimestampSec: new BigNumber('42'),
}; };
const url = `${relayUrl}/fees`; const url = `${relayUrl}/order_config`;
it('gets fees', async () => { it('gets order config', async () => {
fetchMock.post(url, feesResponseJSON); fetchMock.post(url, orderConfigResponseJSON);
const fees = await relayerClient.getFeesAsync(request); const fees = await relayerClient.getOrderConfigAsync(request);
expect(fees).to.be.deep.equal(feesResponse); expect(fees).to.be.deep.equal(orderConfigResponse);
}); });
it('does not mutate input', async () => { it('does not mutate input', async () => {
fetchMock.post(url, feesResponseJSON); fetchMock.post(url, orderConfigResponseJSON);
const makerTokenAmountBefore = new BigNumber(request.makerTokenAmount); const makerAssetAmountBefore = request.makerAssetAmount;
const takerTokenAmountBefore = new BigNumber(request.takerTokenAmount); const takerAssetAmountBefore = request.takerAssetAmount;
const saltBefore = new BigNumber(request.salt); const expirationTimeSecondsBefore = request.expirationTimeSeconds;
const expirationUnixTimestampSecBefore = new BigNumber(request.expirationUnixTimestampSec); await relayerClient.getOrderConfigAsync(request);
await relayerClient.getFeesAsync(request); expect(makerAssetAmountBefore).to.be.deep.equal(request.makerAssetAmount);
expect(makerTokenAmountBefore).to.be.deep.equal(request.makerTokenAmount); expect(takerAssetAmountBefore).to.be.deep.equal(request.takerAssetAmount);
expect(takerTokenAmountBefore).to.be.deep.equal(request.takerTokenAmount); expect(expirationTimeSecondsBefore).to.be.deep.equal(request.expirationTimeSeconds);
expect(saltBefore).to.be.deep.equal(request.salt);
expect(expirationUnixTimestampSecBefore).to.be.deep.equal(request.expirationUnixTimestampSec);
}); });
it('throws an error for invalid JSON response', async () => { it('throws an error for invalid JSON response', async () => {
fetchMock.post(url, { test: 'dummy' }); fetchMock.post(url, { test: 'dummy' });
expect(relayerClient.getFeesAsync(request)).to.be.rejected(); expect(relayerClient.getOrderConfigAsync(request)).to.be.rejected();
});
});
describe('#getFeeRecipientsAsync', () => {
const url = `${relayUrl}/fee_recipients`;
it('gets fee recipients with default page options when none are provided', async () => {
fetchMock.get(url, feeRecipientsResponseJSON);
const feeRecipients = await relayerClient.getFeeRecipientsAsync();
expect(feeRecipients).to.be.deep.equal(feeRecipientsResponse);
});
it('gets fee recipient with specified page options', async () => {
const urlWithQuery = `${url}?networkId=42&page=3&perPage=50`;
fetchMock.get(urlWithQuery, feeRecipientsResponseJSON);
const pagedRequestOptions = {
page: 3,
perPage: 50,
networkId: 42,
};
const feeRecipients = await relayerClient.getFeeRecipientsAsync(pagedRequestOptions);
expect(feeRecipients).to.be.deep.equal(feeRecipientsResponse);
});
it('throws an error for invalid JSON response', async () => {
fetchMock.get(url, { test: 'dummy' });
expect(relayerClient.getFeeRecipientsAsync()).to.be.rejected();
}); });
}); });
}); });

View File

@ -1,45 +0,0 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash';
import 'mocha';
import { orderbookChannelFactory } from '../src/orderbook_channel_factory';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
const emptyOrderbookChannelHandler = {
onSnapshot: () => {
_.noop();
},
onUpdate: () => {
_.noop();
},
onError: () => {
_.noop();
},
onClose: () => {
_.noop();
},
};
describe('orderbookChannelFactory', () => {
const websocketUrl = 'ws://localhost:8080';
describe('#createWebSocketOrderbookChannelAsync', () => {
it('throws when input is not a url', () => {
const badUrlInput = 54;
expect(
orderbookChannelFactory.createWebSocketOrderbookChannelAsync(
badUrlInput as any,
emptyOrderbookChannelHandler,
),
).to.be.rejected();
});
it('throws when handler has the incorrect members', () => {
const badHandlerInput = {};
expect(
orderbookChannelFactory.createWebSocketOrderbookChannelAsync(websocketUrl, badHandlerInput as any),
).to.be.rejected();
});
});
});

View File

@ -1,76 +0,0 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import 'mocha';
import { orderbookChannelMessageParser } from '../src/utils/orderbook_channel_message_parser';
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import { orderbookResponse } from './fixtures/standard_relayer_api/orderbook';
import {
malformedSnapshotOrderbookChannelMessage,
snapshotOrderbookChannelMessage,
} from './fixtures/standard_relayer_api/snapshot_orderbook_channel_message';
import { unknownOrderbookChannelMessage } from './fixtures/standard_relayer_api/unknown_orderbook_channel_message';
import {
malformedUpdateOrderbookChannelMessage,
updateOrderbookChannelMessage,
} from './fixtures/standard_relayer_api/update_orderbook_channel_message';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
describe('orderbookChannelMessageParser', () => {
describe('#parser', () => {
it('parses snapshot messages', () => {
const snapshotMessage = orderbookChannelMessageParser.parse(snapshotOrderbookChannelMessage);
expect(snapshotMessage.type).to.be.equal('snapshot');
expect(snapshotMessage.payload).to.be.deep.equal(orderbookResponse);
});
it('parses update messages', () => {
const updateMessage = orderbookChannelMessageParser.parse(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 = orderbookChannelMessageParser.parse(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",
"requestId": 1,
"payload": {}
}`;
const badCall = () => orderbookChannelMessageParser.parse(typelessMessage);
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
});
it('throws when type is not a string', () => {
const messageWithBadType = `{
"type": 1,
"channel": "orderbook",
"requestId": 1,
"payload": {}
}`;
const badCall = () => orderbookChannelMessageParser.parse(messageWithBadType);
expect(badCall).throws('Expected type to be of type string, encountered: 1');
});
it('throws when snapshot message has malformed payload', () => {
const badCall = () => orderbookChannelMessageParser.parse(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 = () => orderbookChannelMessageParser.parse(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 = () => orderbookChannelMessageParser.parse(nonJsonString);
expect(badCall).throws('Unexpected token h in JSON at position 0');
});
});
});

View File

@ -0,0 +1,34 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash';
import 'mocha';
import { ordersChannelFactory } from '../src/orders_channel_factory';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
const emptyOrdersChannelHandler = {
onUpdate: _.noop.bind(_),
onError: _.noop.bind(_),
onClose: _.noop.bind(_),
};
describe('ordersChannelFactory', () => {
const websocketUrl = 'ws://localhost:8080';
describe('#createWebSocketOrdersChannelAsync', () => {
it('throws when input is not a url', () => {
const badUrlInput = 54;
expect(
ordersChannelFactory.createWebSocketOrdersChannelAsync(badUrlInput as any, emptyOrdersChannelHandler),
).to.be.rejected();
});
it('throws when handler has the incorrect members', () => {
const badHandlerInput = {};
expect(
ordersChannelFactory.createWebSocketOrdersChannelAsync(websocketUrl, badHandlerInput as any),
).to.be.rejected();
});
});
});

View File

@ -0,0 +1,59 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import 'mocha';
import { ordersChannelMessageParser } from '../src/utils/orders_channel_message_parser';
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import { unknownOrdersChannelMessage } from './fixtures/standard_relayer_api/unknown_orders_channel_message';
import {
malformedUpdateOrdersChannelMessage,
updateOrdersChannelMessage,
} from './fixtures/standard_relayer_api/update_orders_channel_message';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
describe('ordersChannelMessageParser', () => {
describe('#parser', () => {
it('parses update messages', () => {
const updateMessage = ordersChannelMessageParser.parse(updateOrdersChannelMessage);
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 = ordersChannelMessageParser.parse(unknownOrdersChannelMessage);
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": "orders",
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
"payload": []
}`;
const badCall = () => ordersChannelMessageParser.parse(typelessMessage);
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
});
it('throws when type is not a string', () => {
const messageWithBadType = `{
"type": 1,
"channel": "orders",
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
"payload": []
}`;
const badCall = () => ordersChannelMessageParser.parse(messageWithBadType);
expect(badCall).throws('Expected type to be of type string, encountered: 1');
});
it('throws when update message has malformed payload', () => {
const badCall = () => ordersChannelMessageParser.parse(malformedUpdateOrdersChannelMessage);
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 = () => ordersChannelMessageParser.parse(nonJsonString);
expect(badCall).throws('Unexpected token h in JSON at position 0');
});
});
});

View File

@ -1,59 +0,0 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
import * as WebSocket from 'websocket';
import { WebSocketOrderbookChannel } from '../src/ws_orderbook_channel';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
const emptyOrderbookChannelHandler = {
onSnapshot: () => {
_.noop();
},
onUpdate: () => {
_.noop();
},
onError: () => {
_.noop();
},
onClose: () => {
_.noop();
},
};
describe('WebSocketOrderbookChannel', () => {
const websocketUrl = 'ws://localhost:8080';
const openClient = new WebSocket.w3cwebsocket(websocketUrl);
Sinon.stub(openClient, 'readyState').get(() => WebSocket.w3cwebsocket.OPEN);
Sinon.stub(openClient, 'send').callsFake(_.noop.bind(_));
const openOrderbookChannel = new WebSocketOrderbookChannel(openClient, emptyOrderbookChannelHandler);
const subscriptionOpts = {
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
snapshot: true,
limit: 100,
};
describe('#subscribe', () => {
it('throws when subscriptionOpts does not conform to schema', () => {
const badSubscribeCall = openOrderbookChannel.subscribe.bind(openOrderbookChannel, {});
expect(badSubscribeCall).throws(
'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
);
});
it('does not throw when inputs are of correct types', () => {
const goodSubscribeCall = openOrderbookChannel.subscribe.bind(openOrderbookChannel, subscriptionOpts);
expect(goodSubscribeCall).to.not.throw();
});
it('throws when client is closed', () => {
const closedClient = new WebSocket.w3cwebsocket(websocketUrl);
Sinon.stub(closedClient, 'readyState').get(() => WebSocket.w3cwebsocket.CLOSED);
const closedOrderbookChannel = new WebSocketOrderbookChannel(closedClient, emptyOrderbookChannelHandler);
const badSubscribeCall = closedOrderbookChannel.subscribe.bind(closedOrderbookChannel, subscriptionOpts);
expect(badSubscribeCall).throws('WebSocket connection is closed');
});
});
});

View File

@ -0,0 +1,49 @@
import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
import * as WebSocket from 'websocket';
import { WebSocketOrdersChannel } from '../src/ws_orders_channel';
chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
const emptyOrdersChannelHandler = {
onUpdate: _.noop.bind(_),
onError: _.noop.bind(_),
onClose: _.noop.bind(_),
};
describe('WebSocketOrdersChannel', () => {
const websocketUrl = 'ws://localhost:8080';
const openClient = new WebSocket.w3cwebsocket(websocketUrl);
Sinon.stub(openClient, 'readyState').get(() => WebSocket.w3cwebsocket.OPEN);
Sinon.stub(openClient, 'send').callsFake(_.noop.bind(_));
const openOrdersChannel = new WebSocketOrdersChannel(openClient, emptyOrdersChannelHandler);
const subscriptionOpts = {
baseAssetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteAssetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
limit: 100,
};
describe('#subscribe', () => {
it('throws when subscriptionOpts does not conform to schema', () => {
const badSubscribeCall = openOrdersChannel.subscribe.bind(openOrdersChannel, {
makerAssetData: 5,
});
expect(badSubscribeCall).throws();
});
it('does not throw when inputs are of correct types', () => {
const goodSubscribeCall = openOrdersChannel.subscribe.bind(openOrdersChannel, subscriptionOpts);
expect(goodSubscribeCall).to.not.throw();
});
it('throws when client is closed', () => {
const closedClient = new WebSocket.w3cwebsocket(websocketUrl);
Sinon.stub(closedClient, 'readyState').get(() => WebSocket.w3cwebsocket.CLOSED);
const closedOrdersChannel = new WebSocketOrdersChannel(closedClient, emptyOrdersChannelHandler);
const badSubscribeCall = closedOrdersChannel.subscribe.bind(closedOrdersChannel, subscriptionOpts);
expect(badSubscribeCall).throws('WebSocket connection is closed');
});
});
});

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Update incorrect relayer api fee recipients response schema",
"pr": 974
}
]
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [

View File

@ -5,7 +5,11 @@ export const relayerApiFeeRecipientsResponseSchema = {
{ $ref: '/paginatedCollectionSchema' }, { $ref: '/paginatedCollectionSchema' },
{ {
properties: { properties: {
records: { $ref: '/addressSchema' }, records: {
id: '/relayerApiFeeRecipientsSchema',
type: 'array',
items: { $ref: '/addressSchema' },
},
}, },
required: ['records'], required: ['records'],
}, },

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.0.1-rc.5",
"changes": [
{
"note": "Add takerAddress to /orders parameters",
"pr": 974
}
]
},
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [

File diff suppressed because one or more lines are too long

View File

@ -150,6 +150,15 @@ export const api: OpenApiSpec = {
$ref: '#/components/schemas/addressSchema', $ref: '#/components/schemas/addressSchema',
}, },
}, },
{
name: 'takerAddress',
in: 'query',
description: `Same as takerAddress in the [0x Protocol v2 Specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#order-message-format)`,
example: '0xe41d2489571d322189246dafa5ebde1f4699f498',
schema: {
$ref: '#/components/schemas/addressSchema',
},
},
{ {
name: 'traderAddress', name: 'traderAddress',
in: 'query', in: 'query',

View File

@ -35,7 +35,7 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-report/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-report/README.md",
"dependencies": { "dependencies": {
"@0xproject/assert": "^1.0.5", "@0xproject/assert": "^1.0.5",
"@0xproject/connect": "^1.0.5", "@0xproject/connect": "1.0.4",
"@0xproject/json-schemas": "^0.8.3", "@0xproject/json-schemas": "^0.8.3",
"@0xproject/order-utils": "^0.0.9", "@0xproject/order-utils": "^0.0.9",
"@0xproject/types": "^0.8.2", "@0xproject/types": "^0.8.2",

View File

@ -0,0 +1,28 @@
0x Protocol Schemas
* [Basic types](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/basic_type_schemas.ts) (e.g Ethereum address, number, hex)
* [Order/SignedOrder](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schemas.ts)
* [OrderHash](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_hash_schema.ts)
0x.js Schemas
* [BlockRange](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/block_range_schema.ts)
* [IndexFilter Values](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/index_filter_values_schema.ts)
* [OrderFillRequests](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_fill_requests_schema.ts)
* [OrderCancellationRequests](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_cancel_schema.ts)
* [OrderFillOrKillRequests](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_fill_or_kill_requests_schema.ts)
* [SignedOrders](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/signed_orders_schema.ts)
* [Token](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/token_schema.ts)
* [TxData](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/tx_data_schema.ts)
Standard Relayer API Schemas
* [Paginated collection](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/paginated_collection_schema.ts)
* [Error response](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_error_response_schema.ts)
* [Order config payload](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_order_config_payload_schema.ts)
* [Order config response](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_order_config_response_schema.ts)
* [Orders channel subscribe](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_orders_channel_subscribe_schema.ts)
* [Orders channel update](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_orders_channel_update_response_schema.ts)
* [Orderbook response](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_orderbook_response_schema.ts)
* [Asset pairs response](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_asset_pairs_response_schema.ts)
* [Fee recipients response](https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/relayer_api_fee_recipients_response_schema.ts)

View File

@ -13,6 +13,7 @@ const IntroMarkdownV1 = require('md/docs/json_schemas/1.0.0/introduction');
const InstallationMarkdownV1 = require('md/docs/json_schemas/1.0.0/installation'); const InstallationMarkdownV1 = require('md/docs/json_schemas/1.0.0/installation');
const UsageMarkdownV1 = require('md/docs/json_schemas/1.0.0/usage'); const UsageMarkdownV1 = require('md/docs/json_schemas/1.0.0/usage');
const SchemasMarkdownV1 = require('md/docs/json_schemas/1.0.0/schemas'); const SchemasMarkdownV1 = require('md/docs/json_schemas/1.0.0/schemas');
const SchemasMarkdownV2 = require('md/docs/json_schemas/2.0.0/schemas');
/* tslint:enable:no-var-requires */ /* tslint:enable:no-var-requires */
const markdownSections = { const markdownSections = {
@ -43,6 +44,12 @@ const docsInfoConfig: DocsInfoConfig = {
[markdownSections.schemas]: SchemasMarkdownV1, [markdownSections.schemas]: SchemasMarkdownV1,
[markdownSections.usage]: UsageMarkdownV1, [markdownSections.usage]: UsageMarkdownV1,
}, },
'1.0.0': {
[markdownSections.introduction]: IntroMarkdownV1,
[markdownSections.installation]: InstallationMarkdownV1,
[markdownSections.schemas]: SchemasMarkdownV2,
[markdownSections.usage]: UsageMarkdownV1,
},
}, },
markdownSections, markdownSections,
}; };

View File

@ -524,6 +524,20 @@
ethers "3.0.22" ethers "3.0.22"
lodash "4.17.10" lodash "4.17.10"
"@0xproject/connect@1.0.4":
version "1.0.4"
resolved "https://registry.npmjs.org/@0xproject/connect/-/connect-1.0.4.tgz#1d5f27f4aa69920dde50178ed7179240f81d1c54"
dependencies:
"@0xproject/assert" "^0.2.14"
"@0xproject/json-schemas" "^0.8.3"
"@0xproject/types" "^0.8.2"
"@0xproject/typescript-typings" "^1.0.3"
"@0xproject/utils" "^1.0.4"
lodash "^4.17.4"
query-string "^5.0.1"
sinon "^4.0.0"
websocket "^1.0.25"
"@0xproject/contract-wrappers@^0.0.5": "@0xproject/contract-wrappers@^0.0.5":
version "0.0.5" version "0.0.5"
resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-0.0.5.tgz#36f91afb7de51478f9d2e4e0a3c4e685b3d18f28" resolved "https://registry.yarnpkg.com/@0xproject/contract-wrappers/-/contract-wrappers-0.0.5.tgz#36f91afb7de51478f9d2e4e0a3c4e685b3d18f28"
@ -1237,7 +1251,7 @@
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d"
"@types/uuid@^3.4.2": "@types/uuid@^3.4.2", "@types/uuid@^3.4.3":
version "3.4.3" version "3.4.3"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754"
dependencies: dependencies: