aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/abi-gen/CHANGELOG.md1
-rw-r--r--packages/abi-gen/package.json2
-rw-r--r--packages/abi-gen/src/index.ts7
-rw-r--r--packages/abi-gen/src/utils.ts9
-rw-r--r--packages/connect/CHANGELOG.md4
-rw-r--r--packages/connect/src/http_client.ts42
-rw-r--r--packages/connect/src/utils/orderbook_channel_message_parser.ts (renamed from packages/connect/src/utils/orderbook_channel_message_parsers.ts)18
-rw-r--r--packages/connect/src/utils/relayer_response_json_parsers.ts37
-rw-r--r--packages/connect/src/utils/type_converters.ts27
-rw-r--r--packages/connect/src/ws_orderbook_channel.ts4
-rw-r--r--packages/connect/test/http_client_test.ts12
-rw-r--r--packages/connect/test/orderbook_channel_message_parsers_test.ts21
-rw-r--r--yarn.lock6
13 files changed, 124 insertions, 66 deletions
diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md
index 700cfb549..983f7a3d1 100644
--- a/packages/abi-gen/CHANGELOG.md
+++ b/packages/abi-gen/CHANGELOG.md
@@ -4,3 +4,4 @@
* Fixed array typings with union types (#295)
* Add event ABIs to context data passed to templates (#302)
+* Add constructor ABIs to context data passed to templates (#304) \ No newline at end of file
diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json
index e2307bccd..b42a7fbce 100644
--- a/packages/abi-gen/package.json
+++ b/packages/abi-gen/package.json
@@ -43,6 +43,6 @@
"shx": "^0.2.2",
"tslint": "5.8.0",
"typescript": "~2.6.1",
- "web3-typescript-typings": "^0.7.2"
+ "web3-typescript-typings": "^0.9.0"
}
}
diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts
index ed71087b2..527af32b1 100644
--- a/packages/abi-gen/src/index.ts
+++ b/packages/abi-gen/src/index.ts
@@ -14,6 +14,7 @@ import * as Web3 from 'web3';
import { ContextData, ParamKind } from './types';
import { utils } from './utils';
+const ABI_TYPE_CONSTRUCTOR = 'constructor';
const ABI_TYPE_METHOD = 'function';
const ABI_TYPE_EVENT = 'event';
const MAIN_TEMPLATE_NAME = 'contract.mustache';
@@ -75,6 +76,11 @@ for (const abiFileName of abiFileNames) {
process.exit(1);
}
+ let ctor = ABI.find((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_CONSTRUCTOR) as Web3.ConstructorAbi;
+ if (_.isUndefined(ctor)) {
+ ctor = utils.getEmptyConstructor(); // The constructor exists, but it's implicit in JSON's ABI definition
+ }
+
const methodAbis = ABI.filter((abi: Web3.AbiDefinition) => abi.type === ABI_TYPE_METHOD) as Web3.MethodAbi[];
const methodsData = _.map(methodAbis, methodAbi => {
_.map(methodAbi.inputs, input => {
@@ -95,6 +101,7 @@ for (const abiFileName of abiFileNames) {
const contextData = {
contractName: namedContent.name,
+ ctor,
methods: methodsData,
events: eventAbis,
};
diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts
index edda016b5..f6291d98d 100644
--- a/packages/abi-gen/src/utils.ts
+++ b/packages/abi-gen/src/utils.ts
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as _ from 'lodash';
import * as path from 'path';
+import * as Web3 from 'web3';
import { ParamKind } from './types';
@@ -61,4 +62,12 @@ export const utils = {
throw new Error(`Failed to read ${filename}: ${err}`);
}
},
+ getEmptyConstructor(): Web3.ConstructorAbi {
+ return {
+ type: 'constructor',
+ stateMutability: 'nonpayable',
+ payable: false,
+ inputs: [],
+ };
+ },
};
diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md
index 87e9090d5..a0bc68a0b 100644
--- a/packages/connect/CHANGELOG.md
+++ b/packages/connect/CHANGELOG.md
@@ -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)
diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts
index 06b5d8ace..5604a9607 100644
--- a/packages/connect/src/http_client.ts
+++ b/packages/connect/src/http_client.ts
@@ -18,7 +18,7 @@ import {
TokenPairsItem,
TokenPairsRequest,
} from './types';
-import { typeConverters } from './utils/type_converters';
+import { relayerResponseJsonParsers } from './utils/relayer_response_json_parsers';
/**
* This class includes all the functionality related to interacting with a set of HTTP endpoints
@@ -48,16 +48,8 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const tokenPairs = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts);
- assert.doesConformToSchema('tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
- _.each(tokenPairs, (tokenPair: object) => {
- typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
- 'tokenA.minAmount',
- 'tokenA.maxAmount',
- 'tokenB.minAmount',
- 'tokenB.maxAmount',
- ]);
- });
+ const responseJson = await this._requestAsync('/token_pairs', HttpRequestType.Get, requestOpts);
+ const tokenPairs = relayerResponseJsonParsers.parseTokenPairsJson(responseJson);
return tokenPairs;
}
/**
@@ -72,9 +64,8 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const orders = await this._requestAsync(`/orders`, HttpRequestType.Get, requestOpts);
- assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
- _.each(orders, (order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
+ const responseJson = await this._requestAsync(`/orders`, HttpRequestType.Get, requestOpts);
+ const orders = relayerResponseJsonParsers.parseOrdersJson(responseJson);
return orders;
}
/**
@@ -84,9 +75,8 @@ export class HttpClient implements Client {
*/
public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const order = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get);
- assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
- typeConverters.convertOrderStringFieldsToBigNumber(order);
+ const responseJson = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get);
+ const order = relayerResponseJsonParsers.parseOrderJson(responseJson);
return order;
}
/**
@@ -99,10 +89,9 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const orderBook = await this._requestAsync('/orderbook', HttpRequestType.Get, requestOpts);
- assert.doesConformToSchema('orderBook', orderBook, schemas.relayerApiOrderBookResponseSchema);
- typeConverters.convertOrderbookStringFieldsToBigNumber(orderBook);
- return orderBook;
+ const responseJson = await this._requestAsync('/orderbook', HttpRequestType.Get, requestOpts);
+ const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(responseJson);
+ return orderbook;
}
/**
* Retrieve fee information from the API
@@ -111,18 +100,11 @@ export class HttpClient implements Client {
*/
public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
- typeConverters.convertBigNumberFieldsToStrings(request, [
- 'makerTokenAmount',
- 'takerTokenAmount',
- 'expirationUnixTimestampSec',
- 'salt',
- ]);
const requestOpts = {
payload: request,
};
- const fees = await this._requestAsync('/fees', HttpRequestType.Post, requestOpts);
- assert.doesConformToSchema('fees', fees, schemas.relayerApiFeesResponseSchema);
- typeConverters.convertStringsFieldsToBigNumbers(fees, ['makerFee', 'takerFee']);
+ const responseJson = await this._requestAsync('/fees', HttpRequestType.Post, requestOpts);
+ const fees = relayerResponseJsonParsers.parseFeesResponseJson(responseJson);
return fees;
}
/**
diff --git a/packages/connect/src/utils/orderbook_channel_message_parsers.ts b/packages/connect/src/utils/orderbook_channel_message_parser.ts
index a4f22dc4b..9a9ca8901 100644
--- a/packages/connect/src/utils/orderbook_channel_message_parsers.ts
+++ b/packages/connect/src/utils/orderbook_channel_message_parser.ts
@@ -4,10 +4,10 @@ import * as _ from 'lodash';
import { OrderbookChannelMessage, OrderbookChannelMessageTypes } from '../types';
-import { typeConverters } from './type_converters';
+import { relayerResponseJsonParsers } from './relayer_response_json_parsers';
-export const orderbookChannelMessageParsers = {
- parser(utf8Data: string): OrderbookChannelMessage {
+export const orderbookChannelMessageParser = {
+ parse(utf8Data: string): OrderbookChannelMessage {
const messageObj = JSON.parse(utf8Data);
const type: string = _.get(messageObj, 'type');
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
@@ -15,15 +15,15 @@ export const orderbookChannelMessageParsers = {
switch (type) {
case OrderbookChannelMessageTypes.Snapshot: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
- const orderbook = messageObj.payload;
- typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook);
- return messageObj;
+ const orderbookJson = messageObj.payload;
+ const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
+ return _.assign(messageObj, { payload: orderbook });
}
case OrderbookChannelMessageTypes.Update: {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
- const order = messageObj.payload;
- typeConverters.convertOrderStringFieldsToBigNumber(order);
- return messageObj;
+ const orderJson = messageObj.payload;
+ const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
+ return _.assign(messageObj, { payload: order });
}
default: {
return {
diff --git a/packages/connect/src/utils/relayer_response_json_parsers.ts b/packages/connect/src/utils/relayer_response_json_parsers.ts
new file mode 100644
index 000000000..668461bf4
--- /dev/null
+++ b/packages/connect/src/utils/relayer_response_json_parsers.ts
@@ -0,0 +1,37 @@
+import { assert } from '@0xproject/assert';
+import { schemas } from '@0xproject/json-schemas';
+import * as _ from 'lodash';
+
+import { FeesResponse, OrderbookResponse, SignedOrder, TokenPairsItem } from '../types';
+
+import { typeConverters } from './type_converters';
+
+export const relayerResponseJsonParsers = {
+ parseTokenPairsJson(json: any): TokenPairsItem[] {
+ assert.doesConformToSchema('tokenPairs', json, schemas.relayerApiTokenPairsResponseSchema);
+ return json.map((tokenPair: any) => {
+ return typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
+ 'tokenA.minAmount',
+ 'tokenA.maxAmount',
+ 'tokenB.minAmount',
+ 'tokenB.maxAmount',
+ ]);
+ });
+ },
+ parseOrdersJson(json: any): SignedOrder[] {
+ assert.doesConformToSchema('orders', json, schemas.signedOrdersSchema);
+ return json.map((order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
+ },
+ parseOrderJson(json: any): SignedOrder {
+ assert.doesConformToSchema('order', json, schemas.signedOrderSchema);
+ return typeConverters.convertOrderStringFieldsToBigNumber(json);
+ },
+ parseOrderbookResponseJson(json: any): OrderbookResponse {
+ assert.doesConformToSchema('orderBook', json, schemas.relayerApiOrderBookResponseSchema);
+ return typeConverters.convertOrderbookStringFieldsToBigNumber(json);
+ },
+ parseFeesResponseJson(json: any): FeesResponse {
+ assert.doesConformToSchema('fees', json, schemas.relayerApiFeesResponseSchema);
+ return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']);
+ },
+};
diff --git a/packages/connect/src/utils/type_converters.ts b/packages/connect/src/utils/type_converters.ts
index 9d20154fd..c1808ce8a 100644
--- a/packages/connect/src/utils/type_converters.ts
+++ b/packages/connect/src/utils/type_converters.ts
@@ -1,15 +1,17 @@
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
-// TODO: convert all of these to non-mutating, pure functions
export const typeConverters = {
- convertOrderbookStringFieldsToBigNumber(orderbook: object): void {
- _.each(orderbook, (orders: object[]) => {
- _.each(orders, (order: object) => this.convertOrderStringFieldsToBigNumber(order));
- });
+ convertOrderbookStringFieldsToBigNumber(orderbook: any): any {
+ const bids = _.get(orderbook, 'bids', []);
+ const asks = _.get(orderbook, 'asks', []);
+ return {
+ bids: bids.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ asks: asks.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ };
},
- convertOrderStringFieldsToBigNumber(order: object): void {
- this.convertStringsFieldsToBigNumbers(order, [
+ convertOrderStringFieldsToBigNumber(order: any): any {
+ return this.convertStringsFieldsToBigNumbers(order, [
'makerTokenAmount',
'takerTokenAmount',
'makerFee',
@@ -18,14 +20,11 @@ export const typeConverters = {
'salt',
]);
},
- convertBigNumberFieldsToStrings(obj: object, fields: string[]): void {
- _.each(fields, field => {
- _.update(obj, field, (value: BigNumber) => value.toString());
- });
- },
- convertStringsFieldsToBigNumbers(obj: object, fields: string[]): void {
+ convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
+ const result = _.assign({}, obj);
_.each(fields, field => {
- _.update(obj, field, (value: string) => new BigNumber(value));
+ _.update(result, field, (value: string) => new BigNumber(value));
});
+ return result;
},
};
diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts
index 399f97319..822a022f4 100644
--- a/packages/connect/src/ws_orderbook_channel.ts
+++ b/packages/connect/src/ws_orderbook_channel.ts
@@ -11,7 +11,7 @@ import {
WebsocketClientEventType,
WebsocketConnectionEventType,
} from './types';
-import { orderbookChannelMessageParsers } from './utils/orderbook_channel_message_parsers';
+import { orderbookChannelMessageParser } from './utils/orderbook_channel_message_parser';
/**
* This class includes all the functionality related to interacting with a websocket endpoint
@@ -104,7 +104,7 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
if (!_.isUndefined(message.utf8Data)) {
try {
const utf8Data = message.utf8Data;
- const parserResult = orderbookChannelMessageParsers.parser(utf8Data);
+ const parserResult = orderbookChannelMessageParser.parse(utf8Data);
if (parserResult.requestId === requestId) {
switch (parserResult.type) {
case OrderbookChannelMessageTypes.Snapshot: {
diff --git a/packages/connect/test/http_client_test.ts b/packages/connect/test/http_client_test.ts
index a0ab000e2..e7096edad 100644
--- a/packages/connect/test/http_client_test.ts
+++ b/packages/connect/test/http_client_test.ts
@@ -122,6 +122,18 @@ describe('HttpClient', () => {
const fees = await relayerClient.getFeesAsync(request);
expect(fees).to.be.deep.equal(feesResponse);
});
+ it('does not mutate input', async () => {
+ fetchMock.post(url, feesResponseJSON);
+ const makerTokenAmountBefore = new BigNumber(request.makerTokenAmount);
+ const takerTokenAmountBefore = new BigNumber(request.takerTokenAmount);
+ const saltBefore = new BigNumber(request.salt);
+ const expirationUnixTimestampSecBefore = new BigNumber(request.expirationUnixTimestampSec);
+ await relayerClient.getFeesAsync(request);
+ expect(makerTokenAmountBefore).to.be.deep.equal(request.makerTokenAmount);
+ expect(takerTokenAmountBefore).to.be.deep.equal(request.takerTokenAmount);
+ 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 () => {
fetchMock.post(url, { test: 'dummy' });
expect(relayerClient.getFeesAsync(request)).to.be.rejected();
diff --git a/packages/connect/test/orderbook_channel_message_parsers_test.ts b/packages/connect/test/orderbook_channel_message_parsers_test.ts
index e6cc05fed..3e1f44384 100644
--- a/packages/connect/test/orderbook_channel_message_parsers_test.ts
+++ b/packages/connect/test/orderbook_channel_message_parsers_test.ts
@@ -2,7 +2,7 @@ import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import 'mocha';
-import { orderbookChannelMessageParsers } from '../src/utils/orderbook_channel_message_parsers';
+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';
@@ -20,20 +20,20 @@ chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
-describe('orderbookChannelMessageParsers', () => {
+describe('orderbookChannelMessageParser', () => {
describe('#parser', () => {
it('parses snapshot messages', () => {
- const snapshotMessage = orderbookChannelMessageParsers.parser(snapshotOrderbookChannelMessage);
+ 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 = orderbookChannelMessageParsers.parser(updateOrderbookChannelMessage);
+ 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 = orderbookChannelMessageParsers.parser(unknownOrderbookChannelMessage);
+ const unknownMessage = orderbookChannelMessageParser.parse(unknownOrderbookChannelMessage);
expect(unknownMessage.type).to.be.equal('unknown');
expect(unknownMessage.payload).to.be.undefined();
});
@@ -43,7 +43,7 @@ describe('orderbookChannelMessageParsers', () => {
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParsers.parser(typelessMessage);
+ const badCall = () => orderbookChannelMessageParser.parse(typelessMessage);
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
});
it('throws when type is not a string', () => {
@@ -53,22 +53,23 @@ describe('orderbookChannelMessageParsers', () => {
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParsers.parser(messageWithBadType);
+ 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 = () => orderbookChannelMessageParsers.parser(malformedSnapshotOrderbookChannelMessage);
+ 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 = () => orderbookChannelMessageParsers.parser(malformedUpdateOrderbookChannelMessage);
+ 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 = () => orderbookChannelMessageParsers.parser(nonJsonString);
+ const badCall = () => orderbookChannelMessageParser.parse(nonJsonString);
expect(badCall).throws('Unexpected token h in JSON at position 0');
});
});
diff --git a/yarn.lock b/yarn.lock
index 1d3824de1..e7d64bbcc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9450,6 +9450,12 @@ web3-typescript-typings@^0.7.2:
dependencies:
bignumber.js "^4.0.2"
+web3-typescript-typings@^0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.9.0.tgz#c658db3c84427d9c05a93613e35e6d8147c931c4"
+ dependencies:
+ bignumber.js "^4.0.2"
+
web3-utils@^1.0.0-beta.26:
version "1.0.0-beta.26"
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.26.tgz#f04ad8c144b1781c6b20c2818e0532cb9e6dca15"