Merge branch 'development' into refactor/httpClientJsonParsing

* development: (21 commits)
  Update connect CHANGELOG
  Changes to abi-gen after code review
  Added constructor ABIs to abi-gen
  Describe #295 in a CHANGELOG
  Add #302 description to changelog
  Fix formatting
  Apply prettier config
  Install prettier
  Remove formatting esilnt rules
  sendTransactionAsync should return txHash string
  Added Event generation to abi-gen
  Publish
  Add dates to CHANGELOG entries
  Update subproviders CHANGELOG
  Support both personal_sign and eth_sign
  Fix Ledger tests given change from `personal_sign` to `eth_sign`
  Update subprovider to catch correct RPC method
  Rename guide
  Update contribution guide
  Fix broken links in the abi-gen README
  ...
This commit is contained in:
Brandon Millman
2018-01-08 18:02:41 -08:00
308 changed files with 6963 additions and 6318 deletions

View File

@@ -1,5 +1,9 @@
# CHANGELOG
vx.x.x
------------------------
* Prevent getFeesAsync method on HttpClient from mutating input (#296)
v0.3.0 - _December 8, 2017_
------------------------
* Expose WebSocketOrderbookChannel and associated types to public interface (#251)

View File

@@ -1,6 +1,6 @@
{
"name": "@0xproject/connect",
"version": "0.3.1",
"version": "0.3.2",
"description": "A javascript library for interacting with the standard relayer api",
"keywords": [
"connect",
@@ -36,9 +36,9 @@
},
"homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
"dependencies": {
"@0xproject/assert": "^0.0.8",
"@0xproject/json-schemas": "^0.7.0",
"@0xproject/utils": "^0.1.1",
"@0xproject/assert": "^0.0.9",
"@0xproject/json-schemas": "^0.7.1",
"@0xproject/utils": "^0.1.2",
"bignumber.js": "~4.1.0",
"isomorphic-fetch": "^2.2.1",
"lodash": "^4.17.4",
@@ -46,7 +46,7 @@
"websocket": "^1.0.25"
},
"devDependencies": {
"@0xproject/tslint-config": "^0.3.0",
"@0xproject/tslint-config": "^0.4.0",
"@types/fetch-mock": "^5.12.1",
"@types/lodash": "^4.14.86",
"@types/mocha": "^2.2.42",

View File

@@ -1,10 +1,10 @@
import {assert} from '@0xproject/assert';
import {schemas} from '@0xproject/json-schemas';
import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas';
import 'isomorphic-fetch';
import * as _ from 'lodash';
import * as queryString from 'query-string';
import {schemas as clientSchemas} from './schemas/schemas';
import { schemas as clientSchemas } from './schemas/schemas';
import {
Client,
FeesRequest,
@@ -18,7 +18,7 @@ import {
TokenPairsItem,
TokenPairsRequest,
} from './types';
import {relayerResponseJsonParsers} from './utils/relayer_response_json_parsers';
import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers';
/**
* This class includes all the functionality related to interacting with a set of HTTP endpoints

View File

@@ -1,10 +1,10 @@
import {bigNumberConfigs} from '@0xproject/utils';
import { bigNumberConfigs } from '@0xproject/utils';
// Customize our BigNumber instances
bigNumberConfigs.configure();
export {HttpClient} from './http_client';
export {WebSocketOrderbookChannel} from './ws_orderbook_channel';
export { HttpClient } from './http_client';
export { WebSocketOrderbookChannel } from './ws_orderbook_channel';
export {
Client,
ECSignature,

View File

@@ -2,7 +2,7 @@ export const relayerOrderBookRequestSchema = {
id: '/RelayerOrderBookRequest',
type: 'object',
properties: {
baseTokenAddress: {$ref: '/Address'},
quoteTokenAddress: {$ref: '/Address'},
baseTokenAddress: { $ref: '/Address' },
quoteTokenAddress: { $ref: '/Address' },
},
};

View File

@@ -2,7 +2,7 @@ export const relayerOrderBookRequestSchema = {
id: '/RelayerOrderBookRequest',
type: 'object',
properties: {
baseTokenAddress: {$ref: '/Address'},
quoteTokenAddress: {$ref: '/Address'},
baseTokenAddress: { $ref: '/Address' },
quoteTokenAddress: { $ref: '/Address' },
},
};

View File

@@ -2,15 +2,15 @@ export const relayerOrdersRequestSchema = {
id: '/RelayerOrdersRequest',
type: 'object',
properties: {
exchangeContractAddress: {$ref: '/Address'},
tokenAddress: {$ref: '/Address'},
makerTokenAddress: {$ref: '/Address'},
takerTokenAddress: {$ref: '/Address'},
tokenA: {$ref: '/Address'},
tokenB: {$ref: '/Address'},
maker: {$ref: '/Address'},
taker: {$ref: '/Address'},
trader: {$ref: '/Address'},
feeRecipient: {$ref: '/Address'},
exchangeContractAddress: { $ref: '/Address' },
tokenAddress: { $ref: '/Address' },
makerTokenAddress: { $ref: '/Address' },
takerTokenAddress: { $ref: '/Address' },
tokenA: { $ref: '/Address' },
tokenB: { $ref: '/Address' },
maker: { $ref: '/Address' },
taker: { $ref: '/Address' },
trader: { $ref: '/Address' },
feeRecipient: { $ref: '/Address' },
},
};

View File

@@ -2,7 +2,7 @@ export const relayerTokenPairsRequestSchema = {
id: '/RelayerTokenPairsRequest',
type: 'object',
properties: {
tokenA: {$ref: '/Address'},
tokenB: {$ref: '/Address'},
tokenA: { $ref: '/Address' },
tokenB: { $ref: '/Address' },
},
};

View File

@@ -1,12 +1,6 @@
import {
relayerOrderBookRequestSchema,
} from './relayer_orderbook_request_schema';
import {
relayerOrdersRequestSchema,
} from './relayer_orders_request_schema';
import {
relayerTokenPairsRequestSchema,
} from './relayer_token_pairs_request_schema';
import { relayerOrderBookRequestSchema } from './relayer_orderbook_request_schema';
import { relayerOrdersRequestSchema } from './relayer_orders_request_schema';
import { relayerTokenPairsRequestSchema } from './relayer_token_pairs_request_schema';
export const schemas = {
relayerOrderBookRequestSchema,

View File

@@ -1,4 +1,4 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
// TODO: Consolidate Order, SignedOrder and ECSignature into a shared package instead of duplicating them from 0x.js
export interface Order {
@@ -57,19 +57,24 @@ export interface OrderbookChannelSubscriptionOpts {
}
export interface OrderbookChannelHandler {
onSnapshot: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts,
snapshot: OrderbookResponse) => void;
onUpdate: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts,
order: SignedOrder) => void;
onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts,
err: Error) => void;
onSnapshot: (
channel: OrderbookChannel,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
snapshot: OrderbookResponse,
) => void;
onUpdate: (
channel: OrderbookChannel,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
order: SignedOrder,
) => void;
onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, err: Error) => void;
onClose: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts) => void;
}
export type OrderbookChannelMessage =
SnapshotOrderbookChannelMessage |
UpdateOrderbookChannelMessage |
UnknownOrderbookChannelMessage;
| SnapshotOrderbookChannelMessage
| UpdateOrderbookChannelMessage
| UnknownOrderbookChannelMessage;
export enum OrderbookChannelMessageTypes {
Snapshot = 'snapshot',

View File

@@ -1,13 +1,10 @@
import {assert} from '@0xproject/assert';
import {schemas} from '@0xproject/json-schemas';
import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash';
import {
OrderbookChannelMessage,
OrderbookChannelMessageTypes,
} from '../types';
import { OrderbookChannelMessage, OrderbookChannelMessageTypes } from '../types';
import {relayerResponseJsonParsers} from './relayer_response_json_parsers';
import { relayerResponseJsonParsers } from './relayer_response_json_parsers';
export const orderbookChannelMessageParser = {
parse(utf8Data: string): OrderbookChannelMessage {
@@ -16,17 +13,17 @@ export const orderbookChannelMessageParser = {
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
assert.isString('type', type);
switch (type) {
case (OrderbookChannelMessageTypes.Snapshot): {
case OrderbookChannelMessageTypes.Snapshot: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
const orderbookJson = messageObj.payload;
const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
return _.assign(messageObj, {payload: orderbook});
return _.assign(messageObj, { payload: orderbook });
}
case (OrderbookChannelMessageTypes.Update): {
case OrderbookChannelMessageTypes.Update: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
const orderJson = messageObj.payload;
const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
return _.assign(messageObj, {payload: order});
return _.assign(messageObj, { payload: order });
}
default: {
return {

View File

@@ -1,4 +1,4 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
import * as _ from 'lodash';
export const typeConverters = {

View File

@@ -1,5 +1,5 @@
import {assert} from '@0xproject/assert';
import {schemas} from '@0xproject/json-schemas';
import { assert } from '@0xproject/assert';
import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash';
import * as WebSocket from 'websocket';
@@ -11,7 +11,7 @@ import {
WebsocketClientEventType,
WebsocketConnectionEventType,
} from './types';
import {orderbookChannelMessageParser} from './utils/orderbook_channel_message_parser';
import { orderbookChannelMessageParser } from './utils/orderbook_channel_message_parser';
/**
* This class includes all the functionality related to interacting with a websocket endpoint
@@ -41,7 +41,10 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
*/
public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
assert.doesConformToSchema(
'subscriptionOpts', subscriptionOpts, schemas.relayerApiOrderbookChannelSubscribePayload);
'subscriptionOpts',
subscriptionOpts,
schemas.relayerApiOrderbookChannelSubscribePayload,
);
assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
assert.isFunction('handler.onError', _.get(handler, 'onError'));
@@ -92,25 +95,32 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
this._client.connect(this._apiEndpointUrl);
}
}
private _handleWebSocketMessage(requestId: number, subscriptionOpts: OrderbookChannelSubscriptionOpts,
message: WebSocket.IMessage, handler: OrderbookChannelHandler): void {
private _handleWebSocketMessage(
requestId: number,
subscriptionOpts: OrderbookChannelSubscriptionOpts,
message: WebSocket.IMessage,
handler: OrderbookChannelHandler,
): void {
if (!_.isUndefined(message.utf8Data)) {
try {
const utf8Data = message.utf8Data;
const parserResult = orderbookChannelMessageParser.parse(utf8Data);
if (parserResult.requestId === requestId) {
switch (parserResult.type) {
case (OrderbookChannelMessageTypes.Snapshot): {
case OrderbookChannelMessageTypes.Snapshot: {
handler.onSnapshot(this, subscriptionOpts, parserResult.payload);
break;
}
case (OrderbookChannelMessageTypes.Update): {
case OrderbookChannelMessageTypes.Update: {
handler.onUpdate(this, subscriptionOpts, parserResult.payload);
break;
}
default: {
handler.onError(
this, subscriptionOpts, new Error(`Message has missing a type parameter: ${utf8Data}`));
this,
subscriptionOpts,
new Error(`Message has missing a type parameter: ${utf8Data}`),
);
}
}
}

View File

@@ -1,6 +1,6 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
import {FeesResponse} from '../../../src/types';
import { FeesResponse } from '../../../src/types';
export const feesResponse: FeesResponse = {
feeRecipient: '0x323b5d4c32345ced77393b3530b1eed0f346429d',

View File

@@ -1,4 +1,4 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
export const orderResponse = {
maker: '0x9e56625509c2f60af937f23b7b532600390e8c8b',

View File

@@ -1,4 +1,4 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
export const orderbookResponse = {
bids: [

View File

@@ -1,4 +1,4 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
export const ordersResponse = [
{

View File

@@ -1,6 +1,6 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
import {TokenPairsItem} from '../../../src/types';
import { TokenPairsItem } from '../../../src/types';
export const tokenPairsResponse: TokenPairsItem[] = [
{

View File

@@ -1,24 +1,21 @@
import {BigNumber} from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as dirtyChai from 'dirty-chai';
import * as fetchMock from 'fetch-mock';
import 'mocha';
import {HttpClient} from '../src/index';
import { HttpClient } from '../src/index';
import {feesResponse} from './fixtures/standard_relayer_api/fees';
import { feesResponse } from './fixtures/standard_relayer_api/fees';
import * as feesResponseJSON from './fixtures/standard_relayer_api/fees.json';
import {
orderResponse,
} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
// tslint:disable-next-line:max-line-length
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import * as orderResponseJSON from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.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 {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 {tokenPairsResponse} from './fixtures/standard_relayer_api/token_pairs';
import { tokenPairsResponse } from './fixtures/standard_relayer_api/token_pairs';
import * as tokenPairsResponseJSON from './fixtures/standard_relayer_api/token_pairs.json';
chai.config.includeStack = true;
@@ -50,7 +47,7 @@ describe('HttpClient', () => {
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
});
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();
});
});
@@ -72,7 +69,7 @@ describe('HttpClient', () => {
expect(orders).to.be.deep.equal(ordersResponse);
});
it('throws an error for invalid JSON response', async () => {
fetchMock.get(url, {test: 'dummy'});
fetchMock.get(url, { test: 'dummy' });
expect(relayerClient.getOrdersAsync()).to.be.rejected();
});
});
@@ -85,7 +82,7 @@ describe('HttpClient', () => {
expect(order).to.be.deep.equal(orderResponse);
});
it('throws an error for invalid JSON response', async () => {
fetchMock.get(url, {test: 'dummy'});
fetchMock.get(url, { test: 'dummy' });
expect(relayerClient.getOrderAsync(orderHash)).to.be.rejected();
});
});
@@ -94,15 +91,16 @@ describe('HttpClient', () => {
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteTokenAddress: '0xa2b31dacf30a9c50ca473337c01d8a201ae33e32',
};
// tslint:disable-next-line:max-line-length
const url = `${relayUrl}/v0/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${request.quoteTokenAddress}`;
const url = `${relayUrl}/v0/orderbook?baseTokenAddress=${request.baseTokenAddress}&quoteTokenAddress=${
request.quoteTokenAddress
}`;
it('gets order book', async () => {
fetchMock.get(url, orderbookJSON);
const orderbook = await relayerClient.getOrderbookAsync(request);
expect(orderbook).to.be.deep.equal(orderbookResponse);
});
it('throws an error for invalid JSON response', async () => {
fetchMock.get(url, {test: 'dummy'});
fetchMock.get(url, { test: 'dummy' });
expect(relayerClient.getOrderbookAsync(request)).to.be.rejected();
});
});
@@ -137,7 +135,7 @@ describe('HttpClient', () => {
expect(expirationUnixTimestampSecBefore).to.be.deep.equal(request.expirationUnixTimestampSec);
});
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();
});
});

View File

@@ -2,16 +2,15 @@ import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import 'mocha';
import {orderbookChannelMessageParser} from '../src/utils/orderbook_channel_message_parser';
import { orderbookChannelMessageParser } from '../src/utils/orderbook_channel_message_parser';
// tslint:disable-next-line:max-line-length
import {orderResponse} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
import {orderbookResponse} from './fixtures/standard_relayer_api/orderbook';
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 { unknownOrderbookChannelMessage } from './fixtures/standard_relayer_api/unknown_orderbook_channel_message';
import {
malformedUpdateOrderbookChannelMessage,
updateOrderbookChannelMessage,

View File

@@ -3,9 +3,7 @@ import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash';
import 'mocha';
import {
WebSocketOrderbookChannel,
} from '../src/ws_orderbook_channel';
import { WebSocketOrderbookChannel } from '../src/ws_orderbook_channel';
chai.config.includeStack = true;
chai.use(dirtyChai);
@@ -21,26 +19,42 @@ describe('WebSocketOrderbookChannel', () => {
limit: 100,
};
const emptyOrderbookChannelHandler = {
onSnapshot: () => { _.noop(); },
onUpdate: () => { _.noop(); },
onError: () => { _.noop(); },
onClose: () => { _.noop(); },
onSnapshot: () => {
_.noop();
},
onUpdate: () => {
_.noop();
},
onError: () => {
_.noop();
},
onClose: () => {
_.noop();
},
};
describe('#subscribe', () => {
it('throws when subscriptionOpts does not conform to schema', () => {
const badSubscribeCall = orderbookChannel.subscribe.bind(
orderbookChannel, {}, emptyOrderbookChannelHandler);
// tslint:disable-next-line:max-line-length
expect(badSubscribeCall).throws('Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"');
orderbookChannel,
{},
emptyOrderbookChannelHandler,
);
expect(badSubscribeCall).throws(
'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
);
});
it('throws when handler has the incorrect members', () => {
const badSubscribeCall = orderbookChannel.subscribe.bind(orderbookChannel, subscriptionOpts, {});
expect(badSubscribeCall)
.throws('Expected handler.onSnapshot to be of type function, encountered: undefined');
expect(badSubscribeCall).throws(
'Expected handler.onSnapshot to be of type function, encountered: undefined',
);
});
it('does not throw when inputs are of correct types', () => {
const goodSubscribeCall = orderbookChannel.subscribe.bind(
orderbookChannel, subscriptionOpts, emptyOrderbookChannelHandler);
orderbookChannel,
subscriptionOpts,
emptyOrderbookChannelHandler,
);
expect(goodSubscribeCall).to.not.throw();
});
});